diff --git a/DEPS b/DEPS
index 1e46bf2..40e14f4 100644
--- a/DEPS
+++ b/DEPS
@@ -105,7 +105,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '767fddfcce612c1618c1b6f6dbac4d88565f0b13',
+  'skia_revision': 'cd8689f0042727666e468dc18bdb4d65b02b6fd9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -117,7 +117,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': '3fd614d06e50fb98d1434fabf31e8583516ad5c1',
+  'angle_revision': '6e5bf36ff50f7976463c3c32151ece044e802284',
   # 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.
@@ -129,7 +129,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': '3e360453cded5f2f435195923ede0935f6847194',
+  'pdfium_revision': '5f4cd74d2693e23b27237b935f28a87611e25e6b',
   # 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.
@@ -165,7 +165,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '5314945fa47aafd1edb930ace2590bdc7c874632',
+  'catapult_revision': 'ed63b1319414a36b099cad8443d497bda8f085a2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -552,7 +552,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '937745b816068ad929ea185686c1627fe1b8b220',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a82d0a22067f72fda9d72c3679f108a9c1550407',
       'condition': 'checkout_linux',
   },
 
@@ -567,7 +567,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + 'c0be499563b84ec733af9fea34b6ae74d9667003',
+      'url': Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + '71adbf6a56865ae5500686eaa221b0f988c3bada',
       'condition': 'checkout_linux',
   },
 
@@ -577,7 +577,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '3d429cf5131a10f0e6ced89e8809820398ece78b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'efb38bb3d729f9d5b27f5202c766d43186c0103a',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -832,7 +832,7 @@
     Var('chromium_git') + '/webm/libwebm.git' + '@' + '01c1d1d76f139345c442bfc8e61b4e1cba809059',
 
   'src/third_party/libyuv':
-    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '55f5d91f11f929c4c59c32621c3d5457cca3ab0b',  # from r1714
+    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + 'd694f0a82b4da9d8ea37e6c453b7a34947eb5790',  # from r1714
 
   'src/third_party/lighttpd': {
       'url': Var('chromium_git') + '/chromium/deps/lighttpd.git' + '@' + Var('lighttpd_revision'),
@@ -1049,7 +1049,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7ca87fb1d3da3b3d2060886e8c58e726d74c8219',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'e9721f2f08307e67c602a3c614ec144c5fb7e5c2',
+    Var('webrtc_git') + '/src.git' + '@' + '74ed734d717a3d3f47f393ab0f8593c7f6a696d6',
 
   '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 1274976..6d3147b 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -333,6 +333,7 @@
     "sticky_keys/sticky_keys_state.h",
     "system/accessibility/dictation_button_tray.h",
     "system/accessibility/select_to_speak_tray.h",
+    "system/accessibility/select_to_speak_tray_utils.h",
     "system/audio/audio_detailed_view.h",
     "system/audio/display_speaker_controller.h",
     "system/audio/tray_audio.h",
@@ -970,6 +971,7 @@
     "sticky_keys/sticky_keys_overlay.cc",
     "system/accessibility/dictation_button_tray.cc",
     "system/accessibility/select_to_speak_tray.cc",
+    "system/accessibility/select_to_speak_tray_utils.cc",
     "system/audio/audio_detailed_view.cc",
     "system/audio/display_speaker_controller.cc",
     "system/audio/tray_audio.cc",
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc
index ae467fe..1e9825b 100644
--- a/ash/accessibility/accessibility_controller.cc
+++ b/ash/accessibility/accessibility_controller.cc
@@ -222,6 +222,18 @@
   message_center->AddNotification(std::move(notification));
 }
 
+AccessibilityPanelLayoutManager* GetLayoutManager() {
+  // The accessibility panel is only shown on the primary display.
+  aura::Window* root = Shell::GetPrimaryRootWindow();
+  aura::Window* container =
+      Shell::GetContainer(root, kShellWindowId_AccessibilityPanelContainer);
+  // TODO(jamescook): Avoid this cast by moving ash::AccessibilityObserver
+  // ownership to this class and notifying it on accessibility panel fullscreen
+  // updates.
+  return static_cast<AccessibilityPanelLayoutManager*>(
+      container->layout_manager());
+}
+
 }  // namespace
 
 AccessibilityController::AccessibilityController(
@@ -700,17 +712,15 @@
   accessibility_highlight_controller_->SetCaretBounds(bounds_in_screen);
 }
 
-void AccessibilityController::SetAccessibilityPanelFullscreen(bool fullscreen) {
-  // The accessibility panel is only shown on the primary display.
-  aura::Window* root = Shell::GetPrimaryRootWindow();
-  aura::Window* container =
-      Shell::GetContainer(root, kShellWindowId_AccessibilityPanelContainer);
-  // TODO(jamescook): Avoid this cast by moving ash::AccessibilityObserver
-  // ownership to this class and notifying it on ChromeVox fullscreen updates.
-  AccessibilityPanelLayoutManager* layout =
-      static_cast<AccessibilityPanelLayoutManager*>(
-          container->layout_manager());
-  layout->SetPanelFullscreen(fullscreen);
+void AccessibilityController::SetAccessibilityPanelAlwaysVisible(
+    bool always_visible) {
+  GetLayoutManager()->SetAlwaysVisible(always_visible);
+}
+
+void AccessibilityController::SetAccessibilityPanelBounds(
+    const gfx::Rect& bounds,
+    mojom::AccessibilityPanelState state) {
+  GetLayoutManager()->SetPanelBounds(bounds, state);
 }
 
 void AccessibilityController::OnSigninScreenPrefServiceInitialized(
diff --git a/ash/accessibility/accessibility_controller.h b/ash/accessibility/accessibility_controller.h
index e1ad856..219d55d 100644
--- a/ash/accessibility/accessibility_controller.h
+++ b/ash/accessibility/accessibility_controller.h
@@ -163,7 +163,10 @@
   void BrailleDisplayStateChanged(bool connected) override;
   void SetFocusHighlightRect(const gfx::Rect& bounds_in_screen) override;
   void SetCaretBounds(const gfx::Rect& bounds_in_screen) override;
-  void SetAccessibilityPanelFullscreen(bool fullscreen) override;
+  void SetAccessibilityPanelAlwaysVisible(bool always_visible) override;
+  void SetAccessibilityPanelBounds(
+      const gfx::Rect& bounds,
+      mojom::AccessibilityPanelState state) override;
   void SetSelectToSpeakState(mojom::SelectToSpeakState state) override;
 
   // SessionObserver:
diff --git a/ash/accessibility/accessibility_panel_layout_manager.cc b/ash/accessibility/accessibility_panel_layout_manager.cc
index 91704ce..949b0b1 100644
--- a/ash/accessibility/accessibility_panel_layout_manager.cc
+++ b/ash/accessibility/accessibility_panel_layout_manager.cc
@@ -26,11 +26,20 @@
   display::Screen::GetScreen()->RemoveObserver(this);
 }
 
-void AccessibilityPanelLayoutManager::SetPanelFullscreen(bool fullscreen) {
-  panel_fullscreen_ = fullscreen;
+void AccessibilityPanelLayoutManager::SetAlwaysVisible(bool always_visible) {
+  always_visible_ = always_visible;
   UpdateWindowBounds();
 }
 
+void AccessibilityPanelLayoutManager::SetPanelBounds(
+    const gfx::Rect& bounds,
+    mojom::AccessibilityPanelState state) {
+  panel_bounds_ = bounds;
+  panel_state_ = state;
+  UpdateWindowBounds();
+  UpdateWorkArea();
+}
+
 void AccessibilityPanelLayoutManager::OnWindowAddedToLayout(
     aura::Window* child) {
   panel_window_ = child;
@@ -90,29 +99,44 @@
   RootWindowController* root_controller =
       RootWindowController::ForWindow(root_window);
 
-  // By default the panel sits at the top of the screen.
-  DCHECK(panel_window_->bounds().origin().IsOrigin());
-  gfx::Rect bounds(0, 0, root_window->bounds().width(), kPanelHeight);
+  gfx::Rect bounds = panel_bounds_;
 
   // The panel can make itself fill the screen (including covering the shelf).
-  if (panel_fullscreen_)
-    bounds.set_height(root_window->bounds().height());
+  if (panel_state_ == mojom::AccessibilityPanelState::FULLSCREEN) {
+    bounds = root_window->bounds();
+  } else if (panel_state_ == mojom::AccessibilityPanelState::FULL_WIDTH) {
+    bounds.set_x(0);
+    bounds.set_width(root_window->bounds().width());
+  }
 
   // If a fullscreen browser window is open, give the panel a height of 0
-  // unless it's active.
-  if (root_controller->GetWindowForFullscreenMode() &&
+  // unless it's active or always_visible_ is true.
+  if (!always_visible_ && root_controller->GetWindowForFullscreenMode() &&
       !::wm::IsActiveWindow(panel_window_)) {
     bounds.set_height(0);
   }
 
-  // Make sure the ChromeVox panel is always below the Docked Magnifier viewport
-  // so it shows up and gets magnified.
-  bounds.Offset(0, root_controller->shelf()->GetDockedMagnifierHeight());
+  // Make sure the accessibility panel is always below the Docked Magnifier
+  // viewport so it shows up and gets magnified.
+  int magnifier_height = root_controller->shelf()->GetDockedMagnifierHeight();
+  if (bounds.y() < magnifier_height)
+    bounds.Offset(0, magnifier_height);
+
+  // Make sure the accessibility panel doesn't go offscreen when the Docked
+  // Magnifier is on.
+  int screen_height = root_window->bounds().height();
+  int available_height = screen_height - magnifier_height;
+  if (bounds.height() > available_height)
+    bounds.set_height(available_height);
 
   panel_window_->SetBounds(bounds);
 }
 
 void AccessibilityPanelLayoutManager::UpdateWorkArea() {
+  if (panel_window_ && panel_window_->bounds().y() != 0)
+    return;
+  if (panel_state_ == mojom::AccessibilityPanelState::FULLSCREEN)
+    return;
   Shell::GetPrimaryRootWindowController()->shelf()->SetAccessibilityPanelHeight(
       panel_window_ ? panel_window_->bounds().height() : 0);
 }
diff --git a/ash/accessibility/accessibility_panel_layout_manager.h b/ash/accessibility/accessibility_panel_layout_manager.h
index f16e818..2c3a849d 100644
--- a/ash/accessibility/accessibility_panel_layout_manager.h
+++ b/ash/accessibility/accessibility_panel_layout_manager.h
@@ -6,10 +6,12 @@
 #define ASH_ACCESSIBILITY_ACCESSIBILITY_PANEL_LAYOUT_MANAGER_H_
 
 #include "ash/ash_export.h"
+#include "ash/public/interfaces/accessibility_controller.mojom.h"
 #include "ash/shell_observer.h"
 #include "base/macros.h"
 #include "ui/aura/layout_manager.h"
 #include "ui/display/display_observer.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/wm/public/activation_change_observer.h"
 
 namespace aura {
@@ -30,13 +32,15 @@
       public ash::ShellObserver {
  public:
   // Height of the panel in DIPs. Public for test.
-  static constexpr int kPanelHeight = 35;
+  static constexpr int kDefaultPanelHeight = 35;
 
   AccessibilityPanelLayoutManager();
   ~AccessibilityPanelLayoutManager() override;
 
-  // Sets whether the panel covers the entire display.
-  void SetPanelFullscreen(bool fullscreen);
+  // Controls the panel's visibility and location.
+  void SetAlwaysVisible(bool always_visible);
+  void SetPanelBounds(const gfx::Rect& bounds,
+                      mojom::AccessibilityPanelState state);
 
   // aura::LayoutManager:
   void OnWindowResized() override {}
@@ -75,8 +79,15 @@
   // The panel being managed (e.g. the ChromeVoxPanel's native aura window).
   aura::Window* panel_window_ = nullptr;
 
-  // Whether the panel itself is filling the display.
-  bool panel_fullscreen_ = false;
+  // Window bounds when not in fullscreen
+  gfx::Rect panel_bounds_ = gfx::Rect(0, 0, 0, 0);
+
+  // Determines whether panel is hidden when browser is in fullscreen.
+  bool always_visible_ = false;
+
+  // Determines how the panel_bounds_ are used when displaying the panel.
+  mojom::AccessibilityPanelState panel_state_ =
+      mojom::AccessibilityPanelState::BOUNDED;
 
   DISALLOW_COPY_AND_ASSIGN(AccessibilityPanelLayoutManager);
 };
diff --git a/ash/accessibility/accessibility_panel_layout_manager_unittest.cc b/ash/accessibility/accessibility_panel_layout_manager_unittest.cc
index 5bd3165..b231a6c 100644
--- a/ash/accessibility/accessibility_panel_layout_manager_unittest.cc
+++ b/ash/accessibility/accessibility_panel_layout_manager_unittest.cc
@@ -17,7 +17,8 @@
 namespace {
 
 // Shorten the name for better line wrapping.
-constexpr int kPanelHeight = AccessibilityPanelLayoutManager::kPanelHeight;
+constexpr int kDefaultPanelHeight =
+    AccessibilityPanelLayoutManager::kDefaultPanelHeight;
 
 AccessibilityPanelLayoutManager* GetLayoutManager() {
   aura::Window* container =
@@ -74,27 +75,6 @@
   // Ash should not crash if the window is still open at shutdown.
 }
 
-TEST_F(AccessibilityPanelLayoutManagerTest, InitialBounds) {
-  display::Screen* screen = display::Screen::GetScreen();
-  gfx::Rect initial_work_area = screen->GetPrimaryDisplay().work_area();
-
-  // Simulate Chrome creating the ChromeVox window, but don't show it yet.
-  std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel();
-
-  // The layout manager has not adjusted the work area yet.
-  EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), initial_work_area);
-
-  // Showing the panel causes the layout manager to adjust the panel bounds and
-  // the display work area.
-  widget->Show();
-  gfx::Rect expected_bounds(0, 0, screen->GetPrimaryDisplay().bounds().width(),
-                            kPanelHeight);
-  EXPECT_EQ(widget->GetNativeWindow()->bounds(), expected_bounds);
-  gfx::Rect expected_work_area = initial_work_area;
-  expected_work_area.Inset(0, kPanelHeight, 0, 0);
-  EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area);
-}
-
 TEST_F(AccessibilityPanelLayoutManagerTest, PanelFullscreen) {
   AccessibilityPanelLayoutManager* layout_manager = GetLayoutManager();
   display::Screen* screen = display::Screen::GetScreen();
@@ -102,38 +82,56 @@
   std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel();
   widget->Show();
 
+  layout_manager->SetPanelBounds(gfx::Rect(0, 0, 0, kDefaultPanelHeight),
+                                 mojom::AccessibilityPanelState::FULL_WIDTH);
+
   gfx::Rect expected_work_area = screen->GetPrimaryDisplay().work_area();
 
   // When the panel is fullscreen it fills the display and does not change the
   // work area.
-  layout_manager->SetPanelFullscreen(true);
+  layout_manager->SetPanelBounds(gfx::Rect(),
+                                 mojom::AccessibilityPanelState::FULLSCREEN);
   EXPECT_EQ(widget->GetNativeWindow()->bounds(),
             screen->GetPrimaryDisplay().bounds());
   EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area);
 
   // Restoring the panel to default size restores the bounds and does not change
   // the work area.
-  layout_manager->SetPanelFullscreen(false);
+  layout_manager->SetPanelBounds(gfx::Rect(0, 0, 0, kDefaultPanelHeight),
+                                 mojom::AccessibilityPanelState::FULL_WIDTH);
   gfx::Rect expected_bounds(0, 0, screen->GetPrimaryDisplay().bounds().width(),
-                            kPanelHeight);
+                            kDefaultPanelHeight);
   EXPECT_EQ(widget->GetNativeWindow()->bounds(), expected_bounds);
   EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area);
 }
 
+TEST_F(AccessibilityPanelLayoutManagerTest, SetBounds) {
+  std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel();
+  widget->Show();
+
+  gfx::Rect bounds(0, 0, 100, 100);
+  GetLayoutManager()->SetPanelBounds(bounds,
+                                     mojom::AccessibilityPanelState::BOUNDED);
+  EXPECT_EQ(widget->GetNativeWindow()->bounds(), bounds);
+}
+
 TEST_F(AccessibilityPanelLayoutManagerTest, DisplayBoundsChange) {
   std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel();
   widget->Show();
+  GetLayoutManager()->SetPanelBounds(
+      gfx::Rect(0, 0, 0, kDefaultPanelHeight),
+      mojom::AccessibilityPanelState::FULL_WIDTH);
 
   // When the display resolution changes the panel still sits at the top of the
   // screen.
   UpdateDisplay("1234,567");
   display::Screen* screen = display::Screen::GetScreen();
   gfx::Rect expected_bounds(0, 0, screen->GetPrimaryDisplay().bounds().width(),
-                            kPanelHeight);
+                            kDefaultPanelHeight);
   EXPECT_EQ(widget->GetNativeWindow()->bounds(), expected_bounds);
 
   gfx::Rect expected_work_area = screen->GetPrimaryDisplay().bounds();
-  expected_work_area.Inset(0, kPanelHeight, 0, kShelfSize);
+  expected_work_area.Inset(0, kDefaultPanelHeight, 0, kShelfSize);
   EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area);
 }
 
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 55980af0..b23f5272 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -547,6 +547,10 @@
                                           ->IsTabletModeWindowManagerEnabled();
 }
 
+void AppListControllerImpl::Back() {
+  presenter_.GetView()->Back();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Methods of |client_|:
 
@@ -595,7 +599,7 @@
     client_->OpenSearchResult(result_id, event_flags);
 
   if (IsHomeLauncherEnabledInTabletMode() && presenter_.IsVisible())
-    presenter_.GetView()->ResetToInitialState();
+    presenter_.GetView()->CloseOpenedPage();
 }
 
 void AppListControllerImpl::InvokeSearchResultAction(
@@ -644,7 +648,7 @@
     client_->ActivateItem(id, event_flags);
 
   if (IsHomeLauncherEnabledInTabletMode() && presenter_.IsVisible())
-    presenter_.GetView()->ResetToInitialState();
+    presenter_.GetView()->CloseOpenedPage();
 }
 
 void AppListControllerImpl::GetContextMenuModel(
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 31fbf7d..11847b4 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -203,6 +203,9 @@
   // Returns true if the home launcher is enabled in tablet mode.
   bool IsHomeLauncherEnabledInTabletMode() const;
 
+  // Performs the 'back' action for the active page.
+  void Back();
+
  private:
   syncer::StringOrdinal GetOemFolderPos();
   std::unique_ptr<app_list::AppListItem> CreateAppListItem(
diff --git a/ash/app_list/app_list_presenter_delegate_impl.cc b/ash/app_list/app_list_presenter_delegate_impl.cc
index 9013af6..e6ee690 100644
--- a/ash/app_list/app_list_presenter_delegate_impl.cc
+++ b/ash/app_list/app_list_presenter_delegate_impl.cc
@@ -218,7 +218,7 @@
   }
 
   aura::Window* window = view_->GetWidget()->GetNativeView()->parent();
-  if (!window->Contains(target) && !presenter_->Back() &&
+  if (!window->Contains(target) && !presenter_->CloseOpenedPage() &&
       !app_list::switches::ShouldNotDismissOnBlur() &&
       !IsHomeLauncherEnabledInTabletMode()) {
     presenter_->Dismiss(event->time_stamp());
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index ea136cf..f35f2dfe 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -485,6 +485,23 @@
             shelf_layout_manager->GetShelfBackgroundType());
 }
 
+// Tests the shelf background type is as expected when in tablet mode.
+TEST_F(AppListPresenterDelegateTest, ShelfBackgroundWithHomeLauncher) {
+  // Enter tablet mode to display the home launcher.
+  GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
+  EnableTabletMode(true);
+  ShelfLayoutManager* shelf_layout_manager =
+      Shelf::ForWindow(Shell::GetRootWindowForDisplayId(GetPrimaryDisplayId()))
+          ->shelf_layout_manager();
+  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_DEFAULT,
+            shelf_layout_manager->GetShelfBackgroundType());
+
+  // Add a window. It should be maximized because it is in tablet mode.
+  auto window = CreateTestWindow();
+  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_MAXIMIZED,
+            shelf_layout_manager->GetShelfBackgroundType());
+}
+
 // Tests that the peeking app list closes if the user taps or clicks outside
 // its bounds.
 TEST_P(AppListPresenterDelegateTest, TapAndClickOutsideClosesPeekingAppList) {
diff --git a/ash/app_list/presenter/app_list_presenter_impl.cc b/ash/app_list/presenter/app_list_presenter_impl.cc
index 9d33cb47..749d9582 100644
--- a/ash/app_list/presenter/app_list_presenter_impl.cc
+++ b/ash/app_list/presenter/app_list_presenter_impl.cc
@@ -147,14 +147,14 @@
   base::RecordAction(base::UserMetricsAction("Launcher_Dismiss"));
 }
 
-bool AppListPresenterImpl::Back() {
+bool AppListPresenterImpl::CloseOpenedPage() {
   if (!is_visible_)
     return false;
 
   // If the app list is currently visible, there should be an existing view.
   DCHECK(view_);
 
-  return view_->app_list_main_view()->contents_view()->Back();
+  return view_->CloseOpenedPage();
 }
 
 void AppListPresenterImpl::ToggleAppList(int64_t display_id,
diff --git a/ash/app_list/presenter/app_list_presenter_impl.h b/ash/app_list/presenter/app_list_presenter_impl.h
index 0ce3a83..3fe4931c 100644
--- a/ash/app_list/presenter/app_list_presenter_impl.h
+++ b/ash/app_list/presenter/app_list_presenter_impl.h
@@ -58,9 +58,9 @@
   // one AppListShowSource or focusing out side of the launcher.
   void Dismiss(base::TimeTicks event_time_stamp);
 
-  // Performs the 'back' action for the active page. Returns whether the action
-  // was handled.
-  bool Back();
+  // Closes opened folder or search result page if they are opened. Returns
+  // whether the action was handled.
+  bool CloseOpenedPage();
 
   // Show the app list if it is visible, hide it if it is hidden. If
   // |event_time_stamp| is not 0, it means |ToggleAppList()| was triggered by
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index 2bc626b7..821b6143 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -395,7 +395,18 @@
   GetWidget()->Deactivate();
 }
 
-void AppListView::ResetToInitialState() {
+bool AppListView::CloseOpenedPage() {
+  if (!app_list_main_view_)
+    return false;
+
+  if (app_list_main_view_->contents_view()->IsShowingSearchResults() ||
+      GetAppsContainerView()->IsInFolderView()) {
+    return app_list_main_view_->contents_view()->Back();
+  }
+  return false;
+}
+
+void AppListView::Back() {
   app_list_main_view_->contents_view()->Back();
 }
 
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h
index 1d47f2b6..fef964f3 100644
--- a/ash/app_list/views/app_list_view.h
+++ b/ash/app_list/views/app_list_view.h
@@ -126,8 +126,12 @@
   // Dismisses the UI, cleans up and sets the state to CLOSED.
   void Dismiss();
 
-  // Resets the UI to initial state.
-  void ResetToInitialState();
+  // Closes opened folder or search result page if they are opened. Returns
+  // whether the action was handled.
+  bool CloseOpenedPage();
+
+  // Performs the 'back' action for the active page.
+  void Back();
 
   // Enables/disables a semi-transparent overlay over the app list (good for
   // hiding the app list when a modal dialog is being shown).
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index 2a04aea..18c9f4c0 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -150,6 +150,7 @@
     params.is_tablet_mode = is_tablet_mode;
     params.is_side_shelf = is_side_shelf;
     view_->Initialize(params);
+    test_api_.reset(new AppsGridViewTestApi(apps_grid_view()));
     EXPECT_FALSE(view_->GetWidget()->IsVisible());
   }
 
@@ -200,8 +201,17 @@
     return view_->app_list_main_view()->search_box_view();
   }
 
+  ContentsView* contents_view() {
+    return view_->app_list_main_view()->contents_view();
+  }
+
+  AppsGridView* apps_grid_view() {
+    return contents_view()->GetAppsContainerView()->apps_grid_view();
+  }
+
   AppListView* view_ = nullptr;  // Owned by native widget.
   std::unique_ptr<AppListTestViewDelegate> delegate_;
+  std::unique_ptr<AppsGridViewTestApi> test_api_;
 
   // Used by AppListFolderView::UpdatePreferredBounds.
   keyboard::KeyboardController keyboard_controller_;
@@ -246,11 +256,11 @@
       is_new_style_launcher_enabled_ = GetParam().is_new_style_launcher_enabled;
     }
     if (is_new_style_launcher_enabled_) {
-      scoped_feature_list_.InitWithFeatures({features::kEnableNewStyleLauncher},
-                                            {});
+      scoped_feature_list_.InitAndEnableFeature(
+          features::kEnableNewStyleLauncher);
     } else {
-      scoped_feature_list_.InitWithFeatures(
-          {}, {features::kEnableNewStyleLauncher});
+      scoped_feature_list_.InitAndDisableFeature(
+          features::kEnableNewStyleLauncher);
     }
 
     views::ViewsTestBase::SetUp();
@@ -582,6 +592,24 @@
                         AppListViewFocusTest,
                         testing::ValuesIn(kAppListViewFocusTestParams));
 
+// Test behaviors in tablet mode when homcher launcher feature is enabled.
+class AppListViewHomeLauncherTest : public AppListViewTest {
+ public:
+  AppListViewHomeLauncherTest() = default;
+  ~AppListViewHomeLauncherTest() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        app_list::features::kEnableHomeLauncher);
+    AppListViewTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppListViewHomeLauncherTest);
+};
+
 // Test behaviors in tablet mode when homcher launcher feature is not enabled.
 class AppListViewNonHomeLauncherTest : public AppListViewTest {
  public:
@@ -589,8 +617,8 @@
   ~AppListViewNonHomeLauncherTest() override = default;
 
   void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {}, {app_list::features::kEnableHomeLauncher});
+    scoped_feature_list_.InitAndDisableFeature(
+        app_list::features::kEnableHomeLauncher);
     AppListViewTest::SetUp();
   }
 
@@ -2021,5 +2049,56 @@
   ASSERT_EQ(AppListViewState::FULLSCREEN_SEARCH, view_->app_list_state());
 }
 
+// Tests the back action in home launcher.
+TEST_F(AppListViewHomeLauncherTest, BackAction) {
+  // Put into fullscreen using tablet mode.
+  Initialize(0, true, false);
+
+  // Populate apps to fill up the first page and add a folder in the second
+  // page.
+  AppListTestModel* model = delegate_->GetTestModel();
+  const int kAppListItemNum = test_api_->TilesPerPage(0);
+  const int kItemNumInFolder = 5;
+  model->PopulateApps(kAppListItemNum);
+  model->CreateAndPopulateFolderWithApps(kItemNumInFolder);
+
+  // Show the app list
+  Show();
+  EXPECT_EQ(AppListViewState::FULLSCREEN_ALL_APPS, view_->app_list_state());
+  EXPECT_EQ(2, apps_grid_view()->pagination_model()->total_pages());
+
+  // Select the second page and open the folder.
+  apps_grid_view()->pagination_model()->SelectPage(1, false);
+  test_api_->PressItemAt(kAppListItemNum);
+  EXPECT_TRUE(contents_view()->GetAppsContainerView()->IsInFolderView());
+  EXPECT_EQ(1, apps_grid_view()->pagination_model()->selected_page());
+
+  // Back action will first close the folder.
+  contents_view()->Back();
+  EXPECT_FALSE(contents_view()->GetAppsContainerView()->IsInFolderView());
+  EXPECT_EQ(1, apps_grid_view()->pagination_model()->selected_page());
+
+  // Back action will then select the first page.
+  contents_view()->Back();
+  EXPECT_FALSE(contents_view()->GetAppsContainerView()->IsInFolderView());
+  EXPECT_EQ(0, apps_grid_view()->pagination_model()->selected_page());
+
+  // Select the second page and open search results page.
+  apps_grid_view()->pagination_model()->SelectPage(1, false);
+  search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("A"));
+  EXPECT_EQ(AppListViewState::FULLSCREEN_SEARCH, view_->app_list_state());
+  EXPECT_EQ(1, apps_grid_view()->pagination_model()->selected_page());
+
+  // Back action will first close the search results page.
+  contents_view()->Back();
+  EXPECT_EQ(AppListViewState::FULLSCREEN_ALL_APPS, view_->app_list_state());
+  EXPECT_EQ(1, apps_grid_view()->pagination_model()->selected_page());
+
+  // Back action will then select the first page.
+  contents_view()->Back();
+  EXPECT_EQ(AppListViewState::FULLSCREEN_ALL_APPS, view_->app_list_state());
+  EXPECT_EQ(0, apps_grid_view()->pagination_model()->selected_page());
+}
+
 }  // namespace test
 }  // namespace app_list
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index 37fc55d..8ee89a1 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -418,14 +418,22 @@
     case ash::AppListState::kStateStart:
       // Close the app list when Back() is called from the start page.
       return false;
-    case ash::AppListState::kStateApps:
+    case ash::AppListState::kStateApps: {
+      PaginationModel* pagination_model =
+          GetAppsContainerView()->apps_grid_view()->pagination_model();
       if (GetAppsContainerView()->IsInFolderView()) {
         GetAppsContainerView()->app_list_folder_view()->CloseFolderPage();
+      } else if (app_list_view_->IsHomeLauncherEnabledInTabletMode() &&
+                 pagination_model->total_pages() > 0 &&
+                 pagination_model->selected_page() > 0) {
+        pagination_model->SelectPage(
+            0, !app_list_view_->ShortAnimationsForTesting());
       } else {
         // Close the app list when Back() is called from the apps page.
         return false;
       }
       break;
+    }
     case ash::AppListState::kStateSearchResults:
       GetSearchBoxView()->ClearSearch();
       GetSearchBoxView()->SetSearchBoxActive(false, ui::ET_UNKNOWN);
diff --git a/ash/public/cpp/app_list/app_list_constants.cc b/ash/public/cpp/app_list/app_list_constants.cc
index 5ba246f..dacb74e 100644
--- a/ash/public/cpp/app_list/app_list_constants.cc
+++ b/ash/public/cpp/app_list/app_list_constants.cc
@@ -114,7 +114,7 @@
 const size_t kMaxFolderItemsPerPage = 16;
 
 // Maximum length of the folder name in chars.
-const size_t kMaxFolderNameChars = 80;
+const size_t kMaxFolderNameChars = 28;
 
 // Font style for app item labels.
 const ui::ResourceBundle::FontStyle kItemTextFontStyle =
diff --git a/ash/public/interfaces/accessibility_controller.mojom b/ash/public/interfaces/accessibility_controller.mojom
index 3951d78..d9cea94 100644
--- a/ash/public/interfaces/accessibility_controller.mojom
+++ b/ash/public/interfaces/accessibility_controller.mojom
@@ -36,6 +36,17 @@
   WINDOW_OVERVIEW_MODE_ENTERED
 };
 
+enum AccessibilityPanelState {
+  // Window bounds are set explicitly.
+  BOUNDED,
+
+  // Width of panel matches screen width, y_coord and height are set by bounds.
+  FULL_WIDTH,
+
+  // Panel occupies the full screen. Bounds are ignored.
+  FULLSCREEN
+};
+
 enum SelectToSpeakState {
   // Select to Speak is not actively selecting text or speaking.
   kSelectToSpeakStateInactive,
@@ -70,9 +81,14 @@
   // Setting off-screen or empty bounds suppresses the highlight.
   SetCaretBounds(gfx.mojom.Rect bounds_in_screen);
 
-  // Sets whether the accessibility panel is filling the entire screen (e.g. to
-  // show the expanded UI for the ChromeVox spoken feedback extension).
-  SetAccessibilityPanelFullscreen(bool fullscreen);
+  // Sets whether the accessibility panel should always be visible, regardless
+  // of whether the window is fullscreen.
+  SetAccessibilityPanelAlwaysVisible(bool always_visible);
+
+  // Sets the bounds for the accessibility panel. Overrides current
+  // configuration (i.e. fullscreen, full-width).
+  SetAccessibilityPanelBounds(gfx.mojom.Rect bounds,
+                              AccessibilityPanelState state);
 
   // Sets the current Select-to-Speak state. This should be used by the Select-
   // to-Speak extension to inform ash of its updated state.
diff --git a/ash/shelf/app_list_shelf_item_delegate.cc b/ash/shelf/app_list_shelf_item_delegate.cc
index 06f52ad..3ecd8caa 100644
--- a/ash/shelf/app_list_shelf_item_delegate.cc
+++ b/ash/shelf/app_list_shelf_item_delegate.cc
@@ -38,13 +38,21 @@
     return;
   }
 
+  // Whether to perform the "back" action for the app list. It will only be
+  // performed if other actions are not performed.
+  bool back_action = true;
+
   // End overview mode.
-  if (Shell::Get()->window_selector_controller()->IsSelecting())
+  if (Shell::Get()->window_selector_controller()->IsSelecting()) {
     Shell::Get()->window_selector_controller()->ToggleOverview();
+    back_action = false;
+  }
 
   // End split view mode.
-  if (Shell::Get()->split_view_controller()->IsSplitViewModeActive())
+  if (Shell::Get()->split_view_controller()->IsSplitViewModeActive()) {
     Shell::Get()->split_view_controller()->EndSplitView();
+    back_action = false;
+  }
 
   // Minimize all windows that aren't the app list.
   aura::Window* app_list_container =
@@ -56,8 +64,12 @@
     if (!app_list_container->Contains(window) &&
         !wm::GetWindowState(window)->IsMinimized()) {
       wm::GetWindowState(window)->Minimize();
+      back_action = false;
     }
   }
+
+  if (back_action)
+    Shell::Get()->app_list_controller()->Back();
 }
 
 void AppListShelfItemDelegate::ExecuteCommand(bool from_context_menu,
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 8fd2a81..ccebe52 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -527,9 +527,13 @@
     return SHELF_BACKGROUND_LOGIN;
   }
 
-  // If the app list is active, hide the shelf background to prevent overlap.
-  if (is_app_list_visible_)
+  // If the app list is active and the home launcher is not shown, hide the
+  // shelf background to prevent overlap.
+  if (is_app_list_visible_ && !Shell::Get()
+                                   ->app_list_controller()
+                                   ->IsHomeLauncherEnabledInTabletMode()) {
     return SHELF_BACKGROUND_APP_LIST;
+  }
 
   if (state_.visibility_state != SHELF_AUTO_HIDE &&
       state_.window_state == wm::WORKSPACE_WINDOW_STATE_MAXIMIZED) {
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index ea1eff12..c3728af7 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -56,7 +56,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_mock_time_message_loop_task_runner.h"
 #include "base/time/time.h"
-#include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/test/aura_test_base.h"
@@ -2935,14 +2934,9 @@
   ASSERT_TRUE(test_api_->IsShowingOverflowBubble());
 }
 
-#if defined(OS_LINUX)
-#define MAYBE_MouseContextMenu DISABLED_MouseContextMenu
-#else
-#define MAYBE_MouseContextMenu MouseContextMenu
-#endif
 // Tests ink drop state transitions for the overflow button when the user
 // right clicks on the button to show the context menu.
-TEST_F(OverflowButtonInkDropTest, MAYBE_MouseContextMenu) {
+TEST_F(OverflowButtonInkDropTest, MouseContextMenu) {
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->MoveMouseTo(GetScreenPointInsideOverflowButton());
 
@@ -3251,14 +3245,9 @@
   EXPECT_FALSE(test_api_->IsShowingOverflowBubble());
 }
 
-#if defined(OS_LINUX)
-#define MAYBE_MouseContextMenu DISABLED_MouseContextMenu
-#else
-#define MAYBE_MouseContextMenu MouseContextMenu
-#endif
 // Tests ink drop state transitions for the overflow button when it is active
 // and the user right clicks on the button to show the context menu.
-TEST_F(OverflowButtonActiveInkDropTest, MAYBE_MouseContextMenu) {
+TEST_F(OverflowButtonActiveInkDropTest, MouseContextMenu) {
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->MoveMouseTo(GetScreenPointInsideOverflowButton());
 
diff --git a/ash/shell.cc b/ash/shell.cc
index d48cd2fd..6fd9c12b 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -1164,7 +1164,7 @@
   power_button_controller_->OnDisplayModeChanged(
       display_configurator_->cached_displays());
 
-  if (!::features::IsAshInBrowserProcess() || ::features::IsSingleProcessMash())
+  if (::features::IsUsingWindowService())
     client_image_registry_ = std::make_unique<ClientImageRegistry>();
 
   drag_drop_controller_ = std::make_unique<DragDropController>();
diff --git a/ash/system/accessibility/select_to_speak_tray.cc b/ash/system/accessibility/select_to_speak_tray.cc
index 8a69bb9d..79028e58 100644
--- a/ash/system/accessibility/select_to_speak_tray.cc
+++ b/ash/system/accessibility/select_to_speak_tray.cc
@@ -73,6 +73,10 @@
   CheckStatusAndUpdateIcon();
 }
 
+bool SelectToSpeakTray::ContainsPointInScreen(const gfx::Point& point) {
+  return GetBoundsInScreen().Contains(point);
+}
+
 void SelectToSpeakTray::CheckStatusAndUpdateIcon() {
   if (!Shell::Get()->accessibility_controller()->IsSelectToSpeakEnabled()) {
     SetVisible(false);
diff --git a/ash/system/accessibility/select_to_speak_tray.h b/ash/system/accessibility/select_to_speak_tray.h
index 05b7f32..96419c52 100644
--- a/ash/system/accessibility/select_to_speak_tray.h
+++ b/ash/system/accessibility/select_to_speak_tray.h
@@ -34,6 +34,10 @@
   // AccessibilityObserver:
   void OnAccessibilityStatusChanged() override;
 
+  // Returns true if the screen point passed in is contained within this tray's
+  // bounds.
+  bool ContainsPointInScreen(const gfx::Point& point);
+
  private:
   friend class SelectToSpeakTrayTest;
 
diff --git a/ash/system/accessibility/select_to_speak_tray_utils.cc b/ash/system/accessibility/select_to_speak_tray_utils.cc
new file mode 100644
index 0000000..f459b629
--- /dev/null
+++ b/ash/system/accessibility/select_to_speak_tray_utils.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/system/accessibility/select_to_speak_tray_utils.h"
+
+#include "ash/session/session_controller.h"
+#include "ash/shelf/shelf.h"
+#include "ash/shell.h"
+#include "ash/system/accessibility/select_to_speak_tray.h"
+#include "ash/system/status_area_widget.h"
+#include "ui/display/display.h"
+#include "ui/gfx/geometry/point.h"
+
+namespace ash {
+namespace select_to_speak_tray_utils {
+
+bool SelectToSpeakTrayContainsPointInScreen(const gfx::Point& point) {
+  for (aura::Window* window : Shell::GetAllRootWindows()) {
+    SelectToSpeakTray* tray =
+        Shelf::ForWindow(window)->GetStatusAreaWidget()->select_to_speak_tray();
+    if (tray && tray->ContainsPointInScreen(point))
+      return true;
+  }
+
+  return false;
+}
+
+}  // namespace select_to_speak_tray_utils
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/system/accessibility/select_to_speak_tray_utils.h b/ash/system/accessibility/select_to_speak_tray_utils.h
new file mode 100644
index 0000000..efa545f
--- /dev/null
+++ b/ash/system/accessibility/select_to_speak_tray_utils.h
@@ -0,0 +1,24 @@
+// 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_ACCESSIBILITY_SELECT_TO_SPEAK_TRAY_UTILS_H_
+#define ASH_SYSTEM_ACCESSIBILITY_SELECT_TO_SPEAK_TRAY_UTILS_H_
+
+#include "ash/ash_export.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace ash {
+namespace select_to_speak_tray_utils {
+
+// Returns true if either the select-to-speak tray icon contains the
+// given point (in screen space).
+ASH_EXPORT bool SelectToSpeakTrayContainsPointInScreen(const gfx::Point& point);
+
+}  // namespace select_to_speak_tray_utils
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_ACCESSIBILITY_SELECT_TO_SPEAK_TRAY_UTILS_H_
diff --git a/ash/wm/overview/overview_utils.h b/ash/wm/overview/overview_utils.h
index 02dd79c..0ff5006 100644
--- a/ash/wm/overview/overview_utils.h
+++ b/ash/wm/overview/overview_utils.h
@@ -5,13 +5,13 @@
 #ifndef ASH_WM_OVERVIEW_OVERVIEW_UTILS_H_
 #define ASH_WM_OVERVIEW_OVERVIEW_UTILS_H_
 
+#include <memory>
+
 #include "ash/ash_export.h"
 #include "ash/wm/overview/overview_animation_type.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/compositor/layer_type.h"
 
-#include <memory>
-
 namespace aura {
 class Window;
 }  // namespace aura
diff --git a/ash/wm/overview/window_grid.cc b/ash/wm/overview/window_grid.cc
index 719c1aa..0779473 100644
--- a/ash/wm/overview/window_grid.cc
+++ b/ash/wm/overview/window_grid.cc
@@ -212,6 +212,28 @@
   return widget;
 }
 
+// Gets the expected grid bounds according the current |indicator_state| during
+// window dragging.
+gfx::Rect GetGridBoundsInScreenDuringDragging(aura::Window* dragged_window,
+                                              IndicatorState indicator_state) {
+  switch (indicator_state) {
+    case IndicatorState::kPreviewAreaLeft:
+      return Shell::Get()
+          ->split_view_controller()
+          ->GetSnappedWindowBoundsInScreen(dragged_window,
+                                           SplitViewController::RIGHT);
+    case IndicatorState::kPreviewAreaRight:
+      return Shell::Get()
+          ->split_view_controller()
+          ->GetSnappedWindowBoundsInScreen(dragged_window,
+                                           SplitViewController::LEFT);
+    default:
+      return Shell::Get()
+          ->split_view_controller()
+          ->GetDisplayWorkAreaBoundsInScreen(dragged_window);
+  }
+}
+
 }  // namespace
 
 // ShieldView contains the background for overview mode. It also contains text
@@ -585,6 +607,31 @@
                                        const gfx::Point& location_in_screen,
                                        IndicatorState indicator_state) {
   DCHECK_EQ(dragged_window->GetRootWindow(), root_window_);
+
+  // Adjust the window grid's bounds and the new selector item's visibility
+  // according to |indicator_state| if split view is not active at the moment.
+  if (!Shell::Get()->split_view_controller()->IsSplitViewModeActive()) {
+    WindowSelectorItem* new_selector_item = GetNewSelectorItem();
+    const bool should_visible =
+        (indicator_state != IndicatorState::kPreviewAreaLeft &&
+         indicator_state != IndicatorState::kPreviewAreaRight);
+    if (new_selector_item) {
+      const bool visible = new_selector_item_widget_->IsVisible();
+      if (should_visible != visible) {
+        new_selector_item_widget_->GetLayer()->SetVisible(should_visible);
+        new_selector_item->SetOpacity(should_visible ? 1.f : 0.f);
+      }
+    }
+
+    // Update the grid's bounds.
+    const gfx::Rect expected_bounds =
+        GetGridBoundsInScreenDuringDragging(dragged_window, indicator_state);
+    if (bounds_ != expected_bounds) {
+      SetBoundsAndUpdatePositionsIgnoringWindow(
+          expected_bounds, should_visible ? nullptr : new_selector_item);
+    }
+  }
+
   // Find the window selector item that contains |location_in_screen|.
   auto iter = std::find_if(
       window_list_.begin(), window_list_.end(),
@@ -678,6 +725,15 @@
          new_selector_item_widget_->GetNativeWindow() == window;
 }
 
+WindowSelectorItem* WindowGrid::GetNewSelectorItem() {
+  if (!new_selector_item_widget_ || window_list_.empty())
+    return nullptr;
+
+  WindowSelectorItem* first_item = window_list_.front().get();
+  return IsNewSelectorItemWindow(first_item->GetWindow()) ? first_item
+                                                          : nullptr;
+}
+
 void WindowGrid::OnWindowDestroying(aura::Window* window) {
   window_observer_.Remove(window);
   window_state_observer_.Remove(wm::GetWindowState(window));
diff --git a/ash/wm/overview/window_grid.h b/ash/wm/overview/window_grid.h
index b5de4bb..4eac75d 100644
--- a/ash/wm/overview/window_grid.h
+++ b/ash/wm/overview/window_grid.h
@@ -137,6 +137,10 @@
   // item.
   bool IsNewSelectorItemWindow(aura::Window* window) const;
 
+  // Returns the selector item that accociates with |new_selector_item_widget_|.
+  // Returns nullptr if overview does not have the new selector item.
+  WindowSelectorItem* GetNewSelectorItem();
+
   // aura::WindowObserver:
   void OnWindowDestroying(aura::Window* window) override;
   // TODO(flackr): Handle window bounds changed in WindowSelectorItem.
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index 9ad77458..a997b66 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -2342,6 +2342,107 @@
   EXPECT_TRUE(wm::GetWindowState(window.get())->IsMaximized());
 }
 
+// Tests that if dragging a window into the preview split area, overivew bounds
+// should be adjusted accordingly.
+TEST_F(SplitViewTabDraggingTest, AdjustOverviewBoundsDuringDragging) {
+  const gfx::Rect bounds(0, 0, 400, 400);
+  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
+  std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
+  std::unique_ptr<aura::Window> window3(CreateWindow(bounds));
+
+  WindowSelectorController* selector_controller =
+      Shell::Get()->window_selector_controller();
+  EXPECT_FALSE(selector_controller->IsSelecting());
+
+  // Start dragging |window1|.
+  std::unique_ptr<WindowResizer> resizer =
+      StartDrag(window1.get(), window1.get());
+  // Overview should have been opened.
+  EXPECT_TRUE(selector_controller->IsSelecting());
+
+  // Test that the new selector item shows up as the first item in overview.
+  WindowGrid* current_grid =
+      selector_controller->window_selector()->GetGridWithRootWindow(
+          window1->GetRootWindow());
+  EXPECT_TRUE(current_grid->GetNewSelectorItem());
+  const gfx::Rect work_area_bounds =
+      split_view_controller()->GetDisplayWorkAreaBoundsInScreen(window1.get());
+  EXPECT_EQ(current_grid->bounds(), work_area_bounds);
+  // The new selector item should be visible.
+  views::Widget* new_selector_widget =
+      current_grid->new_selector_item_widget_for_testing();
+  EXPECT_TRUE(new_selector_widget);
+  EXPECT_TRUE(new_selector_widget->IsVisible());
+
+  // Now drag |window1| to the left preview split area.
+  DragWindowTo(resizer.get(),
+               gfx::Point(0, work_area_bounds.CenterPoint().y()));
+  EXPECT_EQ(current_grid->bounds(),
+            split_view_controller()->GetSnappedWindowBoundsInScreen(
+                window1.get(), SplitViewController::RIGHT));
+  EXPECT_FALSE(new_selector_widget->IsVisible());
+
+  // Drag it to middle.
+  DragWindowTo(resizer.get(), work_area_bounds.CenterPoint());
+  EXPECT_EQ(current_grid->bounds(), work_area_bounds);
+  EXPECT_TRUE(new_selector_widget->IsVisible());
+
+  // Drag |window1| to the right preview split area.
+  DragWindowTo(resizer.get(), gfx::Point(work_area_bounds.right(),
+                                         work_area_bounds.CenterPoint().y()));
+  EXPECT_EQ(current_grid->bounds(),
+            split_view_controller()->GetSnappedWindowBoundsInScreen(
+                window1.get(), SplitViewController::LEFT));
+  EXPECT_FALSE(new_selector_widget->IsVisible());
+
+  CompleteDrag(std::move(resizer));
+  EXPECT_EQ(split_view_controller()->state(),
+            SplitViewController::RIGHT_SNAPPED);
+  EXPECT_TRUE(selector_controller->IsSelecting());
+
+  // Snap another window should end overview.
+  split_view_controller()->SnapWindow(window2.get(), SplitViewController::LEFT);
+  EXPECT_EQ(split_view_controller()->state(),
+            SplitViewController::BOTH_SNAPPED);
+  EXPECT_FALSE(selector_controller->IsSelecting());
+
+  // Now drag |window1| again. Overview and splitview should be both active at
+  // the same time during dragging.
+  resizer = StartDrag(window1.get(), window1.get());
+  EXPECT_TRUE(selector_controller->IsSelecting());
+  EXPECT_EQ(split_view_controller()->state(),
+            SplitViewController::LEFT_SNAPPED);
+
+  current_grid = selector_controller->window_selector()->GetGridWithRootWindow(
+      window1->GetRootWindow());
+  // The new selector item should be visible.
+  new_selector_widget = current_grid->new_selector_item_widget_for_testing();
+  EXPECT_TRUE(new_selector_widget);
+  EXPECT_TRUE(new_selector_widget->IsVisible());
+  EXPECT_EQ(current_grid->bounds(),
+            split_view_controller()->GetSnappedWindowBoundsInScreen(
+                window1.get(), SplitViewController::RIGHT));
+
+  // Drag |window1| to the right preview split area.
+  DragWindowTo(resizer.get(), gfx::Point(work_area_bounds.right(),
+                                         work_area_bounds.CenterPoint().y()));
+  // Overview bounds stays the same.
+  EXPECT_EQ(current_grid->bounds(),
+            split_view_controller()->GetSnappedWindowBoundsInScreen(
+                window1.get(), SplitViewController::RIGHT));
+  EXPECT_TRUE(new_selector_widget->IsVisible());
+
+  // Drag |window1| to the left preview split area.
+  DragWindowTo(resizer.get(),
+               gfx::Point(0, work_area_bounds.CenterPoint().y()));
+  EXPECT_EQ(current_grid->bounds(),
+            split_view_controller()->GetSnappedWindowBoundsInScreen(
+                window1.get(), SplitViewController::RIGHT));
+  EXPECT_TRUE(new_selector_widget->IsVisible());
+
+  CompleteDrag(std::move(resizer));
+}
+
 class TestWindowDelegateWithWidget : public views::WidgetDelegate {
  public:
   TestWindowDelegateWithWidget(bool can_activate)
diff --git a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
index ee4a3d8..60008307 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
@@ -50,9 +50,8 @@
   if (!window_grid || window_grid->empty())
     return gfx::Rect();
 
-  WindowSelectorItem* new_selector_item =
-      window_grid->window_list().front().get();
-  if (!window_grid->IsNewSelectorItemWindow(new_selector_item->GetWindow()))
+  WindowSelectorItem* new_selector_item = window_grid->GetNewSelectorItem();
+  if (!new_selector_item)
     return gfx::Rect();
 
   return new_selector_item->target_bounds();
diff --git a/ash/ws/window_service_owner.cc b/ash/ws/window_service_owner.cc
index 98a1995..0bd5d746 100644
--- a/ash/ws/window_service_owner.cc
+++ b/ash/ws/window_service_owner.cc
@@ -29,7 +29,7 @@
     service_manager::mojom::ServiceRequest request) {
   // This should only be called once. If called more than once it means the
   // WindowService lost its connection to the service_manager, which triggered
-  // a new connection WindowService to be created. That should never happen.
+  // a new WindowService to be created. That should never happen.
   DCHECK(!service_context_);
 
   window_service_delegate_ = std::make_unique<WindowServiceDelegateImpl>();
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 761dc18..a2387da7 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -829,20 +829,22 @@
   parser.add_option('--shared-libraries-runtime-deps',
                     help='Path to file containing runtime deps for shared '
                          'libraries.')
+  parser.add_option('--native-libs',
+                    action='append',
+                    help='GN-list of native libraries for primary '
+                         'android-abi. Can be specified multiple times.',
+                    default=[])
   parser.add_option('--secondary-abi-shared-libraries-runtime-deps',
                     help='Path to file containing runtime deps for secondary '
                          'abi shared libraries.')
   parser.add_option('--secondary-native-libs',
                     action='append',
-                    help='GYP-list of native libraries for secondary '
+                    help='GN-list of native libraries for secondary '
                          'android-abi. Can be specified multiple times.',
                     default=[])
   parser.add_option('--uncompress-shared-libraries', default=False,
                     action='store_true',
                     help='Whether to store native libraries uncompressed')
-  parser.add_option('--extra-shared-libraries',
-                    help='GN-list of paths to extra native libraries stored '
-                    'in the APK.')
   # apk options
   parser.add_option('--apk-path', help='Path to the target\'s apk output.')
   parser.add_option('--incremental-apk-path',
@@ -1220,7 +1222,7 @@
   if options.type in ('android_apk', 'dist_aar',
       'dist_jar', 'android_app_bundle_module', 'android_app_bundle'):
     all_configs = deps_info.get('proguard_configs', [])
-    extra_jars = list(system_jars)
+    extra_jars = list()
     for c in all_library_deps:
       all_configs.extend(
           p for p in c.get('proguard_configs', []) if p not in all_configs)
@@ -1381,12 +1383,12 @@
           secondary_abi_runtime_deps_files)
       secondary_abi_java_libraries_list = _CreateJavaLibrariesList(
           secondary_abi_library_paths)
+    for gn_list in options.secondary_native_libs:
+      secondary_abi_library_paths.extend(build_utils.ParseGnList(gn_list))
 
-    for gyp_list in options.secondary_native_libs:
-      secondary_abi_library_paths.extend(build_utils.ParseGnList(gyp_list))
-
-    extra_shared_libraries = build_utils.ParseGnList(
-        options.extra_shared_libraries)
+    extra_shared_libraries = []
+    for gn_list in options.native_libs:
+      extra_shared_libraries.extend(build_utils.ParseGnList(gn_list))
 
     all_inputs.extend(runtime_deps_files)
     config['native'] = {
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 15aa173..dd1a248 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -284,10 +284,15 @@
       ]
     }
 
+    if (defined(invoker.loadable_modules) && invoker.loadable_modules != []) {
+      _rebased_modules = rebase_path(invoker.loadable_modules, root_build_dir)
+      args += [ "--native-libs=$_rebased_modules" ]
+    }
+
     if (defined(invoker.extra_shared_libraries)) {
       _rebased_extra_shared_libraries =
           rebase_path(invoker.extra_shared_libraries, root_build_dir)
-      args += [ "--extra-shared-libraries=$_rebased_extra_shared_libraries" ]
+      args += [ "--native-libs=$_rebased_extra_shared_libraries" ]
     }
 
     if (defined(invoker.secondary_abi_shared_libraries_runtime_deps_file)) {
@@ -303,9 +308,9 @@
 
     if (defined(invoker.secondary_abi_loadable_modules) &&
         invoker.secondary_abi_loadable_modules != []) {
-      _rebased_modules =
+      _rebased_secondary_abi_modules =
           rebase_path(invoker.secondary_abi_loadable_modules, root_build_dir)
-      args += [ "--secondary-native-libs=$_rebased_modules" ]
+      args += [ "--secondary-native-libs=$_rebased_secondary_abi_modules" ]
     }
 
     if (defined(invoker.uncompress_shared_libraries) &&
@@ -921,6 +926,8 @@
         rebase_path(_output_jar_path, root_build_dir),
         "--classpath",
         "@FileArg($_rebased_build_config:deps_info:proguard_classpath_jars)",
+        "--classpath",
+        "@FileArg($_rebased_build_config:android:sdk_jars)",
       ]
       if (experimental_r8_path != "") {
         args += [
@@ -2884,6 +2891,7 @@
                                "classpath_deps",
                                "gradle_treat_as_prebuilt",
                                "input_jars_paths",
+                               "loadable_modules",
                                "main_class",
                                "proguard_configs",
                                "proguard_enabled",
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index f0da799..63b346e 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2296,6 +2296,7 @@
                                "emma_never_instrument",
                                "java_files",
                                "javac_args",
+                               "loadable_modules",
                                "no_build_hooks",
                                "secondary_abi_loadable_modules",
                                "uncompress_shared_libraries",
@@ -2633,8 +2634,6 @@
         args += [ "--native-libs=$_native_libs_file_arg" ]
       }
       if (_extra_native_libs != []) {
-        # Don't pass in _extra_native_libs_even_when_incremental, since these are
-        # end up in the apk and are not side-loaded.
         _rebased_extra_native_libs =
             rebase_path(_extra_native_libs, root_build_dir)
         args += [ "--native-libs=$_rebased_extra_native_libs" ]
@@ -3640,7 +3639,10 @@
   #      proguarding.
   #
   #    enable_multidex: Optional. Enable multidexing of optimized modules jars
-  #      when using synchronized proguarding.
+  #      when using synchronized proguarding. Only applies to base module.
+  #
+  #    proguard_android_sdk_dep: Optional. android_system_java_prebuilt() target
+  #      used as a library jar for synchronized proguarding.
   #
   # Example:
   #   android_app_bundle("chrome_public_bundle") {
@@ -3692,9 +3694,14 @@
     _build_config = "$target_gen_dir/${target_name}.build_config"
     _rebased_build_config = rebase_path(_build_config, root_build_dir)
     _build_config_target = "${target_name}__build_config"
+    if (defined(invoker.proguard_android_sdk_dep)) {
+      proguard_android_sdk_dep_ = invoker.proguard_android_sdk_dep
+    } else {
+      proguard_android_sdk_dep_ = "//third_party/android_tools:android_sdk_java"
+    }
     write_build_config(_build_config_target) {
       type = "android_app_bundle"
-      possible_config_deps = _module_targets
+      possible_config_deps = _module_targets + [ proguard_android_sdk_dep_ ]
       build_config = _build_config
     }
 
@@ -3770,7 +3777,7 @@
           # http://crbug.com/725224. Fix for bots running out of memory.
           use_pool = true
 
-          if (_enable_multidex) {
+          if (_enable_multidex && _module.name == "base") {
             enable_multidex = _enable_multidex
             extra_main_dex_proguard_config =
                 "$_module_target_gen_dir/$_module_target_name/" +
diff --git a/build/config/fuchsia/sandbox_policy b/build/config/fuchsia/sandbox_policy
index 9419bdd2..44f8c1b 100644
--- a/build/config/fuchsia/sandbox_policy
+++ b/build/config/fuchsia/sandbox_policy
@@ -1,4 +1,4 @@
 {
-  "features": [ "persistent-storage", "root-ssl-certificates", "system-temp" ]
+  "features": [ "persistent-storage", "root-ssl-certificates", "system-temp",
+      "deprecated-all-services" ]
 }
-
diff --git a/build/config/fuchsia/testing_sandbox_policy b/build/config/fuchsia/testing_sandbox_policy
index 9d4db8f..f100a30 100644
--- a/build/config/fuchsia/testing_sandbox_policy
+++ b/build/config/fuchsia/testing_sandbox_policy
@@ -1,5 +1,5 @@
 {
-  "features": [ "persistent-storage", "root-ssl-certificates", "system-temp"],
+  "features": [ "persistent-storage", "root-ssl-certificates", "system-temp",
+      "deprecated-all-services" ],
   "dev": ["null", "zero"]
 }
-
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index ad02739..da40e52d 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -795,6 +795,22 @@
         std::getline(ss, line);
         VLOG(3) << line;
       }
+
+      VLOG(3) << "CC Layer List:";
+      for (auto* layer : *this) {
+        VLOG(3) << "layer id " << layer->id();
+        VLOG(3) << "  element_id: " << layer->element_id();
+        VLOG(3) << "  bounds: " << layer->bounds().ToString();
+        VLOG(3) << "  opacity: " << layer->opacity();
+        VLOG(3) << "  position: " << layer->position().ToString();
+        VLOG(3) << "  draws_content: " << layer->DrawsContent();
+        VLOG(3) << "  scrollable: " << layer->scrollable();
+        VLOG(3) << "  contents_opaque: " << layer->contents_opaque();
+        VLOG(3) << "  transform_tree_index: " << layer->transform_tree_index();
+        VLOG(3) << "  clip_tree_index: " << layer->clip_tree_index();
+        VLOG(3) << "  effect_tree_index: " << layer->effect_tree_index();
+        VLOG(3) << "  scroll_tree_index: " << layer->scroll_tree_index();
+      }
     }
 
   bool painted_content_has_slow_paths = false;
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 39b59af..1584569 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -22,6 +22,9 @@
 import("channel.gni")
 import("java_sources.gni")
 import("static_initializers.gni")
+if (enable_arcore) {
+  import("//chrome/android/modules/ar/ar_module_tmpl.gni")
+}
 
 manifest_package = "org.chromium.chrome"
 
@@ -1705,7 +1708,14 @@
   }
 }
 
-# Generate application bundles if possible.
+# Feature modules that go into Chrome Android application bundles.
+if (enable_arcore) {
+  ar_module_tmpl("ar_public_module") {
+    manifest_package = manifest_package
+  }
+}
+
+# Chrome Android application bundles.
 android_app_bundle("chrome_public_bundle") {
   bundle_name = "ChromePublicBundle"
   base_module_target = ":chrome_public_base_module"
@@ -1729,5 +1739,14 @@
   if (!is_java_debug) {
     proguard_enabled = true
     enable_multidex = true
+    proguard_android_sdk_dep = webview_framework_dep
+  }
+  if (enable_arcore) {
+    extra_modules = [
+      {
+        name = "ar"
+        module_target = ":ar_public_module"
+      },
+    ]
   }
 }
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 4d20dc4..a5ce20b 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -263,23 +263,19 @@
       png_to_webp = true
     }
 
-    if (enable_arcore) {
-      deps += [ "//third_party/arcore-android-sdk:libdynamite_client_java" ]
-    }
-    if (package_arcore) {
-      # We store this as a separate .so in the APK and only load as needed.
-      if (android_64bit_target_cpu && build_apk_secondary_abi) {
-        deps += [
-          "//third_party/arcore-android-sdk:libarcore_library_secondary_abi",
-        ]
-        toolchain_out_dir = get_label_info(
-                "//third_party/arcore-android-sdk:libarcore_library($android_secondary_abi_toolchain)",
-                "root_out_dir")
-        secondary_abi_loadable_modules =
-            [ "${toolchain_out_dir}/libarcore_sdk_c_minimal.so" ]
-      } else {
-        deps += [ "//third_party/arcore-android-sdk:libarcore_library" ]
-        loadable_modules = [ "${root_out_dir}/libarcore_sdk_c_minimal.so" ]
+    if (invoker.target_type == "android_apk") {
+      if (enable_arcore) {
+        deps += [ "//third_party/arcore-android-sdk:libdynamite_client_java" ]
+      }
+      if (package_arcore) {
+        # We store this as a separate .so in the APK and only load as needed.
+        if (android_64bit_target_cpu && build_apk_secondary_abi) {
+          secondary_abi_loadable_modules = [ "//third_party/arcore-android-sdk/libraries/android_arm/libarcore_sdk_c_minimal.so" ]
+        } else if (android_64bit_target_cpu && !build_apk_secondary_abi) {
+          loadable_modules = [ "//third_party/arcore-android-sdk/libraries/android_arm64/libarcore_sdk_c_minimal.so" ]
+        } else {
+          loadable_modules = [ "//third_party/arcore-android-sdk/libraries/android_arm/libarcore_sdk_c_minimal.so" ]
+        }
       }
     }
   }
diff --git a/chrome/android/java/res/drawable-nodpi/widget_preview.png b/chrome/android/java/res/drawable-nodpi/widget_preview.png
index 58627ca..4c17ce8a 100644
--- a/chrome/android/java/res/drawable-nodpi/widget_preview.png
+++ b/chrome/android/java/res/drawable-nodpi/widget_preview.png
Binary files differ
diff --git a/chrome/android/java/res/layout/contacts_picker_toolbar.xml b/chrome/android/java/res/layout/contacts_picker_toolbar.xml
index 6cd3b1e..0cef73a0 100644
--- a/chrome/android/java/res/layout/contacts_picker_toolbar.xml
+++ b/chrome/android/java/res/layout/contacts_picker_toolbar.xml
@@ -18,6 +18,15 @@
         android:layout_height="wrap_content"
         android:layout_gravity="end">
 
+        <!-- The ContentDescription for this View gets set in Java -->
+        <ImageView
+            android:id="@+id/search"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:background="@drawable/ic_search"
+            tools:ignore="ContentDescription" />
+
         <Button
             android:id="@+id/done"
             android:layout_width="wrap_content"
diff --git a/chrome/android/java/res/layout/download_manager_video_item.xml b/chrome/android/java/res/layout/download_manager_video_item.xml
new file mode 100644
index 0000000..c3a8259
--- /dev/null
+++ b/chrome/android/java/res/layout/download_manager_video_item.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+ <android.support.v7.widget.GridLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:clickable="true"
+    android:background="@drawable/hairline_border_card_background"
+    app:columnCount="2"
+    app:rowCount="3">
+
+    <org.chromium.ui.widget.RoundedCornerImageView
+        android:id="@+id/thumbnail"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:scaleType="centerCrop"
+        android:adjustViewBounds="true"
+        android:background="@color/google_grey_300"
+        android:src="@drawable/audio_playing_square"
+        app:layout_column="0"
+        app:layout_row="0"
+        app:layout_columnSpan="2"
+        app:layout_gravity="center"
+        app:cornerRadiusTopStart="@dimen/download_manager_prefetch_thumbnail_corner_radius"
+        app:cornerRadiusTopEnd="@dimen/download_manager_prefetch_thumbnail_corner_radius"/>
+
+   <org.chromium.chrome.browser.download.home.view.SelectionView
+       android:id="@+id/selection"
+       style="@style/DownloadItemSelectionView"
+       app:layout_column="0"
+       app:layout_row="0" />
+
+    <TextView
+        android:id="@+id/title"
+        style="@style/DownloadItemText"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="11dp"
+        android:textAppearance="@style/BlackTitle1"
+        app:layout_column="0"
+        app:layout_row="1"
+        android:layout_marginStart="16dp"
+        app:layout_gravity="fill_horizontal" />
+
+    <TextView
+        android:id="@+id/caption"
+        style="@style/DownloadItemText"
+        android:layout_width="wrap_content"
+        android:layout_marginBottom="11dp"
+        android:textAppearance="@style/BlackHint2"
+        app:layout_column="0"
+        app:layout_row="2"
+        android:layout_marginStart="16dp"
+        app:layout_gravity="fill_horizontal" />
+
+    <include layout="@layout/list_menu_button"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        app:layout_column="1"
+        app:layout_row="1"
+        app:layout_rowSpan="2"
+        app:layout_gravity="center_vertical|end" />
+
+</android.support.v7.widget.GridLayout>
diff --git a/chrome/android/java/res/layout/new_tab_page_layout.xml b/chrome/android/java/res/layout/new_tab_page_layout.xml
index fe391b0d..4105edc 100644
--- a/chrome/android/java/res/layout/new_tab_page_layout.xml
+++ b/chrome/android/java/res/layout/new_tab_page_layout.xml
@@ -43,6 +43,7 @@
         android:paddingTop="3dp"
         android:paddingBottom="3dp" >
         <EditText
+            style="@style/TextAppearance.NewTabPageSearchBoxText"
             android:id="@+id/search_box_text"
             android:layout_width="0dp"
             android:layout_height="match_parent"
@@ -57,8 +58,7 @@
             android:focusable="false"
             android:gravity="center_vertical"
             android:inputType="text"
-            android:singleLine="true"
-            android:textAppearance="@style/TextAppearance.NewTabPageSearchBoxText" />
+            android:singleLine="true" />
         <org.chromium.chrome.browser.widget.TintedImageView
             android:id="@+id/voice_search_button"
             android:layout_width="wrap_content"
diff --git a/chrome/android/java/res/layout/search_activity.xml b/chrome/android/java/res/layout/search_activity.xml
index b75ebec4..df9c8bcb 100644
--- a/chrome/android/java/res/layout/search_activity.xml
+++ b/chrome/android/java/res/layout/search_activity.xml
@@ -17,15 +17,6 @@
             android:background="@android:color/white"
             android:layout="@layout/omnibox_results_container" />
 
-    <ImageView
-            android:id="@+id/toolbar_shadow"
-            android:src="@drawable/toolbar_shadow"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/toolbar_height_no_shadow"
-            android:scaleType="fitXY"
-            tools:ignore="ContentDescription" />
-
     <FrameLayout
             android:id="@+id/toolbar"
             android:layout_width="match_parent"
@@ -38,8 +29,10 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_vertical"
-                android:layout_marginStart="@dimen/search_activity_location_bar_margin_start"
-                android:layout_marginEnd="@dimen/search_activity_location_bar_margin_end" />
+                android:layout_marginTop="@dimen/location_bar_vertical_margin"
+                android:layout_marginBottom="@dimen/location_bar_vertical_margin"
+                android:layout_marginStart="@dimen/modern_toolbar_background_focused_left_margin"
+                android:layout_marginEnd="@dimen/modern_toolbar_background_focused_left_margin" />
 
     </FrameLayout>
 
diff --git a/chrome/android/java/res/layout/search_widget_template.xml b/chrome/android/java/res/layout/search_widget_template.xml
index 4571adf..02d17a75 100644
--- a/chrome/android/java/res/layout/search_widget_template.xml
+++ b/chrome/android/java/res/layout/search_widget_template.xml
@@ -15,7 +15,7 @@
         android:layout_height="wrap_content"
         android:layout_gravity="center"
         android:alpha="0.9"
-        android:background="@drawable/card_single"
+        android:background="@drawable/modern_toolbar_background_white"
         android:gravity="center_vertical"
         android:orientation="horizontal" >
 
@@ -23,6 +23,7 @@
          and sits in a 48dp x 48dp box.  This means there's an implied 12dp of padding
          around the entire microphone. -->
     <TextView
+            style="@style/TextAppearance.NewTabPageSearchBoxText"
             android:id="@+id/title"
             android:layout_width="0dp"
             android:layout_height="48dp"
@@ -31,9 +32,7 @@
             android:gravity="center_vertical"
             android:singleLine="true"
             android:textAlignment="viewStart"
-            android:text="@string/search_widget_default"
-            android:textColor="@color/google_grey_600"
-            android:textSize="17sp" />
+            android:hint="@string/search_widget_default" />
 
     <ImageView
             android:id="@+id/microphone_icon"
diff --git a/chrome/android/java/res/values-sw600dp/dimens.xml b/chrome/android/java/res/values-sw600dp/dimens.xml
index 8408e13d..797143a 100644
--- a/chrome/android/java/res/values-sw600dp/dimens.xml
+++ b/chrome/android/java/res/values-sw600dp/dimens.xml
@@ -43,10 +43,6 @@
     <!-- Payments UI -->
     <dimen name="payments_ui_max_dialog_width">600dp</dimen>
 
-    <!-- Search widget dimensions -->
-    <dimen name="search_activity_location_bar_margin_start">0dp</dimen>
-    <dimen name="search_activity_location_bar_margin_end">4dp</dimen>
-
     <!-- Clear browsing data preferences dimensions -->
     <dimen name="clear_browsing_data_button_margin">16dp</dimen>
 
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 9dd564f..fccda2d1 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -50,7 +50,7 @@
     <!-- Extend MainThemeBase rather than MainTheme to avoid values-v27 navigation bar colors from
          being applied -->
     <style name="SearchActivityTheme" parent="MainThemeBase">
-        <item name="android:windowBackground">@color/google_grey_500</item>
+        <item name="android:windowBackground">@android:color/white</item>
     </style>
 
     <style name="TabbedModeThemeBase" parent="MainTheme">
@@ -615,6 +615,11 @@
     <style name="NewTabPageRecyclerView">
         <item name="android:colorEdgeEffect" tools:targetApi="21">@color/google_grey_300</item>
     </style>
+
+    <!-- Use style="..." for the following search box style as textColorHint
+         can not use android:textAppearance="...".  textColorHint is defined
+         in a parent theme therefore can not be overridden by appearance.
+    -->
     <style name="TextAppearance.NewTabPageSearchBoxText">
         <item name="android:textSize">@dimen/location_bar_url_text_size</item>
         <item name="android:textColorHint">@color/search_box_hint</item>
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml
index 4e642cb..7ef18ef 100644
--- a/chrome/android/java/res/values/colors.xml
+++ b/chrome/android/java/res/values/colors.xml
@@ -44,6 +44,7 @@
 
     <color name="modern_light_grey">#F1F3F4</color>
     <color name="modern_grey_800">#3C4043</color>
+    <color name="modern_grey_700">#5F6368</color>
     <color name="modern_grey_500">#9AA0A6</color>
 
     <!-- Old color palette -->
@@ -56,7 +57,6 @@
     <color name="google_grey_200">#EEEEEE</color>
     <color name="google_grey_300">#E0E0E0</color>
     <color name="google_grey_400">#BDBDBD</color>
-    <color name="google_grey_500">#9E9E9E</color>
     <color name="google_grey_600">#757575</color>
     <color name="light_grey">#CCCCCC</color>
 
@@ -221,7 +221,7 @@
     <color name="toolbar_shadow_color">@color/black_alpha_11</color>
     <color name="bottom_system_nav_color">@android:color/white</color>
     <color name="bottom_system_nav_divider_color">@color/black_alpha_12</color>
-    <color name="search_box_hint">@color/black_alpha_54</color>
+    <color name="search_box_hint">@color/modern_grey_700</color>
     <color name="placeholder_thumbnail_bg_color">#DADCE0</color>
     <color name="clear_browsing_data_selected_tab_color">@color/modern_blue_600</color>
 
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 15860c8a..836c9b3 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -550,10 +550,6 @@
     <!-- Divider Dimensions -->
     <dimen name="divider_height">1dp</dimen>
 
-    <!-- Search widget dimensions -->
-    <dimen name="search_activity_location_bar_margin_start">16dp</dimen>
-    <dimen name="search_activity_location_bar_margin_end">8dp</dimen>
-
     <!-- Reader Mode dimensions -->
     <!-- Padding surrounding the message. -->
     <dimen name="reader_mode_infobar_text_padding">8dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUi.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUi.java
index afc0e6c..9752b67 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUi.java
@@ -22,6 +22,9 @@
  * Thread safety: All methods on this class should be called on the UI thread.
  */
 public class TrustedWebActivityUi {
+    /** The Digital Asset Link relationship used for Trusted Web Activities. */
+    private final static int RELATIONSHIP = CustomTabsService.RELATION_HANDLE_ALL_URLS;
+
     private final TrustedWebActivityUiDelegate mDelegate;
     private final TrustedWebActivityDisclosure mDisclosure;
 
@@ -82,8 +85,8 @@
 
             // This doesn't perform a network request or attempt new verification - it checks to
             // see if a verification already exists for the given inputs.
-            setTrustedWebActivityMode(OriginVerifier.isValidOrigin(packageName, new Origin(url),
-                    CustomTabsService.RELATION_HANDLE_ALL_URLS), tab);
+            setTrustedWebActivityMode(
+                    OriginVerifier.isValidOrigin(packageName, new Origin(url), RELATIONSHIP), tab);
         }
     };
 
@@ -123,6 +126,22 @@
     }
 
     /**
+     * Perform verification for the URL that the CustomTabActivity starts on.
+     */
+    public void attemptVerificationForInitialUrl(String url, Tab tab) {
+        assert mDelegate.getClientPackageName() != null;
+
+        String packageName = mDelegate.getClientPackageName();
+        Origin origin = new Origin(url);
+
+        new OriginVerifier((packageName2, origin2, verified, online) -> {
+            if (!origin.equals(new Origin(tab.getUrl()))) return;
+
+            setTrustedWebActivityMode(verified, tab);
+        }, packageName, RELATIONSHIP).start(origin);
+    }
+
+    /**
      * Updates the UI appropriately for whether or not Trusted Web Activity mode is enabled.
      */
     private void setTrustedWebActivityMode(boolean enabled, Tab tab) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
index c2e16a2..88a2db0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
@@ -57,6 +57,22 @@
     }
 
     /**
+     * Sets the search query (filter) for the contact list. Filtering is by display name.
+     * @param query The search term to use.
+     */
+    public void setSearchString(String query) {
+        String searchString = "%" + query + "%";
+        String[] selectionArgs = {searchString};
+        mContactsCursor.close();
+
+        mContactsCursor = mCategoryView.getActivity().getContentResolver().query(
+                ContactsContract.Contacts.CONTENT_URI, PROJECTION,
+                ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?", selectionArgs,
+                ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " ASC");
+        notifyDataSetChanged();
+    }
+
+    /**
      * Fetches all known contacts and their emails.
      * @return The contact list as a set.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
index 5cb963b..990c044 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
@@ -13,25 +13,30 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
+import android.widget.ImageView;
 import android.widget.RelativeLayout;
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
 import org.chromium.chrome.browser.widget.selection.SelectableListLayout;
+import org.chromium.chrome.browser.widget.selection.SelectableListToolbar;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.ui.ContactsPickerListener;
 import org.chromium.ui.UiUtils;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 
 /**
  * A class for keeping track of common data associated with showing contact details in
  * the contacts picker, for example the RecyclerView.
  */
-public class PickerCategoryView extends RelativeLayout implements View.OnClickListener {
+public class PickerCategoryView extends RelativeLayout
+        implements View.OnClickListener, SelectionDelegate.SelectionObserver<ContactDetails>,
+                   SelectableListToolbar.SearchDelegate {
     // Constants for the RoundedIconGenerator.
     private static final int ICON_SIZE_DP = 32;
     private static final int ICON_CORNER_RADIUS_DP = 20;
@@ -67,6 +72,12 @@
     // The {@link SelectionDelegate} keeping track of which contacts are selected.
     private SelectionDelegate<ContactDetails> mSelectionDelegate;
 
+    // The search icon.
+    private ImageView mSearchButton;
+
+    // Keeps track of list of last selected contacts.
+    List<ContactDetails> mPreviousSelection;
+
     // The Done text button that confirms the selection choice.
     private Button mDoneButton;
 
@@ -79,6 +90,7 @@
         mActivity = (Activity) context;
 
         mSelectionDelegate = new SelectionDelegate<ContactDetails>();
+        mSelectionDelegate.addObserver(this);
 
         Resources resources = getActivity().getResources();
         int iconColor =
@@ -97,7 +109,10 @@
                 R.string.contacts_picker_select_contacts, null, 0, 0, R.color.modern_primary_color,
                 null, false, false);
         mToolbar.setNavigationOnClickListener(this);
+        mToolbar.initializeSearchView(this, R.string.contacts_picker_search, 0);
 
+        mSearchButton = (ImageView) mToolbar.findViewById(R.id.search);
+        mSearchButton.setOnClickListener(this);
         mDoneButton = (Button) mToolbar.findViewById(R.id.done);
         mDoneButton.setOnClickListener(this);
 
@@ -128,6 +143,58 @@
         mPickerAdapter.notifyDataSetChanged();
     }
 
+    private void onStartSearch() {
+        mDoneButton.setVisibility(GONE);
+
+        // Showing the search clears current selection. Save it, so we can restore it after the
+        // search has completed.
+        mPreviousSelection = mSelectionDelegate.getSelectedItems();
+        mSearchButton.setVisibility(GONE);
+        mToolbar.showSearchView();
+    }
+
+    // SelectableListToolbar.SearchDelegate:
+
+    @Override
+    public void onEndSearch() {
+        mPickerAdapter.setSearchString("");
+        mToolbar.showCloseButton();
+        mToolbar.setNavigationOnClickListener(this);
+        mDoneButton.setVisibility(VISIBLE);
+        mSearchButton.setVisibility(VISIBLE);
+
+        // Hiding the search view clears the selection. Save it first and restore to the old
+        // selection, with the new item added during search.
+        // TODO(finnur): This needs to be revisited after UX is finalized.
+        HashSet<ContactDetails> selection = new HashSet<>();
+        for (ContactDetails item : mSelectionDelegate.getSelectedItems()) {
+            selection.add(item);
+        }
+        mToolbar.hideSearchView();
+        for (ContactDetails item : mPreviousSelection) {
+            selection.add(item);
+        }
+
+        // TODO(finnur): Do this asynchronously to make the number roll view show the right number.
+        mSelectionDelegate.setSelectedItems(selection);
+    }
+
+    @Override
+    public void onSearchTextChanged(String query) {
+        mPickerAdapter.setSearchString(query);
+    }
+
+    // SelectionDelegate.SelectionObserver:
+
+    @Override
+    public void onSelectionStateChange(List<ContactDetails> selectedItems) {
+        // Once a selection is made, drop out of search mode. Note: This function is also called
+        // when entering search mode (with selectedItems then being 0 in size).
+        if (mToolbar.isSearching() && selectedItems.size() > 0) {
+            mToolbar.hideSearchView();
+        }
+    }
+
     // OnClickListener:
 
     @Override
@@ -135,6 +202,8 @@
         int id = view.getId();
         if (id == R.id.done) {
             notifyContactsSelected();
+        } else if (id == R.id.search) {
+            onStartSearch();
         } else {
             executeAction(ContactsPickerListener.ContactsPickerAction.CANCEL, null);
         }
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 01b1382..db3e819 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
@@ -660,6 +660,7 @@
         }
 
         if (mTrustedWebActivityUi != null) {
+            mTrustedWebActivityUi.attemptVerificationForInitialUrl(url, getActivityTab());
             mTrustedWebActivityUi.initialShowSnackbarIfNeeded();
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/ThumbnailRequestGlue.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/ThumbnailRequestGlue.java
index a5ff269c..9ec41e03 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/ThumbnailRequestGlue.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/ThumbnailRequestGlue.java
@@ -5,10 +5,14 @@
 package org.chromium.chrome.browser.download.home.glue;
 
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 
 import org.chromium.base.Callback;
+import org.chromium.base.ContextUtils;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.widget.ThumbnailProvider.ThumbnailRequest;
 import org.chromium.components.offline_items_collection.OfflineItem;
+import org.chromium.components.offline_items_collection.OfflineItemFilter;
 import org.chromium.components.offline_items_collection.OfflineItemVisuals;
 import org.chromium.components.offline_items_collection.VisualsCallback;
 
@@ -62,6 +66,14 @@
 
     @Override
     public boolean getThumbnail(Callback<Bitmap> callback) {
+        // TODO(shaktisahu, xingliu): Remove this after video thumbnail generation pipeline is done.
+        if (mItem.filter == OfflineItemFilter.FILTER_VIDEO) {
+            callback.onResult(BitmapFactory.decodeResource(
+                    ContextUtils.getApplicationContext().getResources(),
+                    R.drawable.audio_playing_square));
+            return true;
+        }
+
         return mProvider.getVisualsForItem(mItem.id, (id, visuals) -> {
             if (visuals == null) {
                 callback.onResult(null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
index 93ef2a3..fb2eb0b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
@@ -143,6 +143,11 @@
                     outRect.top = mImagePaddingPx;
                     outRect.bottom = mImagePaddingPx;
                     break;
+                case ListUtils.ViewType.VIDEO:
+                    outRect.left = mPrefetchHorizontalPaddingPx;
+                    outRect.right = mPrefetchHorizontalPaddingPx;
+                    outRect.bottom = mPrefetchHorizontalPaddingPx;
+                    break;
                 case ListUtils.ViewType.PREFETCH:
                     outRect.left = mPrefetchHorizontalPaddingPx;
                     outRect.right = mPrefetchHorizontalPaddingPx;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
index 9152116..cf54202e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
@@ -36,7 +36,7 @@
             case ListUtils.ViewType.GENERIC:
                 return GenericViewHolder.create(parent);
             case ListUtils.ViewType.VIDEO:
-                return new VideoViewHolder(parent);
+                return VideoViewHolder.create(parent);
             case ListUtils.ViewType.IMAGE:
                 return ImageViewHolder.create(parent);
             case ListUtils.ViewType.CUSTOM_VIEW:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java
index 71432b67..03d1fd5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java
@@ -4,25 +4,73 @@
 
 package org.chromium.chrome.browser.download.home.list.holder;
 
-import android.support.v7.widget.AppCompatTextView;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
 import android.view.ViewGroup;
+import android.widget.ImageView;
 import android.widget.TextView;
 
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.download.home.list.ListItem;
+import org.chromium.chrome.browser.download.home.list.UiUtils;
+import org.chromium.chrome.browser.download.home.view.LoadingBackground;
 import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.components.offline_items_collection.OfflineItemVisuals;
 
 /**
  * A {@link RecyclerView.ViewHolder} specifically meant to display a video {@code OfflineItem}.
  */
-public class VideoViewHolder extends ListItemViewHolder {
-    public VideoViewHolder(ViewGroup parent) {
-        super(new AppCompatTextView(parent.getContext()));
+public class VideoViewHolder extends ThumbnailAwareViewHolder {
+    private final TextView mTitle;
+    private final TextView mCaption;
+    private final ImageView mThumbnailView;
+    private LoadingBackground mLoadingBackground;
+
+    /**
+     * Creates a new {@link VideoViewHolder} instance.
+     */
+    public static VideoViewHolder create(ViewGroup parent) {
+        View view = LayoutInflater.from(parent.getContext())
+                            .inflate(R.layout.download_manager_video_item, null);
+        int thumbnailWidth = parent.getContext().getResources().getDimensionPixelSize(
+                R.dimen.download_manager_image_width);
+        int thumbnailHeight = parent.getContext().getResources().getDimensionPixelSize(
+                R.dimen.download_manager_image_width);
+        return new VideoViewHolder(view, thumbnailWidth, thumbnailHeight);
     }
 
-    // ListItemViewHolder implementation.
+    public VideoViewHolder(View view, int thumbnailWidthPx, int thumbnailHeightPx) {
+        super(view, thumbnailWidthPx, thumbnailHeightPx);
+
+        mTitle = (TextView) itemView.findViewById(R.id.title);
+        mCaption = (TextView) itemView.findViewById(R.id.caption);
+        mThumbnailView = (ImageView) itemView.findViewById(R.id.thumbnail);
+        mLoadingBackground = new LoadingBackground(view.getContext());
+    }
+
     @Override
     public void bind(PropertyModel properties, ListItem item) {
+        super.bind(properties, item);
         ListItem.OfflineItemListItem offlineItem = (ListItem.OfflineItemListItem) item;
-        ((TextView) itemView).setText(offlineItem.item.title);
+
+        mTitle.setText(offlineItem.item.title);
+        mCaption.setText(UiUtils.generateGenericCaption(offlineItem.item));
+        mThumbnailView.setContentDescription(offlineItem.item.title);
+    }
+
+    @Override
+    void onVisualsChanged(ImageView view, @Nullable OfflineItemVisuals visuals) {
+        view.setImageBitmap(visuals == null ? null : visuals.icon);
+    }
+
+    @Override
+    protected void showLoadingView(ImageView view) {
+        mLoadingBackground.show(view);
+    }
+
+    @Override
+    protected void hideLoadingView() {
+        mLoadingBackground.hide();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SuggestionView.java
index e289e30e..2cc28e06 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SuggestionView.java
@@ -316,7 +316,10 @@
                 .getDimension(R.dimen.omnibox_suggestion_second_line_text_size));
 
         mRefineViewOffsetPx = useModernDesign ? mRefineViewModernEndPadding : 0;
-        mSuggestionViewStartOffset = useModernDesign ? mSuggestionListModernOffset : 0;
+        mSuggestionViewStartOffset =
+                useModernDesign && !mLocationBar.mustQueryUrlBarLocationForSuggestions()
+                ? mSuggestionListModernOffset
+                : 0;
 
         // Suggestions with attached answers are rendered with rich results regardless of which
         // suggestion type they are.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
index 8f604b10..ad3ac5a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.os.Handler;
 import android.support.annotation.Nullable;
+import android.support.v4.view.ViewCompat;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
@@ -20,6 +21,7 @@
 import org.chromium.chrome.browser.omnibox.UrlBarCoordinator.SelectionState;
 import org.chromium.chrome.browser.omnibox.UrlBarData;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.toolbar.ToolbarPhone;
 import org.chromium.ui.UiUtils;
 
 import java.util.List;
@@ -42,6 +44,25 @@
     public SearchActivityLocationBarLayout(Context context, AttributeSet attrs) {
         super(context, attrs, R.layout.location_bar_base);
         setUrlBarFocusable(true);
+        setBackground(ToolbarPhone.createModernLocationBarBackground(getResources()));
+
+        // Now you might ask yourself what these paddings are doing? Great question, really glad
+        // you asked...OH MY...LOOK BEHIND YOU!!!
+        //
+        // <Sounds of original author running away>
+        //
+        // These numbers were chosen at a single snapshot in time where they roughly equated to
+        // the padding used in the modern tabbed mode focused omnibox.  There is minimal rhyme or
+        // reason to this, but using something that approximated the toolbar logic looked even
+        // worse.
+        //
+        // Shrug. Once that layout logic is cleaned up, this should be universally replaced with
+        // something sane.
+        int toolbarSidePadding = getResources().getDimensionPixelSize(R.dimen.toolbar_edge_padding);
+        int backgroundSidePadding = getResources().getDimensionPixelSize(
+                R.dimen.modern_toolbar_background_focused_left_margin);
+        ViewCompat.setPaddingRelative(this, backgroundSidePadding + toolbarSidePadding,
+                getPaddingTop(), backgroundSidePadding, getPaddingBottom());
         mPendingSearchPromoDecision = LocaleManager.getInstance().needToCheckForSearchEnginePromo();
     }
 
@@ -163,6 +184,6 @@
 
     @Override
     public boolean useModernDesign() {
-        return false;
+        return true;
     }
 }
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 c56ce718..778b978 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
@@ -284,7 +284,7 @@
         String text = TextUtils.isEmpty(engineName) || !shouldShowFullString()
                 ? context.getString(R.string.search_widget_default)
                 : context.getString(R.string.search_with_product, engineName);
-        views.setTextViewText(R.id.title, text);
+        views.setCharSequence(R.id.title, "setHint", text);
 
         return views;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarCoordinator.java
index 23ac32f..6c61519 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarCoordinator.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.toolbar;
 
+import android.content.res.ColorStateList;
+import android.support.v7.content.res.AppCompatResources;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnTouchListener;
@@ -21,6 +23,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.BottomToolbarViewBinder.ViewHolder;
 import org.chromium.chrome.browser.toolbar.ToolbarButtonSlotData.ToolbarButtonData;
+import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.resources.ResourceManager;
 
@@ -40,11 +43,17 @@
     /** The menu button that lives in the bottom toolbar. */
     private final MenuButton mMenuButton;
 
+    /** The light mode tint to be used in bottom toolbar buttons. */
+    private final ColorStateList mLightModeTint;
+
+    /** The dark mode tint to be used in bottom toolbar buttons. */
+    private final ColorStateList mDarkModeTint;
+
     /**
      * Build the coordinator that manages the bottom toolbar.
      * @param fullscreenManager A {@link ChromeFullscreenManager} to update the bottom controls
      *                          height for the renderer.
-     * @param root The root {@link ViewGroup} for locating the vies to inflate.
+     * @param root The root {@link ViewGroup} for locating the views to inflate.
      * @param firstSlotData The data required to fill in the leftmost bottom toolbar button slot.
      * @param secondSlotData The data required to fill in the second bottom toolbar button slot.
      */
@@ -71,6 +80,11 @@
 
         mTabSwitcherButtonCoordinator = new TabSwitcherButtonCoordinator(toolbarRoot);
         mMenuButton = toolbarRoot.findViewById(R.id.menu_button_wrapper);
+
+        mLightModeTint =
+                AppCompatResources.getColorStateList(root.getContext(), R.color.light_mode_tint);
+        mDarkModeTint =
+                AppCompatResources.getColorStateList(root.getContext(), R.color.dark_mode_tint);
     }
 
     /**
@@ -149,6 +163,15 @@
         return mMenuButton.getMenuButton();
     }
 
+    public void setPrimaryColor(int color) {
+        mMediator.setPrimaryColor(color);
+
+        final boolean useLight = ColorUtils.shouldUseLightForegroundOnBackground(color);
+        final ColorStateList tint = useLight ? mLightModeTint : mDarkModeTint;
+        mTabSwitcherButtonCoordinator.setTint(tint);
+        mMenuButton.setTint(tint);
+    }
+
     /**
      * Clean up any state when the bottom toolbar is destroyed.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java
index 5bab11d..a3b1392 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java
@@ -247,4 +247,8 @@
                     mSecondSlotData.tabSwitcherModeButtonData);
         }
     }
+
+    void setPrimaryColor(int color) {
+        mModel.setValue(BottomToolbarModel.PRIMARY_COLOR, color);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java
index bd868dd..7df5c40 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java
@@ -47,10 +47,13 @@
     public static final ObjectPropertyKey<ToolbarButtonData> SECOND_BUTTON_DATA =
             new ObjectPropertyKey<>();
 
+    /** Primary color of bottom toolbar. */
+    public static final IntPropertyKey PRIMARY_COLOR = new IntPropertyKey();
+
     /** Default constructor. */
     public BottomToolbarModel() {
         super(Y_OFFSET, ANDROID_VIEW_VISIBLE, COMPOSITED_VIEW_VISIBLE, LAYOUT_MANAGER,
                 TOOLBAR_SWIPE_LAYOUT, RESOURCE_MANAGER, TOOLBAR_SWIPE_HANDLER, FIRST_BUTTON_DATA,
-                SECOND_BUTTON_DATA);
+                SECOND_BUTTON_DATA, PRIMARY_COLOR);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java
index c8d70aa..fa962b12 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java
@@ -12,6 +12,7 @@
 import org.chromium.chrome.browser.modelutil.PropertyKey;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.toolbar.ToolbarButtonSlotData.ToolbarButtonData;
+import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.widget.TintedImageButton;
 
 /**
@@ -89,21 +90,40 @@
                     model.getValue(BottomToolbarModel.TOOLBAR_SWIPE_HANDLER));
         } else if (BottomToolbarModel.FIRST_BUTTON_DATA == propertyKey) {
             updateButton(view.firstTintedImageButton,
-                    model.getValue(BottomToolbarModel.FIRST_BUTTON_DATA));
+                    model.getValue(BottomToolbarModel.FIRST_BUTTON_DATA), useLightIcons(model));
         } else if (BottomToolbarModel.SECOND_BUTTON_DATA == propertyKey) {
             updateButton(view.secondTintedImageButton,
-                    model.getValue(BottomToolbarModel.SECOND_BUTTON_DATA));
+                    model.getValue(BottomToolbarModel.SECOND_BUTTON_DATA), useLightIcons(model));
+        } else if (BottomToolbarModel.PRIMARY_COLOR == propertyKey) {
+            final boolean useLightIcons = useLightIcons(model);
+            view.toolbarRoot.findViewById(R.id.bottom_sheet_toolbar)
+                    .setBackgroundColor(model.getValue(BottomToolbarModel.PRIMARY_COLOR));
+            updateButtonDrawable(view.firstTintedImageButton,
+                    model.getValue(BottomToolbarModel.FIRST_BUTTON_DATA), useLightIcons);
+            updateButtonDrawable(view.secondTintedImageButton,
+                    model.getValue(BottomToolbarModel.SECOND_BUTTON_DATA), useLightIcons);
         } else {
             assert false : "Unhandled property detected in BottomToolbarViewBinder!";
         }
     }
 
-    private static void updateButton(TintedImageButton button, ToolbarButtonData buttonData) {
+    private static boolean useLightIcons(BottomToolbarModel model) {
+        return ColorUtils.shouldUseLightForegroundOnBackground(
+                model.getValue(BottomToolbarModel.PRIMARY_COLOR));
+    }
+
+    private static void updateButton(
+            TintedImageButton button, ToolbarButtonData buttonData, boolean useLightIcons) {
         if (buttonData == null) {
             button.setVisibility(View.INVISIBLE);
         } else {
-            buttonData.updateButton(button);
+            buttonData.updateButton(button, useLightIcons);
             button.setVisibility(View.VISIBLE);
         }
     }
+
+    private static void updateButtonDrawable(
+            TintedImageButton button, ToolbarButtonData buttonData, boolean useLightIcons) {
+        if (buttonData != null) buttonData.updateButtonDrawable(button, useLightIcons);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java
index 71b036d..1c1159d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java
@@ -5,19 +5,21 @@
 package org.chromium.chrome.browser.toolbar;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnTouchListener;
 import android.widget.FrameLayout;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.widget.TintedImageButton;
 
 /**
  * The overflow menu button.
  */
 class MenuButton extends FrameLayout {
-    /** The view for the menu button. */
-    private View mMenuButtonView;
+    /** The {@link TintedImageButton} for the menu button. */
+    private TintedImageButton mMenuTintedImageButton;
 
     /** The view for the update badge. */
     private View mUpdateBadgeView;
@@ -29,7 +31,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mMenuButtonView = findViewById(R.id.menu_button);
+        mMenuTintedImageButton = findViewById(R.id.menu_button);
         mUpdateBadgeView = findViewById(R.id.menu_badge);
     }
 
@@ -38,7 +40,7 @@
      *                        clicked.
      */
     void setTouchListener(OnTouchListener onTouchListener) {
-        mMenuButtonView.setOnTouchListener(onTouchListener);
+        mMenuTintedImageButton.setOnTouchListener(onTouchListener);
     }
 
     /**
@@ -56,6 +58,14 @@
     }
 
     View getMenuButton() {
-        return mMenuButtonView;
+        return mMenuTintedImageButton;
+    }
+
+    /**
+     * @param tintList The {@link ColorStateList} that will tint the menu button (the badge is not
+     *                 tinted).
+     */
+    void setTint(ColorStateList tintList) {
+        mMenuTintedImageButton.setTint(tintList);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
index 18170a6..d0206a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.toolbar;
 
+import android.content.res.ColorStateList;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 
@@ -121,6 +122,13 @@
         updateTabCount();
     }
 
+    /**
+     * @param tint The {@ColorStateList} used to tint the button.
+     */
+    public void setTint(ColorStateList tint) {
+        mTabSwitcherButtonModel.setValue(TabSwitcherButtonProperties.TINT, tint);
+    }
+
     public void destroy() {
         if (mTabModelSelector != null) mTabModelSelector.removeObserver(mTabModelSelectorObserver);
         if (mTabModelSelectorTabModelObserver != null) mTabModelSelectorTabModelObserver.destroy();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java
index fb50d0f0..57070b9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.toolbar;
 
+import android.content.res.ColorStateList;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 
@@ -26,6 +27,9 @@
     public static final ObjectPropertyKey<OnLongClickListener> ON_LONG_CLICK_LISTENER =
             new ObjectPropertyKey<>();
 
+    /** The button tint. */
+    public static final ObjectPropertyKey<ColorStateList> TINT = new ObjectPropertyKey<>();
+
     public static final PropertyKey[] ALL_KEYS =
-            new PropertyKey[] {NUMBER_OF_TABS, ON_CLICK_LISTENER, ON_LONG_CLICK_LISTENER};
+            new PropertyKey[] {NUMBER_OF_TABS, ON_CLICK_LISTENER, ON_LONG_CLICK_LISTENER, TINT};
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonView.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonView.java
index 7de441d8..3971e59 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonView.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.toolbar;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.util.AttributeSet;
 import android.widget.ImageView;
 
@@ -18,7 +19,7 @@
     /**
      * A drawable for the tab switcher icon.
      */
-    private TabSwitcherDrawable mTabSwitcherButtonButtonDrawable;
+    private TabSwitcherDrawable mTabSwitcherButtonDrawable;
 
     public TabSwitcherButtonView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -28,9 +29,9 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mTabSwitcherButtonButtonDrawable =
+        mTabSwitcherButtonDrawable =
                 TabSwitcherDrawable.createTabSwitcherDrawable(getContext(), false);
-        setImageDrawable(mTabSwitcherButtonButtonDrawable);
+        setImageDrawable(mTabSwitcherButtonDrawable);
     }
 
     /**
@@ -41,6 +42,13 @@
         setContentDescription(getResources().getQuantityString(
                 R.plurals.accessibility_toolbar_btn_tabswitcher_toggle, numberOfTabs,
                 numberOfTabs));
-        mTabSwitcherButtonButtonDrawable.updateForTabCount(numberOfTabs, false);
+        mTabSwitcherButtonDrawable.updateForTabCount(numberOfTabs, false);
+    }
+
+    /**
+     * @param tint The {@ColorStateList} used to tint the button.
+     */
+    public void setTint(ColorStateList tint) {
+        mTabSwitcherButtonDrawable.setTint(tint);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
index 16920b03..3fef53d5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
@@ -32,6 +32,8 @@
         } else if (TabSwitcherButtonProperties.ON_LONG_CLICK_LISTENER == propertyKey) {
             view.setOnLongClickListener(
                     model.getValue(TabSwitcherButtonProperties.ON_LONG_CLICK_LISTENER));
+        } else if (TabSwitcherButtonProperties.TINT == propertyKey) {
+            view.setTint(model.getValue(TabSwitcherButtonProperties.TINT));
         } else {
             assert false : "Unhandled property detected in TabSwitcherViewBinder!";
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonSlotData.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonSlotData.java
index 8849cba5..c01f058 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonSlotData.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonSlotData.java
@@ -5,7 +5,10 @@
 package org.chromium.chrome.browser.toolbar;
 
 import android.content.Context;
-import android.support.v4.content.ContextCompat;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v7.content.res.AppCompatResources;
 import android.view.View.OnClickListener;
 
 import org.chromium.chrome.R;
@@ -34,42 +37,52 @@
      * buttons when entering or leaving tab switching mode.
      */
     static class ToolbarButtonData {
-        private final int mDrawableResId;
-        // TODO(amaralp): Add incognito accessibility string.
-        private final CharSequence mAccessibilityStringResId;
+        private final Drawable mDrawable;
+        private final CharSequence mLightAccessibilityString;
+        private final CharSequence mDarkAccessibilityString;
         private final OnClickListener mOnClickListener;
-        private final boolean mShouldTint;
+
+        private final ColorStateList mLightTint;
+        private final ColorStateList mDarkTint;
 
         /**
-         * @param drawableResId The drawable's resource id.
-         * @param accessibilityStringResId The accessibility's resource id.
-         * @param onClickListener An {@link OnClickListener} that is triggered when this button is
-         *                        clicked.
-         * @param shouldTint Whether the button should be tinted.
-         * @param context The {@link Context} used to get the drawable and accessibility string
-         *                resources.
+         * @param drawable The {@link Drawable} that will be shown in the button slot.
+         * @param lightAccessibilityString The accessibility string to be used in light mode.
+         * @param darkAccessibilityString The accessibility string to be used in dark mode.
+         * @param onClickListener The listener that will be fired when this button is clicked.
+         * @param context The {@link Context} that is used to obtain tinting information.
          */
-        ToolbarButtonData(int drawableResId, int accessibilityStringResId,
-                OnClickListener onClickListener, boolean shouldTint, Context context) {
-            mAccessibilityStringResId = context.getString(accessibilityStringResId);
+        ToolbarButtonData(Drawable drawable, CharSequence lightAccessibilityString,
+                CharSequence darkAccessibilityString, OnClickListener onClickListener,
+                Context context) {
+            mLightTint = AppCompatResources.getColorStateList(context, R.color.light_mode_tint);
+            mDarkTint = AppCompatResources.getColorStateList(context, R.color.dark_mode_tint);
+
+            mDrawable = drawable;
+            mLightAccessibilityString = lightAccessibilityString;
+            mDarkAccessibilityString = darkAccessibilityString;
             mOnClickListener = onClickListener;
-            mDrawableResId = drawableResId;
-            mShouldTint = shouldTint;
         }
 
         /**
          * @param imageButton The {@link TintedImageButton} this button data will fill.
+         * @param isLight Whether or not to use light mode.
          */
-        void updateButton(TintedImageButton imageButton) {
+        void updateButton(TintedImageButton imageButton, boolean isLight) {
             imageButton.setOnClickListener(mOnClickListener);
-            imageButton.setImageResource(mDrawableResId);
-            imageButton.setContentDescription(mAccessibilityStringResId);
-            if (mShouldTint) {
-                imageButton.setImageTintList(ContextCompat.getColorStateList(
-                        imageButton.getContext(), R.color.dark_mode_tint));
-            } else {
-                imageButton.setImageTintList(null);
-            }
+            updateButtonDrawable(imageButton, isLight);
+        }
+
+        /**
+         * @param imageButton The {@link TintedImageButton} this button data will fill.
+         * @param isLight Whether or not to use light mode.
+         */
+        void updateButtonDrawable(TintedImageButton imageButton, boolean isLight) {
+            DrawableCompat.setTintList(mDrawable, isLight ? mLightTint : mDarkTint);
+            imageButton.setImageDrawable(mDrawable);
+            imageButton.setContentDescription(
+                    isLight ? mLightAccessibilityString : mDarkAccessibilityString);
+            imageButton.invalidate();
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 9dece39..6eb78ec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.toolbar;
 
-import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
@@ -14,6 +13,8 @@
 import android.support.annotation.DrawableRes;
 import android.support.annotation.IntDef;
 import android.support.annotation.StringRes;
+import android.support.graphics.drawable.VectorDrawableCompat;
+import android.support.v4.content.ContextCompat;
 import android.support.v7.app.ActionBar;
 import android.text.TextUtils;
 import android.view.View;
@@ -642,9 +643,9 @@
     public void enableBottomToolbar() {
         if (FeatureUtilities.isBottomToolbarEnabled()) {
             final ToolbarButtonSlotData firstButtonSlot =
-                    new ToolbarButtonSlotData(createHomeButton(mActivity));
+                    new ToolbarButtonSlotData(createHomeButton());
             final ToolbarButtonSlotData secondButtonSlot =
-                    new ToolbarButtonSlotData(createSearchAccelerator(mActivity));
+                    new ToolbarButtonSlotData(createSearchAccelerator());
             mBottomToolbarCoordinator = new BottomToolbarCoordinator(
                     mActivity.getFullscreenManager(), mActivity.findViewById(R.id.coordinator),
                     firstButtonSlot, secondButtonSlot);
@@ -672,38 +673,54 @@
         };
     }
 
-    private ToolbarButtonData createHomeButton(Context context) {
+    private ToolbarButtonData createHomeButton() {
         final OnClickListener homeButtonListener = v -> {
             recordBottomToolbarUseForIPH();
             openHomepage();
         };
-        return new ToolbarButtonData(R.drawable.btn_toolbar_home,
-                R.string.accessibility_toolbar_btn_home, homeButtonListener, true, context);
+        final Drawable drawable = ContextCompat.getDrawable(mActivity, R.drawable.btn_toolbar_home);
+        final CharSequence accessibilityString =
+                mActivity.getString(R.string.accessibility_toolbar_btn_home);
+        return new ToolbarButtonData(
+                drawable, accessibilityString, accessibilityString, homeButtonListener, mActivity);
     }
 
-    private ToolbarButtonData createNewTabButton(
-            OnClickListener newTabClickListener, Context context) {
-        return new ToolbarButtonData(R.drawable.btn_new_tab_white_normal,
-                R.string.accessibility_toolbar_btn_new_tab, newTabClickListener, false, context);
+    private ToolbarButtonData createNewTabButton(OnClickListener newTabClickListener) {
+        final CharSequence normalAccessibilityString =
+                mActivity.getString(R.string.accessibility_toolbar_btn_new_tab);
+        final CharSequence incognitoAccessibilityString =
+                mActivity.getString(R.string.accessibility_toolbar_btn_new_incognito_tab);
+
+        final Drawable drawable = VectorDrawableCompat.create(
+                mActivity.getResources(), R.drawable.new_tab_icon, mActivity.getTheme());
+        return new ToolbarButtonData(drawable, normalAccessibilityString,
+                incognitoAccessibilityString, newTabClickListener, mActivity);
     }
 
-    private ToolbarButtonData createSearchAccelerator(Context context) {
+    private ToolbarButtonData createSearchAccelerator() {
         final OnClickListener searchAcceleratorListener = v -> {
             recordBottomToolbarUseForIPH();
             recordOmniboxFocusReason(OmniboxFocusReason.ACCELERATOR_TAP);
             ACCELERATOR_BUTTON_TAP_ACTION.record();
             setUrlBarFocus(true);
         };
-        return new ToolbarButtonData(R.drawable.ic_search,
-                R.string.accessibility_toolbar_btn_search_accelerator, searchAcceleratorListener,
-                true, context);
+        final Drawable drawable = ContextCompat.getDrawable(mActivity, R.drawable.ic_search);
+        final CharSequence accessibilityString =
+                mActivity.getString(R.string.accessibility_toolbar_btn_search_accelerator);
+        return new ToolbarButtonData(drawable, accessibilityString, accessibilityString,
+                searchAcceleratorListener, mActivity);
     }
 
     private ToolbarButtonData createIncognitoToggleButton(
-            OnClickListener incognitoToggleClickHandler, Context context) {
-        return new ToolbarButtonData(R.drawable.btn_tabstrip_switch_normal,
-                R.string.accessibility_tabstrip_btn_incognito_toggle_standard,
-                incognitoToggleClickHandler, false, context);
+            OnClickListener incognitoToggleClickHandler) {
+        final CharSequence normalAccessibilityString =
+                mActivity.getString(R.string.accessibility_tabstrip_btn_incognito_toggle_standard);
+        final CharSequence incognitoAccessibilityString =
+                mActivity.getString(R.string.accessibility_tabstrip_btn_incognito_toggle_incognito);
+        final Drawable drawable =
+                ContextCompat.getDrawable(mActivity, R.drawable.incognito_statusbar);
+        return new ToolbarButtonData(drawable, normalAccessibilityString,
+                incognitoAccessibilityString, incognitoToggleClickHandler, mActivity);
     }
 
     /**
@@ -831,8 +848,7 @@
                     && PrefServiceBridge.getInstance().isIncognitoModeEnabled();
             final ToolbarButtonData firstSlotTabSwitcherButtonData = showIncognitoToggleButton
                     ? createIncognitoToggleButton(
-                              wrapBottomToolbarClickListenerForIPH(incognitoClickHandler),
-                              mActivity)
+                              wrapBottomToolbarClickListenerForIPH(incognitoClickHandler))
                     : null;
             mAppMenuButtonHelper.setOnClickRunnable(() -> recordBottomToolbarUseForIPH());
             mBottomToolbarCoordinator.initializeWithNative(
@@ -842,8 +858,7 @@
                     mAppMenuButtonHelper, mTabModelSelector, mOverviewModeBehavior,
                     mActivity.getContextualSearchManager(), mActivity.getWindowAndroid(),
                     firstSlotTabSwitcherButtonData,
-                    createNewTabButton(
-                            wrapBottomToolbarClickListenerForIPH(newTabClickHandler), mActivity));
+                    createNewTabButton(wrapBottomToolbarClickListenerForIPH(newTabClickHandler)));
 
             Tab currentTab = tabModelSelector.getCurrentTab();
             maybeShowDuetHelpBubble(currentTab);
@@ -1288,6 +1303,10 @@
         mCurrentThemeColor = color;
         mToolbarModel.setPrimaryColor(color);
         mToolbar.onPrimaryColorChanged(shouldAnimate);
+
+        if (mBottomToolbarCoordinator != null) {
+            mBottomToolbarCoordinator.setPrimaryColor(color);
+        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
index 6ead434..3d98256 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
@@ -419,7 +419,7 @@
             Resources res = getResources();
             mModernLocationBarBackgroundHeight =
                     res.getDimensionPixelSize(R.dimen.modern_toolbar_background_size);
-            mLocationBarBackground = createModernLocationBarBackground();
+            mLocationBarBackground = createModernLocationBarBackground(getResources());
             mLocationBarBackground.getPadding(mLocationBarBackgroundPadding);
             mLocationBarBackground.mutate();
             mLocationBar.setPadding(mLocationBarBackgroundPadding.left,
@@ -448,12 +448,12 @@
     /**
      * @return The drawable for the modern location bar background.
      */
-    public Drawable createModernLocationBarBackground() {
+    public static Drawable createModernLocationBarBackground(Resources resources) {
         Drawable drawable = ApiCompatibilityUtils.getDrawable(
-                getResources(), R.drawable.modern_toolbar_background_white);
+                resources, R.drawable.modern_toolbar_background_white);
         drawable.mutate();
         drawable.setColorFilter(
-                ApiCompatibilityUtils.getColor(getResources(), R.color.modern_light_grey),
+                ApiCompatibilityUtils.getColor(resources, R.color.modern_light_grey),
                 PorterDuff.Mode.SRC_IN);
         return drawable;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectionDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectionDelegate.java
index 03f0b03..3c58623 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectionDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectionDelegate.java
@@ -21,6 +21,7 @@
 
     /**
      * Observer interface to be notified of selection changes.
+     * @param <E> The type of the selectable items this delegate interacts with.
      */
     public interface SelectionObserver<E> {
         /**
@@ -60,6 +61,15 @@
     }
 
     /**
+     * Initializes the selected item list with a new set (clears previous selection).
+     * @param items The items to set as selected.
+     */
+    public void setSelectedItems(Set<E> items) {
+        mSelectedItems = items;
+        notifyObservers();
+    }
+
+    /**
      * True if the item is selected. False otherwise.
      * @param item The item.
      * @return Whether the item is selected.
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 59b760e..05fa4293 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -3072,7 +3072,7 @@
         Update available. More options
       </message>
       <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_SEARCH_ACCELERATOR" desc="Content description for the search accelerator button">
-        Search Accelerator
+        Search button
       </message>
       <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_SITE_INFO" desc="Content description for the page icon that gives more site information when clicked. The icon can be a magnifier for search result pages, or other icons representing the page state.">
         Site information
@@ -3401,6 +3401,9 @@
       </message>
 
       <!-- Contacts Picker strings -->
+      <message name="IDS_CONTACTS_PICKER_SEARCH" desc="The hint text for the search box for contacts.">
+        Search your contacts
+      </message>
       <message name="IDS_CONTACTS_PICKER_SELECT_CONTACTS" desc="The label at the top of the dialog that allows users to select contacts from their device and share the details with a web page.">
         Select contacts
       </message>
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_CONTACTS_PICKER_SEARCH.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_CONTACTS_PICKER_SEARCH.png.sha1
new file mode 100644
index 0000000..ced839a
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_CONTACTS_PICKER_SEARCH.png.sha1
@@ -0,0 +1 @@
+26c0c8e6612f7798a1c2648c17f1cba0cb088aa9
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProviderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProviderTest.java
index fcb36f5..ce2bd15 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProviderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProviderTest.java
@@ -249,7 +249,7 @@
                     // Confirm that the string is correct.
                     TextView titleView = (TextView) view.findViewById(R.id.title);
                     Assert.assertEquals(View.VISIBLE, titleView.getVisibility());
-                    Assert.assertEquals(expectedString, titleView.getText());
+                    Assert.assertEquals(expectedString, titleView.getHint());
 
                     // Confirm the visibility of the microphone.
                     View microphoneView = view.findViewById(R.id.microphone_icon);
diff --git a/chrome/android/modules/ar/AndroidManifest.xml b/chrome/android/modules/ar/AndroidManifest.xml
new file mode 100644
index 0000000..f457fc7
--- /dev/null
+++ b/chrome/android/modules/ar/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:dist="http://schemas.android.com/apk/distribution"
+    package="{{manifest_package}}">
+
+    <!-- Chrome AR is only supported on Android N+. -->
+    <uses-sdk
+        android:minSdkVersion="24"
+        android:targetSdkVersion="{{target_sdk_version}}" />
+
+    <!-- TODO(crbug.com/863068): Set dist:onDemand="true" once we can on-demand
+         install modules. -->
+    <!-- TODO(crbug.com/871912: Use @string reference for dist:title. -->
+    <dist:module
+        dist:onDemand="false"
+        dist:title="AR">
+        <dist:fusing dist:include="false" />
+    </dist:module>
+
+    <application>
+    </application>
+</manifest>
diff --git a/chrome/android/modules/ar/OWNERS b/chrome/android/modules/ar/OWNERS
new file mode 100644
index 0000000..4227433e
--- /dev/null
+++ b/chrome/android/modules/ar/OWNERS
@@ -0,0 +1,6 @@
+tiborg@chromium.org
+vollick@chromium.org
+
+# TEAM: xr-dev@chromium.org
+# COMPONENT: Internals>XR>AR
+# OS: Android
\ No newline at end of file
diff --git a/chrome/android/modules/ar/README b/chrome/android/modules/ar/README
new file mode 100644
index 0000000..e621db2
--- /dev/null
+++ b/chrome/android/modules/ar/README
@@ -0,0 +1,3 @@
+The AR dynamic feature module on-demand delivers code necessary to support AR in
+Chrome. This folder contains the template for creating an AR DFM target as well
+as the required Android manifest.
\ No newline at end of file
diff --git a/chrome/android/modules/ar/ar_module_tmpl.gni b/chrome/android/modules/ar/ar_module_tmpl.gni
new file mode 100644
index 0000000..5400cbe
--- /dev/null
+++ b/chrome/android/modules/ar/ar_module_tmpl.gni
@@ -0,0 +1,53 @@
+# 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("//build/config/android/rules.gni")
+import("//device/vr/buildflags/buildflags.gni")
+
+assert(enable_arcore)
+
+template("ar_module_tmpl") {
+  _manifest = "$target_gen_dir/AndroidManifest.xml"
+  assert(defined(invoker.manifest_package))
+
+  _manifest_target = "${target_name}__manifest"
+  jinja_template(_manifest_target) {
+    input = "//chrome/android/modules/ar/AndroidManifest.xml"
+    output = _manifest
+    variables = [
+      "target_sdk_version=$android_sdk_version",
+      "manifest_package=${invoker.manifest_package}",
+    ]
+  }
+
+  android_app_bundle_module(target_name) {
+    module_name = "ArModule"
+    android_manifest = _manifest
+    android_manifest_dep = ":$_manifest_target"
+    deps = [
+      "//third_party/arcore-android-sdk:libdynamite_client_java",
+    ]
+
+    # We only want to add the 32 bit arcore shim even for 64 bit monochrome
+    # builds. AR is never used in 64 bit mode. We store the arcore shim as a
+    # separate .so in the bundle module and only load as needed.
+    if (android_64bit_target_cpu && build_apk_secondary_abi) {
+      secondary_abi_loadable_modules = [ "//third_party/arcore-android-sdk/libraries/android_arm/libarcore_sdk_c_minimal.so" ]
+
+      # Bundletool requires all modules of a bundle to support the same set of
+      # architectures. Monochrome base module supports arm64. Add a zero-byte,
+      # "64 bit" dummy library to trick bundletool into thinking that the AR
+      # module does support arm64, too.
+      loadable_modules =
+          [ "//third_party/arcore-android-sdk/libarcore_dummy.so" ]
+    } else if (android_64bit_target_cpu && !build_apk_secondary_abi) {
+      loadable_modules = [ "//third_party/arcore-android-sdk/libraries/android_arm64/libarcore_sdk_c_minimal.so" ]
+    } else {
+      loadable_modules = [ "//third_party/arcore-android-sdk/libraries/android_arm/libarcore_sdk_c_minimal.so" ]
+    }
+    uncompress_shared_libraries = true
+
+    proguard_enabled = !is_java_debug
+  }
+}
diff --git a/chrome/app/nibs/BUILD.gn b/chrome/app/nibs/BUILD.gn
index 9763cb9..bfd6727f 100644
--- a/chrome/app/nibs/BUILD.gn
+++ b/chrome/app/nibs/BUILD.gn
@@ -20,13 +20,7 @@
 ]
 
 if (!mac_views_browser) {
-  translated_xibs += [
-    "ExtensionInstallPrompt.xib",
-    "ExtensionInstallPromptNoWarnings.xib",
-    "ExtensionInstallPromptWebstoreData.xib",
-    "ExtensionInstalledBubble.xib",
-    "HttpAuthLoginSheet.xib",
-  ]
+  translated_xibs += [ "HttpAuthLoginSheet.xib" ]
 }
 
 untranslated_xibs = [
diff --git a/chrome/app/nibs/ExtensionInstallPrompt.xib b/chrome/app/nibs/ExtensionInstallPrompt.xib
deleted file mode 100644
index 506e2cb..0000000
--- a/chrome/app/nibs/ExtensionInstallPrompt.xib
+++ /dev/null
@@ -1,138 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13F1077" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
-    <dependencies>
-        <deployment version="1090" identifier="macosx"/>
-        <development version="5100" identifier="xcode"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
-    </dependencies>
-    <objects>
-        <customObject id="-2" userLabel="File's Owner" customClass="ExtensionInstallViewController">
-            <connections>
-                <outlet property="cancelButton_" destination="169" id="189"/>
-                <outlet property="iconView_" destination="170" id="188"/>
-                <outlet property="okButton_" destination="168" id="190"/>
-                <outlet property="outlineView_" destination="172" id="194"/>
-                <outlet property="titleField_" destination="167" id="187"/>
-                <outlet property="view" destination="166" id="186"/>
-            </connections>
-        </customObject>
-        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
-        <customObject id="-3" userLabel="Application"/>
-        <customObject id="140" customClass="ChromeUILocalizer"/>
-        <customObject id="141" customClass="GTMUILocalizerAndLayoutTweaker">
-            <connections>
-                <outlet property="localizer_" destination="140" id="142"/>
-                <outlet property="uiObject_" destination="166" id="185"/>
-            </connections>
-        </customObject>
-        <customView id="166">
-            <rect key="frame" x="0.0" y="0.0" width="468" height="149"/>
-            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-            <subviews>
-                <textField verticalHuggingPriority="750" id="167">
-                    <rect key="frame" x="19" y="118" width="357" height="17"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                    <textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Title" id="180">
-                        <font key="font" metaFont="system" size="15"/>
-                        <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
-                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
-                    </textFieldCell>
-                </textField>
-                <button id="168" customClass="ConstrainedWindowButton">
-                    <rect key="frame" x="352" y="19" width="96" height="28"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
-                    <buttonCell key="cell" type="square" title="^IDS_EXTENSION_PROMPT_INSTALL_BUTTON" bezelStyle="shadowlessSquare" image="D4520959-E685-4758-9BD2-329FFA7259A0" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="179" customClass="ConstrainedWindowButtonCell">
-                        <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                        <font key="font" metaFont="system"/>
-                    </buttonCell>
-                    <connections>
-                        <action selector="ok:" target="-2" id="184"/>
-                    </connections>
-                </button>
-                <button id="169" customClass="ConstrainedWindowButton">
-                    <rect key="frame" x="246" y="19" width="96" height="28"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
-                    <buttonCell key="cell" type="square" title="^IDS_CANCEL" bezelStyle="shadowlessSquare" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="178" customClass="ConstrainedWindowButtonCell">
-                        <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                        <font key="font" metaFont="system"/>
-                        <string key="keyEquivalent" base64-UTF8="YES">
-DQ
-</string>
-                    </buttonCell>
-                    <connections>
-                        <action selector="cancel:" target="-2" id="183"/>
-                    </connections>
-                </button>
-                <imageView id="170">
-                    <rect key="frame" x="380" y="64" width="64" height="64"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
-                    <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="177"/>
-                </imageView>
-                <scrollView focusRingType="none" borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="171">
-                    <rect key="frame" x="0.0" y="62" width="376" height="48"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                    <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="IXK-Nb-gJ2">
-                        <rect key="frame" x="0.0" y="0.0" width="376" height="48"/>
-                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
-                        <subviews>
-                            <outlineView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" indentationPerLevel="16" outlineTableColumn="175" id="172">
-                                <rect key="frame" x="0.0" y="0.0" width="378" height="48"/>
-                                <autoresizingMask key="autoresizingMask"/>
-                                <size key="intercellSpacing" width="3" height="2"/>
-                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
-                                <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
-                                <tableColumns>
-                                    <tableColumn editable="NO" width="375" minWidth="16" maxWidth="1000" id="175">
-                                        <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
-                                            <font key="font" size="12" name="LucidaGrande"/>
-                                            <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
-                                            <color key="backgroundColor" white="0.33333298560000002" alpha="1" colorSpace="calibratedWhite"/>
-                                        </tableHeaderCell>
-                                        <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="176">
-                                            <font key="font" metaFont="cellTitle"/>
-                                            <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
-                                            <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
-                                        </textFieldCell>
-                                    </tableColumn>
-                                </tableColumns>
-                                <connections>
-                                    <outlet property="dataSource" destination="-2" id="192"/>
-                                    <outlet property="delegate" destination="-2" id="193"/>
-                                </connections>
-                            </outlineView>
-                        </subviews>
-                        <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
-                    </clipView>
-                    <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="173">
-                        <rect key="frame" x="-100" y="-100" width="283" height="15"/>
-                        <autoresizingMask key="autoresizingMask"/>
-                    </scroller>
-                    <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="174">
-                        <rect key="frame" x="-100" y="-100" width="15" height="17"/>
-                        <autoresizingMask key="autoresizingMask"/>
-                    </scroller>
-                </scrollView>
-            </subviews>
-        </customView>
-    </objects>
-    <resources>
-        <image name="D4520959-E685-4758-9BD2-329FFA7259A0" width="1" height="1">
-            <mutableData key="keyedArchiveRepresentation">
-YnBsaXN0MDDUAQIDBAUGPj9YJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoK4HCBMU
-GR4fIyQsLzI4O1UkbnVsbNUJCgsMDQ4PEBESVk5TU2l6ZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVw
-c1dOU0NvbG9ygAKADRIgwwAAgAOAC1Z7MSwgMX3SFQoWGFpOUy5vYmplY3RzoReABIAK0hUKGh2iGxyA
-BYAGgAkQANIgCiEiXxAUTlNUSUZGUmVwcmVzZW50YXRpb26AB4AITxCsTU0AKgAAAAoAAAANAQAAAwAA
-AAEAAQAAAQEAAwAAAAEAAQAAAQIAAwAAAAIACAAIAQMAAwAAAAEAAQAAAQYAAwAAAAEAAQAAAREABAAA
-AAEAAAAIARIAAwAAAAEAAQAAARUAAwAAAAEAAgAAARYAAwAAAAEAAQAAARcABAAAAAEAAAACARwAAwAA
-AAEAAQAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAANIlJicoWiRjbGFzc25hbWVYJGNsYXNzZXNf
-EBBOU0JpdG1hcEltYWdlUmVwoykqK18QEE5TQml0bWFwSW1hZ2VSZXBaTlNJbWFnZVJlcFhOU09iamVj
-dNIlJi0uV05TQXJyYXmiLSvSJSYwMV5OU011dGFibGVBcnJheaMwLSvTMzQKNTY3V05TV2hpdGVcTlND
-b2xvclNwYWNlRDAgMAAQA4AM0iUmOTpXTlNDb2xvcqI5K9IlJjw9V05TSW1hZ2WiPCtfEA9OU0tleWVk
-QXJjaGl2ZXLRQEFUcm9vdIABAAgAEQAaACMALQAyADcARgBMAFcAXgBlAHIAeQCBAIMAhQCKAIwAjgCV
-AJoApQCnAKkAqwCwALMAtQC3ALkAuwDAANcA2QDbAYoBjwGaAaMBtgG6Ac0B2AHhAeYB7gHxAfYCBQIJ
-AhACGAIlAioCLAIuAjMCOwI+AkMCSwJOAmACYwJoAAAAAAAAAgEAAAAAAAAAQgAAAAAAAAAAAAAAAAAA
-Amo
-</mutableData>
-        </image>
-    </resources>
-</document>
diff --git a/chrome/app/nibs/ExtensionInstallPromptNoWarnings.xib b/chrome/app/nibs/ExtensionInstallPromptNoWarnings.xib
deleted file mode 100644
index 60c62d51..0000000
--- a/chrome/app/nibs/ExtensionInstallPromptNoWarnings.xib
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13F1077" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
-    <dependencies>
-        <deployment version="1090" identifier="macosx"/>
-        <development version="5100" identifier="xcode"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
-    </dependencies>
-    <objects>
-        <customObject id="-2" userLabel="File's Owner" customClass="ExtensionInstallViewController">
-            <connections>
-                <outlet property="cancelButton_" destination="8" id="11"/>
-                <outlet property="iconView_" destination="3" id="19"/>
-                <outlet property="okButton_" destination="7" id="12"/>
-                <outlet property="titleField_" destination="5" id="25"/>
-                <outlet property="view" destination="23" id="24"/>
-            </connections>
-        </customObject>
-        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
-        <customObject id="-3" userLabel="Application"/>
-        <customObject id="15" customClass="ChromeUILocalizer"/>
-        <customObject id="16" customClass="GTMUILocalizerAndLayoutTweaker">
-            <connections>
-                <outlet property="localizer_" destination="15" id="18"/>
-                <outlet property="uiObject_" destination="23" id="26"/>
-            </connections>
-        </customObject>
-        <customView id="23">
-            <rect key="frame" x="0.0" y="0.0" width="468" height="151"/>
-            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-            <subviews>
-                <imageView id="3">
-                    <rect key="frame" x="380" y="66" width="64" height="64"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
-                    <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="4"/>
-                </imageView>
-                <textField verticalHuggingPriority="750" id="5">
-                    <rect key="frame" x="19" y="120" width="347" height="17"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                    <textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Title" id="6">
-                        <font key="font" metaFont="system" size="15"/>
-                        <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
-                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
-                    </textFieldCell>
-                </textField>
-                <button id="7" customClass="ConstrainedWindowButton">
-                    <rect key="frame" x="352" y="19" width="96" height="28"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
-                    <buttonCell key="cell" type="square" title="^IDS_EXTENSION_PROMPT_INSTALL_BUTTON" bezelStyle="shadowlessSquare" image="C25F3098-40C0-44B1-BEBC-90A59D6FCD55" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="10" customClass="ConstrainedWindowButtonCell">
-                        <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                        <font key="font" metaFont="system"/>
-                    </buttonCell>
-                    <connections>
-                        <action selector="ok:" target="-2" id="22"/>
-                    </connections>
-                </button>
-                <button id="8" customClass="ConstrainedWindowButton">
-                    <rect key="frame" x="246" y="19" width="96" height="28"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
-                    <buttonCell key="cell" type="square" title="^IDS_CANCEL" bezelStyle="shadowlessSquare" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="9" customClass="ConstrainedWindowButtonCell">
-                        <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                        <font key="font" metaFont="system"/>
-                        <string key="keyEquivalent" base64-UTF8="YES">
-DQ
-</string>
-                    </buttonCell>
-                    <connections>
-                        <action selector="cancel:" target="-2" id="21"/>
-                    </connections>
-                </button>
-            </subviews>
-        </customView>
-    </objects>
-    <resources>
-        <image name="C25F3098-40C0-44B1-BEBC-90A59D6FCD55" width="1" height="1">
-            <mutableData key="keyedArchiveRepresentation">
-YnBsaXN0MDDUAQIDBAUGPj9YJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoK4HCBMU
-GR4fIyQsLzI4O1UkbnVsbNUJCgsMDQ4PEBESVk5TU2l6ZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVw
-c1dOU0NvbG9ygAKADRIgwwAAgAOAC1Z7MSwgMX3SFQoWGFpOUy5vYmplY3RzoReABIAK0hUKGh2iGxyA
-BYAGgAkQANIgCiEiXxAUTlNUSUZGUmVwcmVzZW50YXRpb26AB4AITxCsTU0AKgAAAAoAAAANAQAAAwAA
-AAEAAQAAAQEAAwAAAAEAAQAAAQIAAwAAAAIACAAIAQMAAwAAAAEAAQAAAQYAAwAAAAEAAQAAAREABAAA
-AAEAAAAIARIAAwAAAAEAAQAAARUAAwAAAAEAAgAAARYAAwAAAAEAAQAAARcABAAAAAEAAAACARwAAwAA
-AAEAAQAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAANIlJicoWiRjbGFzc25hbWVYJGNsYXNzZXNf
-EBBOU0JpdG1hcEltYWdlUmVwoykqK18QEE5TQml0bWFwSW1hZ2VSZXBaTlNJbWFnZVJlcFhOU09iamVj
-dNIlJi0uV05TQXJyYXmiLSvSJSYwMV5OU011dGFibGVBcnJheaMwLSvTMzQKNTY3V05TV2hpdGVcTlND
-b2xvclNwYWNlRDAgMAAQA4AM0iUmOTpXTlNDb2xvcqI5K9IlJjw9V05TSW1hZ2WiPCtfEA9OU0tleWVk
-QXJjaGl2ZXLRQEFUcm9vdIABAAgAEQAaACMALQAyADcARgBMAFcAXgBlAHIAeQCBAIMAhQCKAIwAjgCV
-AJoApQCnAKkAqwCwALMAtQC3ALkAuwDAANcA2QDbAYoBjwGaAaMBtgG6Ac0B2AHhAeYB7gHxAfYCBQIJ
-AhACGAIlAioCLAIuAjMCOwI+AkMCSwJOAmACYwJoAAAAAAAAAgEAAAAAAAAAQgAAAAAAAAAAAAAAAAAA
-Amo
-</mutableData>
-        </image>
-    </resources>
-</document>
diff --git a/chrome/app/nibs/ExtensionInstallPromptWebstoreData.xib b/chrome/app/nibs/ExtensionInstallPromptWebstoreData.xib
deleted file mode 100644
index ebc531b..0000000
--- a/chrome/app/nibs/ExtensionInstallPromptWebstoreData.xib
+++ /dev/null
@@ -1,183 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13F1077" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
-    <dependencies>
-        <deployment version="1090" identifier="macosx"/>
-        <development version="5100" identifier="xcode"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
-    </dependencies>
-    <objects>
-        <customObject id="-2" userLabel="File's Owner" customClass="ExtensionInstallViewController">
-            <connections>
-                <outlet property="cancelButton_" destination="126" id="150"/>
-                <outlet property="iconView_" destination="130" id="137"/>
-                <outlet property="okButton_" destination="124" id="149"/>
-                <outlet property="outlineView_" destination="183" id="188"/>
-                <outlet property="ratingCountField_" destination="169" id="171"/>
-                <outlet property="ratingStars_" destination="172" id="173"/>
-                <outlet property="storeLinkButton_" destination="155" id="194"/>
-                <outlet property="titleField_" destination="118" id="144"/>
-                <outlet property="userCountField_" destination="153" id="158"/>
-                <outlet property="view" destination="191" id="193"/>
-                <outlet property="warningsSeparator_" destination="152" id="180"/>
-            </connections>
-        </customObject>
-        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
-        <customObject id="-3" userLabel="Application"/>
-        <customObject id="140" customClass="ChromeUILocalizer"/>
-        <customObject id="141" customClass="GTMUILocalizerAndLayoutTweaker">
-            <connections>
-                <outlet property="localizer_" destination="140" id="142"/>
-                <outlet property="uiObject_" destination="191" id="192"/>
-            </connections>
-        </customObject>
-        <customView id="191">
-            <rect key="frame" x="0.0" y="0.0" width="468" height="214"/>
-            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-            <subviews>
-                <textField verticalHuggingPriority="750" id="118">
-                    <rect key="frame" x="19" y="183" width="357" height="17"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                    <textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Title" id="119">
-                        <font key="font" metaFont="system" size="15"/>
-                        <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
-                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
-                    </textFieldCell>
-                </textField>
-                <button id="124" customClass="ConstrainedWindowButton">
-                    <rect key="frame" x="352" y="14" width="96" height="28"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
-                    <buttonCell key="cell" type="square" title="^IDS_EXTENSION_PROMPT_INSTALL_BUTTON" bezelStyle="shadowlessSquare" image="454BDBF6-F64D-4B84-9B01-98D6D896672C" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="125" customClass="ConstrainedWindowButtonCell">
-                        <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                        <font key="font" metaFont="system"/>
-                    </buttonCell>
-                    <connections>
-                        <action selector="ok:" target="-2" id="134"/>
-                    </connections>
-                </button>
-                <button id="126" customClass="ConstrainedWindowButton">
-                    <rect key="frame" x="246" y="14" width="96" height="28"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
-                    <buttonCell key="cell" type="square" title="^IDS_CANCEL" bezelStyle="shadowlessSquare" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="127" customClass="ConstrainedWindowButtonCell">
-                        <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                        <font key="font" metaFont="system"/>
-                        <string key="keyEquivalent" base64-UTF8="YES">
-DQ
-</string>
-                    </buttonCell>
-                    <connections>
-                        <action selector="cancel:" target="-2" id="133"/>
-                    </connections>
-                </button>
-                <imageView id="130">
-                    <rect key="frame" x="380" y="129" width="64" height="64"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
-                    <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="131"/>
-                </imageView>
-                <box autoresizesSubviews="NO" verticalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="152">
-                    <rect key="frame" x="22" y="110" width="428" height="5"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                    <color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
-                    <color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
-                    <font key="titleFont" metaFont="system"/>
-                </box>
-                <textField verticalHuggingPriority="750" id="153">
-                    <rect key="frame" x="18" y="148" width="357" height="14"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                    <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="123 users" id="154">
-                        <font key="font" metaFont="cellTitle"/>
-                        <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
-                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
-                    </textFieldCell>
-                </textField>
-                <textField verticalHuggingPriority="750" id="169">
-                    <rect key="frame" x="79" y="164" width="295" height="14"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                    <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="(345)" id="170">
-                        <font key="font" metaFont="cellTitle"/>
-                        <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
-                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
-                    </textFieldCell>
-                </textField>
-                <button id="155">
-                    <rect key="frame" x="19" y="127" width="351" height="17"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                    <buttonCell key="cell" type="square" title="^IDS_EXTENSION_PROMPT_STORE_LINK" bezelStyle="shadowlessSquare" alignment="left" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="156" customClass="HyperlinkButtonCell">
-                        <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                        <font key="font" metaFont="cellTitle"/>
-                    </buttonCell>
-                    <connections>
-                        <action selector="storeLinkClicked:" target="-2" id="157"/>
-                    </connections>
-                </button>
-                <customView focusRingType="none" id="172">
-                    <rect key="frame" x="21" y="166" width="55" height="11"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                </customView>
-                <scrollView focusRingType="none" borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" id="182">
-                    <rect key="frame" x="1" y="57" width="445" height="45"/>
-                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                    <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="SpA-7M-MHM">
-                        <rect key="frame" x="0.0" y="0.0" width="445" height="45"/>
-                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
-                        <subviews>
-                            <outlineView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" indentationPerLevel="16" outlineTableColumn="186" id="183">
-                                <rect key="frame" x="0.0" y="0.0" width="447" height="45"/>
-                                <autoresizingMask key="autoresizingMask"/>
-                                <size key="intercellSpacing" width="3" height="2"/>
-                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
-                                <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
-                                <tableColumns>
-                                    <tableColumn editable="NO" width="444" minWidth="16" maxWidth="1000" id="186">
-                                        <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
-                                            <font key="font" metaFont="smallSystem"/>
-                                            <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
-                                            <color key="backgroundColor" white="0.33333298560000002" alpha="1" colorSpace="calibratedWhite"/>
-                                        </tableHeaderCell>
-                                        <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="187">
-                                            <font key="font" metaFont="cellTitle"/>
-                                            <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
-                                            <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
-                                        </textFieldCell>
-                                    </tableColumn>
-                                </tableColumns>
-                                <connections>
-                                    <outlet property="dataSource" destination="-2" id="189"/>
-                                    <outlet property="delegate" destination="-2" id="190"/>
-                                </connections>
-                            </outlineView>
-                        </subviews>
-                        <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
-                    </clipView>
-                    <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="184">
-                        <rect key="frame" x="-100" y="-100" width="283" height="15"/>
-                        <autoresizingMask key="autoresizingMask"/>
-                    </scroller>
-                    <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="185">
-                        <rect key="frame" x="-100" y="-100" width="15" height="17"/>
-                        <autoresizingMask key="autoresizingMask"/>
-                    </scroller>
-                </scrollView>
-            </subviews>
-        </customView>
-    </objects>
-    <resources>
-        <image name="454BDBF6-F64D-4B84-9B01-98D6D896672C" width="1" height="1">
-            <mutableData key="keyedArchiveRepresentation">
-YnBsaXN0MDDUAQIDBAUGPj9YJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoK4HCBMU
-GR4fIyQsLzI4O1UkbnVsbNUJCgsMDQ4PEBESVk5TU2l6ZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVw
-c1dOU0NvbG9ygAKADRIgwwAAgAOAC1Z7MSwgMX3SFQoWGFpOUy5vYmplY3RzoReABIAK0hUKGh2iGxyA
-BYAGgAkQANIgCiEiXxAUTlNUSUZGUmVwcmVzZW50YXRpb26AB4AITxCsTU0AKgAAAAoAAAANAQAAAwAA
-AAEAAQAAAQEAAwAAAAEAAQAAAQIAAwAAAAIACAAIAQMAAwAAAAEAAQAAAQYAAwAAAAEAAQAAAREABAAA
-AAEAAAAIARIAAwAAAAEAAQAAARUAAwAAAAEAAgAAARYAAwAAAAEAAQAAARcABAAAAAEAAAACARwAAwAA
-AAEAAQAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAANIlJicoWiRjbGFzc25hbWVYJGNsYXNzZXNf
-EBBOU0JpdG1hcEltYWdlUmVwoykqK18QEE5TQml0bWFwSW1hZ2VSZXBaTlNJbWFnZVJlcFhOU09iamVj
-dNIlJi0uV05TQXJyYXmiLSvSJSYwMV5OU011dGFibGVBcnJheaMwLSvTMzQKNTY3V05TV2hpdGVcTlND
-b2xvclNwYWNlRDAgMAAQA4AM0iUmOTpXTlNDb2xvcqI5K9IlJjw9V05TSW1hZ2WiPCtfEA9OU0tleWVk
-QXJjaGl2ZXLRQEFUcm9vdIABAAgAEQAaACMALQAyADcARgBMAFcAXgBlAHIAeQCBAIMAhQCKAIwAjgCV
-AJoApQCnAKkAqwCwALMAtQC3ALkAuwDAANcA2QDbAYoBjwGaAaMBtgG6Ac0B2AHhAeYB7gHxAfYCBQIJ
-AhACGAIlAioCLAIuAjMCOwI+AkMCSwJOAmACYwJoAAAAAAAAAgEAAAAAAAAAQgAAAAAAAAAAAAAAAAAA
-Amo
-</mutableData>
-        </image>
-    </resources>
-</document>
diff --git a/chrome/app/nibs/ExtensionInstalledBubble.xib b/chrome/app/nibs/ExtensionInstalledBubble.xib
deleted file mode 100644
index 969d892..0000000
--- a/chrome/app/nibs/ExtensionInstalledBubble.xib
+++ /dev/null
@@ -1,125 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13F1077" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
-    <dependencies>
-        <deployment version="1090" identifier="macosx"/>
-        <development version="5100" identifier="xcode"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
-    </dependencies>
-    <objects>
-        <customObject id="-2" userLabel="File's Owner" customClass="ExtensionInstalledBubbleController">
-            <connections>
-                <outlet property="appShortcutLink_" destination="74" id="77"/>
-                <outlet property="bubble_" destination="44" id="48"/>
-                <outlet property="closeButton_" destination="15" id="30"/>
-                <outlet property="heading_" destination="33" id="70"/>
-                <outlet property="howToManage_" destination="37" id="71"/>
-                <outlet property="howToUse_" destination="35" id="72"/>
-                <outlet property="iconImage_" destination="18" id="20"/>
-                <outlet property="manageShortcutLink_" destination="49" id="51"/>
-                <outlet property="promoContainer_" destination="67" id="73"/>
-                <outlet property="window" destination="1" id="8"/>
-            </connections>
-        </customObject>
-        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
-        <customObject id="-3" userLabel="Application"/>
-        <customObject id="3" customClass="ChromeUILocalizer"/>
-        <customObject id="4" customClass="GTMUILocalizerAndLayoutTweaker">
-            <connections>
-                <outlet property="localizer_" destination="3" id="9"/>
-                <outlet property="uiObject_" destination="44" id="46"/>
-            </connections>
-        </customObject>
-        <window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="1" customClass="InfoBubbleWindow">
-            <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
-            <windowPositionMask key="initialPositionMask" leftStrut="YES" bottomStrut="YES"/>
-            <rect key="contentRect" x="196" y="339" width="373" height="171"/>
-            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1578"/>
-            <view key="contentView" id="2">
-                <rect key="frame" x="0.0" y="0.0" width="373" height="171"/>
-                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
-                <subviews>
-                    <customView id="44" customClass="InfoBubbleView">
-                        <rect key="frame" x="0.0" y="0.0" width="373" height="171"/>
-                        <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
-                        <userGuides>
-                            <userLayoutGuide location="8" affinity="maxY"/>
-                        </userGuides>
-                        <subviews>
-                            <imageView id="18">
-                                <rect key="frame" x="15" y="110" width="43" height="43"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                                <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="19"/>
-                            </imageView>
-                            <button focusRingType="none" id="15" customClass="HoverCloseButton">
-                                <rect key="frame" x="347" y="138" width="16" height="16"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
-                                <buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" imagePosition="overlaps" alignment="center" focusRingType="none" inset="2" id="16">
-                                    <behavior key="behavior" lightByContents="YES"/>
-                                    <font key="font" metaFont="system"/>
-                                </buttonCell>
-                                <connections>
-                                    <action selector="closeWindow:" target="-2" id="43"/>
-                                </connections>
-                            </button>
-                            <textField focusRingType="none" verticalHuggingPriority="750" id="33">
-                                <rect key="frame" x="71" y="134" width="275" height="17"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                                <textFieldCell key="cell" sendsActionOnEndEditing="YES" focusRingType="none" title="^IDS_EXTENSION_INSTALLED_HEADING" id="34">
-                                    <font key="font" metaFont="system"/>
-                                    <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
-                                    <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
-                                </textFieldCell>
-                            </textField>
-                            <textField hidden="YES" focusRingType="none" verticalHuggingPriority="750" id="35">
-                                <rect key="frame" x="71" y="67" width="285" height="17"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                                <textFieldCell key="cell" sendsActionOnEndEditing="YES" focusRingType="none" title="^IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO" id="36">
-                                    <font key="font" metaFont="system"/>
-                                    <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
-                                    <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
-                                </textFieldCell>
-                            </textField>
-                            <textField focusRingType="none" verticalHuggingPriority="750" id="37">
-                                <rect key="frame" x="71" y="33" width="285" height="17"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                                <textFieldCell key="cell" sendsActionOnEndEditing="YES" focusRingType="none" title="^IDS_EXTENSION_INSTALLED_MANAGE_INFO" id="38">
-                                    <font key="font" metaFont="system"/>
-                                    <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
-                                    <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
-                                </textFieldCell>
-                            </textField>
-                            <button hidden="YES" id="49">
-                                <rect key="frame" x="71" y="10" width="161" height="19"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                                <buttonCell key="cell" type="square" title="^IDS_EXTENSION_INSTALLED_MANAGE_SHORTCUTS" bezelStyle="shadowlessSquare" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="50" customClass="HyperlinkButtonCell">
-                                    <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                                    <font key="font" metaFont="smallSystem"/>
-                                </buttonCell>
-                                <connections>
-                                    <action selector="onManageShortcutClicked:" target="-2" id="53"/>
-                                </connections>
-                            </button>
-                            <button hidden="YES" id="74">
-                                <rect key="frame" x="192" y="50" width="161" height="19"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                                <buttonCell key="cell" type="square" title="^IDS_EXTENSION_INSTALLED_APP_INFO" bezelStyle="shadowlessSquare" alignment="right" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="75" customClass="HyperlinkButtonCell">
-                                    <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                                    <font key="font" metaFont="smallSystem"/>
-                                </buttonCell>
-                                <connections>
-                                    <action selector="onAppShortcutClicked:" target="-2" id="78"/>
-                                </connections>
-                            </button>
-                            <customView id="67" userLabel="Sync Promo Container">
-                                <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
-                            </customView>
-                        </subviews>
-                    </customView>
-                </subviews>
-            </view>
-            <connections>
-                <outlet property="delegate" destination="-2" id="7"/>
-            </connections>
-        </window>
-    </objects>
-</document>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 10fdfba..e380670 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -3382,7 +3382,7 @@
       Add another
     </message>
     <message name="IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_ENTER_PASSWORD_LOCK" desc="Text above a password input field that tells the user they need to submit their password to configure these settings.">
-      To set up Smart Lock, enter your password
+      To set up screen lock, enter your password
     </message>
     <message name="IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_ENTER_PASSWORD_LOGIN_LOCK" desc="Text above a password input field that tells the user they need to submit their password to configure these settings.">
       To set up Smart Lock for sign-in, enter your password
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 75d6bb7..bc08939a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3403,11 +3403,6 @@
     {"use-google-local-ntp", flag_descriptions::kUseGoogleLocalNtpName,
      flag_descriptions::kUseGoogleLocalNtpDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kUseGoogleLocalNtp)},
-
-    {"one-google-bar-on-local-ntp",
-     flag_descriptions::kOneGoogleBarOnLocalNtpName,
-     flag_descriptions::kOneGoogleBarOnLocalNtpDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(features::kOneGoogleBarOnLocalNtp)},
 #endif  // !defined(OS_ANDROID)
 
 #if defined(OS_MACOSX)
@@ -3884,11 +3879,9 @@
      FEATURE_VALUE_TYPE(features::kOobeRecommendAppsScreen)},
 #endif  // OS_CHROMEOS
 
-#if defined(OS_ANDROID)
     {"enable-query-in-omnibox", flag_descriptions::kQueryInOmniboxName,
-     flag_descriptions::kQueryInOmniboxDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kQueryInOmnibox)},
-#endif  // OS_ANDROID
+     flag_descriptions::kQueryInOmniboxDescription, kOsAll,
+     FEATURE_VALUE_TYPE(omnibox::kQueryInOmnibox)},
 
     {"enable-viz-hit-test-draw-quad",
      flag_descriptions::kVizHitTestDrawQuadName,
@@ -4336,6 +4329,11 @@
                                     kResamplingInputEventsFeatureVariations,
                                     "ResamplingScrollEvents")},
 
+    {"enable-autoplay-unified-sound-settings",
+     flag_descriptions::kEnableAutoplayUnifiedSoundSettingsName,
+     flag_descriptions::kEnableAutoplayUnifiedSoundSettingsDescription,
+     kOsDesktop, FEATURE_VALUE_TYPE(media::kAutoplaySoundSettings)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index f68c12a..fe21dcb1 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -129,7 +129,6 @@
     &kProgressBarThrottleFeature,
     &kPwaImprovedSplashScreen,
     &kPwaPersistentNotification,
-    &kQueryInOmnibox,
     &kReaderModeInCCT,
     &kSearchEnginePromoExistingDevice,
     &kSearchEnginePromoNewDevice,
@@ -162,6 +161,7 @@
     &offline_pages::kOfflinePagesSharingFeature,
     &offline_pages::kOfflinePagesLivePageSharingFeature,
     &offline_pages::kPrefetchingOfflinePagesFeature,
+    &omnibox::kQueryInOmnibox,
     &omnibox::kUIExperimentHideSteadyStateUrlSchemeAndSubdomains,
     &password_manager::features::kPasswordExport,
     &password_manager::features::kPasswordSearchMobile,
@@ -375,9 +375,6 @@
 const base::Feature kPwaPersistentNotification{
     "PwaPersistentNotification", base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kQueryInOmnibox{"QueryInOmnibox",
-                                    base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kReaderModeInCCT{"ReaderModeInCCT",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 8e17326..4a9f2a7 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -75,7 +75,6 @@
 extern const base::Feature kProgressBarThrottleFeature;
 extern const base::Feature kPwaImprovedSplashScreen;
 extern const base::Feature kPwaPersistentNotification;
-extern const base::Feature kQueryInOmnibox;
 extern const base::Feature kReaderModeInCCT;
 extern const base::Feature kSimplifiedNTP;
 extern const base::Feature kSoleIntegration;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 0ed7b5f..5516f94 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -83,6 +83,7 @@
     "//chromeos/components/tether",
     "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/multidevice_setup/public/cpp",
+    "//chromeos/services/multidevice_setup/public/cpp:auth_token_validator",
     "//chromeos/services/multidevice_setup/public/cpp:prefs",
     "//chromeos/services/secure_channel/public/cpp/client",
     "//components/arc",
@@ -189,6 +190,7 @@
     # TODO: care about enable_basic_printing and enable_print_preview.
     "//components/sync",
     "//printing",
+    "//remoting/host/it2me:chrome_os_host",
     "//services/ui/public/cpp",
     "//services/ui/public/cpp/input_devices",
     "//skia",
@@ -1287,6 +1289,10 @@
     "mobile/mobile_activator.h",
     "mobile_config.cc",
     "mobile_config.h",
+    "multidevice_setup/auth_token_validator_factory.cc",
+    "multidevice_setup/auth_token_validator_factory.h",
+    "multidevice_setup/auth_token_validator_impl.cc",
+    "multidevice_setup/auth_token_validator_impl.h",
     "multidevice_setup/multidevice_setup_client_factory.cc",
     "multidevice_setup/multidevice_setup_client_factory.h",
     "net/cert_verify_proc_chromeos.cc",
diff --git a/chrome/browser/chromeos/DEPS b/chrome/browser/chromeos/DEPS
index 8641d15..f59b114 100644
--- a/chrome/browser/chromeos/DEPS
+++ b/chrome/browser/chromeos/DEPS
@@ -19,6 +19,7 @@
   "+media/audio/sounds",  # For system sounds
   "+media/base/media_switches.h",  # For media command line switches.
   "+media/mojo/interfaces",  # For platform verification mojom interface.
+  "+remoting/host/it2me",  # For CRD host in remote command
   "+services/device/public",
   "+services/metrics/public",
   "+services/tracing/public",
diff --git a/chrome/browser/chromeos/accessibility/DEPS b/chrome/browser/chromeos/accessibility/DEPS
index 1269720..b0f20e2 100644
--- a/chrome/browser/chromeos/accessibility/DEPS
+++ b/chrome/browser/chromeos/accessibility/DEPS
@@ -12,6 +12,10 @@
     "+ash/magnifier/magnification_controller.h",
     "+ash/shell.h",
   ],
+  "select_to_speak_event_handler\.cc": [
+    # TODO(mash): Port the EventHandler to ash. http://crbug.com/874295
+    "+ash/system/accessibility/select_to_speak_tray_utils.h",
+  ],
   "switch_access_event_handler\.cc": [
     # TODO(mash): Fix. https://crbug.com/854025
     "+ash/shell.h",
diff --git a/chrome/browser/chromeos/accessibility/chromevox_panel.cc b/chrome/browser/chromeos/accessibility/chromevox_panel.cc
index 6bf946b..7fba88fb 100644
--- a/chrome/browser/chromeos/accessibility/chromevox_panel.cc
+++ b/chrome/browser/chromeos/accessibility/chromevox_panel.cc
@@ -18,6 +18,16 @@
 const char kFocusURLFragment[] = "focus";
 const char kFullscreenURLFragment[] = "fullscreen";
 const char kWidgetName[] = "ChromeVoxPanel";
+const int kPanelHeight = 35;
+
+ash::mojom::AccessibilityControllerPtr GetAccessibilityController() {
+  // Connect to the accessibility mojo interface in ash.
+  ash::mojom::AccessibilityControllerPtr accessibility_controller;
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(ash::mojom::kServiceName, &accessibility_controller);
+  return accessibility_controller;
+}
 
 }  // namespace
 
@@ -54,6 +64,8 @@
     : AccessibilityPanel(browser_context, GetUrlForContent(), kWidgetName) {
   web_contents_observer_.reset(
       new ChromeVoxPanelWebContentsObserver(GetWebContents(), this));
+
+  SetAccessibilityPanelFullscreen(false);
 }
 
 ChromeVoxPanel::~ChromeVoxPanel() {}
@@ -76,12 +88,10 @@
 }
 
 void ChromeVoxPanel::SetAccessibilityPanelFullscreen(bool fullscreen) {
-  // Connect to the accessibility mojo interface in ash.
-  ash::mojom::AccessibilityControllerPtr accessibility_controller;
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->BindInterface(ash::mojom::kServiceName, &accessibility_controller);
-  accessibility_controller->SetAccessibilityPanelFullscreen(fullscreen);
+  gfx::Rect bounds(0, 0, 0, kPanelHeight);
+  auto state = fullscreen ? ash::mojom::AccessibilityPanelState::FULLSCREEN
+                          : ash::mojom::AccessibilityPanelState::FULL_WIDTH;
+  GetAccessibilityController()->SetAccessibilityPanelBounds(bounds, state);
 }
 
 std::string ChromeVoxPanel::GetUrlForContent() {
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc
index dc1cddc..91777ec2 100644
--- a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc
+++ b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "ash/system/accessibility/select_to_speak_tray_utils.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/accessibility/event_handler_common.h"
 #include "chrome/common/extensions/extension_constants.h"
@@ -166,8 +167,13 @@
     return;
 
   DCHECK(event);
-  if (state_ == INACTIVE)
+  if (state_ == INACTIVE) {
+    if (event->type() == ui::ET_MOUSE_PRESSED) {
+      // Check if the mouse event occurred on the tray button.
+      CancelEventIfOverSelectToSpeakTray(event);
+    }
     return;
+  }
 
   if (event->type() == ui::ET_MOUSE_PRESSED) {
     if (state_ == SEARCH_DOWN || state_ == MOUSE_RELEASED)
@@ -207,6 +213,12 @@
     return;
 
   DCHECK(event);
+
+  if (state_ == INACTIVE && event->type() == ui::ET_TOUCH_PRESSED) {
+    // Check if the touch event occurred on the tray button.
+    CancelEventIfOverSelectToSpeakTray(event);
+  }
+
   // Only capture touch events if selection was requested or we are capturing
   // touch events already.
   if (state_ != SELECTION_REQUESTED && state_ != CAPTURING_TOUCH_ONLY)
@@ -296,4 +308,16 @@
   }
 }
 
+// TODO(katie): Refactor this for mash, http://crbug.com/874295.
+void SelectToSpeakEventHandler::CancelEventIfOverSelectToSpeakTray(
+    ui::LocatedEvent* event) {
+  if (ash::select_to_speak_tray_utils::SelectToSpeakTrayContainsPointInScreen(
+          event->root_location())) {
+    // Cancel the event so it does not cause any UI changes after a button tap.
+    CancelEvent(event);
+    // Enter the selecting mode as if we've clicked or tapped the button.
+    chromeos::AccessibilityManager::Get()->RequestSelectToSpeakStateChange();
+  }
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h
index 89922e4..0bac6d8 100644
--- a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h
+++ b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h
@@ -52,6 +52,11 @@
   // Forwards a mouse event to the Select-to-Speak extension.
   void ForwardMouseEventToExtension(ui::MouseEvent* event);
 
+  // For touch and mouse events events, the Select-to-Speak tray needs to
+  // cancel all further event propagation so that dialogs and menus do not
+  // close and therefore can be read by the user.
+  void CancelEventIfOverSelectToSpeakTray(ui::LocatedEvent* event);
+
   enum State {
     // The search key is not down, no selection has been requested.
     // No other keys or mouse events are captured.
diff --git a/chrome/browser/chromeos/dbus/finch_features_service_provider_delegate.cc b/chrome/browser/chromeos/dbus/finch_features_service_provider_delegate.cc
index fba84cfa..0e2a9af9 100644
--- a/chrome/browser/chromeos/dbus/finch_features_service_provider_delegate.cc
+++ b/chrome/browser/chromeos/dbus/finch_features_service_provider_delegate.cc
@@ -34,4 +34,8 @@
   return base::FeatureList::IsEnabled(features::kUsbguard);
 }
 
+bool FinchFeaturesServiceProviderDelegate::IsShillSandboxingEnabled() {
+  return base::FeatureList::IsEnabled(features::kShillSandboxing);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/dbus/finch_features_service_provider_delegate.h b/chrome/browser/chromeos/dbus/finch_features_service_provider_delegate.h
index eb1e241..99315ae29 100644
--- a/chrome/browser/chromeos/dbus/finch_features_service_provider_delegate.h
+++ b/chrome/browser/chromeos/dbus/finch_features_service_provider_delegate.h
@@ -20,6 +20,7 @@
   // ChromeServiceProvider::Delegate:
   bool IsCrostiniEnabled(const std::string& user_id_hash) override;
   bool IsUsbguardEnabled() override;
+  bool IsShillSandboxingEnabled() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(FinchFeaturesServiceProviderDelegate);
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 300dcbb..5866c27 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -264,7 +264,8 @@
                       TestCase("renameFileDownloads"),
                       TestCase("renameFileDrive"),
                       TestCase("renameFileDrive").EnableDriveFs(),
-                      TestCase("renameNewFolderDownloads").InGuestMode(),
+                      // https://crbug.com/874954, failed on MSan bots.
+                      // TestCase("renameNewFolderDownloads").InGuestMode(),
                       TestCase("renameNewFolderDownloads"),
                       TestCase("renameNewFolderDrive"),
                       TestCase("renameNewFolderDrive").EnableDriveFs()));
diff --git a/chrome/browser/chromeos/login/signin/token_handle_fetcher.cc b/chrome/browser/chromeos/login/signin/token_handle_fetcher.cc
index 773abd431..c824f8c3 100644
--- a/chrome/browser/chromeos/login/signin/token_handle_fetcher.cc
+++ b/chrome/browser/chromeos/login/signin/token_handle_fetcher.cc
@@ -15,6 +15,7 @@
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "google_apis/gaia/gaia_constants.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace {
 const int kMaxRetries = 3;
@@ -120,7 +121,7 @@
 void TokenHandleFetcher::FillForAccessToken(const std::string& access_token) {
   if (!gaia_client_.get())
     gaia_client_.reset(
-        new gaia::GaiaOAuthClient(profile_->GetRequestContext()));
+        new gaia::GaiaOAuthClient(profile_->GetURLLoaderFactory()));
   tokeninfo_response_start_time_ = base::TimeTicks::Now();
   gaia_client_->GetTokenInfo(access_token, kMaxRetries, this);
 }
diff --git a/chrome/browser/chromeos/login/signin/token_handle_util.cc b/chrome/browser/chromeos/login/signin/token_handle_util.cc
index 7892d6b..9c2fdae 100644
--- a/chrome/browser/chromeos/login/signin/token_handle_util.cc
+++ b/chrome/browser/chromeos/login/signin/token_handle_util.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "components/user_manager/known_user.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace {
 
@@ -86,9 +87,11 @@
   }
 
   if (!gaia_client_.get()) {
-    auto* request_context =
-        chromeos::ProfileHelper::Get()->GetSigninProfile()->GetRequestContext();
-    gaia_client_.reset(new gaia::GaiaOAuthClient(request_context));
+    auto url_loader_factory = chromeos::ProfileHelper::Get()
+                                  ->GetSigninProfile()
+                                  ->GetURLLoaderFactory();
+    gaia_client_.reset(
+        new gaia::GaiaOAuthClient(std::move(url_loader_factory)));
   }
 
   validation_delegates_[token] =
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
index 1fd2223..9d23d6f 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/chromeos/login/ui/login_display_host_mojo.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
+#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/browser/ui/ash/login_screen_client.h"
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
@@ -36,6 +37,31 @@
 
 }  // namespace
 
+class OobeWebDialogView : public views::WebDialogView {
+ public:
+  OobeWebDialogView(content::BrowserContext* context,
+                    ui::WebDialogDelegate* delegate,
+                    WebContentsHandler* handler)
+      : views::WebDialogView(context, delegate, handler) {}
+
+  // views::WebDialogView:
+  void RequestMediaAccessPermission(
+      content::WebContents* web_contents,
+      const content::MediaStreamRequest& request,
+      content::MediaResponseCallback callback) override {
+    // This is required for accessing the camera for SAML logins.
+    MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest(
+        web_contents, request, std::move(callback), nullptr /* extension */);
+  }
+
+  bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
+                                  const GURL& security_origin,
+                                  content::MediaStreamType type) override {
+    return MediaCaptureDevicesDispatcher::GetInstance()
+        ->CheckMediaAccessPermission(render_frame_host, security_origin, type);
+  }
+};
+
 OobeUIDialogDelegate::OobeUIDialogDelegate(
     base::WeakPtr<LoginDisplayHostMojo> controller)
     : controller_(controller),
@@ -62,8 +88,8 @@
   // Widget owns a root view which has |dialog_view_| as its child view.
   // Before the widget is destroyed, it will clean up the view hierarchy
   // starting from root view.
-  dialog_view_ = new views::WebDialogView(ProfileHelper::GetSigninProfile(),
-                                          this, new ChromeWebContentsHandler);
+  dialog_view_ = new OobeWebDialogView(ProfileHelper::GetSigninProfile(), this,
+                                       new ChromeWebContentsHandler);
   views::Widget::InitParams params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
   params.delegate = dialog_view_;
diff --git a/chrome/browser/chromeos/multidevice_setup/auth_token_validator_factory.cc b/chrome/browser/chromeos/multidevice_setup/auth_token_validator_factory.cc
new file mode 100644
index 0000000..b00cda5
--- /dev/null
+++ b/chrome/browser/chromeos/multidevice_setup/auth_token_validator_factory.cc
@@ -0,0 +1,46 @@
+// 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/chromeos/multidevice_setup/auth_token_validator_factory.h"
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h"
+#include "chrome/browser/chromeos/multidevice_setup/auth_token_validator_impl.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "content/public/browser/browser_context.h"
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+// static
+AuthTokenValidator* AuthTokenValidatorFactory::GetForProfile(Profile* profile) {
+  return static_cast<AuthTokenValidatorImpl*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+AuthTokenValidatorFactory* AuthTokenValidatorFactory::GetInstance() {
+  return base::Singleton<AuthTokenValidatorFactory>::get();
+}
+
+AuthTokenValidatorFactory::AuthTokenValidatorFactory()
+    : BrowserContextKeyedServiceFactory(
+          "AuthTokenValidatorFactory",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+AuthTokenValidatorFactory::~AuthTokenValidatorFactory() {}
+
+KeyedService* AuthTokenValidatorFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new AuthTokenValidatorImpl(
+      chromeos::quick_unlock::QuickUnlockFactory::GetForProfile(
+          Profile::FromBrowserContext(context)));
+}
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/multidevice_setup/auth_token_validator_factory.h b/chrome/browser/chromeos/multidevice_setup/auth_token_validator_factory.h
new file mode 100644
index 0000000..66e1e8e
--- /dev/null
+++ b/chrome/browser/chromeos/multidevice_setup/auth_token_validator_factory.h
@@ -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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_MULTIDEVICE_SETUP_AUTH_TOKEN_VALIDATOR_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_MULTIDEVICE_SETUP_AUTH_TOKEN_VALIDATOR_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class KeyedService;
+class Profile;
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+class AuthTokenValidator;
+
+// Owns AuthTokenValidator instances and associates them with Profiles.
+class AuthTokenValidatorFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  static AuthTokenValidator* GetForProfile(Profile* profile);
+
+  static AuthTokenValidatorFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<AuthTokenValidatorFactory>;
+
+  AuthTokenValidatorFactory();
+  ~AuthTokenValidatorFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(AuthTokenValidatorFactory);
+};
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_MULTIDEVICE_SETUP_AUTH_TOKEN_VALIDATOR_FACTORY_H_
diff --git a/chrome/browser/chromeos/multidevice_setup/auth_token_validator_impl.cc b/chrome/browser/chromeos/multidevice_setup/auth_token_validator_impl.cc
new file mode 100644
index 0000000..ecaf8422
--- /dev/null
+++ b/chrome/browser/chromeos/multidevice_setup/auth_token_validator_impl.cc
@@ -0,0 +1,32 @@
+// 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/chromeos/multidevice_setup/auth_token_validator_impl.h"
+
+#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h"
+#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h"
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+AuthTokenValidatorImpl::AuthTokenValidatorImpl(
+    quick_unlock::QuickUnlockStorage* quick_unlock_storage)
+    : quick_unlock_storage_(quick_unlock_storage) {}
+
+AuthTokenValidatorImpl::~AuthTokenValidatorImpl() = default;
+
+bool AuthTokenValidatorImpl::IsAuthTokenValid(const std::string& auth_token) {
+  return quick_unlock_storage_ &&
+         !quick_unlock_storage_->GetAuthTokenExpired() &&
+         auth_token == quick_unlock_storage_->GetAuthToken();
+}
+
+void AuthTokenValidatorImpl::Shutdown() {
+  quick_unlock_storage_ = nullptr;
+}
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/multidevice_setup/auth_token_validator_impl.h b/chrome/browser/chromeos/multidevice_setup/auth_token_validator_impl.h
new file mode 100644
index 0000000..f3777d8
--- /dev/null
+++ b/chrome/browser/chromeos/multidevice_setup/auth_token_validator_impl.h
@@ -0,0 +1,45 @@
+// 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_CHROMEOS_MULTIDEVICE_SETUP_AUTH_TOKEN_VALIDATOR_IMPL_H_
+#define CHROME_BROWSER_CHROMEOS_MULTIDEVICE_SETUP_AUTH_TOKEN_VALIDATOR_IMPL_H_
+
+#include "chromeos/services/multidevice_setup/public/cpp/auth_token_validator.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace chromeos {
+
+namespace quick_unlock {
+class QuickUnlockStorage;
+}  // namespace quick_unlock
+
+namespace multidevice_setup {
+
+// Concrete AuthTokenValidator implementation.
+//
+// The functionality of this class is very simple, to the point that it does not
+// merit a test. If this class becomes any more complex, simple unit tests
+// should be added.
+class AuthTokenValidatorImpl : public AuthTokenValidator, public KeyedService {
+ public:
+  AuthTokenValidatorImpl(
+      quick_unlock::QuickUnlockStorage* quick_unlock_storage);
+  ~AuthTokenValidatorImpl() override;
+
+  bool IsAuthTokenValid(const std::string& auth_token) override;
+
+ private:
+  // KeyedService:
+  void Shutdown() override;
+
+  quick_unlock::QuickUnlockStorage* quick_unlock_storage_;
+
+  DISALLOW_COPY_AND_ASSIGN(AuthTokenValidatorImpl);
+};
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_MULTIDEVICE_SETUP_AUTH_TOKEN_VALIDATOR_IMPL_H_
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
index 1c07ebe..9a2bdf3 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
@@ -58,10 +58,11 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
-#include "net/url_request/test_url_fetcher_factory.h"
+#include "google_apis/gaia/gaia_urls.h"
 #include "net/url_request/url_request_test_util.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -161,20 +162,18 @@
     RegisterLocalState(local_state_.registry());
     manager_->Init(&schema_registry_);
 
-    // DeviceOAuth2TokenService uses the system request context to fetch
+    // DeviceOAuth2TokenService uses the system url loader factory fetch
     // OAuth tokens, then writes the token to local state, encrypting it
     // first with methods in CryptohomeTokenEncryptor.
-    request_context_getter_ = new net::TestURLRequestContextGetter(
-        base::ThreadTaskRunnerHandle::Get());
-    TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(
-        request_context_getter_.get());
+    TestingBrowserProcess::GetGlobal()->SetSharedURLLoaderFactory(
+        test_shared_loader_factory_);
     TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
     // SystemSaltGetter is used in DeviceOAuth2TokenService.
     chromeos::SystemSaltGetter::Initialize();
     chromeos::DeviceOAuth2TokenServiceFactory::Initialize(
         test_shared_loader_factory_);
 
-    url_fetcher_response_code_ = 200;
+    url_fetcher_response_code_ = net::HTTP_OK;
     url_fetcher_response_string_ = "{\"access_token\":\"accessToken4Test\","
                                    "\"expires_in\":1234,"
                                    "\"refresh_token\":\"refreshToken4Test\"}";
@@ -266,9 +265,7 @@
 
   std::unique_ptr<chromeos::InstallAttributes> install_attributes_;
 
-  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
-  net::TestURLFetcherFactory url_fetcher_factory_;
-  int url_fetcher_response_code_;
+  net::HttpStatusCode url_fetcher_response_code_;
   std::string url_fetcher_response_string_;
   TestingPrefServiceSimple local_state_;
   MockDeviceManagementService device_management_service_;
@@ -282,9 +279,9 @@
   SchemaRegistry schema_registry_;
   std::unique_ptr<TestingDeviceCloudPolicyManagerChromeOS> manager_;
   std::unique_ptr<DeviceCloudPolicyInitializer> initializer_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
 
  private:
-  network::TestURLLoaderFactory test_url_loader_factory_;
   scoped_refptr<network::WeakWrapperSharedURLLoaderFactory>
       test_shared_loader_factory_;
 
@@ -586,17 +583,14 @@
     // Process robot refresh token fetch if the auth code fetch succeeded.
     // DeviceCloudPolicyInitializer holds an EnrollmentHandlerChromeOS which
     // holds a GaiaOAuthClient that fetches the refresh token during enrollment.
-    // We return a successful OAuth response via a TestURLFetcher to trigger the
-    // happy path for these classes so that enrollment can continue.
+    // We return a successful OAuth response via a TestURLLoaderFactory to
+    // trigger the happy path for these classes so that enrollment can continue.
     if (robot_auth_fetch_status_ == DM_STATUS_SUCCESS) {
-      net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(
-          gaia::GaiaOAuthClient::kUrlFetcherId);
-      ASSERT_TRUE(url_fetcher);
-      url_fetcher->SetMaxRetriesOn5xx(0);
-      url_fetcher->set_status(net::URLRequestStatus());
-      url_fetcher->set_response_code(url_fetcher_response_code_);
-      url_fetcher->SetResponseString(url_fetcher_response_string_);
-      url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+      test_url_loader_factory_.SimulateResponseForPendingRequest(
+          GaiaUrls::GetInstance()->oauth2_token_url(),
+          network::URLLoaderCompletionStatus(net::OK),
+          network::CreateResourceResponseHead(url_fetcher_response_code_),
+          url_fetcher_response_string_);
     }
 
     // Process robot refresh token store and policy store.
@@ -714,10 +708,10 @@
 
 TEST_P(DeviceCloudPolicyManagerChromeOSEnrollmentTest,
        RobotRefreshTokenFetchResponseCodeFailed) {
-  url_fetcher_response_code_ = 400;
+  url_fetcher_response_code_ = net::HTTP_BAD_REQUEST;
   RunTest();
   ExpectFailedEnrollment(EnrollmentStatus::ROBOT_REFRESH_FETCH_FAILED);
-  EXPECT_EQ(400, status_.http_status());
+  EXPECT_EQ(net::HTTP_BAD_REQUEST, status_.http_status());
 }
 
 TEST_P(DeviceCloudPolicyManagerChromeOSEnrollmentTest,
diff --git a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
index de36090..d8a12fd4 100644
--- a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
+++ b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
@@ -38,6 +38,7 @@
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "net/http/http_status_code.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace em = enterprise_management;
 
@@ -566,8 +567,8 @@
   client_info.redirect_uri = "oob";
 
   // Use the system request context to avoid sending user cookies.
-  gaia_oauth_client_.reset(
-      new gaia::GaiaOAuthClient(g_browser_process->system_request_context()));
+  gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(
+      g_browser_process->shared_url_loader_factory()));
   gaia_oauth_client_->GetTokensFromAuthCode(
       client_info, client->robot_api_auth_code(), 0 /* max_retries */, this);
 }
diff --git a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
index 790e2201..4573b86 100644
--- a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
@@ -5,15 +5,21 @@
 #include "chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.h"
 
 #include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
 #include "components/user_manager/user_manager.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
+#include "extensions/browser/api/messaging/native_message_host.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/oauth2_token_service.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
+#include "remoting/host/it2me/it2me_native_messaging_host_chromeos.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "ui/base/user_activity/user_activity_detector.h"
 
@@ -21,7 +27,52 @@
 
 namespace {
 
-const char kICEConfigURL[] =
+// TODO(https://crbug.com/864455): move these constants to some place
+// that they can be reused by both this code and It2MeNativeMessagingHost.
+
+// Communication with CRD Host, messages sent to host:
+constexpr char kCRDMessageTypeKey[] = "type";
+
+constexpr char kCRDMessageHello[] = "hello";
+constexpr char kCRDMessageConnect[] = "connect";
+constexpr char kCRDMessageDisconnect[] = "disconnect";
+
+// Communication with CRD Host, messages received from host:
+constexpr char kCRDResponseHello[] = "helloResponse";
+constexpr char kCRDResponseConnect[] = "connectResponse";
+constexpr char kCRDStateChanged[] = "hostStateChanged";
+constexpr char kCRDResponseDisconnect[] = "disconnectResponse";
+
+// Connect message parameters:
+constexpr char kCRDConnectUserName[] = "userName";
+constexpr char kCRDConnectAuth[] = "authServiceWithToken";
+constexpr char kCRDConnectXMPPServer[] = "xmppServerAddress";
+constexpr char kCRDConnectXMPPTLS[] = "xmppServerUseTls";
+constexpr char kCRDConnectDirectoryBot[] = "directoryBotJid";
+constexpr char kCRDConnectICEConfig[] = "iceConfig";
+constexpr char kCRDConnectNoDialogs[] = "noDialogs";
+
+// Connect message parameter values:
+constexpr char kCRDConnectXMPPServerValue[] = "talk.google.com:443";
+constexpr char kCRDConnectDirectoryBotValue[] = "remoting@bot.talk.google.com";
+
+// CRD host states we care about:
+constexpr char kCRDStateKey[] = "state";
+constexpr char kCRDStateError[] = "ERROR";
+constexpr char kCRDStateStarting[] = "STARTING";
+constexpr char kCRDStateAccessCodeRequested[] = "REQUESTED_ACCESS_CODE";
+constexpr char kCRDStateDomainError[] = "INVALID_DOMAIN_ERROR";
+constexpr char kCRDStateAccessCode[] = "RECEIVED_ACCESS_CODE";
+constexpr char kCRDStateRemoteDisconnected[] = "DISCONNECTED";
+constexpr char kCRDStateRemoteConnected[] = "CONNECTED";
+
+constexpr char kCRDErrorCodeKey[] = "error_code";
+constexpr char kCRDAccessCodeKey[] = "accessCode";
+constexpr char kCRDAccessCodeLifetimeKey[] = "accessCodeLifetime";
+
+constexpr char kCRDConnectClientKey[] = "client";
+
+constexpr char kICEConfigURL[] =
     "https://www.googleapis.com/chromoting/v1/@me/iceconfig";
 
 // OAuth2 Token scopes
@@ -76,14 +127,14 @@
     : OAuth2TokenService::Consumer("crd_host_delegate"), weak_factory_(this) {}
 
 CRDHostDelegate::~CRDHostDelegate() {
-  // TODO(antrim): shutdown host somewhat correctly.
 }
 
 bool CRDHostDelegate::HasActiveSession() const {
-  return false;
+  return host_ != nullptr;
 }
 
 void CRDHostDelegate::TerminateSession(base::OnceClosure callback) {
+  DoShutdownHost();
   std::move(callback).Run();
 }
 
@@ -97,8 +148,16 @@
 bool CRDHostDelegate::IsRunningKiosk() const {
   auto* user_manager = user_manager::UserManager::Get();
   // TODO(antrim): find out if Arc Kiosk is also eligible.
-  // TODO(antrim): find out if only auto-started Kiosks are elidgible.
-  return user_manager->IsLoggedInAsKioskApp() && GetKioskProfile() != nullptr;
+  if (!user_manager->IsLoggedInAsKioskApp())
+    return false;
+  if (!GetKioskProfile())
+    return false;
+  chromeos::KioskAppManager* manager = chromeos::KioskAppManager::Get();
+  if (manager->GetAutoLaunchApp().empty())
+    return false;
+  chromeos::KioskAppManager::App app;
+  CHECK(manager->GetApp(manager->GetAutoLaunchApp(), &app));
+  return app.was_auto_launched_with_zero_delay;
 }
 
 base::TimeDelta CRDHostDelegate::GetIdlenessPeriod() const {
@@ -188,24 +247,255 @@
                "Could not parse config");
       return;
     }
-    error_callback_.Reset();
-    std::move(ice_success_callback_).Run(std::move(*value));
-  } else {
-    ice_success_callback_.Reset();
-    std::move(error_callback_)
-        .Run(DeviceCommandStartCRDSessionJob::FAILURE_NO_ICE_CONFIG,
-             std::string());
+    auto* config = value->FindKeyOfType("data", base::Value::Type::DICTIONARY);
+    if (config) {
+      error_callback_.Reset();
+      std::move(ice_success_callback_).Run(std::move(*config));
+      return;
+    }
   }
+
+  ice_success_callback_.Reset();
+  std::move(error_callback_)
+      .Run(DeviceCommandStartCRDSessionJob::FAILURE_NO_ICE_CONFIG,
+           std::string());
 }
 
 void CRDHostDelegate::StartCRDHostAndGetCode(
-    const std::string& directory_bot_jid,
     const std::string& oauth_token,
     base::Value ice_config,
-    DeviceCommandStartCRDSessionJob::AuthCodeCallback success_callback,
+    DeviceCommandStartCRDSessionJob::AccessCodeCallback success_callback,
     DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) {
-  // TODO(antrim): actual implementation
-  std::move(success_callback).Run(std::string("TODO: Auth Code"));
+  DCHECK(!host_);
+  DCHECK(!code_success_callback_);
+  DCHECK(!error_callback_);
+
+  // Store all parameters for future connect call.
+  base::Value connect_params(base::Value::Type::DICTIONARY);
+  std::string username =
+      chromeos::DeviceOAuth2TokenServiceFactory::Get()->GetRobotAccountId();
+
+  connect_params.SetKey(kCRDConnectUserName, base::Value(username));
+  connect_params.SetKey(kCRDConnectAuth, base::Value("oauth2:" + oauth_token));
+  connect_params.SetKey(kCRDConnectXMPPServer,
+                        base::Value(kCRDConnectXMPPServerValue));
+  connect_params.SetKey(kCRDConnectXMPPTLS, base::Value(true));
+  connect_params.SetKey(kCRDConnectDirectoryBot,
+                        base::Value(kCRDConnectDirectoryBotValue));
+  connect_params.SetKey(kCRDConnectICEConfig, std::move(ice_config));
+  connect_params.SetKey(kCRDConnectNoDialogs, base::Value(true));
+  connect_params_ = std::move(connect_params);
+
+  remote_connected_ = false;
+  command_awaiting_crd_access_code_ = true;
+
+  code_success_callback_ = std::move(success_callback);
+  error_callback_ = std::move(error_callback);
+
+  // TODO(antrim): set up watchdog timer (reasonable cutoff).
+  host_ = remoting::CreateIt2MeNativeMessagingHostForChromeOS(
+      g_browser_process->system_request_context(),
+      content::BrowserThread::GetTaskRunnerForThread(
+          content::BrowserThread::IO),
+      content::BrowserThread::GetTaskRunnerForThread(
+          content::BrowserThread::UI),
+      g_browser_process->policy_service());
+  host_->Start(this);
+
+  base::Value params(base::Value::Type::DICTIONARY);
+  SendMessageToHost(kCRDMessageHello, params);
+}
+
+void CRDHostDelegate::PostMessageFromNativeHost(const std::string& message) {
+  std::unique_ptr<base::Value> message_value = base::JSONReader::Read(message);
+  if (!message_value->is_dict()) {
+    OnProtocolBroken("Message is not a dictionary");
+    return;
+  }
+
+  auto* type_value = message_value->FindKeyOfType(kCRDMessageTypeKey,
+                                                  base::Value::Type::STRING);
+  if (!type_value) {
+    OnProtocolBroken("Message without type");
+    return;
+  }
+  std::string type = type_value->GetString();
+
+  if (type == kCRDResponseHello) {
+    OnHelloResponse();
+    return;
+  } else if (type == kCRDResponseConnect) {
+    // Ok, just ignore.
+    return;
+  } else if (type == kCRDResponseDisconnect) {
+    OnDisconnectResponse();
+    return;
+  } else if (type == kCRDStateChanged) {
+    // Handle CRD host state changes
+    auto* state_value =
+        message_value->FindKeyOfType(kCRDStateKey, base::Value::Type::STRING);
+    if (!state_value) {
+      OnProtocolBroken("No state in message");
+      return;
+    }
+    std::string state = state_value->GetString();
+
+    if (state == kCRDStateAccessCode) {
+      OnStateReceivedAccessCode(*message_value);
+    } else if (state == kCRDStateRemoteConnected) {
+      OnStateRemoteConnected(*message_value);
+    } else if (state == kCRDStateRemoteDisconnected) {
+      OnStateRemoteDisconnected();
+    } else if (state == kCRDStateError || state == kCRDStateDomainError) {
+      OnStateError(state, *message_value);
+    } else if (state == kCRDStateStarting ||
+               state == kCRDStateAccessCodeRequested) {
+      // Just ignore these states.
+    } else {
+      LOG(WARNING) << "Unhandled state :" << type;
+    }
+    return;
+  }
+  LOG(WARNING) << "Unknown message type :" << type;
+}
+
+void CRDHostDelegate::OnHelloResponse() {
+  // Host is initialized, start connection.
+  SendMessageToHost(kCRDMessageConnect, connect_params_);
+}
+
+void CRDHostDelegate::OnDisconnectResponse() {
+  // Should happen only when remoting session finished and we
+  // have requested host to shut down, or when we have got second auth code
+  // without receiving connection.
+  DCHECK(!command_awaiting_crd_access_code_);
+  DCHECK(!remote_connected_);
+  ShutdownHost();
+}
+
+void CRDHostDelegate::OnStateError(std::string error_state,
+                                   base::Value& message) {
+  std::string error_message;
+  if (error_state == kCRDStateDomainError) {
+    error_message = "CRD Error : Invalid domain";
+  } else {
+    auto* error_code_value =
+        message.FindKeyOfType(kCRDErrorCodeKey, base::Value::Type::STRING);
+    if (error_code_value)
+      error_message = error_code_value->GetString();
+    else
+      error_message = "Unknown CRD Error";
+  }
+  // Notify callback if command is still running.
+  if (command_awaiting_crd_access_code_) {
+    command_awaiting_crd_access_code_ = false;
+    std::move(error_callback_)
+        .Run(DeviceCommandStartCRDSessionJob::FAILURE_CRD_HOST_ERROR,
+             "CRD Error state " + error_state);
+    code_success_callback_.Reset();
+  }
+  // Shut down host, if any
+  ShutdownHost();
+}
+
+void CRDHostDelegate::OnStateRemoteConnected(base::Value& message) {
+  remote_connected_ = true;
+  // TODO(antrim): set up watchdog timer (session duration).
+  auto* client_value =
+      message.FindKeyOfType(kCRDConnectClientKey, base::Value::Type::STRING);
+  if (client_value) {
+    VLOG(1) << "Remote connection by " << client_value->GetString();
+  }
+}
+
+void CRDHostDelegate::OnStateRemoteDisconnected() {
+  // There could be a connection attempt that was not successful, we will
+  // receive "disconnected" message without actually receiving "connected".
+  if (!remote_connected_)
+    return;
+  remote_connected_ = false;
+  // Remote has disconnected, time to send "disconnect" that would result
+  // in shutting down the host.
+  base::Value params(base::Value::Type::DICTIONARY);
+  SendMessageToHost(kCRDMessageDisconnect, params);
+}
+
+void CRDHostDelegate::OnStateReceivedAccessCode(base::Value& message) {
+  if (!command_awaiting_crd_access_code_) {
+    if (!remote_connected_) {
+      // We have already sent the access code back to the server which initiated
+      // this CRD session through a remote command, and we can not send a new
+      // access code. Assuming that the old access code is no longer valid, we
+      // can only terminate the current CRD session.
+      base::Value params(base::Value::Type::DICTIONARY);
+      SendMessageToHost(kCRDMessageDisconnect, params);
+    }
+    return;
+  }
+
+  auto* code_value =
+      message.FindKeyOfType(kCRDAccessCodeKey, base::Value::Type::STRING);
+  auto* code_lifetime_value = message.FindKeyOfType(kCRDAccessCodeLifetimeKey,
+                                                    base::Value::Type::INTEGER);
+  if (!code_value || !code_lifetime_value) {
+    OnProtocolBroken("Can not obtain access code");
+    return;
+  }
+  // TODO(antrim): set up watchdog timer (access code lifetime).
+  command_awaiting_crd_access_code_ = false;
+  std::move(code_success_callback_).Run(std::string(code_value->GetString()));
+  error_callback_.Reset();
+}
+
+void CRDHostDelegate::CloseChannel(const std::string& error_message) {
+  LOG(ERROR) << "CRD Host closed channel" << error_message;
+  command_awaiting_crd_access_code_ = false;
+
+  if (error_callback_) {
+    std::move(error_callback_)
+        .Run(DeviceCommandStartCRDSessionJob::FAILURE_CRD_HOST_ERROR,
+             error_message);
+  }
+  code_success_callback_.Reset();
+  ShutdownHost();
+}
+
+void CRDHostDelegate::SendMessageToHost(const std::string& type,
+                                        base::Value& params) {
+  std::string message_json;
+  params.SetKey(kCRDMessageTypeKey, base::Value(type));
+  base::JSONWriter::Write(params, &message_json);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&CRDHostDelegate::DoSendMessage,
+                                weak_factory_.GetWeakPtr(), message_json));
+}
+
+void CRDHostDelegate::DoSendMessage(const std::string& json) {
+  if (!host_)
+    return;
+  host_->OnMessage(json);
+}
+
+void CRDHostDelegate::OnProtocolBroken(const std::string& message) {
+  LOG(ERROR) << "Error communicating with CRD Host : " << message;
+  command_awaiting_crd_access_code_ = false;
+
+  std::move(error_callback_)
+      .Run(DeviceCommandStartCRDSessionJob::FAILURE_CRD_HOST_ERROR, message);
+  code_success_callback_.Reset();
+  ShutdownHost();
+}
+
+void CRDHostDelegate::ShutdownHost() {
+  if (!host_)
+    return;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&CRDHostDelegate::DoShutdownHost,
+                                weak_factory_.GetWeakPtr()));
+}
+
+void CRDHostDelegate::DoShutdownHost() {
+  host_.reset();
 }
 
 Profile* CRDHostDelegate::GetKioskProfile() const {
diff --git a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.h b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.h
index ccc27a10..0e370259 100644
--- a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.h
+++ b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.h
@@ -13,6 +13,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_job.h"
+#include "extensions/browser/api/messaging/native_message_host.h"
 #include "google_apis/gaia/oauth2_token_service.h"
 
 namespace network {
@@ -25,7 +26,8 @@
 
 // An implementation of the |DeviceCommandStartCRDSessionJob::Delegate|.
 class CRDHostDelegate : public DeviceCommandStartCRDSessionJob::Delegate,
-                        public OAuth2TokenService::Consumer {
+                        public OAuth2TokenService::Consumer,
+                        public extensions::NativeMessageHost::Client {
  public:
   CRDHostDelegate();
   ~CRDHostDelegate() override;
@@ -45,10 +47,9 @@
       DeviceCommandStartCRDSessionJob::ICEConfigCallback success_callback,
       DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) override;
   void StartCRDHostAndGetCode(
-      const std::string& directory_bot_jid,
       const std::string& oauth_token,
       base::Value ice_config,
-      DeviceCommandStartCRDSessionJob::AuthCodeCallback success_callback,
+      DeviceCommandStartCRDSessionJob::AccessCodeCallback success_callback,
       DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) override;
 
   // OAuth2TokenService::Consumer:
@@ -58,16 +59,51 @@
   void OnGetTokenFailure(const OAuth2TokenService::Request* request,
                          const GoogleServiceAuthError& error) override;
 
+  // extensions::NativeMessageHost::Client:
+  // Invoked when native host sends a message
+  void PostMessageFromNativeHost(const std::string& message) override;
+  void CloseChannel(const std::string& error_message) override;
+
   void OnICEConfigurationLoaded(std::unique_ptr<std::string> response_body);
+  // Sends message to host in separate task.
+  void SendMessageToHost(const std::string& type, base::Value& params);
+  // Actually sends message to host.
+  void DoSendMessage(const std::string& json);
+  void OnProtocolBroken(const std::string& message);
+  // Shuts down host in a separate task.
+  void ShutdownHost();
+  // Actually shuts down a host.
+  void DoShutdownHost();
+
+  // Handlers for messages from host
+  void OnHelloResponse();
+  void OnDisconnectResponse();
+
+  void OnStateError(std::string error_state, base::Value& message);
+  void OnStateRemoteConnected(base::Value& message);
+  void OnStateRemoteDisconnected();
+  void OnStateReceivedAccessCode(base::Value& message);
 
   Profile* GetKioskProfile() const;
 
   DeviceCommandStartCRDSessionJob::OAuthTokenCallback oauth_success_callback_;
   DeviceCommandStartCRDSessionJob::ICEConfigCallback ice_success_callback_;
+  DeviceCommandStartCRDSessionJob::AccessCodeCallback code_success_callback_;
   DeviceCommandStartCRDSessionJob::ErrorCallback error_callback_;
 
   std::unique_ptr<OAuth2TokenService::Request> oauth_request_;
   std::unique_ptr<network::SimpleURLLoader> ice_config_loader_;
+  std::unique_ptr<extensions::NativeMessageHost> host_;
+
+  // Filled structure with parameters for "connect" message.
+  base::Value connect_params_;
+
+  // Determines actions when receiving messages from CRD host,
+  // if command is still running (no error / access code), then
+  // callbacks have to be called.
+  bool command_awaiting_crd_access_code_;
+  // True if remote session was established.
+  bool remote_connected_;
 
   base::WeakPtrFactory<CRDHostDelegate> weak_factory_;
 
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_job.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_job.cc
index 3089162..f51c543 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_job.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_job.cc
@@ -26,16 +26,13 @@
 // activity.
 const char kIdlenessCutoffFieldName[] = "idlenessCutoffSec";
 
-// Parameters that are directly passed to CRD Host.
-const char kDirectoryBotJIDFieldName[] = "directoryBotJID";
-
 // Result payload fields:
 
 // Integer value containing DeviceCommandStartCRDSessionJob::ResultCode
 const char kResultCodeFieldName[] = "resultCode";
 
-// CRD Auth Code if job was completed successfully
-const char kResultAuthCodeFieldName[] = "authCode";
+// CRD Access Code if job was completed successfully
+const char kResultAccessCodeFieldName[] = "accessCode";
 
 // Optional detailed error message for error result codes.
 const char kResultMessageFieldName[] = "message";
@@ -50,13 +47,13 @@
     : public RemoteCommandJob::ResultPayload {
  public:
   ResultPayload(ResultCode result_code,
-                const base::Optional<std::string>& auth_code,
+                const base::Optional<std::string>& access_code,
                 const base::Optional<base::TimeDelta>& time_delta,
                 const base::Optional<std::string>& error_message);
   ~ResultPayload() override {}
 
   static std::unique_ptr<ResultPayload> CreateSuccessPayload(
-      const std::string& auth_code);
+      const std::string& access_code);
   static std::unique_ptr<ResultPayload> CreateNonIdlePayload(
       const base::TimeDelta& time_delta);
   static std::unique_ptr<ResultPayload> CreateErrorPayload(
@@ -72,15 +69,15 @@
 
 DeviceCommandStartCRDSessionJob::ResultPayload::ResultPayload(
     ResultCode result_code,
-    const base::Optional<std::string>& auth_code,
+    const base::Optional<std::string>& access_code,
     const base::Optional<base::TimeDelta>& time_delta,
     const base::Optional<std::string>& error_message) {
   base::Value value(base::Value::Type::DICTIONARY);
   value.SetKey(kResultCodeFieldName, base::Value(result_code));
   if (error_message && !error_message.value().empty())
     value.SetKey(kResultMessageFieldName, base::Value(error_message.value()));
-  if (auth_code)
-    value.SetKey(kResultAuthCodeFieldName, base::Value(auth_code.value()));
+  if (access_code)
+    value.SetKey(kResultAccessCodeFieldName, base::Value(access_code.value()));
   if (time_delta) {
     value.SetKey(kResultLastActivityFieldName,
                  base::Value(static_cast<int>(time_delta.value().InSeconds())));
@@ -90,8 +87,8 @@
 
 std::unique_ptr<DeviceCommandStartCRDSessionJob::ResultPayload>
 DeviceCommandStartCRDSessionJob::ResultPayload::CreateSuccessPayload(
-    const std::string& auth_code) {
-  return std::make_unique<ResultPayload>(ResultCode::SUCCESS, auth_code,
+    const std::string& access_code) {
+  return std::make_unique<ResultPayload>(ResultCode::SUCCESS, access_code,
                                          base::nullopt /*time_delta*/,
                                          base::nullopt /* error_message */);
 }
@@ -100,7 +97,7 @@
 DeviceCommandStartCRDSessionJob::ResultPayload::CreateNonIdlePayload(
     const base::TimeDelta& time_delta) {
   return std::make_unique<ResultPayload>(
-      ResultCode::FAILURE_NOT_IDLE, base::nullopt /* auth_code */, time_delta,
+      ResultCode::FAILURE_NOT_IDLE, base::nullopt /* access_code */, time_delta,
       base::nullopt /* error_message */);
 }
 
@@ -111,8 +108,8 @@
   DCHECK(result_code != ResultCode::SUCCESS);
   DCHECK(result_code != ResultCode::FAILURE_NOT_IDLE);
   return std::make_unique<ResultPayload>(
-      result_code, base::nullopt /* auth_code */, base::nullopt /*time_delta*/,
-      error_message);
+      result_code, base::nullopt /* access_code */,
+      base::nullopt /*time_delta*/, error_message);
 }
 
 std::unique_ptr<std::string>
@@ -151,12 +148,6 @@
     idleness_cutoff_ = base::TimeDelta::FromSeconds(0);
   }
 
-  base::Value* directory_jid_value =
-      root->FindKeyOfType(kDirectoryBotJIDFieldName, base::Value::Type::STRING);
-  if (!directory_jid_value)
-    return false;
-  directory_bot_jid_ = directory_jid_value->GetString();
-
   return true;
 }
 
@@ -230,21 +221,21 @@
     base::Value ice_config) {
   ice_config_ = std::move(ice_config);
   delegate_->StartCRDHostAndGetCode(
-      directory_bot_jid_, oauth_token_, std::move(ice_config_),
-      base::BindOnce(&DeviceCommandStartCRDSessionJob::OnAuthCodeReceived,
+      oauth_token_, std::move(ice_config_),
+      base::BindOnce(&DeviceCommandStartCRDSessionJob::OnAccessCodeReceived,
                      weak_factory_.GetWeakPtr()),
       base::BindOnce(&DeviceCommandStartCRDSessionJob::FinishWithError,
                      weak_factory_.GetWeakPtr()));
 }
 
-void DeviceCommandStartCRDSessionJob::OnAuthCodeReceived(
-    const std::string& auth_code) {
+void DeviceCommandStartCRDSessionJob::OnAccessCodeReceived(
+    const std::string& access_code) {
   if (!succeeded_callback_)
     return;  // Task was terminated.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(std::move(succeeded_callback_),
-                     ResultPayload::CreateSuccessPayload(auth_code)));
+                     ResultPayload::CreateSuccessPayload(access_code)));
 }
 
 void DeviceCommandStartCRDSessionJob::TerminateImpl() {
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_job.h b/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_job.h
index 29bb0b89..96857ed 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_job.h
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_job.h
@@ -44,7 +44,7 @@
   };
 
   using OAuthTokenCallback = base::OnceCallback<void(const std::string&)>;
-  using AuthCodeCallback = base::OnceCallback<void(const std::string&)>;
+  using AccessCodeCallback = base::OnceCallback<void(const std::string&)>;
   using ICEConfigCallback = base::OnceCallback<void(base::Value)>;
   using ErrorCallback =
       base::OnceCallback<void(ResultCode, const std::string&)>;
@@ -80,10 +80,9 @@
                                 ErrorCallback error_callback) = 0;
 
     // Attempts to start CRD host and get Auth Code.
-    virtual void StartCRDHostAndGetCode(const std::string& directory_bot_jid,
-                                        const std::string& oauth_token,
+    virtual void StartCRDHostAndGetCode(const std::string& oauth_token,
                                         base::Value ice_config,
-                                        AuthCodeCallback success_callback,
+                                        AccessCodeCallback success_callback,
                                         ErrorCallback error_callback) = 0;
   };
 
@@ -108,7 +107,7 @@
 
   void OnOAuthTokenReceived(const std::string& token);
   void OnICEConfigReceived(base::Value ice_config);
-  void OnAuthCodeReceived(const std::string& token);
+  void OnAccessCodeReceived(const std::string& access_code);
 
   // The callback that will be called when the access code was successfully
   // obtained.
@@ -123,7 +122,6 @@
   base::TimeDelta idleness_cutoff_;
 
   std::string oauth_token_;
-  std::string directory_bot_jid_;
   base::Value ice_config_;
 
   // The Delegate is used to interact with chrome services and CRD host.
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_unittest.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_unittest.cc
index 742018b7..63a9992 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_unittest.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_unittest.cc
@@ -29,25 +29,21 @@
 
 constexpr char kResultCodeFieldName[] = "resultCode";
 constexpr char kResultMessageFieldName[] = "message";
-constexpr char kResultAuthCodeFieldName[] = "authCode";
+constexpr char kResultAccessCodeFieldName[] = "accessCode";
 constexpr char kResultLastActivityFieldName[] = "lastActivitySec";
 
 constexpr RemoteCommandJob::UniqueIDType kUniqueID = 123456789;
 
-constexpr char kTestDirectoryBotJID[] = "remote@bot.jabber.nowhere.org";
-
 constexpr char kTestOAuthToken[] = "test-oauth-token";
-constexpr char kTestAuthCode[] = "1111-2222-3333";
+constexpr char kTestAccessCode[] = "111122223333";
 constexpr char kTestNoOAuthTokenReason[] = "oops-no-oauth-token";
 constexpr char kTestNoICEConfigReason[] = "oops-no-ice-config";
 
 constexpr char kIdlenessCutoffFieldName[] = "idlenessCutoffSec";
-constexpr char kDirectoryBotJIDFieldName[] = "directoryBotJID";
 
 em::RemoteCommand GenerateCommandProto(RemoteCommandJob::UniqueIDType unique_id,
                                        base::TimeDelta age_of_command,
-                                       base::TimeDelta idleness_cutoff,
-                                       const std::string& directory_bot_jid) {
+                                       base::TimeDelta idleness_cutoff) {
   em::RemoteCommand command_proto;
   command_proto.set_type(
       enterprise_management::RemoteCommand_Type_DEVICE_START_CRD_SESSION);
@@ -58,7 +54,6 @@
   base::Value root_dict(base::Value::Type::DICTIONARY);
   root_dict.SetKey(kIdlenessCutoffFieldName,
                    base::Value((int)idleness_cutoff.InSeconds()));
-  root_dict.SetKey(kDirectoryBotJIDFieldName, base::Value(directory_bot_jid));
   base::JSONWriter::Write(root_dict, &payload);
   command_proto.set_payload(payload);
   return command_proto;
@@ -72,7 +67,7 @@
                       base::TimeDelta idleness_period,
                       bool oauth_token_success,
                       bool ice_config_success,
-                      bool auth_code_success);
+                      bool access_code_success);
   ~StubCRDHostDelegate() override;
 
   bool HasActiveSession() const override;
@@ -92,10 +87,9 @@
       DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) override;
 
   void StartCRDHostAndGetCode(
-      const std::string& directory_bot_jid,
       const std::string& oauth_token,
       base::Value ice_config,
-      DeviceCommandStartCRDSessionJob::AuthCodeCallback success_callback,
+      DeviceCommandStartCRDSessionJob::AccessCodeCallback success_callback,
       DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) override;
 
  private:
@@ -105,7 +99,7 @@
   base::TimeDelta idleness_period_;
   bool oauth_token_success_;
   bool ice_config_success_;
-  bool auth_code_success_;
+  bool access_code_success_;
 
   DISALLOW_COPY_AND_ASSIGN(StubCRDHostDelegate);
 };
@@ -116,14 +110,14 @@
                                          base::TimeDelta idleness_period,
                                          bool oauth_token_success,
                                          bool ice_config_success,
-                                         bool auth_code_success)
+                                         bool access_code_success)
     : has_active_session_(has_active_session),
       are_services_ready_(are_services_ready),
       is_running_kiosk_(is_running_kiosk),
       idleness_period_(idleness_period),
       oauth_token_success_(oauth_token_success),
       ice_config_success_(ice_config_success),
-      auth_code_success_(auth_code_success) {}
+      access_code_success_(access_code_success) {}
 
 StubCRDHostDelegate::~StubCRDHostDelegate() {}
 
@@ -175,13 +169,12 @@
 }
 
 void StubCRDHostDelegate::StartCRDHostAndGetCode(
-    const std::string& directory_bot_jid,
     const std::string& oauth_token,
     base::Value ice_config,
-    DeviceCommandStartCRDSessionJob::AuthCodeCallback success_callback,
+    DeviceCommandStartCRDSessionJob::AccessCodeCallback success_callback,
     DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) {
-  if (auth_code_success_) {
-    std::move(success_callback).Run(kTestAuthCode);
+  if (access_code_success_) {
+    std::move(success_callback).Run(kTestAccessCode);
   } else {
     std::move(error_callback)
         .Run(DeviceCommandStartCRDSessionJob::FAILURE_CRD_HOST_ERROR,
@@ -206,10 +199,9 @@
   void InitializeJob(RemoteCommandJob* job,
                      RemoteCommandJob::UniqueIDType unique_id,
                      base::TimeTicks issued_time,
-                     base::TimeDelta idleness_cutoff,
-                     const std::string& directory_bot_jid);
+                     base::TimeDelta idleness_cutoff);
 
-  std::string CreateSuccessPayload(const std::string& auth_code);
+  std::string CreateSuccessPayload(const std::string& access_code);
   std::string CreateErrorPayload(
       DeviceCommandStartCRDSessionJob::ResultCode result_code,
       const std::string& error_message);
@@ -234,24 +226,23 @@
     RemoteCommandJob* job,
     RemoteCommandJob::UniqueIDType unique_id,
     base::TimeTicks issued_time,
-    base::TimeDelta idleness_cutoff,
-    const std::string& directory_bot_jid) {
+    base::TimeDelta idleness_cutoff) {
   EXPECT_TRUE(job->Init(
       base::TimeTicks::Now(),
       GenerateCommandProto(unique_id, base::TimeTicks::Now() - issued_time,
-                           idleness_cutoff, directory_bot_jid)));
+                           idleness_cutoff)));
 
   EXPECT_EQ(unique_id, job->unique_id());
   EXPECT_EQ(RemoteCommandJob::NOT_STARTED, job->status());
 }
 
 std::string DeviceCommandStartCRDSessionJobTest::CreateSuccessPayload(
-    const std::string& auth_code) {
+    const std::string& access_code) {
   std::string payload;
   base::Value root(base::Value::Type::DICTIONARY);
   root.SetKey(kResultCodeFieldName,
               base::Value(DeviceCommandStartCRDSessionJob::SUCCESS));
-  root.SetKey(kResultAuthCodeFieldName, base::Value(auth_code));
+  root.SetKey(kResultAccessCodeFieldName, base::Value(access_code));
   base::JSONWriter::Write(root, &payload);
   return payload;
 }
@@ -297,18 +288,18 @@
       true /* is_running_kiosk */,
       base::TimeDelta::FromHours(1) /* idleness_period */,
       true /* oauth_token_success */, true /* ice_config_success */,
-      true /* auth_code_success */);
+      true /* access_code_success */);
 
   std::unique_ptr<RemoteCommandJob> job =
       std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate);
   InitializeJob(job.get(), kUniqueID, test_start_time_,
-                base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID);
+                base::TimeDelta::FromSeconds(30));
   bool success = job->Run(
       base::TimeTicks::Now(),
       base::BindOnce(&DeviceCommandStartCRDSessionJobTest::VerifyResults,
                      base::Unretained(this), base::Unretained(job.get()),
                      RemoteCommandJob::SUCCEEDED,
-                     CreateSuccessPayload(kTestAuthCode)));
+                     CreateSuccessPayload(kTestAccessCode)));
   EXPECT_TRUE(success);
   run_loop_.Run();
 }
@@ -319,18 +310,18 @@
       true /* is_running_kiosk */,
       base::TimeDelta::FromHours(1) /* idleness_period */,
       true /* oauth_token_success */, true /* ice_config_success */,
-      true /* auth_code_success */);
+      true /* access_code_success */);
 
   std::unique_ptr<RemoteCommandJob> job =
       std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate);
   InitializeJob(job.get(), kUniqueID, test_start_time_,
-                base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID);
+                base::TimeDelta::FromSeconds(30));
   bool success = job->Run(
       base::TimeTicks::Now(),
       base::BindOnce(&DeviceCommandStartCRDSessionJobTest::VerifyResults,
                      base::Unretained(this), base::Unretained(job.get()),
                      RemoteCommandJob::SUCCEEDED,
-                     CreateSuccessPayload(kTestAuthCode)));
+                     CreateSuccessPayload(kTestAccessCode)));
   EXPECT_TRUE(success);
   run_loop_.Run();
 }
@@ -341,12 +332,12 @@
       true /* is_running_kiosk */,
       base::TimeDelta::FromHours(1) /* idleness_period */,
       true /* oauth_token_success */, true /* ice_config_success */,
-      true /* auth_code_success */);
+      true /* access_code_success */);
 
   std::unique_ptr<RemoteCommandJob> job =
       std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate);
   InitializeJob(job.get(), kUniqueID, test_start_time_,
-                base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID);
+                base::TimeDelta::FromSeconds(30));
   bool success = job->Run(
       base::TimeTicks::Now(),
       base::BindOnce(
@@ -366,12 +357,12 @@
       false /* is_running_kiosk */,
       base::TimeDelta::FromHours(1) /* idleness_period */,
       true /* oauth_token_success */, true /* ice_config_success */,
-      true /* auth_code_success */);
+      true /* access_code_success */);
 
   std::unique_ptr<RemoteCommandJob> job =
       std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate);
   InitializeJob(job.get(), kUniqueID, test_start_time_,
-                base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID);
+                base::TimeDelta::FromSeconds(30));
   bool success = job->Run(
       base::TimeTicks::Now(),
       base::BindOnce(&DeviceCommandStartCRDSessionJobTest::VerifyResults,
@@ -390,12 +381,12 @@
       true /* is_running_kiosk */,
       base::TimeDelta::FromSeconds(1) /* idleness_period */,
       true /* oauth_token_success */, true /* ice_config_success */,
-      true /* auth_code_success */);
+      true /* access_code_success */);
 
   std::unique_ptr<RemoteCommandJob> job =
       std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate);
   InitializeJob(job.get(), kUniqueID, test_start_time_,
-                base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID);
+                base::TimeDelta::FromSeconds(30));
   bool success = job->Run(
       base::TimeTicks::Now(),
       base::BindOnce(&DeviceCommandStartCRDSessionJobTest::VerifyResults,
@@ -412,12 +403,12 @@
       true /* is_running_kiosk */,
       base::TimeDelta::FromHours(1) /* idleness_period */,
       false /* oauth_token_success */, true /* ice_config_success */,
-      true /* auth_code_success */);
+      true /* access_code_success */);
 
   std::unique_ptr<RemoteCommandJob> job =
       std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate);
   InitializeJob(job.get(), kUniqueID, test_start_time_,
-                base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID);
+                base::TimeDelta::FromSeconds(30));
   bool success =
       job->Run(base::TimeTicks::Now(),
                base::BindOnce(
@@ -437,12 +428,12 @@
       true /* is_running_kiosk */,
       base::TimeDelta::FromHours(1) /* idleness_period */,
       true /* oauth_token_success */, false /* ice_config_success */,
-      true /* auth_code_success */);
+      true /* access_code_success */);
 
   std::unique_ptr<RemoteCommandJob> job =
       std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate);
   InitializeJob(job.get(), kUniqueID, test_start_time_,
-                base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID);
+                base::TimeDelta::FromSeconds(30));
   bool success = job->Run(
       base::TimeTicks::Now(),
       base::BindOnce(&DeviceCommandStartCRDSessionJobTest::VerifyResults,
@@ -461,12 +452,12 @@
       true /* is_running_kiosk */,
       base::TimeDelta::FromHours(1) /* idleness_period */,
       true /* oauth_token_success */, true /* ice_config_success */,
-      false /* auth_code_success */);
+      false /* access_code_success */);
 
   std::unique_ptr<RemoteCommandJob> job =
       std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate);
   InitializeJob(job.get(), kUniqueID, test_start_time_,
-                base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID);
+                base::TimeDelta::FromSeconds(30));
   bool success =
       job->Run(base::TimeTicks::Now(),
                base::BindOnce(
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc
index fcbbeb7..eb9d85d 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc
@@ -281,8 +281,8 @@
 
   state_ = STATE_VALIDATION_STARTED;
 
-  gaia_oauth_client_.reset(
-      new gaia::GaiaOAuthClient(g_browser_process->system_request_context()));
+  gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(
+      g_browser_process->shared_url_loader_factory()));
 
   GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
   gaia::OAuthClientInfo client_info;
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
index 01e58efa..c6fcfd4 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
@@ -35,11 +35,9 @@
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/oauth2_token_service_test_util.h"
 #include "net/http/http_status_code.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_test_util.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -63,16 +61,16 @@
 
 }  // namespace
 
-static const int kValidatorUrlFetcherId = gaia::GaiaOAuthClient::kUrlFetcherId;
-
 class DeviceOAuth2TokenServiceTest : public testing::Test {
  public:
   DeviceOAuth2TokenServiceTest()
       : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()),
         test_shared_loader_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-                &test_url_loader_factory_)) {}
-  ~DeviceOAuth2TokenServiceTest() override {}
+                &test_url_loader_factory_)) {
+    TestingBrowserProcess::GetGlobal()->SetSharedURLLoaderFactory(
+        test_shared_loader_factory_);
+  }
 
   // Most tests just want a noop crypto impl with a dummy refresh token value in
   // Local State (if the value is an empty string, it will be ignored).
@@ -127,6 +125,7 @@
   }
 
   void TearDown() override {
+    TestingBrowserProcess::GetGlobal()->SetSharedURLLoaderFactory(nullptr);
     oauth2_service_.reset();
     test_shared_loader_factory_->Detach();
     CrosSettings::Shutdown();
@@ -176,16 +175,15 @@
 
   // A utility method to return fake URL results, for testing the refresh token
   // validation logic.  For a successful validation attempt, this method will be
-  // called three times for the steps listed below (steps 1 and 2 happen in
-  // parallel).
+  // called three times for the steps listed below.
   //
   // Step 1a: fetch the access token for the tokeninfo API.
   // Step 1b: call the tokeninfo API.
   // Step 2:  Fetch the access token for the requested scope
   //          (in this case, cloudprint).
-  void ReturnOAuthUrlFetchResults(int fetcher_id,
+  void ReturnOAuthUrlFetchResults(const std::string& url,
                                   net::HttpStatusCode response_code,
-                                  const std::string&  response_string);
+                                  const std::string& response_string);
 
   // Generates URL fetch replies with the specified results for requests
   // generated by the token service.
@@ -218,7 +216,6 @@
   network::TestURLLoaderFactory test_url_loader_factory_;
   scoped_refptr<network::WeakWrapperSharedURLLoaderFactory>
       test_shared_loader_factory_;
-  net::TestURLFetcherFactory factory_;
   FakeCryptohomeClient* fake_cryptohome_client_;
   FakeSessionManagerClient session_manager_client_;
   policy::DevicePolicyBuilder device_policy_;
@@ -228,16 +225,13 @@
 };
 
 void DeviceOAuth2TokenServiceTest::ReturnOAuthUrlFetchResults(
-    int fetcher_id,
+    const std::string& url,
     net::HttpStatusCode response_code,
     const std::string& response_string) {
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(fetcher_id);
-  if (fetcher) {
-    factory_.RemoveFetcherFromMap(fetcher_id);
-    fetcher->set_response_code(response_code);
-    fetcher->SetResponseString(response_string);
-    fetcher->delegate()->OnURLFetchComplete(fetcher);
-    base::RunLoop().RunUntilIdle();
+  if (test_url_loader_factory_.IsPending(url)) {
+    test_url_loader_factory_.SimulateResponseForPendingRequest(
+        GURL(url), network::URLLoaderCompletionStatus(net::OK),
+        network::CreateResourceResponseHead(response_code), response_string);
   }
 }
 
@@ -248,21 +242,17 @@
     const std::string& tokeninfo_fetch_response,
     net::HttpStatusCode service_access_token_status,
     const std::string& service_access_token_response) {
-  test_url_loader_factory_.AddResponse(
-      GaiaUrls::GetInstance()->oauth2_token_url().spec(),
-      service_access_token_response, service_access_token_status);
-
-  // This sends a response to the OAuth token request made by
-  // GaiaOAuthClient::Core, that should eventually be ported to SimpleURLLoader.
-  ReturnOAuthUrlFetchResults(
-      kValidatorUrlFetcherId,
-      tokeninfo_access_token_status,
-      tokeninfo_access_token_response);
+  ReturnOAuthUrlFetchResults(GaiaUrls::GetInstance()->oauth2_token_url().spec(),
+                             tokeninfo_access_token_status,
+                             tokeninfo_access_token_response);
 
   ReturnOAuthUrlFetchResults(
-      kValidatorUrlFetcherId,
-      tokeninfo_fetch_status,
-      tokeninfo_fetch_response);
+      GaiaUrls::GetInstance()->oauth2_token_info_url().spec(),
+      tokeninfo_fetch_status, tokeninfo_fetch_response);
+
+  ReturnOAuthUrlFetchResults(GaiaUrls::GetInstance()->oauth2_token_url().spec(),
+                             service_access_token_status,
+                             service_access_token_response);
 }
 
 void DeviceOAuth2TokenServiceTest::PerformURLFetches() {
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index a6529fc..e0fbac6 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -1496,12 +1496,21 @@
   ASSERT_EQ(items[2]->GetTargetFilePath().value(), item_name);
 }
 
+// https://crbug.com/874946, flaky on Win.
+#if defined(OS_WIN)
+#define MAYBE_DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito \
+  DISABLED_DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito
+#else
+#define MAYBE_DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito \
+  DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito
+#endif
 // Test that incognito downloads are only visible in incognito contexts, and
 // test that on-record downloads are visible in both incognito and on-record
 // contexts, for DownloadsSearchFunction, DownloadsPauseFunction,
 // DownloadsResumeFunction, and DownloadsCancelFunction.
-IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
-    DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito) {
+IN_PROC_BROWSER_TEST_F(
+    DownloadExtensionTest,
+    MAYBE_DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito) {
   std::unique_ptr<base::Value> result_value;
   base::ListValue* result_list = NULL;
   base::DictionaryValue* result_dict = NULL;
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 46d68e7..6d1da85 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -1611,9 +1611,7 @@
         base::Unretained(this)));
     ASSERT_TRUE(https_test_server_.InitializeAndListen());
     ExtensionApiTest::SetUp();
-    feature_list_.InitWithFeatures(
-        {::features::kUseGoogleLocalNtp, ::features::kOneGoogleBarOnLocalNtp},
-        {});
+    feature_list_.InitWithFeatures({::features::kUseGoogleLocalNtp}, {});
   }
   void SetUpCommandLine(base::CommandLine* command_line) override {
     ExtensionApiTest::SetUpCommandLine(command_line);
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 25ccc34b..7ed258a 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -440,6 +440,11 @@
 const char kEnableAutoplayIgnoreWebAudioDescription[] =
     "If enabled, autoplay restrictions will be ignored for WebAudio.";
 
+const char kEnableAutoplayUnifiedSoundSettingsName[] =
+    "Autoplay unified sound settings UI";
+const char kEnableAutoplayUnifiedSoundSettingsDescription[] =
+    "If enabled, shows the new unified autoplay sound settings UI.";
+
 const char kEnableBreakingNewsPushName[] = "Breaking News Push";
 const char kEnableBreakingNewsPushDescription[] =
     "Listen for breaking news content suggestions (e.g. for New Tab Page) "
@@ -2812,12 +2817,6 @@
     "Enable receiving tail suggestions, a type of search suggestion based on "
     "the last few words in the query, for the Omnibox.";
 
-const char kOneGoogleBarOnLocalNtpName[] =
-    "Enable the OneGoogleBar on the local NTP";
-const char kOneGoogleBarOnLocalNtpDescription[] =
-    "Show a OneGoogleBar on the local New Tab page if Google is the default "
-    "search engine.";
-
 const char kPageAlmostIdleName[] = "Page Almost Idle";
 const char kPageAlmostIdleDescription[] =
     "Make session restore use a definition of loading that waits for CPU and "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 07311c4..1fd39d1 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -296,6 +296,9 @@
 extern const char kEnableAutoplayIgnoreWebAudioName[];
 extern const char kEnableAutoplayIgnoreWebAudioDescription[];
 
+extern const char kEnableAutoplayUnifiedSoundSettingsName[];
+extern const char kEnableAutoplayUnifiedSoundSettingsDescription[];
+
 extern const char kEnableBreakingNewsPushName[];
 extern const char kEnableBreakingNewsPushDescription[];
 
@@ -1697,9 +1700,6 @@
 extern const char kOmniboxTailSuggestionsName[];
 extern const char kOmniboxTailSuggestionsDescription[];
 
-extern const char kOneGoogleBarOnLocalNtpName[];
-extern const char kOneGoogleBarOnLocalNtpDescription[];
-
 extern const char kPageAlmostIdleName[];
 extern const char kPageAlmostIdleDescription[];
 
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc
index 1a29372..f893686 100644
--- a/chrome/browser/infobars/infobars_browsertest.cc
+++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -415,8 +415,7 @@
     case IBD::DATA_REDUCTION_PROXY_PREVIEW_INFOBAR_DELEGATE:
       PreviewsInfoBarDelegate::Create(
           GetWebContents(), previews::PreviewsType::LOFI, base::Time::Now(),
-          true, true,
-          PreviewsInfoBarDelegate::OnDismissPreviewsInfobarCallback(), nullptr);
+          true, true, OnDismissPreviewsUICallback(), nullptr);
       break;
 
     case IBD::AUTOMATION_INFOBAR_DELEGATE:
diff --git a/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker.cc b/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker.cc
index a2f95488..ec94801 100644
--- a/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker.cc
+++ b/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker.cc
@@ -32,10 +32,34 @@
   browser->tab_strip_model()->RemoveObserver(this);
 }
 
-void AudibleContentsTracker::TabClosingAt(TabStripModel* model,
-                                          content::WebContents* web_contents,
-                                          int index) {
-  RemoveAudibleWebContents(web_contents);
+void AudibleContentsTracker::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  if (change.type() != TabStripModelChange::kRemoved &&
+      change.type() != TabStripModelChange::kReplaced)
+    return;
+
+  for (const auto& delta : change.deltas()) {
+    content::WebContents* removed_contents = nullptr;
+    content::WebContents* added_contents = nullptr;
+    if (change.type() == TabStripModelChange::kReplaced) {
+      removed_contents = delta.replace.old_contents;
+      added_contents = delta.replace.new_contents;
+    } else if (delta.remove.will_be_deleted) {
+      removed_contents = delta.remove.contents;
+    }
+
+    if (removed_contents)
+      RemoveAudibleWebContents(removed_contents);
+
+    if (added_contents) {
+      auto* audible_helper =
+          RecentlyAudibleHelper::FromWebContents(added_contents);
+      if (audible_helper->WasRecentlyAudible())
+        AddAudibleWebContents(added_contents);
+    }
+  }
 }
 
 void AudibleContentsTracker::TabChangedAt(content::WebContents* web_contents,
@@ -52,18 +76,6 @@
     RemoveAudibleWebContents(web_contents);
 }
 
-void AudibleContentsTracker::TabReplacedAt(
-    TabStripModel* model,
-    content::WebContents* old_web_contents,
-    content::WebContents* new_web_contents,
-    int index) {
-  RemoveAudibleWebContents(old_web_contents);
-  auto* audible_helper =
-      RecentlyAudibleHelper::FromWebContents(new_web_contents);
-  if (audible_helper->WasRecentlyAudible())
-    AddAudibleWebContents(new_web_contents);
-}
-
 void AudibleContentsTracker::AddAudibleWebContents(
     content::WebContents* web_contents) {
   // The first web contents to become audible indicates that audio has started.
diff --git a/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker.h b/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker.h
index eb51e3e8..0f295f4bf 100644
--- a/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker.h
+++ b/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker.h
@@ -50,16 +50,13 @@
   void OnBrowserRemoved(Browser* browser) override;
 
   // TabStripModelObserver:
-  void TabClosingAt(TabStripModel* model,
-                    content::WebContents* web_contents,
-                    int index) override;
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
   void TabChangedAt(content::WebContents* web_contents,
                     int index,
                     TabChangeType change_type) override;
-  void TabReplacedAt(TabStripModel* model,
-                     content::WebContents* old_web_contents,
-                     content::WebContents* new_web_contents,
-                     int index) override;
 
   // Used for managing audible_contents_, and invoking OnAudioStart and
   // OnAudioEnd callbacks.
diff --git a/chrome/browser/metrics/tab_reactivation_tracker.cc b/chrome/browser/metrics/tab_reactivation_tracker.cc
index 7a2a1e2..20c11dc 100644
--- a/chrome/browser/metrics/tab_reactivation_tracker.cc
+++ b/chrome/browser/metrics/tab_reactivation_tracker.cc
@@ -75,25 +75,23 @@
 
 TabReactivationTracker::~TabReactivationTracker() = default;
 
-void TabReactivationTracker::TabInsertedAt(TabStripModel* tab_strip_model,
-                                           content::WebContents* contents,
-                                           int index,
-                                           bool foreground) {}
+void TabReactivationTracker::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  if (change.type() == TabStripModelChange::kRemoved) {
+    for (const auto& delta : change.deltas()) {
+      if (delta.remove.will_be_deleted)
+        GetHelper(delta.remove.contents)->OnTabClosing();
+    }
+  }
 
-void TabReactivationTracker::TabClosingAt(TabStripModel* tab_strip_model,
-                                          content::WebContents* contents,
-                                          int index) {
-  GetHelper(contents)->OnTabClosing();
-}
-
-void TabReactivationTracker::ActiveTabChanged(
-    content::WebContents* old_contents,
-    content::WebContents* new_contents,
-    int index,
-    int reason) {
-  if (old_contents)
-    GetHelper(old_contents)->OnTabDeactivating();
-  GetHelper(new_contents)->OnTabActivating();
+  if (selection.active_tab_changed()) {
+    if (selection.old_contents)
+      GetHelper(selection.old_contents)->OnTabDeactivating();
+    if (selection.new_contents)
+      GetHelper(selection.new_contents)->OnTabActivating();
+  }
 }
 
 void TabReactivationTracker::NotifyTabDeactivating(
diff --git a/chrome/browser/metrics/tab_reactivation_tracker.h b/chrome/browser/metrics/tab_reactivation_tracker.h
index 177e18f7..39278c9 100644
--- a/chrome/browser/metrics/tab_reactivation_tracker.h
+++ b/chrome/browser/metrics/tab_reactivation_tracker.h
@@ -31,17 +31,10 @@
   ~TabReactivationTracker() override;
 
   // TabStripModelObserver:
-  void TabInsertedAt(TabStripModel* tab_strip_model,
-                     content::WebContents* contents,
-                     int index,
-                     bool foreground) override;
-  void TabClosingAt(TabStripModel* tab_strip_model,
-                    content::WebContents* contents,
-                    int index) override;
-  void ActiveTabChanged(content::WebContents* old_contents,
-                        content::WebContents* new_contents,
-                        int index,
-                        int reason) override;
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
 
   void NotifyTabDeactivating(content::WebContents* contents);
   void NotifyTabReactivating(content::WebContents* contents);
diff --git a/chrome/browser/metrics/tab_stats_tracker.cc b/chrome/browser/metrics/tab_stats_tracker.cc
index 2180049..364e16ca 100644
--- a/chrome/browser/metrics/tab_stats_tracker.cc
+++ b/chrome/browser/metrics/tab_stats_tracker.cc
@@ -240,15 +240,32 @@
   browser->tab_strip_model()->RemoveObserver(this);
 }
 
-void TabStatsTracker::TabInsertedAt(TabStripModel* model,
-                                    content::WebContents* web_contents,
-                                    int index,
-                                    bool foreground) {
+void TabStatsTracker::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  OnInitialOrInsertedTab(web_contents);
+  if (change.type() == TabStripModelChange::kInserted) {
+    for (const auto& delta : change.deltas())
+      OnInitialOrInsertedTab(delta.insert.contents);
 
-  tab_stats_data_store_->UpdateMaxTabsPerWindowIfNeeded(
-      static_cast<size_t>(model->count()));
+    tab_stats_data_store_->UpdateMaxTabsPerWindowIfNeeded(
+        static_cast<size_t>(tab_strip_model->count()));
+
+    return;
+  }
+
+  if (change.type() == TabStripModelChange::kReplaced) {
+    for (const auto& delta : change.deltas()) {
+      content::WebContents* old_contents = delta.replace.old_contents;
+      content::WebContents* new_contents = delta.replace.new_contents;
+      tab_stats_data_store_->OnTabReplaced(old_contents, new_contents);
+      web_contents_usage_observers_.insert(std::make_pair(
+          new_contents,
+          std::make_unique<WebContentsUsageObserver>(new_contents, this)));
+      web_contents_usage_observers_.erase(old_contents);
+    }
+  }
 }
 
 void TabStatsTracker::TabChangedAt(content::WebContents* web_contents,
@@ -262,18 +279,6 @@
     tab_stats_data_store_->OnTabAudible(web_contents);
 }
 
-void TabStatsTracker::TabReplacedAt(TabStripModel* tab_strip_model,
-                                    content::WebContents* old_contents,
-                                    content::WebContents* new_contents,
-                                    int index) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  tab_stats_data_store_->OnTabReplaced(old_contents, new_contents);
-  web_contents_usage_observers_.insert(std::make_pair(
-      new_contents,
-      std::make_unique<WebContentsUsageObserver>(new_contents, this)));
-  web_contents_usage_observers_.erase(old_contents);
-}
-
 void TabStatsTracker::OnResume() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   reporting_delegate_->ReportTabCountOnResume(
diff --git a/chrome/browser/metrics/tab_stats_tracker.h b/chrome/browser/metrics/tab_stats_tracker.h
index a6ecbcf..25703a2 100644
--- a/chrome/browser/metrics/tab_stats_tracker.h
+++ b/chrome/browser/metrics/tab_stats_tracker.h
@@ -131,18 +131,13 @@
   void OnBrowserRemoved(Browser* browser) override;
 
   // TabStripModelObserver:
-  void TabInsertedAt(TabStripModel* tab_strip_model,
-                     content::WebContents* contents,
-                     int index,
-                     bool foreground) override;
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
   void TabChangedAt(content::WebContents* web_contents,
                     int index,
                     TabChangeType change_type) override;
-  void TabReplacedAt(TabStripModel* tab_strip_model,
-                     content::WebContents* old_contents,
-                     content::WebContents* new_contents,
-                     int index) override;
-
   // base::PowerObserver:
   void OnResume() override;
 
diff --git a/chrome/browser/metrics/tab_usage_recorder.cc b/chrome/browser/metrics/tab_usage_recorder.cc
index b121f73..6c9d042f 100644
--- a/chrome/browser/metrics/tab_usage_recorder.cc
+++ b/chrome/browser/metrics/tab_usage_recorder.cc
@@ -101,12 +101,18 @@
   GetWebContentsData(contents)->RecordTabReactivation();
 }
 
-void TabUsageRecorder::TabInsertedAt(TabStripModel* tab_strip_model,
-                                     content::WebContents* contents,
-                                     int index,
-                                     bool foreground) {
-  // Set the initial pin state.
-  TabPinnedStateChanged(tab_strip_model, contents, index);
+void TabUsageRecorder::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  if (change.type() != TabStripModelChange::kInserted)
+    return;
+
+  for (const auto& delta : change.deltas()) {
+    // Set the initial pin state.
+    TabPinnedStateChanged(tab_strip_model, delta.insert.contents,
+                          delta.insert.index);
+  }
 }
 
 void TabUsageRecorder::TabPinnedStateChanged(TabStripModel* tab_strip_model,
diff --git a/chrome/browser/metrics/tab_usage_recorder.h b/chrome/browser/metrics/tab_usage_recorder.h
index 55d5b6e..8dd1f93 100644
--- a/chrome/browser/metrics/tab_usage_recorder.h
+++ b/chrome/browser/metrics/tab_usage_recorder.h
@@ -28,10 +28,10 @@
   void OnTabReactivated(content::WebContents* contents) override;
 
   // TabStripModelObserver:
-  void TabInsertedAt(TabStripModel* tab_strip_model,
-                     content::WebContents* contents,
-                     int index,
-                     bool foreground) override;
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
   void TabPinnedStateChanged(TabStripModel* tab_strip_model,
                              content::WebContents* contents,
                              int index) override;
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
index 5ce825b..d215b80 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
@@ -61,12 +61,6 @@
 void OnLoFiResponseReceivedOnUI(content::WebContents* web_contents) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  // Retrieve PreviewsUIService* from |web_contents| if available.
-  PreviewsService* previews_service = PreviewsServiceFactory::GetForProfile(
-      Profile::FromBrowserContext(web_contents->GetBrowserContext()));
-  previews::PreviewsUIService* previews_ui_service =
-      previews_service ? previews_service->previews_ui_service() : nullptr;
-
   PreviewsUITabHelper* ui_tab_helper =
       PreviewsUITabHelper::FromWebContents(web_contents);
 
@@ -75,17 +69,15 @@
     page_id = ui_tab_helper->previews_user_data()->page_id();
   }
 
-  PreviewsInfoBarDelegate::Create(
-      web_contents, previews::PreviewsType::LOFI,
-      base::Time() /* previews_freshness */, true /* is_data_saver_user */,
-      false /* is_reload */,
+  ui_tab_helper->ShowUIElement(
+      previews::PreviewsType::LOFI, base::Time() /* previews_freshness */,
+      true /* is_data_saver_user */, false /* is_reload */,
       base::BindOnce(&AddPreviewNavigationToBlackListCallback,
                      web_contents->GetBrowserContext(),
                      web_contents->GetController()
                          .GetLastCommittedEntry()
                          ->GetRedirectChain()[0],
-                     previews::PreviewsType::LOFI, page_id),
-      previews_ui_service);
+                     previews::PreviewsType::LOFI, page_id));
 }
 
 }  // namespace
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
index efec4022..1dcdd5f6 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
@@ -640,7 +640,8 @@
     const mojom::PageLoadTiming& timing,
     const mojom::PageLoadMetadata& metadata,
     const mojom::PageLoadFeatures& new_features,
-    const mojom::PageLoadDataUse& new_data_use) {
+    const mojom::PageLoadDataUse& new_data_use,
+    const std::vector<mojom::ResourceDataUpdatePtr>& resources) {
   // We may receive notifications from frames that have been navigated away
   // from. We simply ignore them.
   if (GetMainFrame(render_frame_host) != web_contents()->GetMainFrame()) {
@@ -673,7 +674,8 @@
 
   if (committed_load_) {
     committed_load_->metrics_update_dispatcher()->UpdateMetrics(
-        render_frame_host, timing, metadata, new_features, new_data_use);
+        render_frame_host, timing, metadata, new_features, new_data_use,
+        resources);
   }
 }
 
@@ -681,11 +683,12 @@
     const mojom::PageLoadTimingPtr timing,
     const mojom::PageLoadMetadataPtr metadata,
     const mojom::PageLoadFeaturesPtr new_features,
-    const mojom::PageLoadDataUsePtr new_data_use) {
+    const mojom::PageLoadDataUsePtr new_data_use,
+    const std::vector<mojom::ResourceDataUpdatePtr> resources) {
   content::RenderFrameHost* render_frame_host =
       page_load_metrics_binding_.GetCurrentTargetFrame();
   OnTimingUpdated(render_frame_host, *timing, *metadata, *new_features,
-                  *new_data_use);
+                  *new_data_use, resources);
 }
 
 bool MetricsWebContentsObserver::ShouldTrackNavigation(
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
index e6430afa..55ba9146 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
@@ -146,11 +146,13 @@
   void RemoveTestingObserver(TestingObserver* observer);
 
   // public only for testing
-  void OnTimingUpdated(content::RenderFrameHost* render_frame_host,
-                       const mojom::PageLoadTiming& timing,
-                       const mojom::PageLoadMetadata& metadata,
-                       const mojom::PageLoadFeatures& new_features,
-                       const mojom::PageLoadDataUse& new_data_use);
+  void OnTimingUpdated(
+      content::RenderFrameHost* render_frame_host,
+      const mojom::PageLoadTiming& timing,
+      const mojom::PageLoadMetadata& metadata,
+      const mojom::PageLoadFeatures& new_features,
+      const mojom::PageLoadDataUse& new_data_use,
+      const std::vector<mojom::ResourceDataUpdatePtr>& resources);
 
   // Informs the observers of the currently committed load that the event
   // corresponding to |event_key| has occurred. This should not be called within
@@ -162,10 +164,12 @@
   friend class content::WebContentsUserData<MetricsWebContentsObserver>;
 
   // page_load_metrics::mojom::PageLoadMetrics implementation.
-  void UpdateTiming(const mojom::PageLoadTimingPtr timing,
-                    const mojom::PageLoadMetadataPtr metadata,
-                    const mojom::PageLoadFeaturesPtr new_features,
-                    const mojom::PageLoadDataUsePtr new_data_use) override;
+  void UpdateTiming(
+      const mojom::PageLoadTimingPtr timing,
+      const mojom::PageLoadMetadataPtr metadata,
+      const mojom::PageLoadFeaturesPtr new_features,
+      const mojom::PageLoadDataUsePtr new_data_use,
+      const std::vector<mojom::ResourceDataUpdatePtr> resources) override;
 
   void HandleFailedNavigationForTrackedLoad(
       content::NavigationHandle* navigation_handle,
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
index 6ca129e6..8307a50 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
@@ -258,7 +258,8 @@
       content::RenderFrameHost* render_frame_host) {
     observer()->OnTimingUpdated(
         render_frame_host, timing, mojom::PageLoadMetadata(),
-        mojom::PageLoadFeatures(), mojom::PageLoadDataUse());
+        mojom::PageLoadFeatures(), mojom::PageLoadDataUse(),
+        std::vector<mojom::ResourceDataUpdatePtr>());
   }
 
   void AttachObserver() {
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
index 641e4d27..9d99233 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
@@ -88,7 +88,8 @@
     const mojom::PageLoadMetadata& metadata,
     const mojom::PageLoadFeatures& new_features) {
   observer_->OnTimingUpdated(web_contents()->GetMainFrame(), timing, metadata,
-                             new_features, mojom::PageLoadDataUse());
+                             new_features, mojom::PageLoadDataUse(),
+                             std::vector<mojom::ResourceDataUpdatePtr>());
   // If sending the timing update caused the PageLoadMetricsUpdateDispatcher to
   // schedule a buffering timer, then fire it now so metrics are dispatched to
   // observers.
@@ -101,7 +102,8 @@
     const mojom::PageLoadDataUse& data_use) {
   observer_->OnTimingUpdated(web_contents()->GetMainFrame(),
                              mojom::PageLoadTiming(), mojom::PageLoadMetadata(),
-                             mojom::PageLoadFeatures(), data_use);
+                             mojom::PageLoadFeatures(), data_use,
+                             std::vector<mojom::ResourceDataUpdatePtr>());
 }
 
 void PageLoadMetricsObserverTester::SimulateLoadedResource(
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
index 8a390ddf..e8fdfc7 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -1852,3 +1852,89 @@
   waiter->AddMinimumPageLoadDataUseExpectation(7 * (one_frame_page_size - 100));
   waiter->Wait();
 }
+
+IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
+                       ReceivedAggregateResourceDataLength) {
+  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+  waiter->AddPageExpectation(TimingField::kLoadEvent);
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL(
+                     "foo.com", "/cross_site_iframe_factory.html?foo"));
+  waiter->Wait();
+  int64_t one_frame_page_size = waiter->current_resource_bytes();
+
+  waiter = CreatePageLoadMetricsTestWaiter();
+  waiter->AddPageExpectation(TimingField::kLoadEvent);
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL(
+          "a.com", "/cross_site_iframe_factory.html?a(b,c,d(e,f,g))"));
+  // Verify that 7 iframes are fetched, with some amount of tolerance since
+  // favicon is fetched only once.
+  waiter->AddMinimumResourceBytesExpectation(7 * (one_frame_page_size - 100));
+  waiter->Wait();
+}
+
+IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, ReceivedCompleteResources) {
+  const char kHttpResponseHeader[] =
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Type: text/html; charset=utf-8\r\n"
+      "\r\n";
+  auto main_html_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          embedded_test_server(), "/mock_page.html",
+          true /*relative_url_is_prefix*/);
+  auto script_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          embedded_test_server(), "/script.js",
+          true /*relative_url_is_prefix*/);
+  auto iframe_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          embedded_test_server(), "/iframe.html",
+          true /*relative_url_is_prefix*/);
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+
+  browser()->OpenURL(content::OpenURLParams(
+      embedded_test_server()->GetURL("/mock_page.html"), content::Referrer(),
+      WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false));
+
+  main_html_response->WaitForRequest();
+  main_html_response->Send(kHttpResponseHeader);
+  main_html_response->Send(
+      "<html><body></body><script src=\"script.js\"></script></html>");
+  main_html_response->Send(std::string(1000, ' '));
+  main_html_response->Done();
+  waiter->AddCompleteResourcesExpectation(1);
+  waiter->AddMinimumResourceBytesExpectation(1000);
+  waiter->Wait();
+
+  script_response->WaitForRequest();
+  script_response->Send(kHttpResponseHeader);
+  script_response->Send(
+      "var iframe = document.createElement(\"iframe\");"
+      "iframe.src =\"iframe.html\";"
+      "document.body.appendChild(iframe);");
+  script_response->Send(std::string(1000, ' '));
+  // Data received but resource not complete
+  waiter->AddCompleteResourcesExpectation(1);
+  waiter->AddMinimumResourceBytesExpectation(2000);
+  waiter->Wait();
+  script_response->Done();
+  waiter->AddCompleteResourcesExpectation(2);
+  waiter->Wait();
+
+  // Make sure main resources are loaded correctly
+  iframe_response->WaitForRequest();
+  iframe_response->Send(kHttpResponseHeader);
+  iframe_response->Send(std::string(2000, ' '));
+  iframe_response->Done();
+  waiter->AddCompleteResourcesExpectation(3);
+  waiter->AddMinimumResourceBytesExpectation(4000);
+  waiter->Wait();
+}
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.h b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
index ce97795a..9fb569c 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
@@ -428,6 +428,12 @@
   virtual void OnDataUseObserved(int64_t received_data_length,
                                  int64_t data_reduction_proxy_bytes_saved) {}
 
+  // Invoked when there is data use for loading a resource on the page
+  // acrosss all frames. This only contains resources that have had new
+  // data use since the last callback.
+  virtual void OnResourceDataUseObserved(
+      const std::vector<mojom::ResourceDataUpdatePtr>& resources) {}
+
   // Invoked when a media element starts playing.
   virtual void MediaStartedPlaying(
       const content::WebContentsObserver::MediaPlayerInfo& video_type,
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
index 23f1652..6a69dc5 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
@@ -38,6 +38,16 @@
     page_expected_fields_.Set(field);
 }
 
+void PageLoadMetricsTestWaiter::AddCompleteResourcesExpectation(
+    int expected_num_complete_resources) {
+  expected_num_complete_resources_ = expected_num_complete_resources;
+}
+
+void PageLoadMetricsTestWaiter::AddMinimumResourceBytesExpectation(
+    int expected_minimum_resource_bytes) {
+  expected_minimum_resource_bytes_ = expected_minimum_resource_bytes;
+}
+
 void PageLoadMetricsTestWaiter::AddMinimumPageLoadDataUseExpectation(
     int expected_minimum_page_load_data_use) {
   expected_minimum_page_load_data_use_ = expected_minimum_page_load_data_use;
@@ -48,21 +58,21 @@
 }
 
 void PageLoadMetricsTestWaiter::Wait() {
-  if (expectations_satisfied())
+  if (ExpectationsSatisfied())
     return;
 
   run_loop_ = std::make_unique<base::RunLoop>();
   run_loop_->Run();
   run_loop_ = nullptr;
 
-  EXPECT_TRUE(expectations_satisfied());
+  EXPECT_TRUE(ExpectationsSatisfied());
 }
 
 void PageLoadMetricsTestWaiter::OnTimingUpdated(
     content::RenderFrameHost* subframe_rfh,
     const page_load_metrics::mojom::PageLoadTiming& timing,
     const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  if (expectations_satisfied())
+  if (ExpectationsSatisfied())
     return;
   const page_load_metrics::mojom::PageLoadMetadata& metadata =
       subframe_rfh ? extra_info.subframe_metadata
@@ -74,14 +84,14 @@
     page_expected_fields_.ClearMatching(matched_bits);
     observed_page_fields_.Merge(matched_bits);
   }
-  if (expectations_satisfied() && run_loop_)
+  if (ExpectationsSatisfied() && run_loop_)
     run_loop_->Quit();
 }
 
 void PageLoadMetricsTestWaiter::OnLoadedResource(
     const page_load_metrics::ExtraRequestCompleteInfo&
         extra_request_complete_info) {
-  if (expectations_satisfied())
+  if (ExpectationsSatisfied())
     return;
 
   if (extra_request_complete_info.resource_type !=
@@ -97,7 +107,7 @@
     observed_page_fields_.Set(TimingField::kLoadTimingInfo);
   }
 
-  if (expectations_satisfied() && run_loop_)
+  if (ExpectationsSatisfied() && run_loop_)
     run_loop_->Quit();
 }
 
@@ -105,7 +115,27 @@
     int64_t received_data_length,
     int64_t data_reduction_proxy_bytes_saved) {
   current_page_load_data_use_ += received_data_length;
-  if (expectations_satisfied() && run_loop_)
+  if (ExpectationsSatisfied() && run_loop_)
+    run_loop_->Quit();
+}
+
+void PageLoadMetricsTestWaiter::OnResourceDataUseObserved(
+    const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
+        resources) {
+  for (auto const& resource : resources) {
+    auto it = page_resources_.find(resource->request_id);
+    if (it != page_resources_.end()) {
+      it->second = resource.get();
+    } else {
+      page_resources_.emplace(std::piecewise_construct,
+                              std::forward_as_tuple(resource->request_id),
+                              std::forward_as_tuple(resource.get()));
+    }
+    if (resource->is_complete)
+      current_complete_resources_++;
+    current_resource_bytes_ += resource->delta_bytes;
+  }
+  if (ExpectationsSatisfied() && run_loop_)
     run_loop_->Quit();
 }
 
@@ -166,12 +196,23 @@
   did_add_observer_ = true;
 }
 
-bool PageLoadMetricsTestWaiter::expectations_satisfied() const {
-  return subframe_expected_fields_.Empty() && page_expected_fields_.Empty() &&
-         (expected_minimum_page_load_data_use_ == 0 ||
+bool PageLoadMetricsTestWaiter::ResourceUseExpectationsSatisfied() const {
+  return (expected_num_complete_resources_ == 0 ||
+          current_complete_resources_ == expected_num_complete_resources_) &&
+         (expected_minimum_resource_bytes_ == 0 ||
+          current_resource_bytes_ >= expected_minimum_resource_bytes_);
+}
+
+bool PageLoadMetricsTestWaiter::DataUseExpectationsSatisfied() const {
+  return (expected_minimum_page_load_data_use_ == 0 ||
           current_page_load_data_use_ >= expected_minimum_page_load_data_use_);
 }
 
+bool PageLoadMetricsTestWaiter::ExpectationsSatisfied() const {
+  return subframe_expected_fields_.Empty() && page_expected_fields_.Empty() &&
+         DataUseExpectationsSatisfied() && ResourceUseExpectationsSatisfied();
+}
+
 PageLoadMetricsTestWaiter::WaiterMetricsObserver::~WaiterMetricsObserver() {}
 
 PageLoadMetricsTestWaiter::WaiterMetricsObserver::WaiterMetricsObserver(
@@ -202,4 +243,12 @@
   }
 }
 
+void PageLoadMetricsTestWaiter::WaiterMetricsObserver::
+    OnResourceDataUseObserved(
+        const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
+            resources) {
+  if (waiter_)
+    waiter_->OnResourceDataUseObserved(resources);
+}
+
 }  // namespace page_load_metrics
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
index c206f419..17ece08 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
@@ -40,6 +40,12 @@
   // Add a subframe-level expectation.
   void AddSubFrameExpectation(TimingField field);
 
+  // Add completed resource expectation
+  void AddCompleteResourcesExpectation(int expected_minimum_complete_resources);
+
+  // Add aggregate received resource bytes expectation
+  void AddMinimumResourceBytesExpectation(int expected_minimum_resource_bytes);
+
   // Add a data use expectation
   void AddMinimumPageLoadDataUseExpectation(
       int expected_minimum_page_load_data_use);
@@ -56,6 +62,8 @@
     return current_page_load_data_use_;
   }
 
+  int64_t current_resource_bytes() const { return current_resource_bytes_; }
+
  private:
   // PageLoadMetricsObserver used by the PageLoadMetricsTestWaiter to observe
   // metrics updates.
@@ -79,6 +87,10 @@
     void OnDataUseObserved(int64_t received_data_length,
                            int64_t data_reduction_proxy_bytes_saved) override;
 
+    void OnResourceDataUseObserved(
+        const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
+            resources) override;
+
    private:
     const base::WeakPtr<PageLoadMetricsTestWaiter> waiter_;
   };
@@ -139,11 +151,22 @@
   void OnDataUseObserved(int64_t received_data_length,
                          int64_t data_reduction_proxy_bytes_saved);
 
+  // Updates resource map and associated data counters as updates are received
+  // from a resource load. Stops waiting if expectations are satisfied after
+  // update.
+  void OnResourceDataUseObserved(
+      const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
+          resources);
+
   void OnTrackerCreated(page_load_metrics::PageLoadTracker* tracker) override;
 
   void OnCommit(page_load_metrics::PageLoadTracker* tracker) override;
 
-  bool expectations_satisfied() const;
+  bool ResourceUseExpectationsSatisfied() const;
+
+  bool DataUseExpectationsSatisfied() const;
+
+  virtual bool ExpectationsSatisfied() const;
 
   std::unique_ptr<base::RunLoop> run_loop_;
 
@@ -154,10 +177,19 @@
 
   int64_t expected_minimum_page_load_data_use_ = 0;
   int64_t current_page_load_data_use_ = 0;
+  int current_complete_resources_ = 0;
+  int64_t current_resource_bytes_ = 0;
+  int expected_num_complete_resources_ = 0;
+  int expected_minimum_resource_bytes_ = 0;
 
   bool attach_on_tracker_creation_ = false;
   bool did_add_observer_ = false;
 
+  // Map of all resources loaded by the page, keyed by resource request id.
+  // Contains ongoing and completed resources. Contains only the most recent
+  // update (version) of the resource.
+  std::map<int, page_load_metrics::mojom::ResourceDataUpdate*> page_resources_;
+
   base::WeakPtrFactory<PageLoadMetricsTestWaiter> weak_factory_;
 };
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
index 91be0e2..32ac745 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
@@ -431,7 +431,8 @@
     const mojom::PageLoadTiming& new_timing,
     const mojom::PageLoadMetadata& new_metadata,
     const mojom::PageLoadFeatures& new_features,
-    const mojom::PageLoadDataUse& new_data_use) {
+    const mojom::PageLoadDataUse& new_data_use,
+    const std::vector<mojom::ResourceDataUpdatePtr>& resources) {
   if (render_frame_host->GetLastCommittedURL().SchemeIs(
           extensions::kExtensionScheme)) {
     // Extensions can inject child frames into a page. We don't want to track
@@ -442,6 +443,7 @@
   // Report data usage before new timing and metadata for messages that have
   // both updates.
   client_->UpdateDataUse(new_data_use);
+  client_->UpdateResourceDataUse(resources);
   if (render_frame_host->GetParent() == nullptr) {
     UpdateMainFrameMetadata(new_metadata);
     UpdateMainFrameTiming(new_timing);
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
index 70728700..ee61a70e 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
@@ -110,6 +110,8 @@
     virtual void UpdateFeaturesUsage(
         const mojom::PageLoadFeatures& new_features) = 0;
     virtual void UpdateDataUse(const mojom::PageLoadDataUse& new_data_use) = 0;
+    virtual void UpdateResourceDataUse(
+        const std::vector<mojom::ResourceDataUpdatePtr>& resources) = 0;
   };
 
   // The |client| instance must outlive this object.
@@ -119,11 +121,13 @@
       PageLoadMetricsEmbedderInterface* embedder_interface);
   ~PageLoadMetricsUpdateDispatcher();
 
-  void UpdateMetrics(content::RenderFrameHost* render_frame_host,
-                     const mojom::PageLoadTiming& new_timing,
-                     const mojom::PageLoadMetadata& new_metadata,
-                     const mojom::PageLoadFeatures& new_features,
-                     const mojom::PageLoadDataUse& new_data_use);
+  void UpdateMetrics(
+      content::RenderFrameHost* render_frame_host,
+      const mojom::PageLoadTiming& new_timing,
+      const mojom::PageLoadMetadata& new_metadata,
+      const mojom::PageLoadFeatures& new_features,
+      const mojom::PageLoadDataUse& new_data_use,
+      const std::vector<mojom::ResourceDataUpdatePtr>& resources);
 
   // This method is only intended to be called for PageLoadFeatures being
   // recorded directly from the browser process. Features coming from the
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.cc b/chrome/browser/page_load_metrics/page_load_tracker.cc
index eedce78..2d020269 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.cc
+++ b/chrome/browser/page_load_metrics/page_load_tracker.cc
@@ -651,4 +651,11 @@
   }
 }
 
+void PageLoadTracker::UpdateResourceDataUse(
+    const std::vector<mojom::ResourceDataUpdatePtr>& resources) {
+  for (const auto& observer : observers_) {
+    observer->OnResourceDataUseObserved(resources);
+  }
+}
+
 }  // namespace page_load_metrics
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.h b/chrome/browser/page_load_metrics/page_load_tracker.h
index 4c52200..00f956bb 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.h
+++ b/chrome/browser/page_load_metrics/page_load_tracker.h
@@ -181,6 +181,8 @@
   void UpdateFeaturesUsage(
       const mojom::PageLoadFeatures& new_features) override;
   void UpdateDataUse(const mojom::PageLoadDataUse& new_datause) override;
+  void UpdateResourceDataUse(
+      const std::vector<mojom::ResourceDataUpdatePtr>& resources) override;
 
   void Redirect(content::NavigationHandle* navigation_handle);
   void WillProcessNavigationResponse(
diff --git a/chrome/browser/prefs/pref_service_incognito_whitelist.cc b/chrome/browser/prefs/pref_service_incognito_whitelist.cc
index df1b7a9..cd6bbcb 100644
--- a/chrome/browser/prefs/pref_service_incognito_whitelist.cc
+++ b/chrome/browser/prefs/pref_service_incognito_whitelist.cc
@@ -684,11 +684,6 @@
 #endif  // defined(GOOGLE_CHROME_BUILD)
 #endif  // defined(OS_WIN)
 
-    prefs::kSettingsResetPromptPromptWave,
-    prefs::kSettingsResetPromptLastTriggeredForDefaultSearch,
-    prefs::kSettingsResetPromptLastTriggeredForStartupUrls,
-    prefs::kSettingsResetPromptLastTriggeredForHomepage,
-
 #if defined(OS_ANDROID)
     prefs::kClipboardLastModifiedTime,
 #endif
diff --git a/chrome/browser/previews/previews_infobar_delegate.cc b/chrome/browser/previews/previews_infobar_delegate.cc
index 1cfcd42..e305c38 100644
--- a/chrome/browser/previews/previews_infobar_delegate.cc
+++ b/chrome/browser/previews/previews_infobar_delegate.cc
@@ -115,7 +115,7 @@
     base::Time previews_freshness,
     bool is_data_saver_user,
     bool is_reload,
-    OnDismissPreviewsInfobarCallback on_dismiss_callback,
+    OnDismissPreviewsUICallback on_dismiss_callback,
     previews::PreviewsUIService* previews_ui_service) {
   PreviewsUITabHelper* ui_tab_helper =
       PreviewsUITabHelper::FromWebContents(web_contents);
@@ -167,7 +167,7 @@
     base::Time previews_freshness,
     bool is_data_saver_user,
     bool is_reload,
-    OnDismissPreviewsInfobarCallback on_dismiss_callback)
+    OnDismissPreviewsUICallback on_dismiss_callback)
     : ConfirmInfoBarDelegate(),
       ui_tab_helper_(ui_tab_helper),
       previews_type_(previews_type),
diff --git a/chrome/browser/previews/previews_infobar_delegate.h b/chrome/browser/previews/previews_infobar_delegate.h
index 99f7bdb0..23f40fb 100644
--- a/chrome/browser/previews/previews_infobar_delegate.h
+++ b/chrome/browser/previews/previews_infobar_delegate.h
@@ -5,9 +5,9 @@
 #ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_INFOBAR_DELEGATE_H_
 #define CHROME_BROWSER_PREVIEWS_PREVIEWS_INFOBAR_DELEGATE_H_
 
-#include "base/callback.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
+#include "chrome/browser/previews/previews_ui_tab_helper.h"
 #include "components/infobars/core/confirm_infobar_delegate.h"
 #include "components/previews/core/previews_experiments.h"
 
@@ -27,9 +27,6 @@
 // infobar.
 class PreviewsInfoBarDelegate : public ConfirmInfoBarDelegate {
  public:
-  typedef base::OnceCallback<void(bool opt_out)>
-      OnDismissPreviewsInfobarCallback;
-
   // Actions on the previews infobar. This enum must remain synchronized with
   // the enum of the same name in metrics/histograms/histograms.xml.
   enum PreviewsInfoBarAction {
@@ -59,16 +56,13 @@
   // Creates a preview infobar and corresponding delegate and adds the infobar
   // to InfoBarService. |on_dismiss_callback| is called when the InfoBar is
   // dismissed.
-  static void Create(
-      content::WebContents* web_contents,
-      previews::PreviewsType previews_type,
-      base::Time previews_freshness,
-      bool is_data_saver_user,
-      bool is_reload,
-      // TODO(ryansturm): Replace |on_dismiss_callback| with direct call to
-      // |previews_ui_service|.
-      OnDismissPreviewsInfobarCallback on_dismiss_callback,
-      previews::PreviewsUIService* previews_ui_service);
+  static void Create(content::WebContents* web_contents,
+                     previews::PreviewsType previews_type,
+                     base::Time previews_freshness,
+                     bool is_data_saver_user,
+                     bool is_reload,
+                     OnDismissPreviewsUICallback on_dismiss_callback,
+                     previews::PreviewsUIService* previews_ui_service);
 
   // ConfirmInfoBarDelegate overrides:
   int GetIconId() const override;
@@ -86,7 +80,7 @@
                           base::Time previews_freshness,
                           bool is_data_saver_user,
                           bool is_reload,
-                          OnDismissPreviewsInfobarCallback on_dismiss_callback);
+                          OnDismissPreviewsUICallback on_dismiss_callback);
 
   // ConfirmInfoBarDelegate overrides:
   infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
@@ -105,7 +99,7 @@
 
   const base::string16 message_text_;
 
-  OnDismissPreviewsInfobarCallback on_dismiss_callback_;
+  OnDismissPreviewsUICallback on_dismiss_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(PreviewsInfoBarDelegate);
 };
diff --git a/chrome/browser/previews/previews_infobar_delegate_unittest.cc b/chrome/browser/previews/previews_infobar_delegate_unittest.cc
index 1e844ec14..b79f6667 100644
--- a/chrome/browser/previews/previews_infobar_delegate_unittest.cc
+++ b/chrome/browser/previews/previews_infobar_delegate_unittest.cc
@@ -320,8 +320,7 @@
   PreviewsInfoBarDelegate::Create(
       web_contents(), previews::PreviewsType::LOFI,
       base::Time() /* previews_freshness */, true /* is_data_saver_user */,
-      false /* is_reload */,
-      PreviewsInfoBarDelegate::OnDismissPreviewsInfobarCallback(),
+      false /* is_reload */, OnDismissPreviewsUICallback(),
       previews_ui_service_.get());
   EXPECT_EQ(1U, infobar_service()->infobar_count());
 
@@ -348,8 +347,7 @@
   PreviewsInfoBarDelegate::Create(
       web_contents(), previews::PreviewsType::LOFI,
       base::Time() /* previews_freshness */, true /* is_data_saver_user */,
-      false /* is_reload */,
-      PreviewsInfoBarDelegate::OnDismissPreviewsInfobarCallback(),
+      false /* is_reload */, OnDismissPreviewsUICallback(),
       previews_ui_service_.get());
   EXPECT_EQ(1U, infobar_service()->infobar_count());
 
@@ -478,8 +476,7 @@
   PreviewsInfoBarDelegate::Create(
       web_contents(), previews::PreviewsType::LOFI,
       base::Time() /* previews_freshness */, true /* is_data_saver_user */,
-      false /* is_reload */,
-      PreviewsInfoBarDelegate::OnDismissPreviewsInfobarCallback(),
+      false /* is_reload */, OnDismissPreviewsUICallback(),
       previews_ui_service_.get());
 
   // Infobar should not be shown again since a navigation hasn't happened.
diff --git a/chrome/browser/previews/previews_ui_tab_helper.cc b/chrome/browser/previews/previews_ui_tab_helper.cc
index 1c63309..62c6405 100644
--- a/chrome/browser/previews/previews_ui_tab_helper.cc
+++ b/chrome/browser/previews/previews_ui_tab_helper.cc
@@ -53,6 +53,23 @@
 
 PreviewsUITabHelper::~PreviewsUITabHelper() {}
 
+void PreviewsUITabHelper::ShowUIElement(
+    previews::PreviewsType previews_type,
+    base::Time previews_freshness,
+    bool is_data_saver_user,
+    bool is_reload,
+    OnDismissPreviewsUICallback on_dismiss_callback) {
+  // Retrieve PreviewsUIService* from |web_contents| if available.
+  PreviewsService* previews_service = PreviewsServiceFactory::GetForProfile(
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
+  previews::PreviewsUIService* previews_ui_service =
+      previews_service ? previews_service->previews_ui_service() : nullptr;
+
+  PreviewsInfoBarDelegate::Create(
+      web_contents(), previews_type, previews_freshness, is_data_saver_user,
+      is_reload, std::move(on_dismiss_callback), previews_ui_service);
+}
+
 PreviewsUITabHelper::PreviewsUITabHelper(content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
       displayed_preview_ui_(false),
@@ -86,12 +103,6 @@
   displayed_preview_ui_ = false;
   displayed_preview_timestamp_ = false;
 
-  // Retrieve PreviewsUIService* from |web_contents| if available.
-  PreviewsService* previews_service = PreviewsServiceFactory::GetForProfile(
-      Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
-  previews::PreviewsUIService* previews_ui_service =
-      previews_service ? previews_service->previews_ui_service() : nullptr;
-
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
   offline_pages::OfflinePageTabHelper* tab_helper =
       offline_pages::OfflinePageTabHelper::FromWebContents(web_contents());
@@ -128,16 +139,15 @@
                                true,
                                data_use_measurement::DataUseUserData::OTHER, 0);
 
-    PreviewsInfoBarDelegate::Create(
-        web_contents(), previews::PreviewsType::OFFLINE,
-        base::Time() /* previews_freshness */,
+    PreviewsUITabHelper::ShowUIElement(
+        previews::PreviewsType::OFFLINE, base::Time() /* previews_freshness */,
         data_reduction_proxy_settings && data_saver_enabled,
         false /* is_reload */,
         base::BindOnce(&AddPreviewNavigationCallback,
                        web_contents()->GetBrowserContext(),
                        navigation_handle->GetRedirectChain()[0],
-                       previews::PreviewsType::OFFLINE, page_id),
-        previews_ui_service);
+                       previews::PreviewsType::OFFLINE, page_id));
+
     // Don't try to show other UIs if this is an offline preview.
     return;
   }
@@ -157,14 +167,13 @@
           headers->GetDateValue(&previews_freshness);
       }
 
-      PreviewsInfoBarDelegate::Create(
-          web_contents(), main_frame_preview, previews_freshness,
-          true /* is_data_saver_user */, is_reload,
+      PreviewsUITabHelper::ShowUIElement(
+          main_frame_preview, previews_freshness, true /* is_data_saver_user */,
+          is_reload,
           base::BindOnce(&AddPreviewNavigationCallback,
                          web_contents()->GetBrowserContext(),
                          navigation_handle->GetRedirectChain()[0],
-                         main_frame_preview, page_id),
-          previews_ui_service);
+                         main_frame_preview, page_id));
     }
   }
 }
diff --git a/chrome/browser/previews/previews_ui_tab_helper.h b/chrome/browser/previews/previews_ui_tab_helper.h
index 2143bf9..bbec8027d 100644
--- a/chrome/browser/previews/previews_ui_tab_helper.h
+++ b/chrome/browser/previews/previews_ui_tab_helper.h
@@ -5,12 +5,17 @@
 #ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_UI_TAB_HELPER_H_
 #define CHROME_BROWSER_PREVIEWS_PREVIEWS_UI_TAB_HELPER_H_
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "base/time/time.h"
+#include "components/previews/core/previews_experiments.h"
 #include "components/previews/core/previews_user_data.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
+typedef base::OnceCallback<void(bool opt_out)> OnDismissPreviewsUICallback;
+
 // Tracks whether a previews UI has been shown for a page. Handles showing
 // the UI when the main frame response indicates a Lite Page.
 class PreviewsUITabHelper
@@ -19,6 +24,13 @@
  public:
   ~PreviewsUITabHelper() override;
 
+  // Trigger the Previews UI to be shown to the user.
+  void ShowUIElement(previews::PreviewsType previews_type,
+                     base::Time previews_freshness,
+                     bool is_data_saver_user,
+                     bool is_reload,
+                     OnDismissPreviewsUICallback on_dismiss_callback);
+
   // Indicates whether the UI for a preview has been shown for the page.
   bool displayed_preview_ui() const { return displayed_preview_ui_; }
 
diff --git a/chrome/browser/printing/cloud_print/privet_notifications.cc b/chrome/browser/printing/cloud_print/privet_notifications.cc
index 0a63a3d..3ff101b 100644
--- a/chrome/browser/printing/cloud_print/privet_notifications.cc
+++ b/chrome/browser/printing/cloud_print/privet_notifications.cc
@@ -338,9 +338,8 @@
   } else if (*enable_privet_notification_member_) {
     ReportPrivetUmaEvent(PRIVET_SERVICE_STARTED);
     traffic_detector_ = base::MakeRefCounted<PrivetTrafficDetector>(
-        net::ADDRESS_FAMILY_IPV4, profile_,
-        base::BindRepeating(&PrivetNotificationService::StartLister,
-                            AsWeakPtr()));
+        profile_, base::BindRepeating(&PrivetNotificationService::StartLister,
+                                      AsWeakPtr()));
     traffic_detector_->Start();
   } else {
     device_lister_.reset();
diff --git a/chrome/browser/printing/cloud_print/privet_traffic_detector.cc b/chrome/browser/printing/cloud_print/privet_traffic_detector.cc
index 5176dad..18959f8 100644
--- a/chrome/browser/printing/cloud_print/privet_traffic_detector.cc
+++ b/chrome/browser/printing/cloud_print/privet_traffic_detector.cc
@@ -4,10 +4,8 @@
 
 #include "chrome/browser/printing/cloud_print/privet_traffic_detector.h"
 
-#include <stddef.h>
+#include <utility>
 
-#include "base/location.h"
-#include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/sys_byteorder.h"
@@ -17,8 +15,8 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/storage_partition.h"
+#include "net/base/address_family.h"
 #include "net/base/ip_address.h"
-#include "net/base/net_errors.h"
 #include "net/base/network_interfaces.h"
 #include "net/dns/dns_protocol.h"
 #include "net/dns/mdns_client.h"
@@ -26,22 +24,20 @@
 namespace {
 
 const int kMaxRestartAttempts = 10;
-const char kPrivetDeviceTypeDnsString[] = "\x07_privet";
 
 void GetNetworkListInBackground(
-    const base::Callback<void(const net::NetworkInterfaceList&)> callback) {
+    base::OnceCallback<void(net::NetworkInterfaceList)> callback) {
   base::AssertBlockingAllowed();
   net::NetworkInterfaceList networks;
   if (!GetNetworkList(&networks, net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES))
     return;
 
   net::NetworkInterfaceList ip4_networks;
-  for (size_t i = 0; i < networks.size(); ++i) {
-    net::AddressFamily address_family =
-        net::GetAddressFamily(networks[i].address);
+  for (const auto& network : networks) {
+    net::AddressFamily address_family = net::GetAddressFamily(network.address);
     if (address_family == net::ADDRESS_FAMILY_IPV4 &&
-        networks[i].prefix_length >= 24) {
-      ip4_networks.push_back(networks[i]);
+        network.prefix_length >= 24) {
+      ip4_networks.push_back(network);
     }
   }
 
@@ -54,8 +50,20 @@
                             localhost_prefix,
                             8,
                             net::IP_ADDRESS_ATTRIBUTE_NONE));
-  content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
-                                   base::BindOnce(callback, ip4_networks));
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::BindOnce(std::move(callback), std::move(ip4_networks)));
+}
+
+void CreateUDPSocketOnUIThread(
+    content::BrowserContext* profile,
+    network::mojom::UDPSocketRequest request,
+    network::mojom::UDPSocketReceiverPtr receiver_ptr) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  network::mojom::NetworkContext* network_context =
+      content::BrowserContext::GetDefaultStoragePartition(profile)
+          ->GetNetworkContext();
+  network_context->CreateUDPSocket(std::move(request), std::move(receiver_ptr));
 }
 
 }  // namespace
@@ -63,16 +71,15 @@
 namespace cloud_print {
 
 PrivetTrafficDetector::PrivetTrafficDetector(
-    net::AddressFamily address_family,
     content::BrowserContext* profile,
-    const base::Closure& on_traffic_detected)
+    const base::RepeatingClosure& on_traffic_detected)
     : on_traffic_detected_(on_traffic_detected),
-      callback_runner_(base::ThreadTaskRunnerHandle::Get()),
-      address_family_(address_family),
       restart_attempts_(kMaxRestartAttempts),
       receiver_binding_(this),
       profile_(profile),
-      weak_ptr_factory_(this) {}
+      weak_ptr_factory_(this) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
 
 PrivetTrafficDetector::~PrivetTrafficDetector() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
@@ -108,14 +115,14 @@
   base::PostDelayedTaskWithTraits(
       FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
       base::BindOnce(&GetNetworkListInBackground,
-                     base::Bind(&PrivetTrafficDetector::Restart,
-                                weak_ptr_factory_.GetWeakPtr())),
+                     base::BindOnce(&PrivetTrafficDetector::Restart,
+                                    weak_ptr_factory_.GetWeakPtr())),
       base::TimeDelta::FromSeconds(3));
 }
 
-void PrivetTrafficDetector::Restart(const net::NetworkInterfaceList& networks) {
+void PrivetTrafficDetector::Restart(net::NetworkInterfaceList networks) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  networks_ = networks;
+  networks_ = std::move(networks);
   Bind();
 }
 
@@ -133,10 +140,11 @@
   receiver_binding_.Bind(std::move(receiver_request));
   content::BrowserThread::PostTask(
       content::BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&PrivetTrafficDetector::CreateUDPSocketOnUIThread, this,
+      base::BindOnce(&CreateUDPSocketOnUIThread, profile_,
                      mojo::MakeRequest(&socket_), std::move(receiver_ptr)));
 
-  net::IPEndPoint multicast_addr = net::GetMDnsIPEndPoint(address_family_);
+  net::IPEndPoint multicast_addr =
+      net::GetMDnsIPEndPoint(net::ADDRESS_FAMILY_IPV4);
   net::IPEndPoint bind_endpoint(
       net::IPAddress::AllZeros(multicast_addr.address().size()),
       multicast_addr.port());
@@ -151,35 +159,27 @@
                                multicast_addr));
 }
 
-void PrivetTrafficDetector::CreateUDPSocketOnUIThread(
-    network::mojom::UDPSocketRequest request,
-    network::mojom::UDPSocketReceiverPtr receiver_ptr) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  network::mojom::NetworkContext* network_context =
-      content::BrowserContext::GetDefaultStoragePartition(profile_)
-          ->GetNetworkContext();
-  network_context->CreateUDPSocket(std::move(request), std::move(receiver_ptr));
-}
-
 void PrivetTrafficDetector::OnBindComplete(
     net::IPEndPoint multicast_addr,
     int rv,
     const base::Optional<net::IPEndPoint>& ip_endpoint) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  if (rv != net::OK) {
-    if ((restart_attempts_--) > 0)
-      ScheduleRestart();
-  } else {
+  if (rv == net::OK) {
     socket_->JoinGroup(
         multicast_addr.address(),
         base::BindOnce(&PrivetTrafficDetector::OnJoinGroupComplete, this));
+    return;
   }
+
+  if (restart_attempts_-- > 0)
+    ScheduleRestart();
 }
 
 bool PrivetTrafficDetector::IsSourceAcceptable() const {
-  for (size_t i = 0; i < networks_.size(); ++i) {
-    if (net::IPAddressMatchesPrefix(recv_addr_.address(), networks_[i].address,
-                                    networks_[i].prefix_length)) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  for (const auto& network : networks_) {
+    if (net::IPAddressMatchesPrefix(recv_addr_.address(), network.address,
+                                    network.prefix_length)) {
       return true;
     }
   }
@@ -188,6 +188,7 @@
 
 bool PrivetTrafficDetector::IsPrivetPacket(
     base::span<const uint8_t> data) const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   if (data.size() <= sizeof(net::dns_protocol::Header) ||
       !IsSourceAcceptable()) {
     return false;
@@ -200,6 +201,8 @@
   // Check if response packet.
   if (!(header->flags & base::HostToNet16(net::dns_protocol::kFlagResponse)))
     return false;
+
+  static const char kPrivetDeviceTypeDnsString[] = "\x07_privet";
   const char* substring_begin = kPrivetDeviceTypeDnsString;
   const char* substring_end = substring_begin +
                               arraysize(kPrivetDeviceTypeDnsString) - 1;
@@ -210,14 +213,15 @@
 
 void PrivetTrafficDetector::OnJoinGroupComplete(int rv) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  if (rv != net::OK) {
-    if ((restart_attempts_--) > 0)
-      ScheduleRestart();
-  } else {
+  if (rv == net::OK) {
     // Reset on success.
     restart_attempts_ = kMaxRestartAttempts;
     socket_->ReceiveMoreWithBufferSize(1, net::dns_protocol::kMaxMulticastSize);
+    return;
   }
+
+  if (restart_attempts_-- > 0)
+    ScheduleRestart();
 }
 
 void PrivetTrafficDetector::ResetConnection() {
@@ -248,7 +252,8 @@
   recv_addr_ = src_addr.value();
   if (IsPrivetPacket(data.value())) {
     ResetConnection();
-    callback_runner_->PostTask(FROM_HERE, on_traffic_detected_);
+    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+                                     on_traffic_detected_);
     base::TimeDelta time_delta = base::Time::Now() - start_time_;
     UMA_HISTOGRAM_LONG_TIMES("LocalDiscovery.DetectorTriggerTime", time_delta);
   } else {
diff --git a/chrome/browser/printing/cloud_print/privet_traffic_detector.h b/chrome/browser/printing/cloud_print/privet_traffic_detector.h
index f8153442..e749a0de 100644
--- a/chrome/browser/printing/cloud_print/privet_traffic_detector.h
+++ b/chrome/browser/printing/cloud_print/privet_traffic_detector.h
@@ -8,11 +8,9 @@
 #include <memory>
 
 #include "base/callback.h"
-#include "base/cancelable_callback.h"
 #include "base/macros.h"
 #include "content/public/browser/browser_thread.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "net/base/address_family.h"
 #include "net/base/ip_endpoint.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
 #include "services/network/public/mojom/udp_socket.mojom.h"
@@ -23,10 +21,13 @@
 
 namespace cloud_print {
 
-// Detects mDns traffic that looks like "Privet" protocol.
-// Can produce false positives results, but main task of the class is to avoid
-// running full mDns listener if user doesn't have devices.
-// When traffic is detected, class fires callback and shutdowns itself.
+// Detects mDns traffic that looks like the "Privet" protocol. This can produce
+// false positives results, but the main task of the class is to avoid running a
+// full mDns listener if user doesn't have devices.
+// When potential "Privet" traffic has been detected, fire a callback and stop
+// listening for traffic.
+// When the network changes, restarts itself to start listening for traffic
+// again on the new network(s).
 class PrivetTrafficDetector
     : public base::RefCountedThreadSafe<
           PrivetTrafficDetector,
@@ -34,10 +35,11 @@
       private network::NetworkConnectionTracker::NetworkConnectionObserver,
       public network::mojom::UDPSocketReceiver {
  public:
-  PrivetTrafficDetector(net::AddressFamily address_family,
-                        content::BrowserContext* profile,
-                        const base::Closure& on_traffic_detected);
+  // Called on the UI thread.
+  PrivetTrafficDetector(content::BrowserContext* profile,
+                        const base::RepeatingClosure& on_traffic_detected);
 
+  // Called on the UI thread.
   void Start();
   void Stop();
 
@@ -45,17 +47,14 @@
   friend struct content::BrowserThread::DeleteOnThread<
       content::BrowserThread::IO>;
   friend class base::DeleteHelper<PrivetTrafficDetector>;
+
   ~PrivetTrafficDetector() override;
 
+  // Unless otherwise noted, all methods are called on the IO thread.
   void HandleConnectionChanged(network::mojom::ConnectionType type);
-
-  void StartOnIOThread();
   void ScheduleRestart();
-  void Restart(const net::NetworkInterfaceList& networks);
+  void Restart(net::NetworkInterfaceList networks);
   void Bind();
-  void CreateUDPSocketOnUIThread(
-      network::mojom::UDPSocketRequest request,
-      network::mojom::UDPSocketReceiverPtr receiver_ptr);
   void OnBindComplete(net::IPEndPoint multicast_addr,
                       int rv,
                       const base::Optional<net::IPEndPoint>& ip_address);
@@ -65,6 +64,7 @@
   void ResetConnection();
 
   // network::NetworkConnectionTracker::NetworkConnectionObserver:
+  // Called on the UI thread.
   void OnConnectionChanged(network::mojom::ConnectionType type) override;
 
   // network::mojom::UDPSocketReceiver implementation
@@ -72,22 +72,22 @@
                   const base::Optional<net::IPEndPoint>& src_addr,
                   base::Optional<base::span<const uint8_t>> data) override;
 
-  base::Closure on_traffic_detected_;
-  scoped_refptr<base::TaskRunner> callback_runner_;
-  net::NetworkInterfaceList networks_;
-  net::AddressFamily address_family_;
-  net::IPEndPoint recv_addr_;
-  base::Time start_time_;
+  // Initialized on the UI thread, but only accessed on the IO thread.
+  base::RepeatingClosure on_traffic_detected_;
   int restart_attempts_;
 
-  // Only accessed on the IO thread
-
+  // Only accessed on the IO thread.
+  net::NetworkInterfaceList networks_;
+  net::IPEndPoint recv_addr_;
+  base::Time start_time_;
   network::mojom::UDPSocketPtr socket_;
-  // Implementation of socket receiver callback
+
+  // Implementation of socket receiver callback.
+  // Initialized on the UI thread, but only accessed on the IO thread.
   mojo::Binding<network::mojom::UDPSocketReceiver> receiver_binding_;
 
-  // Only accessed on the UI thread
-
+  // Initialized on the UI thread, but only accessed on the IO thread for the
+  // purpose of passing it back to the UI thread. Safe because it is const.
   content::BrowserContext* const profile_;
 
   base::WeakPtrFactory<PrivetTrafficDetector> weak_ptr_factory_;
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 9af4767..94eb633 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -149,6 +149,8 @@
 #include "chrome/browser/chromeos/device_sync/device_sync_client_factory.h"
 #include "chrome/browser/chromeos/locale_change_guard.h"
 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
+#include "chrome/browser/chromeos/multidevice_setup/auth_token_validator_factory.h"
+#include "chrome/browser/chromeos/multidevice_setup/auth_token_validator_impl.h"
 #include "chrome/browser/chromeos/net/delay_network_call.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
 #include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
@@ -1524,7 +1526,9 @@
       GetPrefs(),
       chromeos::device_sync::DeviceSyncClientFactory::GetForProfile(this),
       chromeos::secure_channel::SecureChannelClientProvider::GetInstance()
-          ->GetClient());
+          ->GetClient(),
+      chromeos::multidevice_setup::AuthTokenValidatorFactory::GetForProfile(
+          this));
 }
 
 #endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/resources/chromeos/switch_access/automation_manager.js b/chrome/browser/resources/chromeos/switch_access/automation_manager.js
index 3d76554e..b7fe09a 100644
--- a/chrome/browser/resources/chromeos/switch_access/automation_manager.js
+++ b/chrome/browser/resources/chromeos/switch_access/automation_manager.js
@@ -64,15 +64,9 @@
 
 AutomationManager.prototype = {
   /**
-   * Set this.node_, this.root_, and this.desktop_ to the desktop node, and
-   * creates an initial tree walker.
-   *
    * @private
    */
   init_: function() {
-    console.log('AutomationNode for desktop is loaded');
-    this.printNode_(this.node_);
-
     this.desktop_.addEventListener(
         chrome.automation.EventType.FOCUS, this.handleFocusChange_.bind(this),
         false);
@@ -95,7 +89,6 @@
   handleFocusChange_: function(event) {
     if (this.node_ === event.target)
       return;
-    console.log('Focus changed');
 
     // Rebuild scope stack and set scope for focused node.
     this.buildScopeStack_(event.target);
@@ -104,10 +97,9 @@
     this.node_ = event.target;
 
     // In case the node that gained focus is not a subtreeLeaf.
-    if (SwitchAccessPredicate.isSubtreeLeaf(this.node_, this.scope_)) {
-      this.printNode_(this.node_);
+    if (SwitchAccessPredicate.isSubtreeLeaf(this.node_, this.scope_))
       this.updateFocusRing_();
-    } else
+    else
       this.moveForward();
   },
 
@@ -163,7 +155,6 @@
     if (!removedByRWA && treeChange.target !== this.node_)
       return;
 
-    console.log('Node removed');
     chrome.accessibilityPrivate.setFocusRing([]);
 
     // Current node not invalid until after treeChange callback, so move to
@@ -247,7 +238,6 @@
    */
   setCurrentNode_: function(node) {
     this.node_ = node;
-    this.printNode_(this.node_);
     this.updateFocusRing_();
   },
 
@@ -258,7 +248,6 @@
     this.node_ = this.scope_;
     this.visitingScopeAsActionable_ = true;
 
-    this.printNode_(this.node_);
     this.updateFocusRing_(AutomationManager.Color.LEAF);
   },
 
@@ -292,22 +281,17 @@
       } while (!this.scope_.role && this.scopeStack_.length > 0);
 
       this.updateFocusRing_();
-      console.log('Moved to previous scope');
-      this.printNode_(this.node_);
       return;
     }
 
     if (SwitchAccessPredicate.isGroup(this.node_, this.scope_)) {
       this.scopeStack_.push(this.scope_);
       this.scope_ = this.node_;
-      console.log('Entered scope');
       this.moveForward();
       return;
     }
 
     this.node_.doDefault();
-    console.log('Performed default action');
-    console.log('\n');
   },
 
   /**
@@ -341,7 +325,6 @@
   startAtValidNode_: function() {
     if (this.node_.role)
       return;
-    console.log('Finding new valid node');
 
     // Current node is invalid, but current scope is still valid, so set node
     // to the current scope.
diff --git a/chrome/browser/resources/chromeos/switch_access/keyboard_handler.js b/chrome/browser/resources/chromeos/switch_access/keyboard_handler.js
index 23bf3f6e4c..d043e3b 100644
--- a/chrome/browser/resources/chromeos/switch_access/keyboard_handler.js
+++ b/chrome/browser/resources/chromeos/switch_access/keyboard_handler.js
@@ -65,7 +65,6 @@
     for (let command of this.switchAccess_.getCommands()) {
       if (this.keyCodeFor_(command) === event.keyCode) {
         let key = event.key.toUpperCase();
-        console.log('\'' + key + '\' pressed for command: ' + command);
         this.switchAccess_.runCommand(command);
         this.switchAccess_.performedUserAction();
         return;
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js
index 319a2a8..0eacd4d 100644
--- a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js
+++ b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+let StateType = chrome.automation.StateType;
+let RoleType = chrome.automation.RoleType;
+
 /**
  * Class containing predicates for the chrome automation API. Each predicate
  * can be run on one or more AutomationNodes and returns a boolean value.
@@ -61,7 +64,7 @@
  */
 SwitchAccessPredicate.visit = function(scope) {
   return function(node) {
-    return node.role !== chrome.automation.RoleType.DESKTOP &&
+    return node.role !== RoleType.DESKTOP &&
         SwitchAccessPredicate.isSubtreeLeaf(node, scope);
   }.bind(scope);
 };
@@ -98,8 +101,8 @@
   // Work around for client nested in client. No need to have user select both
   // clients for a window. Once locations for outer client updates correctly,
   // this won't be needed.
-  if (node.role === chrome.automation.RoleType.CLIENT &&
-      node.role === scope.role && node !== scope)
+  if (node.role === RoleType.CLIENT && node.role === scope.role &&
+      node !== scope)
     return false;
 
   let interestingBranches = 0;
@@ -154,28 +157,22 @@
   let role = node.role;
   let state = node.state;
 
-  // TODO(elichtenberg): Define shorthand for chrome.automation.RoleType and
-  // StateType.
-
   // Skip things that are offscreen
-  if (state[chrome.automation.StateType.OFFSCREEN] || loc.top < 0 ||
-      loc.left < 0)
+  if (state[StateType.OFFSCREEN] || loc.top < 0 || loc.left < 0)
     return false;
 
   // Should just leave these as groups
-  if (role === chrome.automation.RoleType.WEB_VIEW ||
-      role === chrome.automation.RoleType.ROOT_WEB_AREA)
+  if (role === RoleType.WEB_VIEW || role === RoleType.ROOT_WEB_AREA)
     return false;
 
   if (parent) {
     // crbug.com/710559
     // Work around for browser tabs
-    if (role === chrome.automation.RoleType.TAB &&
-        parent.role === chrome.automation.RoleType.TAB_LIST &&
-        root.role === chrome.automation.RoleType.DESKTOP)
+    if (role === RoleType.TAB && parent.role === RoleType.TAB_LIST &&
+        root.role === RoleType.DESKTOP)
       return true;
   }
 
   // The general rule that applies to everything.
-  return state[chrome.automation.StateType.FOCUSABLE] === true;
+  return state[StateType.FOCUSABLE];
 };
diff --git a/chrome/browser/resources/local_ntp/local_ntp.html b/chrome/browser/resources/local_ntp/local_ntp.html
index 489280c..8d8286b3 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.html
+++ b/chrome/browser/resources/local_ntp/local_ntp.html
@@ -79,7 +79,7 @@
     <div id="attribution"><div id="attribution-text"></div></div>
 
     <div id="edit-bg" tabindex="0" hidden>
-      <button id="edit-bg-gear" tabindex="-1"></button>
+      <div id="edit-bg-gear" tabindex="-1" role="button"></div>
     </div>
 
     <div id="message-box-container" class="message-box-hide">
diff --git a/chrome/browser/resources/print_preview/new/preview_area.js b/chrome/browser/resources/print_preview/new/preview_area.js
index 98fb39c0..161ba287 100644
--- a/chrome/browser/resources/print_preview/new/preview_area.js
+++ b/chrome/browser/resources/print_preview/new/preview_area.js
@@ -503,18 +503,15 @@
     // We only care about: PageUp, PageDown, Left, Up, Right, Down.
     // If the user is holding a modifier key, ignore.
     if (!this.pluginProxy_.pluginReady() ||
-        !arrayContains(
-            [
-              'PageUp', 'PageDown', 'ArrowLeft', 'ArrowRight', 'ArrowUp',
-              'ArrowDown'
-            ],
-            e.code) ||
+        !['PageUp', 'PageDown', 'ArrowLeft', 'ArrowRight', 'ArrowUp',
+          'ArrowDown']
+             .includes(e.code) ||
         hasKeyModifiers(e)) {
       return;
     }
 
     // Don't handle the key event for these elements.
-    const tagName = e.path[0].tagName;
+    const tagName = e.composedPath()[0].tagName;
     if (['INPUT', 'SELECT', 'EMBED'].includes(tagName))
       return;
 
@@ -523,13 +520,13 @@
     // element, and work up the DOM tree to see if any element has a
     // scrollbar. If there exists a scrollbar, do not handle the key event
     // here.
-    let element = e.target;
-    while (element) {
-      if (element.scrollHeight > element.clientHeight ||
-          element.scrollWidth > element.clientWidth) {
+    const isEventHorizontal = ['ArrowLeft', 'ArrowRight'].includes(e.code);
+    for (let i = 0; i < e.composedPath().length; i++) {
+      const element = e.composedPath()[i];
+      if (element.scrollHeight > element.clientHeight && !isEventHorizontal ||
+          element.scrollWidth > element.clientWidth && isEventHorizontal) {
         return;
       }
-      element = element.parentElement;
     }
 
     // No scroll bar anywhere, or the active element is something else, like a
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_constants.js b/chrome/browser/resources/settings/multidevice_page/multidevice_constants.js
index a4928d7..b2f5470 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_constants.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_constants.js
@@ -19,70 +19,47 @@
   };
 
   /**
-   * MultiDevice software features. Note that this is copied from (and must
-   * include an analog of all values in) the enum of the same name in
-   * //components/cryptauth/proto/cryptauth_api.proto.
+   * Possible states of MultiDevice features. Note that this is copied from (and
+   * must include an analog of all values in) the FeatureState enum in
+   * //chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.
    * @enum {number}
    */
-  MultiDeviceSoftwareFeature = {
-    UNKNOWN_FEATURE: 0,
-    BETTER_TOGETHER_HOST: 1,
-    BETTER_TOGETHER_CLIENT: 2,
-    EASY_UNLOCK_HOST: 3,
-    EASY_UNLOCK_CLIENT: 4,
-    MAGIC_TETHER_HOST: 5,
-    MAGIC_TETHER_CLIENT: 6,
-    SMS_CONNECT_HOST: 7,
-    SMS_CONNECT_CLIENT: 8,
-  };
-
-  /**
-   * Possible states of MultiDevice software features. Note that this is based
-   * on (and must include an analog of all values in) the enum of the same name
-   * in //components/cryptauth/software_feature_state.h.
-   * @enum {number}
-   */
-  MultiDeviceSoftwareFeatureState = {
-    NOT_SUPPORTED: 0,
-    SUPPORTED: 1,
-    ENABLED: 2,
+  MultiDeviceFeatureState = {
+    DISABLED_BY_POLICY: 0,
+    DISABLED_BY_USER: 1,
+    ENABLED_BY_USER: 2,
+    NOT_SUPPORTED_BY_CHROMEBOOK: 3,
+    NOT_SUPPORTED_BY_PHONE: 4,
+    UNAVAILABLE_NO_VERIFIED_HOST: 5,
+    UNAVAILABLE_INSUFFICIENT_SECURITY: 6,
   };
 
   return {
     MultiDeviceSettingsMode: MultiDeviceSettingsMode,
-    MultiDeviceSoftwareFeature: MultiDeviceSoftwareFeature,
-    MultiDeviceSoftwareFeatureState: MultiDeviceSoftwareFeatureState,
+    MultiDeviceFeatureState: MultiDeviceFeatureState,
   };
 });
 
 /**
- * Represents a multidevice host, i.e. a phone set by the user to connect to
- * their Chromebook(s). The type is a subset of the RemoteDevice structure
- * defined by CryptAuth (components/cryptauth/remote_device.h). It contains the
- * host device's name (e.g. Pixel, Nexus 5) and the map softwareFeatures
- * sending each MultiDevice feature to the host device's state with regards to
- * that feature.
- *
- * @typedef {{
- *   name: string,
- *   softwareFeatures:
- *       !Object<settings.MultiDeviceSoftwareFeature,
- *           settings.MultiDeviceSoftwareFeatureState>
- * }}
- */
-let RemoteDevice;
-
-/**
  * Container for the initial data that the page requires in order to display
  * the correct content. It is also used for receiving status updates during
- * use. Note that the host may be verified (enabled or disabled), awaiting
- * verification, or it may have failed setup because it was not able to connect
- * to the server. If the property is null or undefined, then no host has been
- * set up, although there may be potential hosts on the account.
+ * use. Note that the host device may be verified (enabled or disabled),
+ * awaiting verification, or it may have failed setup because it was not able
+ * to connect to the server.
+ *
+ * For each MultiDevice feature (including the "suite" feature, which acts as a
+ * gatekeeper for the others), the corresponding *State property is an enum
+ * containing the data necessary to display it. Note that hostDeviceName should
+ * be undefined if and only if no host has been set up, regardless of whether
+ * there are potential hosts on the account.
  *
  * @typedef {{
  *   mode: !settings.MultiDeviceSettingsMode,
- *   hostDevice: (RemoteDevice|undefined)
+ *   hostDeviceName: (string|undefined),
+ *   betterTogetherState: !settings.MultiDeviceFeatureState,
+ *   instantTetheringState: !settings.MultiDeviceFeatureState,
+ *   messagesState: !settings.MultiDeviceFeatureState,
+ *   smartLockState: !settings.MultiDeviceFeatureState,
  * }}
  */
 let MultiDevicePageContentData;
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_page.html b/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
index cb2e46d..12d24756 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
@@ -58,7 +58,7 @@
       </neon-animatable>
       <template is="dom-if" route-path="/multidevice/features">
         <settings-subpage associated-control="[[$$('#multidevice-item')]]"
-            page-title="[[pageContentData.hostDevice.name]]">
+            page-title="[[pageContentData.hostDeviceName]]">
             <settings-multidevice-subpage prefs="{{prefs}}"
                 page-content-data="[[pageContentData]]">
             </settings-multidevice-subpage>
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_page.js b/chrome/browser/resources/settings/multidevice_page/multidevice_page.js
index 97054e51..2f7f8be 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_page.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_page.js
@@ -49,8 +49,7 @@
    * @private
    */
   getLabelText_: function() {
-    return !!this.pageContentData.hostDevice ?
-        this.pageContentData.hostDevice.name :
+    return this.pageContentData.hostDeviceName ||
         this.i18n('multideviceSetupItemHeading');
   },
 
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.js b/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.js
index 87143142..87cb6087 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.js
@@ -71,8 +71,8 @@
 
   /**
    * If the new mode corresponds to no eligible host or unset potential hosts
-   * (i.e. NO_ELIGIBLE_HOSTS or NO_HOST_SET), then newHostDevice should be null
-   * or undefined. Otherwise it should be defined and non-null.
+   * (i.e. NO_ELIGIBLE_HOSTS or NO_HOST_SET), then newHostDeviceName should be
+   * falsy. Otherwise it should be truthy.
    * @param {!MultiDevicePageContentData} newData
    * @private
    */
@@ -81,7 +81,7 @@
       settings.MultiDeviceSettingsMode.NO_ELIGIBLE_HOSTS,
       settings.MultiDeviceSettingsMode.NO_HOST_SET,
     ];
-    return !newData.hostDevice == noHostModes.includes(newData.mode);
+    return !newData.hostDeviceName === noHostModes.includes(newData.mode);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/printing_page/BUILD.gn b/chrome/browser/resources/settings/printing_page/BUILD.gn
index aeb05cf..5452ef3 100644
--- a/chrome/browser/resources/settings/printing_page/BUILD.gn
+++ b/chrome/browser/resources/settings/printing_page/BUILD.gn
@@ -9,6 +9,7 @@
     ":cloud_printers",
     ":cups_add_printer_dialog",
     ":cups_add_printer_dialog_elements",
+    ":cups_add_printer_dialog_util",
     ":cups_edit_printer_dialog",
     ":cups_printers",
     ":cups_printers_browser_proxy",
@@ -40,6 +41,12 @@
   ]
 }
 
+js_library("cups_add_printer_dialog_util") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+  ]
+}
+
 js_library("cups_edit_printer_dialog") {
   deps = [
     ":cups_set_manufacturer_model_behavior",
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
index d8557cc9..b0c6432 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
@@ -8,6 +8,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 <link rel="import" href="../i18n_setup.html">
 <link rel="import" href="cups_add_printer_dialog_elements.html">
+<link rel="import" href="cups_add_printer_dialog_util.html">
 <link rel="import" href="cups_printer_shared_css.html">
 <link rel="import" href="cups_printers_browser_proxy.html">
 <link rel="import" href="cups_set_manufacturer_model_behavior.html">
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
index fc6d7f1..c882c08 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
@@ -193,49 +193,13 @@
   },
 
   /**
-   * This function uses regular expressions to determine whether the provided
-   * printer address is valid. Address can be either an ipv4/6 address or a
-   * hostname followed by an optional port.
-   * NOTE: The regular expression for hostnames will allow hostnames that are
-   * over 255 characters.
-   * @param {String} name
-   * @param {String} address
+   * @param {string} name
+   * @param {string} address
    * @return {boolean} Whether the add printer button is enabled.
    * @private
    */
   canAddPrinter_: function(name, address) {
-    if (!name || !address)
-      return false;
-
-    const hostnamePrefix = '([a-z\\d]|[a-z\\d][a-z\\d\\-]{0,61}[a-z\\d])';
-
-    // Matches an arbitrary number of 'prefix patterns' which are separated by a
-    // dot.
-    const hostnameSuffix = `(\\.${hostnamePrefix})*`;
-
-    // Matches an optional port at the end of the address.
-    const portNumber = '(:\\d+)?';
-
-    const ipv6Full = '(([a-f\\d]){1,4}(:(:)?([a-f\\d]){1,4}){1,7})';
-
-    // Special cases for addresses using a shorthand notation.
-    const ipv6Prefix = '(::([a-f\\d]){1,4})';
-    const ipv6Suffix = '(([a-f\\d]){1,4}::)';
-    const ipv6Combined = `(${ipv6Full}|${ipv6Prefix}|${ipv6Suffix})`;
-    const ipv6WithPort = `(\\[${ipv6Combined}\\]${portNumber})`;
-
-    // Matches valid hostnames and ipv4 addresses.
-    const hostnameRegex =
-        new RegExp(`^${hostnamePrefix}${hostnameSuffix}${portNumber}$`, 'i');
-
-    // Matches valid ipv6 addresses.
-    const ipv6AddressRegex =
-        new RegExp(`^(${ipv6Combined}|${ipv6WithPort})$`, 'i');
-
-    const invalidIpv6Regex = new RegExp('.*::.*::.*');
-
-    return hostnameRegex.test(address) ||
-        (ipv6AddressRegex.test(address) && !invalidIpv6Regex.test(address));
+    return settings.printing.isNameAndAddressValid(name, address);
   },
 });
 
@@ -271,7 +235,8 @@
    * @private
    */
   canAddPrinter_: function(ppdManufacturer, ppdModel, printerPPDPath) {
-    return !!((ppdManufacturer && ppdModel) || printerPPDPath);
+    return settings.printing.isPPDInfoValid(
+        ppdManufacturer, ppdModel, printerPPDPath);
   },
 });
 
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
new file mode 100644
index 0000000..c7d68b6b
--- /dev/null
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="cups_add_printer_dialog_util.js"></script>
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.js b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.js
new file mode 100644
index 0000000..6cff6ec
--- /dev/null
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.js
@@ -0,0 +1,71 @@
+// 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.
+
+/**
+ * @fileoverview  Utility functions that are used in Cups printer setup dialogs.
+ */
+
+cr.define('settings.printing', function() {
+  /**
+   * Returns true if the printer's name and address is valid. This function
+   * uses regular expressions to determine whether the provided printer name
+   * and address are valid. Address can be either an ipv4/6 address or a
+   * hostname followed by an optional port.
+   * NOTE: The regular expression for hostnames will allow hostnames that are
+   * over 255 characters.
+   * @param {string} name
+   * @param {string} address
+   * @return {boolean}
+   */
+  function isNameAndAddressValid(name, address) {
+    if (!name || !address)
+      return false;
+
+    const hostnamePrefix = '([a-z\\d]|[a-z\\d][a-z\\d\\-]{0,61}[a-z\\d])';
+
+    // Matches an arbitrary number of 'prefix patterns' which are separated by a
+    // dot.
+    const hostnameSuffix = `(\\.${hostnamePrefix})*`;
+
+    // Matches an optional port at the end of the address.
+    const portNumber = '(:\\d+)?';
+
+    const ipv6Full = '(([a-f\\d]){1,4}(:(:)?([a-f\\d]){1,4}){1,7})';
+
+    // Special cases for addresses using a shorthand notation.
+    const ipv6Prefix = '(::([a-f\\d]){1,4})';
+    const ipv6Suffix = '(([a-f\\d]){1,4}::)';
+    const ipv6Combined = `(${ipv6Full}|${ipv6Prefix}|${ipv6Suffix})`;
+    const ipv6WithPort = `(\\[${ipv6Combined}\\]${portNumber})`;
+
+    // Matches valid hostnames and ipv4 addresses.
+    const hostnameRegex =
+        new RegExp(`^${hostnamePrefix}${hostnameSuffix}${portNumber}$`, 'i');
+
+    // Matches valid ipv6 addresses.
+    const ipv6AddressRegex =
+        new RegExp(`^(${ipv6Combined}|${ipv6WithPort})$`, 'i');
+
+    const invalidIpv6Regex = new RegExp('.*::.*::.*');
+
+    return hostnameRegex.test(address) ||
+        (ipv6AddressRegex.test(address) && !invalidIpv6Regex.test(address));
+  }
+
+  /**
+   * Returns true if the printer's manufacturer and model or ppd path is valid.
+   * @param {string} manufacturer
+   * @param {string} model
+   * @param {string} ppdPath
+   * @return {boolean}
+   */
+  function isPPDInfoValid(manufacturer, model, ppdPath) {
+    return !!((manufacturer && model) || ppdPath);
+  }
+
+  return {
+    isNameAndAddressValid: isNameAndAddressValid,
+    isPPDInfoValid: isPPDInfoValid,
+  };
+});
diff --git a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
index 31456eb6..c544bcf 100644
--- a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
+++ b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
@@ -5,6 +5,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="cups_add_printer_dialog_elements.html">
+<link rel="import" href="cups_add_printer_dialog_util.html">
 <link rel="import" href="cups_printer_shared_css.html">
 <link rel="import" href="cups_printers_browser_proxy.html">
 <link rel="import" href="cups_set_manufacturer_model_behavior.html">
@@ -116,7 +117,8 @@
             on-click="onCancelTap_">
           $i18n{cancel}
         </paper-button>
-        <paper-button class="action-button" on-click="onSaveTap_">
+        <paper-button class="action-button" on-click="onSaveTap_"
+            disabled="[[!canSavePrinter_(activePrinter.*)]]">
           $i18n{editPrinterButtonText}
         </paper-button>
       </div>
diff --git a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
index 32ca08a..488afe0 100644
--- a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
+++ b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
@@ -136,4 +136,17 @@
   isNetworkProtocol_: function(protocol) {
     return ['ipp', 'ipps', 'http', 'https', 'socket', 'lpd'].includes(protocol);
   },
+
+  /**
+   * @return {boolean} Whether the Save button is enabled.
+   * @private
+   */
+  canSavePrinter_: function() {
+    return settings.printing.isNameAndAddressValid(
+               this.activePrinter.printerName,
+               this.activePrinter.printerAddress) &&
+        settings.printing.isPPDInfoValid(
+            this.activePrinter.ppdManufacturer, this.activePrinter.ppdModel,
+            this.activePrinter.printerPPDPath);
+  },
 });
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index aefffa5..e569323 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -906,6 +906,12 @@
         <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_ELEMENTS_JS"
                    file="printing_page/cups_add_printer_dialog_elements.js"
                    type="chrome_html" />
+        <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_UTIL_HTML"
+                   file="printing_page/cups_add_printer_dialog_util.html"
+                   type="chrome_html" />
+        <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_UTIL_JS"
+                   file="printing_page/cups_add_printer_dialog_util.js"
+                   type="chrome_html" />
       </if>
       <if expr="not chromeos">
         <structure name="IDR_SETTINGS_PRINTING_BROWSER_PROXY_HTML"
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc
index df6f5cd..b04f0f7 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc
+++ b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc
@@ -258,12 +258,10 @@
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     GoogleURLTracker* google_url_tracker,
     const std::string& application_locale,
-    const base::Optional<std::string>& api_url_override,
     bool account_consistency_mirror_required)
     : url_loader_factory_(url_loader_factory),
       google_url_tracker_(google_url_tracker),
       application_locale_(application_locale),
-      api_url_override_(api_url_override),
       account_consistency_mirror_required_(account_consistency_mirror_required),
       weak_ptr_factory_(this) {}
 
@@ -292,8 +290,7 @@
     google_base_url = google_url_tracker_->google_url();
   }
 
-  GURL api_url =
-      google_base_url.Resolve(api_url_override_.value_or(kNewTabOgbApiPath));
+  GURL api_url = google_base_url.Resolve(kNewTabOgbApiPath);
 
   // Add the "hl=" parameter.
   api_url = net::AppendQueryParameter(api_url, "hl", application_locale_);
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.h b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.h
index 70428084..f359cba 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.h
+++ b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.h
@@ -32,13 +32,10 @@
 // See https://crbug.com/751534.
 class OneGoogleBarLoaderImpl : public OneGoogleBarLoader {
  public:
-  // |api_url_override| can be either absolute, or relative to the Google base
-  // URL.
   OneGoogleBarLoaderImpl(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       GoogleURLTracker* google_url_tracker,
       const std::string& application_locale,
-      const base::Optional<std::string>& api_url_override,
       bool account_consistency_mirror_required);
   ~OneGoogleBarLoaderImpl() override;
 
@@ -62,7 +59,6 @@
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   GoogleURLTracker* google_url_tracker_;
   const std::string application_locale_;
-  const base::Optional<std::string> api_url_override_;
   const bool account_consistency_mirror_required_;
 
   std::vector<OneGoogleCallback> callbacks_;
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc
index 0fe8719c..21b8982e 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc
+++ b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc
@@ -68,21 +68,9 @@
  public:
   OneGoogleBarLoaderImplTest()
       : OneGoogleBarLoaderImplTest(
-            /*api_url_override=*/base::nullopt,
             /*account_consistency_mirror_required=*/false) {}
 
   explicit OneGoogleBarLoaderImplTest(
-      const base::Optional<std::string>& api_url_override)
-      : OneGoogleBarLoaderImplTest(
-            api_url_override,
-            /*account_consistency_mirror_required=*/false) {}
-
-  explicit OneGoogleBarLoaderImplTest(bool account_consistency_mirror_required)
-      : OneGoogleBarLoaderImplTest(/*api_url_override=*/base::nullopt,
-                                   account_consistency_mirror_required) {}
-
-  OneGoogleBarLoaderImplTest(
-      const base::Optional<std::string>& api_url_override,
       bool account_consistency_mirror_required)
       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
         google_url_tracker_(std::make_unique<GoogleURLTrackerClientStub>(),
@@ -90,7 +78,6 @@
         test_shared_loader_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                 &test_url_loader_factory_)),
-        api_url_override_(api_url_override),
         account_consistency_mirror_required_(
             account_consistency_mirror_required) {}
 
@@ -103,7 +90,7 @@
 
     one_google_bar_loader_ = std::make_unique<OneGoogleBarLoaderImpl>(
         test_shared_loader_factory_, &google_url_tracker_, kApplicationLocale,
-        api_url_override_, account_consistency_mirror_required_);
+        account_consistency_mirror_required_);
   }
 
   void SetUpResponseWithData(const std::string& response) {
@@ -143,7 +130,6 @@
   GoogleURLTracker google_url_tracker_;
   network::TestURLLoaderFactory test_url_loader_factory_;
   scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
-  base::Optional<std::string> api_url_override_;
   bool account_consistency_mirror_required_;
 
   GURL last_request_url_;
@@ -376,56 +362,3 @@
       last_request_headers().HasHeader(signin::kChromeConnectedHeader));
 #endif
 }
-
-class OneGoogleBarLoaderImplWithRelativeApiUrlOverrideTest
-    : public OneGoogleBarLoaderImplTest {
- public:
-  OneGoogleBarLoaderImplWithRelativeApiUrlOverrideTest()
-      : OneGoogleBarLoaderImplTest(std::string("/testapi?q=a")) {}
-};
-
-TEST_F(OneGoogleBarLoaderImplWithRelativeApiUrlOverrideTest,
-       RequestUrlRespectsOverride) {
-  SetUpResponseWithData(kMinimalValidResponse);
-
-  // Trigger a request.
-  base::MockCallback<OneGoogleBarLoader::OneGoogleCallback> callback;
-  one_google_bar_loader()->Load(callback.Get());
-
-  base::RunLoop loop;
-  EXPECT_CALL(callback, Run(_, _)).WillOnce(Quit(&loop));
-  loop.Run();
-
-  // Make sure the request URL corresponds to the override, but also contains
-  // the "hl=" query param.
-  EXPECT_EQ("/testapi", last_request_url().path());
-  std::string expected_query =
-      base::StringPrintf("q=a&hl=%s&async=fixed:0", kApplicationLocale);
-  EXPECT_EQ(expected_query, last_request_url().query());
-}
-
-class OneGoogleBarLoaderImplWithAbsoluteApiUrlOverrideTest
-    : public OneGoogleBarLoaderImplTest {
- public:
-  OneGoogleBarLoaderImplWithAbsoluteApiUrlOverrideTest()
-      : OneGoogleBarLoaderImplTest(std::string("http://test.com/path?q=a")) {}
-};
-
-TEST_F(OneGoogleBarLoaderImplWithAbsoluteApiUrlOverrideTest,
-       RequestUrlRespectsOverride) {
-  SetUpResponseWithData(kMinimalValidResponse);
-
-  // Trigger a request.
-  base::MockCallback<OneGoogleBarLoader::OneGoogleCallback> callback;
-  one_google_bar_loader()->Load(callback.Get());
-
-  base::RunLoop loop;
-  EXPECT_CALL(callback, Run(_, _)).WillOnce(Quit(&loop));
-  loop.Run();
-
-  // Make sure the request URL corresponds to the override, but also contains
-  // the "hl=" query param.
-  GURL expected_url = GURL(base::StringPrintf(
-      "http://test.com/path?q=a&hl=%s&async=fixed:0", kApplicationLocale));
-  EXPECT_EQ(expected_url, last_request_url());
-}
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_service_factory.cc b/chrome/browser/search/one_google_bar/one_google_bar_service_factory.cc
index 4721b46..904da48 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_service_factory.cc
+++ b/chrome/browser/search/one_google_bar/one_google_bar_service_factory.cc
@@ -49,21 +49,12 @@
 
 KeyedService* OneGoogleBarServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  if (!base::FeatureList::IsEnabled(features::kOneGoogleBarOnLocalNtp)) {
-    return nullptr;
-  }
 
   Profile* profile = Profile::FromBrowserContext(context);
   GaiaCookieManagerService* cookie_service =
       GaiaCookieManagerServiceFactory::GetForProfile(profile);
   GoogleURLTracker* google_url_tracker =
       GoogleURLTrackerFactory::GetForProfile(profile);
-  std::string override_api_url_str = base::GetFieldTrialParamValueByFeature(
-      features::kOneGoogleBarOnLocalNtp, "one-google-api-url");
-  base::Optional<std::string> override_api_url;
-  if (!override_api_url_str.empty()) {
-    override_api_url = override_api_url_str;
-  }
   content_settings::CookieSettings* cookie_settings =
       CookieSettingsFactory::GetForProfile(profile).get();
   auto url_loader_factory =
@@ -73,7 +64,7 @@
       cookie_service,
       std::make_unique<OneGoogleBarLoaderImpl>(
           url_loader_factory, google_url_tracker,
-          g_browser_process->GetApplicationLocale(), override_api_url,
+          g_browser_process->GetApplicationLocale(),
           AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile) &&
               signin::SettingsAllowSigninCookies(cookie_settings)));
 }
diff --git a/chrome/browser/signin/chrome_signin_client.cc b/chrome/browser/signin/chrome_signin_client.cc
index 0bc9e35..8d1d26f 100644
--- a/chrome/browser/signin/chrome_signin_client.cc
+++ b/chrome/browser/signin/chrome_signin_client.cc
@@ -367,8 +367,7 @@
   // verification that the token is still valid (i.e. the password has not
   // been changed).
     if (!oauth_client_) {
-        oauth_client_.reset(new gaia::GaiaOAuthClient(
-            profile_->GetRequestContext()));
+      oauth_client_.reset(new gaia::GaiaOAuthClient(GetURLLoaderFactory()));
     }
     oauth_client_->GetTokenInfo(access_token, 3 /* retries */, this);
 }
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index d884e614..ad8ba3be 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -4012,13 +4012,12 @@
   content::SetBrowserClientForTesting(old_browser_client);
 }
 
-// Flaky on Windows 7 (dbg) trybot, see https://crbug.com/443374.
-#if defined(OS_WIN) && !defined(NDEBUG)
+// Flaky on Windows 7 bot, see https://crbug.com/874959.
+#if defined(OS_WIN)
 #define MAYBE_MixedContentSubFrame DISABLED_MixedContentSubFrame
 #else
 #define MAYBE_MixedContentSubFrame MixedContentSubFrame
 #endif
-
 // This test checks that all mixed content requests from a dedicated worker
 // which is started from a subframe are blocked if
 // allow_running_insecure_content setting is false or
diff --git a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
index 4a3892f..0c89b21 100644
--- a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
@@ -143,7 +143,7 @@
 
 // https://crbug.com/874929, flaky on all platform.
 IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest,
-                       MAYBE_SetPassphraseAndThenSetupSync) {
+                       DISABLED_SetPassphraseAndThenSetupSync) {
   ASSERT_TRUE(SetupClients());
 
   ASSERT_TRUE(GetClient(0)->SetupSync());
diff --git a/chrome/browser/themes/theme_properties.cc b/chrome/browser/themes/theme_properties.cc
index b44a0375..42919e3 100644
--- a/chrome/browser/themes/theme_properties.cc
+++ b/chrome/browser/themes/theme_properties.cc
@@ -51,7 +51,7 @@
 
 const SkColor kDefaultDetachedBookmarkBarBackground = SK_ColorWHITE;
 const SkColor kDefaultDetachedBookmarkBarBackgroundIncognito =
-    SkColorSetRGB(0x32, 0x32, 0x32);
+    SkColorSetRGB(0x32, 0x36, 0x39);
 
 // "Toolbar" text is used for active tabs and the bookmarks bar.
 constexpr SkColor kDefaultColorToolbarText = SK_ColorBLACK;
diff --git a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
index 1bd7bd31..d56e4c1 100644
--- a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
+++ b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
@@ -69,6 +69,7 @@
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/web/web_context_menu_data.h"
+#include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
 
 namespace {
@@ -423,8 +424,9 @@
     // Ensures it is really handled a reload.
     const content::LoadCommittedDetails& nav_details =
         nav_observer.load_committed_details();
-    EXPECT_TRUE(nav_details.entry != NULL);  // There was a navigation.
-    EXPECT_EQ(content::NAVIGATION_TYPE_EXISTING_PAGE, nav_details.type);
+    EXPECT_TRUE(nav_details.entry);  // There was a navigation.
+    EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+        ui::PAGE_TRANSITION_RELOAD, nav_details.entry->GetTransitionType()));
 
     // The TranslateManager class processes the navigation entry committed
     // notification in a posted task; process that task.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 4d608b4..f676ec9 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -144,8 +144,6 @@
       "cocoa/bubble_anchor_util_views_mac.mm",
       "cocoa/bubble_combobox.h",
       "cocoa/bubble_combobox.mm",
-      "cocoa/bubble_sync_promo_controller.h",
-      "cocoa/bubble_sync_promo_controller.mm",
       "cocoa/bubble_view.h",
       "cocoa/bubble_view.mm",
       "cocoa/certificate_viewer_mac_cocoa.h",
@@ -231,12 +229,6 @@
       "cocoa/extensions/chooser_dialog_cocoa_controller.mm",
       "cocoa/extensions/extension_action_platform_delegate_cocoa.h",
       "cocoa/extensions/extension_action_platform_delegate_cocoa.mm",
-      "cocoa/extensions/extension_install_dialog_controller.h",
-      "cocoa/extensions/extension_install_dialog_controller.mm",
-      "cocoa/extensions/extension_install_view_controller.h",
-      "cocoa/extensions/extension_install_view_controller.mm",
-      "cocoa/extensions/extension_installed_bubble_controller.h",
-      "cocoa/extensions/extension_installed_bubble_controller.mm",
       "cocoa/extensions/extension_keybinding_registry_cocoa.h",
       "cocoa/extensions/extension_keybinding_registry_cocoa.mm",
       "cocoa/extensions/extension_popup_controller.h",
@@ -254,8 +246,6 @@
       "cocoa/extensions/toolbar_actions_bar_bubble_mac.mm",
       "cocoa/extensions/toolbar_actions_bar_bubble_views_presenter.h",
       "cocoa/extensions/toolbar_actions_bar_bubble_views_presenter.mm",
-      "cocoa/extensions/windowed_install_dialog_controller.h",
-      "cocoa/extensions/windowed_install_dialog_controller.mm",
       "cocoa/external_protocol_dialog.h",
       "cocoa/external_protocol_dialog_cocoa.mm",
       "cocoa/external_protocol_dialog_views_mac.mm",
@@ -2545,15 +2535,7 @@
         "cocoa/extensions/chooser_dialog_cocoa.mm",
         "cocoa/extensions/chooser_dialog_cocoa_controller.h",
         "cocoa/extensions/chooser_dialog_cocoa_controller.mm",
-        "cocoa/extensions/extension_install_dialog_controller.h",
-        "cocoa/extensions/extension_install_dialog_controller.mm",
-        "cocoa/extensions/extension_install_view_controller.h",
-        "cocoa/extensions/extension_install_view_controller.mm",
-        "cocoa/extensions/extension_installed_bubble_controller.h",
-        "cocoa/extensions/extension_installed_bubble_controller.mm",
         "cocoa/extensions/extension_uninstall_dialog_cocoa.mm",
-        "cocoa/extensions/windowed_install_dialog_controller.h",
-        "cocoa/extensions/windowed_install_dialog_controller.mm",
         "cocoa/external_protocol_dialog_views_mac.mm",
         "cocoa/global_error_bubble_controller.h",
         "cocoa/global_error_bubble_controller.mm",
diff --git a/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc b/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc
index d031b97..26a760a 100644
--- a/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc
+++ b/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc
@@ -38,7 +38,10 @@
   void BrailleDisplayStateChanged(bool connected) override {}
   void SetFocusHighlightRect(const gfx::Rect& bounds_in_screen) override {}
   void SetCaretBounds(const gfx::Rect& bounds_in_screen) override {}
-  void SetAccessibilityPanelFullscreen(bool fullscreen) override {}
+  void SetAccessibilityPanelAlwaysVisible(bool always_visible) override {}
+  void SetAccessibilityPanelBounds(
+      const gfx::Rect& bounds,
+      ash::mojom::AccessibilityPanelState state) override {}
   void SetSelectToSpeakState(ash::mojom::SelectToSpeakState state) override {}
 
   bool was_client_set() const { return was_client_set_; }
diff --git a/chrome/browser/ui/cocoa/bubble_sync_promo_controller.h b/chrome/browser/ui/cocoa/bubble_sync_promo_controller.h
deleted file mode 100644
index b9edbe1..0000000
--- a/chrome/browser/ui/cocoa/bubble_sync_promo_controller.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_BUBBLE_SYNC_PROMO_CONTROLLER_H_
-#define CHROME_BROWSER_UI_COCOA_BUBBLE_SYNC_PROMO_CONTROLLER_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/mac/scoped_nsobject.h"
-#include "components/signin/core/browser/signin_metrics.h"
-
-class Browser;
-@class HyperlinkTextView;
-
-// Controller of the bookmark sync promo displayed at the bottom of the
-// bookmark bubble.
-@interface BubbleSyncPromoController : NSViewController<NSTextViewDelegate> {
- @private
-  // The browser in which the sign in page will be loaded.
-  Browser* browser_;  // weak
-
-  // The text view that displays the promo message. Ownership is shared between
-  // the controller and its view.
-  base::scoped_nsobject<HyperlinkTextView> textView_;
-
-  // The resource ids of the promo string and link string.
-  int promoStringId_;
-  int linkStringId_;
-
-  // The access point for signing in.
-  signin_metrics::AccessPoint accessPoint_;
-}
-
-@property(nonatomic, readonly) CGFloat borderWidth;
-
-- (id)initWithBrowser:(Browser*)browser
-        promoStringId:(int)promoStringId
-         linkStringId:(int)linkStringId
-          accessPoint:(signin_metrics::AccessPoint)accessPoint;
-
-// Preferred height of the sync promo view for a given width. The border is
-// is included in the provided width and in the returned height.
-- (CGFloat)preferredHeightForWidth:(CGFloat)width;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_BUBBLE_SYNC_PROMO_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/bubble_sync_promo_controller.mm b/chrome/browser/ui/cocoa/bubble_sync_promo_controller.mm
deleted file mode 100644
index c93fe73a..0000000
--- a/chrome/browser/ui/cocoa/bubble_sync_promo_controller.mm
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/bubble_sync_promo_controller.h"
-
-#include <stddef.h>
-
-#include "base/strings/sys_string_conversions.h"
-#include "chrome/browser/signin/signin_promo.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/cocoa/chrome_style.h"
-#include "chrome/browser/ui/cocoa/cocoa_util.h"
-#include "skia/ext/skia_utils_mac.h"
-#include "third_party/skia/include/core/SkColor.h"
-#import "ui/base/cocoa/controls/hyperlink_text_view.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-
-namespace {
-
-const SkColor kPromoTextColor = SkColorSetRGB(0x66, 0x66, 0x66);
-const SkColor kPromoViewBackgroundColor = SkColorSetRGB(0xf5, 0xf5, 0xf5);
-const SkColor kPromoBorderColor = SkColorSetRGB(0xe5, 0xe5, 0xe5);
-
-// Vertical padding of the promo (dp).
-const CGFloat kPromoVerticalPadding = 15;
-
-// Width of the border (dp).
-const CGFloat kPromoBorderWidth = 1.0;
-
-// Font size of the promo text (pt).
-const int kPromoFontSize = 11;
-
-}  // namespace
-
-@implementation BubbleSyncPromoController
-
-- (id)initWithBrowser:(Browser*)browser
-        promoStringId:(int)promoStringId
-         linkStringId:(int)linkStringId
-          accessPoint:(signin_metrics::AccessPoint)accessPoint {
-  if ((self = [super init])) {
-    browser_ = browser;
-    promoStringId_ = promoStringId;
-    linkStringId_ = linkStringId;
-    accessPoint_ = accessPoint;
-  }
-  return self;
-}
-
-- (CGFloat)borderWidth {
-  return kPromoBorderWidth;
-}
-
-- (CGFloat)preferredHeightForWidth:(CGFloat)width {
-  CGFloat availableWidth =
-      width - (2 * chrome_style::kHorizontalPadding) - (2 * kPromoBorderWidth);
-  NSRect frame = [[textView_ textStorage]
-      boundingRectWithSize:NSMakeSize(availableWidth, 0.0)
-                   options:NSStringDrawingUsesLineFragmentOrigin];
-  return frame.size.height + (2 * kPromoVerticalPadding) +
-         (2 * kPromoBorderWidth);
-}
-
-- (void)loadView {
-  NSBox* promoView = [[[NSBox alloc] init] autorelease];
-  [promoView setBoxType:NSBoxCustom];
-  [promoView
-      setFillColor:skia::SkColorToDeviceNSColor(kPromoViewBackgroundColor)];
-  [promoView setContentViewMargins:NSMakeSize(chrome_style::kHorizontalPadding,
-                                              kPromoVerticalPadding)];
-  [promoView setBorderType:NSLineBorder];
-  [promoView setBorderWidth:kPromoBorderWidth];
-  [promoView setBorderColor:skia::SkColorToDeviceNSColor(kPromoBorderColor)];
-
-  // Add the sync promo text.
-  size_t offset;
-  const base::string16 linkText = l10n_util::GetStringUTF16(linkStringId_);
-  const base::string16 promoText =
-      l10n_util::GetStringFUTF16(promoStringId_, linkText, &offset);
-  NSString* nsPromoText = base::SysUTF16ToNSString(promoText);
-  NSString* nsLinkText = base::SysUTF16ToNSString(linkText);
-  NSFont* font = [NSFont labelFontOfSize:kPromoFontSize];
-  NSColor* linkColor = skia::SkColorToCalibratedNSColor(
-      chrome_style::GetLinkColor());
-
-  textView_.reset([[HyperlinkTextView alloc] init]);
-  [textView_ setMessage:nsPromoText
-               withFont:font
-           messageColor:skia::SkColorToDeviceNSColor(kPromoTextColor)];
-  [textView_ addLinkRange:NSMakeRange(offset, [nsLinkText length])
-                  withURL:nil
-                linkColor:linkColor];
-  [textView_ setRefusesFirstResponder:YES];
-  [[textView_ textContainer] setLineFragmentPadding:0.0];
-  cocoa_util::RemoveUnderlining(textView_, offset, linkText.size());
-  [textView_ setDelegate:self];
-
-  [promoView setContentView:textView_];
-
-  [self setView:promoView];
-}
-
-- (BOOL)textView:(NSTextView *)textView
-   clickedOnLink:(id)link
-         atIndex:(NSUInteger)charIndex {
-  chrome::ShowBrowserSignin(browser_, accessPoint_);
-  return YES;
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h
deleted file mode 100644
index dd2d076..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALL_DIALOG_CONTROLLER_H_
-#define CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALL_DIALOG_CONTROLLER_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/mac/scoped_nsobject.h"
-#include "base/macros.h"
-#include "chrome/browser/extensions/extension_install_prompt.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h"
-#import "chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h"
-
-class ExtensionInstallPromptShowParams;
-@class ExtensionInstallViewController;
-
-// Displays an extension install prompt as a tab modal dialog.
-class ExtensionInstallDialogController :
-    public ExtensionInstallViewDelegate,
-    public ConstrainedWindowMacDelegate {
- public:
-  ExtensionInstallDialogController(
-      ExtensionInstallPromptShowParams* show_params,
-      const ExtensionInstallPrompt::DoneCallback& done_callback,
-      std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt);
-  ~ExtensionInstallDialogController() override;
-
-  // ExtensionInstallViewDelegate implementation.
-  void OnOkButtonClicked() override;
-  void OnCancelButtonClicked() override;
-  void OnStoreLinkClicked() override;
-
-  // ConstrainedWindowMacDelegate implementation.
-  void OnConstrainedWindowClosed(ConstrainedWindowMac* window) override;
-
-  ConstrainedWindowMac* constrained_window() const {
-    return constrained_window_.get();
-  }
-  ExtensionInstallViewController* view_controller() const {
-    return view_controller_;
-  }
-
- private:
-  void OnPromptButtonClicked(ExtensionInstallPrompt::Result result);
-
-  ExtensionInstallPrompt::DoneCallback done_callback_;
-  base::scoped_nsobject<ExtensionInstallViewController> view_controller_;
-  std::unique_ptr<ConstrainedWindowMac> constrained_window_;
-
-  DISALLOW_COPY_AND_ASSIGN(ExtensionInstallDialogController);
-};
-
-#endif  // CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALL_DIALOG_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm
deleted file mode 100644
index a44a5f9..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2012 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 "chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/extensions/extension_install_prompt_show_params.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/cocoa/browser_dialogs_views_mac.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
-#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_window.h"
-#import "chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "content/public/browser/web_contents.h"
-#include "ui/base/ui_features.h"
-
-namespace {
-
-void ShowExtensionInstallDialogImpl(
-    ExtensionInstallPromptShowParams* show_params,
-    const ExtensionInstallPrompt::DoneCallback& done_callback,
-    std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt) {
-  // These objects will delete themselves when the dialog closes.
-  if (!show_params->GetParentWebContents()) {
-    new WindowedInstallDialogController(show_params, done_callback,
-                                        std::move(prompt));
-    return;
-  }
-
-  new ExtensionInstallDialogController(show_params, done_callback,
-                                       std::move(prompt));
-}
-
-}  // namespace
-
-ExtensionInstallDialogController::ExtensionInstallDialogController(
-    ExtensionInstallPromptShowParams* show_params,
-    const ExtensionInstallPrompt::DoneCallback& done_callback,
-    std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt)
-    : done_callback_(done_callback) {
-  view_controller_.reset([[ExtensionInstallViewController alloc]
-      initWithProfile:show_params->profile()
-            navigator:show_params->GetParentWebContents()
-             delegate:this
-               prompt:std::move(prompt)]);
-
-  base::scoped_nsobject<NSWindow> window([[ConstrainedWindowCustomWindow alloc]
-      initWithContentRect:[[view_controller_ view] bounds]]);
-  [[window contentView] addSubview:[view_controller_ view]];
-  [window setBackgroundColor:[NSColor whiteColor]];
-
-  base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
-      [[CustomConstrainedWindowSheet alloc] initWithCustomWindow:window]);
-  // The extension install dialog can cause window server crashes when using the
-  // complex animations provided by private APIs, so use simple animations. See
-  // https://crbug.com/548824.
-  [sheet setUseSimpleAnimations:YES];
-  constrained_window_ = CreateAndShowWebModalDialogMac(
-      this, show_params->GetParentWebContents(), sheet);
-}
-
-ExtensionInstallDialogController::~ExtensionInstallDialogController() {
-}
-
-void ExtensionInstallDialogController::OnOkButtonClicked() {
-  OnPromptButtonClicked(ExtensionInstallPrompt::Result::ACCEPTED);
-}
-
-void ExtensionInstallDialogController::OnCancelButtonClicked() {
-  OnPromptButtonClicked(ExtensionInstallPrompt::Result::USER_CANCELED);
-}
-
-void ExtensionInstallDialogController::OnStoreLinkClicked() {
-  OnPromptButtonClicked(ExtensionInstallPrompt::Result::USER_CANCELED);
-}
-
-void ExtensionInstallDialogController::OnConstrainedWindowClosed(
-    ConstrainedWindowMac* window) {
-  if (!done_callback_.is_null()) {
-    base::ResetAndReturn(&done_callback_).Run(
-        ExtensionInstallPrompt::Result::ABORTED);
-  }
-  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
-}
-
-void ExtensionInstallDialogController::OnPromptButtonClicked(
-    ExtensionInstallPrompt::Result result) {
-  base::ResetAndReturn(&done_callback_).Run(result);
-  constrained_window_->CloseWebContentsModalDialog();
-}
-
-// static
-ExtensionInstallPrompt::ShowDialogCallback
-ExtensionInstallPrompt::GetDefaultShowDialogCallback() {
-  return base::BindRepeating(&ShowExtensionInstallDialogImpl);
-}
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller_browsertest.mm b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller_browsertest.mm
deleted file mode 100644
index 9668fd2..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller_browsertest.mm
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2012 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 "chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h"
-
-#include <utility>
-
-#include "chrome/browser/extensions/extension_install_prompt_show_params.h"
-#include "chrome/browser/extensions/extension_install_prompt_test_helper.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
-#import "chrome/browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h"
-#import "chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "extensions/common/extension.h"
-
-using extensions::Extension;
-
-class ExtensionInstallDialogControllerTest : public InProcessBrowserTest {
-public:
-  ExtensionInstallDialogControllerTest() {}
-
-  void SetUpOnMainThread() override {
-    extension_ = chrome::LoadInstallPromptExtension();
-  }
-
- protected:
-  scoped_refptr<Extension> extension_;
-};
-
-IN_PROC_BROWSER_TEST_F(ExtensionInstallDialogControllerTest, BasicTest) {
-  content::WebContents* tab = browser()->tab_strip_model()->GetWebContentsAt(0);
-  ExtensionInstallPromptShowParams show_params(tab);
-
-  ExtensionInstallPromptTestHelper test_helper;
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt =
-      chrome::BuildExtensionInstallPrompt(extension_.get());
-
-  ExtensionInstallDialogController* controller =
-      new ExtensionInstallDialogController(
-          &show_params, test_helper.GetCallback(), std::move(prompt));
-
-  base::scoped_nsobject<NSWindow> window(
-      [[[controller->view_controller() view] window] retain]);
-  EXPECT_TRUE([window isVisible]);
-
-  // Press cancel to close the window.
-  [[controller->view_controller() cancelButton] performClick:nil];
-
-  // Wait for the window to finish closing.
-  EXPECT_FALSE([window isVisible]);
-
-  EXPECT_EQ(ExtensionInstallPrompt::Result::USER_CANCELED,
-            test_helper.result());
-}
-
-IN_PROC_BROWSER_TEST_F(ExtensionInstallDialogControllerTest,
-                       DISABLED_Permissions) {
-  content::WebContents* tab = browser()->tab_strip_model()->GetWebContentsAt(0);
-  ExtensionInstallPromptShowParams show_params(tab);
-
-  ExtensionInstallPromptTestHelper test_helper;
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt =
-      chrome::BuildExtensionPostInstallPermissionsPrompt(extension_.get());
-
-  ExtensionInstallDialogController* controller =
-      new ExtensionInstallDialogController(
-          &show_params, test_helper.GetCallback(), std::move(prompt));
-
-  base::scoped_nsobject<NSWindow> window(
-      [[[controller->view_controller() view] window] retain]);
-  EXPECT_TRUE([window isVisible]);
-
-  // Press cancel to close the window.
-  [[controller->view_controller() cancelButton] performClick:nil];
-
-  // Wait for the window to finish closing.
-  EXPECT_FALSE([window isVisible]);
-
-  EXPECT_EQ(ExtensionInstallPrompt::Result::USER_CANCELED,
-            test_helper.result());
-}
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h b/chrome/browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h
deleted file mode 100644
index 7f9f663..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALL_PROMPT_TEST_UTILS_H_
-#define CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALL_PROMPT_TEST_UTILS_H_
-
-#include "base/memory/ref_counted.h"
-#include "chrome/browser/extensions/extension_install_prompt.h"
-
-namespace chrome {
-
-// Loads the test extension from the given test directory and manifest file.
-scoped_refptr<extensions::Extension> LoadInstallPromptExtension(
-    const char* extension_dir_name,
-    const char* manifest_file);
-
-// Loads the default install_prompt test extension.
-scoped_refptr<extensions::Extension> LoadInstallPromptExtension();
-
-// Loads the icon for the install prompt extension.
-gfx::Image LoadInstallPromptIcon();
-
-// Builds a prompt using the given extension.
-std::unique_ptr<ExtensionInstallPrompt::Prompt> BuildExtensionInstallPrompt(
-    extensions::Extension* extension);
-
-std::unique_ptr<ExtensionInstallPrompt::Prompt>
-BuildExtensionPostInstallPermissionsPrompt(extensions::Extension* extension);
-
-}  // namespace chrome
-
-#endif  // CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALL_PROMPT_TEST_UTILS_H_
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_prompt_test_utils.mm b/chrome/browser/ui/cocoa/extensions/extension_install_prompt_test_utils.mm
deleted file mode 100644
index 3cb657b..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_install_prompt_test_utils.mm
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) 2012 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 "chrome/browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h"
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/json/json_file_value_serializer.h"
-#include "base/path_service.h"
-#include "base/threading/thread_restrictions.h"
-#include "chrome/common/chrome_paths.h"
-#include "extensions/common/extension.h"
-
-using extensions::Extension;
-
-namespace chrome {
-
-scoped_refptr<extensions::Extension> LoadInstallPromptExtension(
-    const char* extension_dir_name,
-    const char* manifest_file) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  scoped_refptr<Extension> extension;
-
-  base::FilePath path;
-  base::PathService::Get(chrome::DIR_TEST_DATA, &path);
-  path = path.AppendASCII("extensions")
-             .AppendASCII(extension_dir_name)
-             .AppendASCII(manifest_file);
-
-  std::string error;
-  JSONFileValueDeserializer deserializer(path);
-  std::unique_ptr<base::DictionaryValue> value =
-      base::DictionaryValue::From(deserializer.Deserialize(NULL, &error));
-  if (!value.get()) {
-    LOG(ERROR) << error;
-    return extension;
-  }
-
-  extension = Extension::Create(
-      path.DirName(), extensions::Manifest::INVALID_LOCATION, *value,
-      Extension::NO_FLAGS, &error);
-  if (!extension.get())
-    LOG(ERROR) << error;
-
-  return extension;
-}
-
-scoped_refptr<Extension> LoadInstallPromptExtension() {
-  return LoadInstallPromptExtension("install_prompt", "extension.json");
-}
-
-gfx::Image LoadInstallPromptIcon() {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  base::FilePath path;
-  base::PathService::Get(chrome::DIR_TEST_DATA, &path);
-  path = path.AppendASCII("extensions")
-             .AppendASCII("install_prompt")
-             .AppendASCII("icon.png");
-
-  std::string file_contents;
-  base::ReadFileToString(path, &file_contents);
-
-  return gfx::Image::CreateFrom1xPNGBytes(
-      reinterpret_cast<const unsigned char*>(file_contents.c_str()),
-      file_contents.length());
-}
-
-std::unique_ptr<ExtensionInstallPrompt::Prompt> BuildExtensionInstallPrompt(
-    Extension* extension) {
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt(
-      new ExtensionInstallPrompt::Prompt(
-          ExtensionInstallPrompt::INSTALL_PROMPT));
-  prompt->set_extension(extension);
-  prompt->set_icon(LoadInstallPromptIcon());
-  return prompt;
-}
-
-std::unique_ptr<ExtensionInstallPrompt::Prompt>
-BuildExtensionPostInstallPermissionsPrompt(Extension* extension) {
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt(
-      new ExtensionInstallPrompt::Prompt(
-          ExtensionInstallPrompt::POST_INSTALL_PERMISSIONS_PROMPT));
-  prompt->set_extension(extension);
-  prompt->set_icon(LoadInstallPromptIcon());
-  return prompt;
-}
-
-}  // namespace chrome
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h b/chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h
deleted file mode 100644
index ff284af..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALL_VIEW_CONTROLLER_H_
-#define CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALL_VIEW_CONTROLLER_H_
-
-#include <vector>
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/mac/scoped_nsobject.h"
-#include "base/memory/ref_counted.h"
-#include "base/strings/string16.h"
-#include "chrome/browser/extensions/extension_install_prompt.h"
-#include "ui/gfx/image/image_skia.h"
-
-class Profile;
-
-namespace content {
-class PageNavigator;
-}
-
-class ExtensionInstallViewDelegate {
- public:
-  virtual void OnOkButtonClicked() = 0;
-  virtual void OnCancelButtonClicked() = 0;
-  virtual void OnStoreLinkClicked() = 0;
-
- protected:
-  virtual ~ExtensionInstallViewDelegate() {}
-};
-
-// Displays the extension install prompt, and notifies the Delegate of success
-// or failure.
-@interface ExtensionInstallViewController : NSViewController
-                                           <NSOutlineViewDataSource,
-                                            NSOutlineViewDelegate> {
-  IBOutlet NSImageView* iconView_;
-  IBOutlet NSTextField* titleField_;
-  IBOutlet NSTextField* itemsField_;
-  IBOutlet NSButton* cancelButton_;
-  IBOutlet NSButton* okButton_;
-
-  // Present only when the dialog has permission warnings issues to display.
-  IBOutlet NSOutlineView* outlineView_;
-
-  // Present only in the install dialogs with webstore data (inline and
-  // external).
-  IBOutlet NSBox* warningsSeparator_; // Only when there are permissions.
-  IBOutlet NSView* ratingStars_;
-  IBOutlet NSTextField* ratingCountField_;
-  IBOutlet NSTextField* userCountField_;
-  IBOutlet NSButton* storeLinkButton_;
-
-  Profile* profile_; // weak
-  content::PageNavigator* navigator_;  // weak
-  ExtensionInstallViewDelegate* delegate_;  // weak
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt_;
-
-  base::scoped_nsobject<NSArray> warnings_;
-  BOOL isComputingRowHeight_;
-}
-
-// For unit test use only.
-@property(nonatomic, readonly) NSImageView* iconView;
-@property(nonatomic, readonly) NSTextField* titleField;
-@property(nonatomic, readonly) NSTextField* itemsField;
-@property(nonatomic, readonly) NSButton* cancelButton;
-@property(nonatomic, readonly) NSButton* okButton;
-@property(nonatomic, readonly) NSOutlineView* outlineView;
-@property(nonatomic, readonly) NSBox* warningsSeparator;
-@property(nonatomic, readonly) NSView* ratingStars;
-@property(nonatomic, readonly) NSTextField* ratingCountField;
-@property(nonatomic, readonly) NSTextField* userCountField;
-@property(nonatomic, readonly) NSButton* storeLinkButton;
-
-- (id)initWithProfile:(Profile*)profile
-            navigator:(content::PageNavigator*)navigator
-             delegate:(ExtensionInstallViewDelegate*)delegate
-               prompt:(std::unique_ptr<ExtensionInstallPrompt::Prompt>)prompt;
-- (IBAction)storeLinkClicked:(id)sender; // Callback for "View details" link.
-- (IBAction)cancel:(id)sender;
-- (IBAction)ok:(id)sender;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALL_VIEW_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_install_view_controller.mm
deleted file mode 100644
index 47463649..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller.mm
+++ /dev/null
@@ -1,833 +0,0 @@
-// Copyright (c) 2012 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 "chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h"
-
-#include <stddef.h>
-
-#include <utility>
-
-#include "base/auto_reset.h"
-#include "base/i18n/rtl.h"
-#include "base/mac/bundle_locations.h"
-#include "base/mac/mac_util.h"
-#include "base/strings/string_util.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#import "chrome/browser/ui/cocoa/chrome_style.h"
-#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/grit/generated_resources.h"
-#include "content/public/browser/page_navigator.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_urls.h"
-#include "skia/ext/skia_utils_mac.h"
-#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
-#import "ui/base/cocoa/a11y_util.h"
-#import "ui/base/cocoa/controls/hyperlink_button_cell.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-#include "ui/gfx/image/image_skia_util_mac.h"
-
-using base::SysUTF16ToNSString;
-using content::OpenURLParams;
-using content::Referrer;
-
-namespace {
-
-// A collection of attributes (bitmask) for how to draw a cell, the expand
-// marker and the text in the cell.
-enum CellAttributesMask {
-  kBoldText                = 1 << 0,
-  kNoExpandMarker          = 1 << 1,
-  kUseBullet               = 1 << 2,
-  kAutoExpandCell          = 1 << 3,
-  kUseCustomLinkCell       = 1 << 4,
-  kCanExpand               = 1 << 5,
-};
-
-typedef NSUInteger CellAttributes;
-
-}  // namespace.
-
-@interface ExtensionInstallViewController ()
-- (BOOL)hasWebstoreData;
-- (void)appendRatingStar:(const gfx::ImageSkia*)skiaImage;
-- (void)onOutlineViewRowCountDidChange;
-- (NSDictionary*)buildItemWithTitle:(NSString*)title
-                     cellAttributes:(CellAttributes)cellAttributes
-                           children:(NSArray*)children;
-- (NSDictionary*)buildDetailToggleItem:(size_t)type
-                 permissionsDetailIndex:(size_t)index;
-- (NSArray*)buildWarnings:(const ExtensionInstallPrompt::Prompt&)prompt;
-// Adds permissions of |type| from |prompt| to |children| and returns the
-// the appropriate permissions header. If no permissions are found, NULL is
-// returned.
-- (NSString*)
-appendPermissionsForPrompt:(const ExtensionInstallPrompt::Prompt&)prompt
-                  children:(NSMutableArray*)children;
-- (void)updateViewFrame:(NSRect)frame;
-@end
-
-@interface DetailToggleHyperlinkButtonCell : HyperlinkButtonCell {
-  NSUInteger permissionsDetailIndex_;
-  ExtensionInstallPrompt::DetailsType permissionsDetailType_;
-  SEL linkClickedAction_;
-}
-
-@property(assign, nonatomic) NSUInteger permissionsDetailIndex;
-@property(assign, nonatomic)
-    ExtensionInstallPrompt::DetailsType permissionsDetailType;
-@property(assign, nonatomic) SEL linkClickedAction;
-
-@end
-
-namespace {
-
-// Padding above the warnings separator, we must also subtract this when hiding
-// it.
-const CGFloat kWarningsSeparatorPadding = 14;
-
-// The left padding for the link cell.
-const CGFloat kLinkCellPaddingLeft = 3;
-
-// Maximum height we will adjust controls to when trying to accomodate their
-// contents.
-const CGFloat kMaxControlHeight = 250;
-
-NSString* const kTitleKey = @"title";
-NSString* const kChildrenKey = @"children";
-NSString* const kCellAttributesKey = @"cellAttributes";
-NSString* const kPermissionsDetailIndex = @"permissionsDetailIndex";
-NSString* const kPermissionsDetailType = @"permissionsDetailType";
-
-// Computes the |control|'s desired height to fit its contents, constrained to
-// be kMaxControlHeight at most.
-CGFloat ComputeDesiredControlHeight(NSControl* control) {
-  NSRect rect = [control frame];
-  rect.size.height = kMaxControlHeight;
-  return [[control cell] cellSizeForBounds:rect].height;
-}
-
-// Adjust the |control|'s height so that its content is not clipped.
-// This also adds the change in height to the |total_offset| and shifts the
-// control down by that amount.
-void OffsetControlVerticallyToFitContent(NSControl* control,
-                                         CGFloat* total_offset) {
-  // Adjust the control's height so that its content is not clipped.
-  NSRect current_rect = [control frame];
-  CGFloat desired_height = ComputeDesiredControlHeight(control);
-  CGFloat offset = desired_height - NSHeight(current_rect);
-
-  [control setFrameSize:NSMakeSize(NSWidth(current_rect),
-                                   NSHeight(current_rect) + offset)];
-
-  *total_offset += offset;
-
-  // Move the control vertically by the new total offset.
-  NSPoint origin = [control frame].origin;
-  origin.y -= *total_offset;
-  [control setFrameOrigin:origin];
-}
-
-// Adjust the |view|'s height so that its subviews are not clipped.
-// This also adds the change in height to the |total_offset| and shifts the
-// control down by that amount.
-void OffsetViewVerticallyToFitContent(NSView* view, CGFloat* total_offset) {
-  // Adjust the view's height so that its subviews are not clipped.
-  CGFloat desired_height = 0;
-  for (NSView* subview in [view subviews]) {
-    int required_height = NSMaxY([subview frame]);
-    if (required_height > desired_height)
-      desired_height = required_height;
-  }
-  NSRect current_rect = [view frame];
-  CGFloat offset = desired_height - NSHeight(current_rect);
-
-  [view setFrameSize:NSMakeSize(NSWidth(current_rect),
-                                NSHeight(current_rect) + offset)];
-
-  *total_offset += offset;
-
-  // Move the view vertically by the new total offset.
-  NSPoint origin = [view frame].origin;
-  origin.y -= *total_offset;
-  [view setFrameOrigin:origin];
-}
-
-// Gets the desired height of |outline_view|. Simply using the view's frame
-// doesn't work if an animation is pending.
-CGFloat GetDesiredOutlineViewHeight(NSOutlineView* outline_view) {
-  CGFloat height = 0;
-  for (NSInteger i = 0; i < [outline_view numberOfRows]; ++i)
-    height += NSHeight([outline_view rectOfRow:i]);
-  return height;
-}
-
-void OffsetOutlineViewVerticallyToFitContent(NSOutlineView* outline_view,
-                                             CGFloat* total_offset) {
-  NSScrollView* scroll_view = [outline_view enclosingScrollView];
-  NSRect frame = [scroll_view frame];
-  CGFloat desired_height = GetDesiredOutlineViewHeight(outline_view);
-  if (desired_height > kMaxControlHeight)
-    desired_height = kMaxControlHeight;
-  CGFloat offset = desired_height - NSHeight(frame);
-  frame.size.height += offset;
-
-  *total_offset += offset;
-
-  // Move the control vertically by the new total offset.
-  frame.origin.y -= *total_offset;
-  [scroll_view setFrame:frame];
-}
-
-void AppendRatingStarsShim(const gfx::ImageSkia* skia_image, void* data) {
-  ExtensionInstallViewController* controller =
-      static_cast<ExtensionInstallViewController*>(data);
-  [controller appendRatingStar:skia_image];
-}
-
-void DrawBulletInFrame(NSRect frame) {
-  NSRect rect;
-  rect.size.width = std::min(NSWidth(frame), NSHeight(frame)) * 0.25;
-  rect.size.height = NSWidth(rect);
-  rect.origin.x = frame.origin.x + (NSWidth(frame) - NSWidth(rect)) / 2.0;
-  rect.origin.y = frame.origin.y + (NSHeight(frame) - NSHeight(rect)) / 2.0;
-  rect = NSIntegralRect(rect);
-
-  [[NSColor colorWithCalibratedWhite:0.0 alpha:0.42] set];
-  [[NSBezierPath bezierPathWithOvalInRect:rect] fill];
-}
-
-bool HasAttribute(id item, CellAttributesMask attributeMask) {
-  return [[item objectForKey:kCellAttributesKey] intValue] & attributeMask;
-}
-
-}  // namespace
-
-@implementation ExtensionInstallViewController
-
-@synthesize iconView = iconView_;
-@synthesize titleField = titleField_;
-@synthesize itemsField = itemsField_;
-@synthesize cancelButton = cancelButton_;
-@synthesize okButton = okButton_;
-@synthesize outlineView = outlineView_;
-@synthesize warningsSeparator = warningsSeparator_;
-@synthesize ratingStars = ratingStars_;
-@synthesize ratingCountField = ratingCountField_;
-@synthesize userCountField = userCountField_;
-@synthesize storeLinkButton = storeLinkButton_;
-
-- (id)initWithProfile:(Profile*)profile
-            navigator:(content::PageNavigator*)navigator
-             delegate:(ExtensionInstallViewDelegate*)delegate
-               prompt:(std::unique_ptr<ExtensionInstallPrompt::Prompt>)prompt {
-  // We use a different XIB in the case of installs with webstore data, or no
-  // permission warnings. These are laid out nicely for the data they display.
-  NSString* nibName = nil;
-  if (prompt->has_webstore_data()) {
-    nibName = @"ExtensionInstallPromptWebstoreData";
-  } else if (!prompt->ShouldShowPermissions() &&
-             prompt->GetRetainedFileCount() == 0 &&
-             prompt->GetRetainedDeviceCount() == 0) {
-    nibName = @"ExtensionInstallPromptNoWarnings";
-  } else {
-    nibName = @"ExtensionInstallPrompt";
-  }
-
-  if ((self = [super initWithNibName:nibName
-                              bundle:base::mac::FrameworkBundle()])) {
-    profile_ = profile;
-    navigator_ = navigator;
-    delegate_ = delegate;
-    prompt_ = std::move(prompt);
-    warnings_.reset([[self buildWarnings:*prompt_] retain]);
-  }
-  return self;
-}
-
-- (IBAction)storeLinkClicked:(id)sender {
-  GURL store_url(extension_urls::GetWebstoreItemDetailURLPrefix() +
-                 prompt_->extension()->id());
-  OpenURLParams params(store_url, Referrer(),
-                       WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                       ui::PAGE_TRANSITION_LINK, false);
-  if (navigator_) {
-    navigator_->OpenURL(params);
-  } else {
-    chrome::ScopedTabbedBrowserDisplayer displayer(profile_);
-    displayer.browser()->OpenURL(params);
-  }
-
-  delegate_->OnStoreLinkClicked();
-}
-
-- (IBAction)cancel:(id)sender {
-  delegate_->OnCancelButtonClicked();
-}
-
-- (IBAction)ok:(id)sender {
-  delegate_->OnOkButtonClicked();
-}
-
-- (void)awakeFromNib {
-  // Since linking to 10.10, |outlineView_| needs an explicit background to
-  // ensure subpixel antialiasing is enabled for the permissions text. At the
-  // same time, the animation that shows the prompt breaks whenever the scroll
-  // view is present. Giving the scroll view a layer restores the animation, and
-  // since its contents has an opaque background, subpixel AA isn't affected.
-  [[outlineView_ enclosingScrollView] setWantsLayer:YES];
-  [outlineView_ setBackgroundColor:[NSColor whiteColor]];
-
-  // Set control labels.
-  [titleField_ setStringValue:base::SysUTF16ToNSString(
-      prompt_->GetDialogTitle())];
-  NSRect okButtonRect;
-  base::string16 acceptButtonLabel = prompt_->GetAcceptButtonLabel();
-  if (!acceptButtonLabel.empty()) {
-    [okButton_ setTitle:base::SysUTF16ToNSString(acceptButtonLabel)];
-  } else {
-    [okButton_ removeFromSuperview];
-    okButtonRect = [okButton_ frame];
-    okButton_ = nil;
-  }
-  [cancelButton_ setTitle:base::SysUTF16ToNSString(
-      prompt_->GetAbortButtonLabel())];
-  if ([self hasWebstoreData]) {
-    prompt_->AppendRatingStars(AppendRatingStarsShim, self);
-    [ratingCountField_ setStringValue:base::SysUTF16ToNSString(
-        prompt_->GetRatingCount())];
-    [userCountField_ setStringValue:base::SysUTF16ToNSString(
-        prompt_->GetUserCount())];
-    [[storeLinkButton_ cell] setUnderlineBehavior:
-        hyperlink_button_cell::UnderlineBehavior::ON_HOVER];
-    [[storeLinkButton_ cell] setTextColor:
-        skia::SkColorToCalibratedNSColor(chrome_style::GetLinkColor())];
-  }
-
-  [iconView_ setImage:prompt_->icon().ToNSImage()];
-  // The icon does not add any additional information for VoiceOver beyond what
-  // the title already gives. Ignore the icon in VoiceOver.
-  ui::a11y_util::HideImageFromAccessibilityOrder(iconView_);
-
-  // The dialog is laid out in the NIB exactly how we want it assuming that
-  // each label fits on one line. However, for each label, we want to allow
-  // wrapping onto multiple lines. So we accumulate an offset by measuring how
-  // big each label wants to be, and comparing it to how big it actually is.
-  // Then we shift each label down and resize by the appropriate amount, then
-  // finally resize the window.
-  CGFloat totalOffset = 0.0;
-
-  OffsetControlVerticallyToFitContent(titleField_, &totalOffset);
-
-  if ([self hasWebstoreData]) {
-    OffsetControlVerticallyToFitContent(ratingCountField_, &totalOffset);
-    OffsetViewVerticallyToFitContent(ratingStars_, &totalOffset);
-    OffsetControlVerticallyToFitContent(userCountField_, &totalOffset);
-    OffsetControlVerticallyToFitContent(storeLinkButton_, &totalOffset);
-    NSPoint separatorOrigin = [warningsSeparator_ frame].origin;
-    separatorOrigin.y -= totalOffset;
-    [warningsSeparator_ setFrameOrigin:separatorOrigin];
-  }
-
-  // Resize |okButton_| and |cancelButton_| to fit the button labels, but keep
-  // them right-aligned.
-  NSSize buttonDelta;
-  if (okButton_) {
-    buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:okButton_];
-    if (buttonDelta.width) {
-      [okButton_ setFrame:NSOffsetRect([okButton_ frame],
-                                       -buttonDelta.width, 0)];
-      [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame],
-                                           -buttonDelta.width, 0)];
-    }
-  } else {
-    // Make |cancelButton_| right-aligned in the absence of |okButton_|.
-    NSRect cancelButtonRect = [cancelButton_ frame];
-    cancelButtonRect.origin.x =
-        NSMaxX(okButtonRect) - NSWidth(cancelButtonRect);
-    [cancelButton_ setFrame:cancelButtonRect];
-  }
-  buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:cancelButton_];
-  if (buttonDelta.width) {
-    [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame],
-                                         -buttonDelta.width, 0)];
-  }
-
-  // If there are any warnings, retained devices or retained files, then we
-  // have to do some special layout.
-  if (prompt_->ShouldShowPermissions() || prompt_->GetRetainedFileCount() > 0) {
-    NSSize spacing = [outlineView_ intercellSpacing];
-    spacing.width += 2;
-    spacing.height += 2;
-    [outlineView_ setIntercellSpacing:spacing];
-    [[[[outlineView_ tableColumns] objectAtIndex:0] dataCell] setWraps:YES];
-    for (id item in warnings_.get())
-      [self expandItemAndChildren:item];
-
-    // Adjust the outline view to fit the warnings.
-    OffsetOutlineViewVerticallyToFitContent(outlineView_, &totalOffset);
-  } else if ([self hasWebstoreData]) {
-    // Installs with webstore data that don't have a permissions section need to
-    // hide controls related to that and shrink the window by the space they
-    // take up.
-    NSRect hiddenRect = NSUnionRect([warningsSeparator_ frame],
-                                    [[outlineView_ enclosingScrollView] frame]);
-    [warningsSeparator_ setHidden:YES];
-    [[outlineView_ enclosingScrollView] setHidden:YES];
-    totalOffset -= NSHeight(hiddenRect) + kWarningsSeparatorPadding;
-  }
-
-  // If necessary, adjust the window size.
-  if (totalOffset) {
-    NSRect currentRect = [[self view] bounds];
-    currentRect.size.height += totalOffset;
-    [self updateViewFrame:currentRect];
-  }
-}
-
-- (BOOL)hasWebstoreData {
-  return prompt_->has_webstore_data();
-}
-
-- (void)appendRatingStar:(const gfx::ImageSkia*)skiaImage {
-  NSImage* image = gfx::NSImageFromImageSkiaWithColorSpace(
-      *skiaImage, base::mac::GetSystemColorSpace());
-  NSRect frame = NSMakeRect(0, 0, skiaImage->width(), skiaImage->height());
-  base::scoped_nsobject<NSImageView> view(
-      [[NSImageView alloc] initWithFrame:frame]);
-  [view setImage:image];
-
-  // Add this star after all the other ones
-  CGFloat maxStarRight = 0;
-  if ([[ratingStars_ subviews] count]) {
-    maxStarRight = NSMaxX([[[ratingStars_ subviews] lastObject] frame]);
-  }
-  NSRect starBounds = NSMakeRect(maxStarRight, 0,
-                                 skiaImage->width(), skiaImage->height());
-  [view setFrame:starBounds];
-  [ratingStars_ addSubview:view];
-}
-
-- (void)onOutlineViewRowCountDidChange {
-  // Force the outline view to update.
-  [outlineView_ reloadData];
-
-  CGFloat totalOffset = 0.0;
-  OffsetOutlineViewVerticallyToFitContent(outlineView_, &totalOffset);
-  if (totalOffset) {
-    NSRect currentRect = [[self view] bounds];
-    currentRect.size.height += totalOffset;
-    [self updateViewFrame:currentRect];
-  }
-}
-
-- (id)outlineView:(NSOutlineView*)outlineView
-            child:(NSInteger)index
-           ofItem:(id)item {
-  if (!item)
-    return [warnings_ objectAtIndex:index];
-  if ([item isKindOfClass:[NSDictionary class]])
-    return [[item objectForKey:kChildrenKey] objectAtIndex:index];
-  NOTREACHED();
-  return nil;
-}
-
-- (BOOL)outlineView:(NSOutlineView*)outlineView
-   isItemExpandable:(id)item {
-  return [self outlineView:outlineView numberOfChildrenOfItem:item] > 0;
-}
-
-- (NSInteger)outlineView:(NSOutlineView*)outlineView
-  numberOfChildrenOfItem:(id)item {
-  if (!item)
-    return [warnings_ count];
-
-  if ([item isKindOfClass:[NSDictionary class]])
-    return [[item objectForKey:kChildrenKey] count];
-
-  NOTREACHED();
-  return 0;
-}
-
-- (id)outlineView:(NSOutlineView*)outlineView
-    objectValueForTableColumn:(NSTableColumn *)tableColumn
-                       byItem:(id)item {
-  return [item objectForKey:kTitleKey];
-}
-
-- (BOOL)outlineView:(NSOutlineView *)outlineView
-   shouldExpandItem:(id)item {
-  return HasAttribute(item, kCanExpand);
-}
-
-- (void)outlineViewItemDidExpand:sender {
-  // Call via run loop to avoid animation glitches.
-  [self performSelector:@selector(onOutlineViewRowCountDidChange)
-             withObject:nil
-             afterDelay:0];
-}
-
-- (void)outlineViewItemDidCollapse:sender {
-  // Call via run loop to avoid animation glitches.
-  [self performSelector:@selector(onOutlineViewRowCountDidChange)
-             withObject:nil
-             afterDelay:0];
-}
-
-- (CGFloat)outlineView:(NSOutlineView *)outlineView
-     heightOfRowByItem:(id)item {
-  // Prevent reentrancy due to the frameOfCellAtColumn:row: call below.
-  if (isComputingRowHeight_)
-    return 1;
-  base::AutoReset<BOOL> reset(&isComputingRowHeight_, YES);
-
-  NSCell* cell = [[[outlineView_ tableColumns] objectAtIndex:0] dataCell];
-  [cell setStringValue:[item objectForKey:kTitleKey]];
-  NSRect bounds = NSZeroRect;
-  NSInteger row = [outlineView_ rowForItem:item];
-  bounds.size.width = NSWidth([outlineView_ frameOfCellAtColumn:0 row:row]);
-  bounds.size.height = kMaxControlHeight;
-
-  return [cell cellSizeForBounds:bounds].height;
-}
-
-- (BOOL)outlineView:(NSOutlineView*)outlineView
-    shouldShowOutlineCellForItem:(id)item {
-  return !HasAttribute(item, kNoExpandMarker);
-}
-
-- (BOOL)outlineView:(NSOutlineView*)outlineView
-    shouldTrackCell:(NSCell*)cell
-     forTableColumn:(NSTableColumn*)tableColumn
-               item:(id)item {
-  return HasAttribute(item, kUseCustomLinkCell);
-}
-
-- (void)outlineView:(NSOutlineView*)outlineView
-    willDisplayCell:(id)cell
-     forTableColumn:(NSTableColumn *)tableColumn
-               item:(id)item {
-  if (HasAttribute(item, kBoldText))
-    [cell setFont:[NSFont boldSystemFontOfSize:12.0]];
-  else
-    [cell setFont:[NSFont systemFontOfSize:12.0]];
-}
-
-- (void)outlineView:(NSOutlineView *)outlineView
-    willDisplayOutlineCell:(id)cell
-            forTableColumn:(NSTableColumn *)tableColumn
-                      item:(id)item {
-  if (HasAttribute(item, kNoExpandMarker)) {
-    [cell setImagePosition:NSNoImage];
-    return;
-  }
-
-  if (HasAttribute(item, kUseBullet)) {
-    // Replace disclosure triangles with bullet lists for leaf nodes.
-    [cell setImagePosition:NSNoImage];
-    DrawBulletInFrame([outlineView_ frameOfOutlineCellAtRow:
-        [outlineView_ rowForItem:item]]);
-    return;
-  }
-
-  // Reset image to default value.
-  [cell setImagePosition:NSImageOverlaps];
-}
-
-- (BOOL)outlineView:(NSOutlineView *)outlineView
-   shouldSelectItem:(id)item {
-  return false;
-}
-
-- (NSCell*)outlineView:(NSOutlineView*)outlineView
-    dataCellForTableColumn:(NSTableColumn*)tableColumn
-                  item:(id)item {
-  if (HasAttribute(item, kUseCustomLinkCell)) {
-    base::scoped_nsobject<DetailToggleHyperlinkButtonCell> cell(
-        [[DetailToggleHyperlinkButtonCell alloc] initTextCell:@""]);
-    [cell setTarget:self];
-    [cell setLinkClickedAction:@selector(onToggleDetailsLinkClicked:)];
-    [cell setAlignment:NSLeftTextAlignment];
-    [cell setUnderlineBehavior:
-        hyperlink_button_cell::UnderlineBehavior::ON_HOVER];
-    [cell setTextColor:
-        skia::SkColorToCalibratedNSColor(chrome_style::GetLinkColor())];
-
-    size_t detailsIndex =
-        [[item objectForKey:kPermissionsDetailIndex] unsignedIntegerValue];
-    [cell setPermissionsDetailIndex:detailsIndex];
-
-    ExtensionInstallPrompt::DetailsType detailsType =
-        static_cast<ExtensionInstallPrompt::DetailsType>(
-            [[item objectForKey:kPermissionsDetailType] unsignedIntegerValue]);
-    [cell setPermissionsDetailType:detailsType];
-
-    if (prompt_->GetIsShowingDetails(detailsType, detailsIndex)) {
-      [cell setTitle:
-          l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_HIDE_DETAILS)];
-    } else {
-      [cell setTitle:
-          l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_SHOW_DETAILS)];
-    }
-
-    return cell.autorelease();
-  } else {
-    return [tableColumn dataCell];
-  }
-}
-
-- (void)expandItemAndChildren:(id)item {
-  if (HasAttribute(item, kAutoExpandCell))
-    [outlineView_ expandItem:item expandChildren:NO];
-
-  for (id child in [item objectForKey:kChildrenKey])
-    [self expandItemAndChildren:child];
-}
-
-- (void)onToggleDetailsLinkClicked:(id)sender {
-  size_t index = [sender permissionsDetailIndex];
-  ExtensionInstallPrompt::DetailsType type = [sender permissionsDetailType];
-  prompt_->SetIsShowingDetails(
-      type, index, !prompt_->GetIsShowingDetails(type, index));
-
-  warnings_.reset([[self buildWarnings:*prompt_] retain]);
-  [outlineView_ reloadData];
-
-  for (id item in warnings_.get())
-    [self expandItemAndChildren:item];
-}
-
-- (NSDictionary*)buildItemWithTitle:(NSString*)title
-                     cellAttributes:(CellAttributes)cellAttributes
-                           children:(NSArray*)children {
-  if (!children || ([children count] == 0 && cellAttributes & kUseBullet)) {
-    // Add a dummy child even though this is a leaf node. This will cause
-    // the outline view to show a disclosure triangle for this item.
-    // This is later overriden in willDisplayOutlineCell: to draw a bullet
-    // instead. (The bullet could be placed in the title instead but then
-    // the bullet wouldn't line up with disclosure triangles of sibling nodes.)
-    children = [NSArray arrayWithObject:[NSDictionary dictionary]];
-  } else {
-    cellAttributes = cellAttributes | kCanExpand;
-  }
-
-  return @{
-    kTitleKey : title,
-    kChildrenKey : children,
-    kCellAttributesKey : [NSNumber numberWithInt:cellAttributes],
-    kPermissionsDetailIndex : @0ul,
-    kPermissionsDetailType : @0ul,
-  };
-}
-
-- (NSDictionary*)buildDetailToggleItem:(size_t)type
-                permissionsDetailIndex:(size_t)index {
-  return @{
-    kTitleKey : @"",
-    kChildrenKey : @[ @{} ],
-    kCellAttributesKey : [NSNumber numberWithInt:kUseCustomLinkCell |
-                                                 kNoExpandMarker],
-    kPermissionsDetailIndex : [NSNumber numberWithUnsignedInteger:index],
-    kPermissionsDetailType : [NSNumber numberWithUnsignedInteger:type],
-  };
-}
-
-- (NSArray*)buildWarnings:(const ExtensionInstallPrompt::Prompt&)prompt {
-  NSMutableArray* warnings = [NSMutableArray array];
-  NSString* heading = nil;
-
-  bool hasPermissions = prompt.GetPermissionCount(
-      ExtensionInstallPrompt::PermissionsType::ALL_PERMISSIONS);
-  CellAttributes warningCellAttributes =
-      kBoldText | kAutoExpandCell | kNoExpandMarker;
-  if (prompt.ShouldShowPermissions()) {
-    NSMutableArray* children = [NSMutableArray array];
-
-    heading =
-        [self appendPermissionsForPrompt:prompt
-                                children:children];
-
-    if (!hasPermissions) {
-      [children addObject:
-          [self buildItemWithTitle:
-              l10n_util::GetNSString(IDS_EXTENSION_NO_SPECIAL_PERMISSIONS)
-                    cellAttributes:kUseBullet
-                          children:nil]];
-      heading = @"";
-    }
-
-    if (heading) {
-      [warnings addObject:[self buildItemWithTitle:heading
-                                    cellAttributes:warningCellAttributes
-                                          children:children]];
-    }
-  }
-
-  if (prompt.GetRetainedFileCount() > 0) {
-    const ExtensionInstallPrompt::DetailsType type =
-        ExtensionInstallPrompt::RETAINED_FILES_DETAILS;
-
-    NSMutableArray* children = [NSMutableArray array];
-
-    if (prompt.GetIsShowingDetails(type, 0)) {
-      for (size_t i = 0; i < prompt.GetRetainedFileCount(); ++i) {
-        NSString* title = SysUTF16ToNSString(prompt.GetRetainedFile(i));
-        [children addObject:[self buildItemWithTitle:title
-                                      cellAttributes:kUseBullet
-                                            children:nil]];
-      }
-    }
-
-    NSString* title = SysUTF16ToNSString(prompt.GetRetainedFilesHeading());
-    [warnings addObject:[self buildItemWithTitle:title
-                                  cellAttributes:warningCellAttributes
-                                        children:children]];
-
-    // Add a row for the link.
-    [warnings addObject:
-        [self buildDetailToggleItem:type permissionsDetailIndex:0]];
-  }
-
-  if (prompt.GetRetainedDeviceCount() > 0) {
-    const ExtensionInstallPrompt::DetailsType type =
-        ExtensionInstallPrompt::RETAINED_DEVICES_DETAILS;
-
-    NSMutableArray* children = [NSMutableArray array];
-
-    if (prompt.GetIsShowingDetails(type, 0)) {
-      for (size_t i = 0; i < prompt.GetRetainedDeviceCount(); ++i) {
-        NSString* title =
-            SysUTF16ToNSString(prompt.GetRetainedDeviceMessageString(i));
-        [children addObject:[self buildItemWithTitle:title
-                                      cellAttributes:kUseBullet
-                                            children:nil]];
-      }
-    }
-
-    NSString* title = SysUTF16ToNSString(prompt.GetRetainedDevicesHeading());
-    [warnings addObject:[self buildItemWithTitle:title
-                                  cellAttributes:warningCellAttributes
-                                        children:children]];
-
-    // Add a row for the link.
-    [warnings
-        addObject:[self buildDetailToggleItem:type permissionsDetailIndex:0]];
-  }
-
-  return warnings;
-}
-
-- (NSString*)
-appendPermissionsForPrompt:(const ExtensionInstallPrompt::Prompt&)prompt
-                 children:(NSMutableArray*)children {
-  size_t permissionsCount = prompt.GetPermissionCount();
-  if (permissionsCount == 0)
-    return NULL;
-
-  for (size_t i = 0; i < permissionsCount; ++i) {
-    NSDictionary* item =
-        [self buildItemWithTitle:SysUTF16ToNSString(prompt.GetPermission(i))
-                  cellAttributes:kUseBullet
-                        children:nil];
-    [children addObject:item];
-
-    // If there are additional details, add them below this item.
-    if (!prompt.GetPermissionsDetails(i).empty()) {
-      if (prompt.GetIsShowingDetails(
-              ExtensionInstallPrompt::PERMISSIONS_DETAILS, i)) {
-        item = [self buildItemWithTitle:SysUTF16ToNSString(
-                                            prompt.GetPermissionsDetails(i))
-                         cellAttributes:kNoExpandMarker
-                               children:nil];
-        [children addObject:item];
-      }
-
-      // Add a row for the link.
-      [children addObject:
-          [self buildDetailToggleItem:type permissionsDetailIndex:i]];
-    }
-  }
-
-  return SysUTF16ToNSString(prompt.GetPermissionsHeading());
-}
-
-- (void)updateViewFrame:(NSRect)frame {
-  NSWindow* window = [[self view] window];
-  [window setFrame:[window frameRectForContentRect:frame] display:YES];
-  [[self view] setFrame:frame];
-}
-
-@end
-
-
-@implementation DetailToggleHyperlinkButtonCell
-
-@synthesize permissionsDetailIndex = permissionsDetailIndex_;
-@synthesize permissionsDetailType = permissionsDetailType_;
-@synthesize linkClickedAction = linkClickedAction_;
-
-+ (BOOL)prefersTrackingUntilMouseUp {
-  return YES;
-}
-
-- (NSRect)drawingRectForBounds:(NSRect)rect {
-  NSRect rectInset = NSMakeRect(rect.origin.x + kLinkCellPaddingLeft,
-                                rect.origin.y,
-                                rect.size.width - kLinkCellPaddingLeft,
-                                rect.size.height);
-  return [super drawingRectForBounds:rectInset];
-}
-
-- (NSUInteger)hitTestForEvent:(NSEvent*)event
-                       inRect:(NSRect)cellFrame
-                       ofView:(NSView*)controlView {
-  NSUInteger hitTestResult =
-      [super hitTestForEvent:event inRect:cellFrame ofView:controlView];
-  if ((hitTestResult & NSCellHitContentArea) != 0)
-    hitTestResult |= NSCellHitTrackableArea;
-  return hitTestResult;
-}
-
-- (void)handleLinkClicked {
-  [NSApp sendAction:linkClickedAction_ to:[self target] from:self];
-}
-
-- (BOOL)trackMouse:(NSEvent*)event
-            inRect:(NSRect)cellFrame
-            ofView:(NSView*)controlView
-      untilMouseUp:(BOOL)flag {
-  BOOL result = YES;
-  NSUInteger hitTestResult =
-      [self hitTestForEvent:event inRect:cellFrame ofView:controlView];
-  if ((hitTestResult & NSCellHitContentArea) != 0) {
-    result = [super trackMouse:event
-                        inRect:cellFrame
-                        ofView:controlView
-                  untilMouseUp:flag];
-    event = [NSApp currentEvent];
-    hitTestResult =
-        [self hitTestForEvent:event inRect:cellFrame ofView:controlView];
-    if ((hitTestResult & NSCellHitContentArea) != 0)
-      [self handleLinkClicked];
-  }
-  return result;
-}
-
-- (NSArray*)accessibilityActionNames {
-  return [[super accessibilityActionNames]
-      arrayByAddingObject:NSAccessibilityPressAction];
-}
-
-- (void)accessibilityPerformAction:(NSString*)action {
-  if ([action isEqualToString:NSAccessibilityPressAction])
-    [self handleLinkClicked];
-  else
-    [super accessibilityPerformAction:action];
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm b/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm
deleted file mode 100644
index fc11f65b..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm
+++ /dev/null
@@ -1,379 +0,0 @@
-// Copyright (c) 2012 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 "chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h"
-
-#import <Cocoa/Cocoa.h>
-
-#include <utility>
-
-#import "base/mac/scoped_nsobject.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#import "chrome/browser/extensions/extension_install_prompt.h"
-#include "chrome/browser/ui/browser.h"
-#import "chrome/browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h"
-#include "chrome/browser/ui/cocoa/test/cocoa_profile_test.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/permissions/permission_message_provider.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#import "testing/gtest_mac.h"
-
-using extensions::Extension;
-using extensions::PermissionIDSet;
-using extensions::PermissionMessage;
-using extensions::PermissionMessages;
-
-namespace {
-
-class MockExtensionInstallViewDelegate : public ExtensionInstallViewDelegate {
- public:
-  enum class Action {
-    UNDEFINED,
-    OKAY,
-    CANCEL,
-    LINK,
-  };
-
-  MockExtensionInstallViewDelegate() : action_(Action::UNDEFINED) {}
-  ~MockExtensionInstallViewDelegate() override {}
-
-  void OnOkButtonClicked() override { SetAction(Action::OKAY); }
-  void OnCancelButtonClicked() override { SetAction(Action::CANCEL); }
-  void OnStoreLinkClicked() override { SetAction(Action::LINK); }
-
-  Action action() const { return action_; }
-
- private:
-  void SetAction(Action action) {
-    if (action_ != Action::UNDEFINED)
-      ADD_FAILURE() << "SetAction() called twice!";
-    action_ = action;
-  }
-
-  Action action_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockExtensionInstallViewDelegate);
-};
-
-// Base class for our tests.
-class ExtensionInstallViewControllerTest : public CocoaProfileTest {
- public:
-  ExtensionInstallViewControllerTest() {
-    extension_ = chrome::LoadInstallPromptExtension();
-  }
-
- protected:
-  scoped_refptr<extensions::Extension> extension_;
-};
-
-}  // namespace
-
-// Test that we can load the two kinds of prompts correctly, that the outlets
-// are hooked up, and that the dialog calls cancel when cancel is pressed.
-TEST_F(ExtensionInstallViewControllerTest, BasicsNormalCancel) {
-  MockExtensionInstallViewDelegate delegate;
-
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt(
-      chrome::BuildExtensionInstallPrompt(extension_.get()));
-  ExtensionInstallPrompt::PermissionsType type =
-      ExtensionInstallPrompt::PermissionsType::REGULAR_PERMISSIONS;
-
-  PermissionMessages permissions;
-  permissions.push_back(PermissionMessage(base::UTF8ToUTF16("warning 1"),
-                                          PermissionIDSet()));
-  prompt->AddPermissions(permissions, type);
-  base::string16 permissionString = prompt->GetPermission(0, type);
-
-  base::scoped_nsobject<ExtensionInstallViewController> controller(
-      [[ExtensionInstallViewController alloc]
-          initWithProfile:profile()
-                navigator:browser()
-                 delegate:&delegate
-                   prompt:std::move(prompt)]);
-
-  [controller view];  // Force nib load.
-
-  // Test the right nib loaded.
-  EXPECT_NSEQ(@"ExtensionInstallPrompt", [controller nibName]);
-
-  // Check all the controls.
-  // Make sure everything is non-nil, and that the fields that are
-  // auto-translated don't start with a caret (that would indicate that they
-  // were not translated).
-  EXPECT_TRUE([controller iconView]);
-  EXPECT_TRUE([[controller iconView] image]);
-
-  EXPECT_TRUE([controller titleField]);
-  EXPECT_NE(0u, [[[controller titleField] stringValue] length]);
-
-  NSOutlineView* outlineView = [controller outlineView];
-  EXPECT_TRUE(outlineView);
-  EXPECT_EQ(2, [outlineView numberOfRows]);
-  EXPECT_NSEQ(base::SysUTF16ToNSString(permissionString),
-              [[outlineView dataSource] outlineView:outlineView
-                          objectValueForTableColumn:nil
-                                             byItem:[outlineView itemAtRow:1]]);
-
-  EXPECT_TRUE([controller cancelButton]);
-  EXPECT_NE(0u, [[[controller cancelButton] stringValue] length]);
-  EXPECT_NE('^', [[[controller cancelButton] stringValue] characterAtIndex:0]);
-
-  EXPECT_TRUE([controller okButton]);
-  EXPECT_NE(0u, [[[controller okButton] stringValue] length]);
-  EXPECT_NE('^', [[[controller okButton] stringValue] characterAtIndex:0]);
-
-  // Test that cancel calls our callback.
-  [controller cancel:nil];
-  EXPECT_EQ(MockExtensionInstallViewDelegate::Action::CANCEL,
-            delegate.action());
-}
-
-TEST_F(ExtensionInstallViewControllerTest, BasicsNormalOK) {
-  MockExtensionInstallViewDelegate delegate;
-
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt(
-      chrome::BuildExtensionInstallPrompt(extension_.get()));
-  ExtensionInstallPrompt::PermissionsType type =
-      ExtensionInstallPrompt::PermissionsType::REGULAR_PERMISSIONS;
-
-  PermissionMessages permissions;
-  permissions.push_back(PermissionMessage(base::UTF8ToUTF16("warning 1"),
-                                          PermissionIDSet()));
-  prompt->AddPermissions(permissions, type);
-
-  base::scoped_nsobject<ExtensionInstallViewController> controller(
-      [[ExtensionInstallViewController alloc]
-          initWithProfile:profile()
-                navigator:browser()
-                 delegate:&delegate
-                   prompt:std::move(prompt)]);
-
-  [controller view];  // Force nib load.
-  [controller ok:nil];
-
-  EXPECT_EQ(MockExtensionInstallViewDelegate::Action::OKAY,
-            delegate.action());
-}
-
-// Test that controls get repositioned when there are two warnings vs one
-// warning.
-TEST_F(ExtensionInstallViewControllerTest, MultipleWarnings) {
-  MockExtensionInstallViewDelegate delegate1;
-  MockExtensionInstallViewDelegate delegate2;
-
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> one_warning_prompt(
-      chrome::BuildExtensionInstallPrompt(extension_.get()));
-  ExtensionInstallPrompt::PermissionsType type =
-      ExtensionInstallPrompt::PermissionsType::REGULAR_PERMISSIONS;
-
-  PermissionMessages permissions;
-  permissions.push_back(PermissionMessage(base::UTF8ToUTF16("warning 1"),
-                                          PermissionIDSet()));
-  one_warning_prompt->AddPermissions(permissions, type);
-
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> two_warnings_prompt(
-      chrome::BuildExtensionInstallPrompt(extension_.get()));
-  permissions.push_back(PermissionMessage(base::UTF8ToUTF16("warning 2"),
-                                          PermissionIDSet()));
-  two_warnings_prompt->AddPermissions(permissions, type);
-
-  base::scoped_nsobject<ExtensionInstallViewController> controller1(
-      [[ExtensionInstallViewController alloc]
-          initWithProfile:profile()
-                navigator:browser()
-                 delegate:&delegate1
-                   prompt:std::move(one_warning_prompt)]);
-
-  [controller1 view];  // Force nib load.
-
-  base::scoped_nsobject<ExtensionInstallViewController> controller2(
-      [[ExtensionInstallViewController alloc]
-          initWithProfile:profile()
-                navigator:browser()
-                 delegate:&delegate2
-                   prompt:std::move(two_warnings_prompt)]);
-
-  [controller2 view];  // Force nib load.
-
-  // Test control positioning. We don't test exact positioning because we don't
-  // want this to depend on string details and localization. But we do know the
-  // relative effect that adding a second warning should have on the layout.
-  ASSERT_LT([[controller1 view] frame].size.height,
-            [[controller2 view] frame].size.height);
-
-  ASSERT_LT([[controller1 view] frame].size.height,
-            [[controller2 view] frame].size.height);
-}
-
-// Test that we can load the skinny prompt correctly, and that the outlets are
-// are hooked up.
-TEST_F(ExtensionInstallViewControllerTest, BasicsSkinny) {
-  MockExtensionInstallViewDelegate delegate;
-
-  // No warnings should trigger skinny prompt.
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> no_warnings_prompt(
-      chrome::BuildExtensionInstallPrompt(extension_.get()));
-
-  base::scoped_nsobject<ExtensionInstallViewController> controller(
-      [[ExtensionInstallViewController alloc]
-          initWithProfile:profile()
-                navigator:browser()
-                 delegate:&delegate
-                   prompt:std::move(no_warnings_prompt)]);
-
-  [controller view];  // Force nib load.
-
-  // Test the right nib loaded.
-  EXPECT_NSEQ(@"ExtensionInstallPromptNoWarnings", [controller nibName]);
-
-  // Check all the controls.
-  // In the skinny prompt, only the icon, title and buttons are non-nill.
-  // Everything else is nil.
-  EXPECT_TRUE([controller iconView]);
-  EXPECT_TRUE([[controller iconView] image]);
-
-  EXPECT_TRUE([controller titleField]);
-  EXPECT_NE(0u, [[[controller titleField] stringValue] length]);
-
-  EXPECT_TRUE([controller cancelButton]);
-  EXPECT_NE(0u, [[[controller cancelButton] stringValue] length]);
-  EXPECT_NE('^', [[[controller cancelButton] stringValue] characterAtIndex:0]);
-
-  EXPECT_TRUE([controller okButton]);
-  EXPECT_NE(0u, [[[controller okButton] stringValue] length]);
-  EXPECT_NE('^', [[[controller okButton] stringValue] characterAtIndex:0]);
-
-  EXPECT_FALSE([controller outlineView]);
-}
-
-
-// Test that we can load the inline prompt correctly, and that the outlets are
-// are hooked up.
-TEST_F(ExtensionInstallViewControllerTest, BasicsInline) {
-  MockExtensionInstallViewDelegate delegate;
-
-  // No warnings should trigger skinny prompt.
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> inline_prompt(
-      new ExtensionInstallPrompt::Prompt(
-          ExtensionInstallPrompt::INLINE_INSTALL_PROMPT));
-  inline_prompt->SetWebstoreData("1,000", true, 3.5, 200);
-  inline_prompt->set_extension(extension_.get());
-  inline_prompt->set_icon(chrome::LoadInstallPromptIcon());
-
-  base::scoped_nsobject<ExtensionInstallViewController> controller(
-      [[ExtensionInstallViewController alloc]
-          initWithProfile:profile()
-                navigator:browser()
-                 delegate:&delegate
-                   prompt:std::move(inline_prompt)]);
-
-  [controller view];  // Force nib load.
-
-  // Test the right nib loaded.
-  EXPECT_NSEQ(@"ExtensionInstallPromptWebstoreData", [controller nibName]);
-
-  // Check all the controls.
-  EXPECT_TRUE([controller iconView]);
-  EXPECT_TRUE([[controller iconView] image]);
-
-  EXPECT_TRUE([controller titleField]);
-  EXPECT_NE(0u, [[[controller titleField] stringValue] length]);
-
-  EXPECT_TRUE([controller cancelButton]);
-  EXPECT_NE(0u, [[[controller cancelButton] stringValue] length]);
-  EXPECT_NE('^', [[[controller cancelButton] stringValue] characterAtIndex:0]);
-
-  EXPECT_TRUE([controller okButton]);
-  EXPECT_NE(0u, [[[controller okButton] stringValue] length]);
-  EXPECT_NE('^', [[[controller okButton] stringValue] characterAtIndex:0]);
-
-  EXPECT_TRUE([controller ratingStars]);
-  EXPECT_EQ(5u, [[[controller ratingStars] subviews] count]);
-
-  EXPECT_TRUE([controller ratingCountField]);
-  EXPECT_NE(0u, [[[controller ratingCountField] stringValue] length]);
-
-  EXPECT_TRUE([controller userCountField]);
-  EXPECT_NE(0u, [[[controller userCountField] stringValue] length]);
-
-  EXPECT_TRUE([controller storeLinkButton]);
-  EXPECT_NE(0u, [[[controller storeLinkButton] stringValue] length]);
-  EXPECT_NE('^',
-            [[[controller storeLinkButton] stringValue] characterAtIndex:0]);
-
-  // Though we have no permissions warnings, these should still be hooked up,
-  // just invisible.
-  EXPECT_TRUE([controller outlineView]);
-  EXPECT_TRUE([[[controller outlineView] enclosingScrollView] isHidden]);
-  EXPECT_TRUE([controller warningsSeparator]);
-  EXPECT_TRUE([[controller warningsSeparator] isHidden]);
-}
-
-TEST_F(ExtensionInstallViewControllerTest, PostInstallPermissionsPrompt) {
-  MockExtensionInstallViewDelegate delegate;
-
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt(
-      chrome::BuildExtensionPostInstallPermissionsPrompt(extension_.get()));
-  ExtensionInstallPrompt::PermissionsType type =
-      ExtensionInstallPrompt::PermissionsType::REGULAR_PERMISSIONS;
-
-  PermissionMessages permissions;
-  permissions.push_back(PermissionMessage(base::UTF8ToUTF16("warning 1"),
-                                          PermissionIDSet()));
-  prompt->AddPermissions(permissions, type);
-
-  base::scoped_nsobject<ExtensionInstallViewController> controller(
-      [[ExtensionInstallViewController alloc]
-          initWithProfile:profile()
-                navigator:browser()
-                 delegate:&delegate
-                   prompt:std::move(prompt)]);
-
-  [controller view];  // Force nib load.
-
-  EXPECT_TRUE([controller cancelButton]);
-  EXPECT_FALSE([controller okButton]);
-
-  [controller cancel:nil];
-  EXPECT_EQ(MockExtensionInstallViewDelegate::Action::CANCEL,
-            delegate.action());
-}
-
-// Test that permission details show up.
-TEST_F(ExtensionInstallViewControllerTest, PermissionsDetails) {
-  MockExtensionInstallViewDelegate delegate;
-
-  std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt(
-      chrome::BuildExtensionInstallPrompt(extension_.get()));
-  ExtensionInstallPrompt::PermissionsType type =
-      ExtensionInstallPrompt::PermissionsType::REGULAR_PERMISSIONS;
-
-  PermissionMessages permissions;
-  permissions.push_back(PermissionMessage(
-      base::UTF8ToUTF16("warning 1"),
-      PermissionIDSet(),
-      std::vector<base::string16>(1, base::UTF8ToUTF16("Detail 1"))));
-  prompt->AddPermissions(permissions, type);
-  prompt->SetIsShowingDetails(
-      ExtensionInstallPrompt::PERMISSIONS_DETAILS, 0, true);
-  base::string16 permissionString = prompt->GetPermissionsDetails(0, type);
-
-  base::scoped_nsobject<ExtensionInstallViewController> controller(
-      [[ExtensionInstallViewController alloc]
-          initWithProfile:profile()
-                navigator:browser()
-                 delegate:&delegate
-                   prompt:std::move(prompt)]);
-
-  [controller view];  // Force nib load.
-
-  NSOutlineView* outlineView = [controller outlineView];
-  EXPECT_TRUE(outlineView);
-  EXPECT_EQ(4, [outlineView numberOfRows]);
-  EXPECT_NSEQ(base::SysUTF16ToNSString(permissionString),
-              [[outlineView dataSource] outlineView:outlineView
-                          objectValueForTableColumn:nil
-                                             byItem:[outlineView itemAtRow:2]]);
-}
diff --git a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.h b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.h
deleted file mode 100644
index 80817c62..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALLED_BUBBLE_CONTROLLER_H_
-#define CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALLED_BUBBLE_CONTROLLER_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/mac/scoped_nsobject.h"
-#import "chrome/browser/ui/cocoa/base_bubble_controller.h"
-
-class Browser;
-class ExtensionInstalledBubble;
-@class HyperlinkTextView;
-@class HoverCloseButton;
-@class BubbleSyncPromoController;
-
-namespace extensions {
-class Extension;
-}
-
-namespace extension_installed_bubble {
-
-// Maximum height or width of extension's icon (corresponds to Windows & GTK).
-const int kIconSize = 43;
-
-// Outer vertical margin for text, icon, and closing x.
-const int kOuterVerticalMargin = 15;
-
-// Inner vertical margin for text messages.
-const int kInnerVerticalMargin = 10;
-
-// An offset we apply to position the point of the bubble's arrow pointing at
-// the NewTabButtonCocoa.
-const int kAppsBubbleArrowOffset = 4;
-
-// We use a different kind of notification for each of these extension types.
-enum ExtensionType {
-  kApp,
-  kExtension,
-};
-
-}  // namespace extension_installed_bubble
-
-// Controller for the extension installed bubble.  This bubble pops up after
-// an extension has been installed to inform the user that the install happened
-// properly, and to let the user know how to manage this extension in the
-// future.
-@interface ExtensionInstalledBubbleController : BaseBubbleController {
- @private
-  const extensions::Extension* extension_;  // weak
-  Browser* browser_;  // weak
-  base::scoped_nsobject<NSImage> icon_;
-
-  extension_installed_bubble::ExtensionType type_;
-
-  // A weak reference to the bubble. It's owned by the BubbleManager.
-  ExtensionInstalledBubble* installedBubble_;
-
-  // The controller for the sync promo.
-  base::scoped_nsobject<BubbleSyncPromoController> syncPromoController_;
-
-  // References below are weak, being obtained from the nib.
-  IBOutlet HoverCloseButton* closeButton_;
-  IBOutlet NSImageView* iconImage_;
-  IBOutlet NSTextField* heading_;
-  // Only shown for browser actions, page actions and omnibox keywords.
-  IBOutlet NSTextField* howToUse_;
-  IBOutlet NSTextField* howToManage_;
-  // Only shown for app installs.
-  IBOutlet NSButton* appShortcutLink_;
-  // Only shown for extensions with commands.
-  IBOutlet NSButton* manageShortcutLink_;
-  // Only shown if the sign-in promo is active.
-  IBOutlet NSView* promoContainer_;
-}
-
-@property(nonatomic, readonly) ExtensionInstalledBubble* installedBubble;
-@property(nonatomic, readonly) NSView* heading;
-@property(nonatomic, readonly) NSView* closeButton;
-@property(nonatomic, readonly) NSView* howToUse;
-@property(nonatomic, readonly) NSView* howToManage;
-@property(nonatomic, readonly) NSView* appInstalledShortcutLink;
-@property(nonatomic, readonly) NSView* manageShortcutLink;
-@property(nonatomic, readonly) NSView* promoContainer;
-@property(nonatomic, readonly) NSView* iconImage;
-
-// Initialize the window. It will be shown by the BubbleManager.
-- (id)initWithParentWindow:(NSWindow*)parentWindow
-           extensionBubble:(ExtensionInstalledBubble*)extensionBubble;
-
-// Action for close button.
-- (IBAction)closeWindow:(id)sender;
-
-// Displays the extension installed bubble. This callback is triggered by
-// the extensionObserver when the extension has completed loading.
-- (void)showWindow:(id)sender;
-
-// Opens the shortcut configuration UI.
-- (IBAction)onManageShortcutClicked:(id)sender;
-
-// Shows the new app installed animation.
-- (IBAction)onAppShortcutClicked:(id)sender;
-
-// Should be called by the extension bridge to close this window.
-- (void)doClose;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_INSTALLED_BUBBLE_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm
deleted file mode 100644
index 5043545..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm
+++ /dev/null
@@ -1,458 +0,0 @@
-// Copyright (c) 2012 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 "chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.h"
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/i18n/rtl.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/extensions/extension_action.h"
-#include "chrome/browser/extensions/extension_action_manager.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_navigator.h"
-#include "chrome/browser/ui/browser_navigator_params.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/bubble_anchor_util.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/cocoa/browser_dialogs_views_mac.h"
-#include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
-#include "chrome/browser/ui/cocoa/browser_window_controller.h"
-#include "chrome/browser/ui/cocoa/bubble_anchor_helper_views.h"
-#import "chrome/browser/ui/cocoa/bubble_sync_promo_controller.h"
-#include "chrome/browser/ui/cocoa/chrome_style.h"
-#include "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h"
-#include "chrome/browser/ui/cocoa/hover_close_button.h"
-#include "chrome/browser/ui/cocoa/info_bubble_view.h"
-#include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
-#include "chrome/browser/ui/cocoa/new_tab_button.h"
-#include "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
-#include "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
-#include "chrome/browser/ui/extensions/extension_install_ui_factory.h"
-#include "chrome/browser/ui/extensions/extension_installed_bubble.h"
-#include "chrome/browser/ui/singleton_tabs.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
-#include "chrome/common/extensions/api/omnibox/omnibox_handler.h"
-#include "chrome/common/extensions/sync_helper.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/chromium_strings.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/bubble/bubble_controller.h"
-#include "components/bubble/bubble_ui.h"
-#include "components/signin/core/browser/signin_metrics.h"
-#include "content/public/browser/browser_thread.h"
-#include "extensions/browser/install/extension_install_ui.h"
-#include "extensions/common/extension.h"
-#import "skia/ext/skia_utils_mac.h"
-#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/cocoa/cocoa_base_utils.h"
-#import "ui/base/cocoa/controls/hyperlink_text_view.h"
-#include "ui/base/l10n/l10n_util.h"
-#import "ui/gfx/mac/coordinate_conversion.h"
-
-using content::BrowserThread;
-using extensions::Extension;
-
-@interface ExtensionInstalledBubbleController ()
-
-- (const Extension*)extension;
-- (void)windowWillClose:(NSNotification*)notification;
-- (void)windowDidResignKey:(NSNotification*)notification;
-- (NSPoint)calculateArrowPoint;
-- (NSWindow*)initializeWindow;
-- (int)calculateWindowHeight;
-- (void)setMessageFrames:(int)newWindowHeight;
-- (void)updateAnchorPosition;
-
-@end  // ExtensionInstalledBubbleController ()
-
-namespace {
-
-class ExtensionInstalledBubbleBridge : public BubbleUi {
- public:
-  explicit ExtensionInstalledBubbleBridge(
-      ExtensionInstalledBubbleController* controller);
-  ~ExtensionInstalledBubbleBridge() override;
-
- private:
-  // BubbleUi:
-  void Show(BubbleReference bubble_reference) override;
-  void Close() override;
-  void UpdateAnchorPosition() override;
-
-  // Weak reference to the controller. |controller_| will outlive the bridge.
-  ExtensionInstalledBubbleController* controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(ExtensionInstalledBubbleBridge);
-};
-
-ExtensionInstalledBubbleBridge::ExtensionInstalledBubbleBridge(
-    ExtensionInstalledBubbleController* controller)
-    : controller_(controller) {
-}
-
-ExtensionInstalledBubbleBridge::~ExtensionInstalledBubbleBridge() {
-}
-
-void ExtensionInstalledBubbleBridge::Show(BubbleReference bubble_reference) {
-  [controller_ setBubbleReference:bubble_reference];
-  [controller_ showWindow:controller_];
-}
-
-void ExtensionInstalledBubbleBridge::Close() {
-  [controller_ doClose];
-}
-
-void ExtensionInstalledBubbleBridge::UpdateAnchorPosition() {
-  [controller_ updateAnchorPosition];
-}
-
-}  // namespace
-
-// Cocoa specific implementation.
-bool ExtensionInstalledBubble::ShouldShow() {
-  return true;
-}
-
-gfx::Point ExtensionInstalledBubble::GetAnchorPoint(
-    gfx::NativeWindow window) const {
-  return bubble_anchor_util::GetExtensionInstalledAnchorPointCocoa(window,
-                                                                   this);
-}
-
-// Implemented here to create the platform specific instance of the BubbleUi.
-std::unique_ptr<BubbleUi> ExtensionInstalledBubble::BuildBubbleUi() {
-  if (chrome::ShowAllDialogsWithViewsToolkit())
-    return chrome::BuildViewsExtensionInstalledBubbleUi(this);
-
-  // |controller| is owned by the parent window.
-  ExtensionInstalledBubbleController* controller =
-      [[ExtensionInstalledBubbleController alloc]
-          initWithParentWindow:browser()->window()->GetNativeWindow()
-               extensionBubble:this];
-
-  // The bridge to the C++ object that performs shared logic across platforms.
-  // This tells the controller when to show the bubble.
-  return base::WrapUnique(new ExtensionInstalledBubbleBridge(controller));
-}
-
-@implementation ExtensionInstalledBubbleController
-
-@synthesize installedBubble = installedBubble_;
-// Exposed for unit tests.
-@synthesize heading = heading_;
-@synthesize closeButton = closeButton_;
-@synthesize howToUse = howToUse_;
-@synthesize howToManage = howToManage_;
-@synthesize appInstalledShortcutLink = appInstalledShortcutLink_;
-@synthesize manageShortcutLink = manageShortcutLink_;
-@synthesize promoContainer = promoContainer_;
-@synthesize iconImage = iconImage_;
-
-- (id)initWithParentWindow:(NSWindow*)parentWindow
-           extensionBubble:(ExtensionInstalledBubble*)extensionBubble {
-  if ((self = [super initWithWindowNibPath:@"ExtensionInstalledBubble"
-                              parentWindow:parentWindow
-                                anchoredAt:NSZeroPoint])) {
-    DCHECK(extensionBubble);
-    const extensions::Extension* extension = extensionBubble->extension();
-    browser_ = extensionBubble->browser();
-    DCHECK(browser_);
-    icon_.reset([skia::SkBitmapToNSImage(extensionBubble->icon()) retain]);
-
-    type_ = extension->is_app() ? extension_installed_bubble::kApp :
-        extension_installed_bubble::kExtension;
-
-    installedBubble_ = extensionBubble;
-  }
-  return self;
-}
-
-- (const Extension*)extension {
-  if (!installedBubble_)
-    return nullptr;
-  return installedBubble_->extension();
-}
-
-- (void)windowWillClose:(NSNotification*)notification {
-  // Turn off page action icon preview when the window closes, unless we
-  // already removed it when the window resigned key status.
-  browser_ = nullptr;
-  [closeButton_ setTrackingEnabled:NO];
-  [super windowWillClose:notification];
-}
-
-// The controller is the delegate of the window, so it receives "did resign
-// key" notifications.  When key is resigned, close the window.
-- (void)windowDidResignKey:(NSNotification*)notification {
-  // If the browser window is closing, we need to remove the page action
-  // immediately, otherwise the closing animation may overlap with
-  // browser destruction.
-  [super windowDidResignKey:notification];
-}
-
-- (IBAction)closeWindow:(id)sender {
-  DCHECK([[self window] isVisible]);
-  DCHECK([self bubbleReference]);
-  bool didClose =
-      [self bubbleReference]->CloseBubble(BUBBLE_CLOSE_USER_DISMISSED);
-  DCHECK(didClose);
-}
-
-// The extension installed bubble points at the browser action icon or the
-// page action icon (shown as a preview), depending on the extension type.
-// We need to calculate the location of these icons and the size of the
-// message itself (which varies with the title of the extension) in order
-// to figure out the origin point for the extension installed bubble.
-- (NSPoint)calculateArrowPoint {
-  BrowserWindowCocoa* window =
-      static_cast<BrowserWindowCocoa*>(browser_->window());
-  if (type_ == extension_installed_bubble::kApp) {
-    TabStripView* view = [window->cocoa_controller() tabStripView];
-    NewTabButtonCocoa* button = [view getNewTabButton];
-    NSRect bounds = [button bounds];
-    NSPoint anchor = NSMakePoint(
-        NSMidX(bounds),
-        NSMaxY(bounds) - extension_installed_bubble::kAppsBubbleArrowOffset);
-    return ui::ConvertPointFromWindowToScreen(
-        window->GetNativeWindow(), [button convertPoint:anchor toView:nil]);
-  }
-
-  DCHECK(installedBubble_);
-  return gfx::ScreenPointToNSPoint(
-      installedBubble_->GetAnchorPoint(window->GetNativeWindow()));
-}
-
-// Override -[BaseBubbleController showWindow:] to tweak bubble location and
-// set up UI elements.
-- (void)showWindow:(id)sender {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // Load nib and calculate height based on messages to be shown.
-  NSWindow* window = [self initializeWindow];
-  int newWindowHeight = [self calculateWindowHeight];
-  [self.bubble setFrameSize:NSMakeSize(
-      NSWidth([[window contentView] bounds]), newWindowHeight)];
-  NSSize windowDelta = NSMakeSize(
-      0, newWindowHeight - NSHeight([[window contentView] bounds]));
-  windowDelta = [[window contentView] convertSize:windowDelta toView:nil];
-  NSRect newFrame = [window frame];
-  newFrame.size.height += windowDelta.height;
-  [window setFrame:newFrame display:NO];
-
-  // Now that we have resized the window, adjust y pos of the messages.
-  [self setMessageFrames:newWindowHeight];
-
-  // Find window origin, taking into account bubble size and arrow location.
-  [self updateAnchorPosition];
-
-  if (syncPromoController_) {
-    signin_metrics::RecordSigninImpressionUserActionForAccessPoint(
-        signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE);
-  }
-  [super showWindow:sender];
-}
-
-// Finish nib loading, set arrow location and load icon into window.  This
-// function is exposed for unit testing.
-- (NSWindow*)initializeWindow {
-  NSWindow* window = [self window];  // completes nib load
-
-  if (installedBubble_ &&
-      installedBubble_->anchor_position() ==
-          ExtensionInstalledBubble::ANCHOR_OMNIBOX) {
-    [self.bubble setArrowLocation:info_bubble::kTopLeading];
-  } else {
-    [self.bubble setArrowLocation:info_bubble::kTopTrailing];
-  }
-
-  // Set appropriate icon, resizing if necessary.
-  if ([icon_ size].width > extension_installed_bubble::kIconSize) {
-    [icon_ setSize:NSMakeSize(extension_installed_bubble::kIconSize,
-                              extension_installed_bubble::kIconSize)];
-  }
-  [iconImage_ setImage:icon_];
-  [iconImage_ setNeedsDisplay:YES];
-  return window;
-}
-
-// Calculate the height of each install message, resizing messages in their
-// frames to fit window width.  Return the new window height, based on the
-// total of all message heights.
-- (int)calculateWindowHeight {
-  // Adjust the window height to reflect the sum height of all messages
-  // and vertical padding.
-  // If there's few enough messages, the icon area may be larger than the
-  // messages.
-  int contentColumnHeight =
-      2 * extension_installed_bubble::kOuterVerticalMargin;
-  int iconColumnHeight = 2 * extension_installed_bubble::kOuterVerticalMargin +
-                         NSHeight([iconImage_ frame]);
-
-  CGFloat syncPromoHeight = 0;
-  if (installedBubble_->options() & ExtensionInstalledBubble::SIGN_IN_PROMO) {
-    signin_metrics::AccessPoint accessPoint =
-       signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE;
-    syncPromoController_.reset(
-        [[BubbleSyncPromoController alloc]
-            initWithBrowser:browser_
-              promoStringId:IDS_EXTENSION_INSTALLED_SYNC_PROMO_NEW
-               linkStringId:IDS_EXTENSION_INSTALLED_SYNC_PROMO_LINK_NEW
-                accessPoint:accessPoint]);
-    [promoContainer_ addSubview:[syncPromoController_ view]];
-
-    // Resize the sync promo and its placeholder.
-    NSRect syncPromoPlaceholderFrame = [promoContainer_ frame];
-    CGFloat windowWidth = NSWidth([[self bubble] frame]);
-    syncPromoPlaceholderFrame.size.width = windowWidth;
-    syncPromoHeight =
-        [syncPromoController_ preferredHeightForWidth:windowWidth];
-    syncPromoPlaceholderFrame.size.height = syncPromoHeight;
-
-    [promoContainer_ setFrame:syncPromoPlaceholderFrame];
-    [[syncPromoController_ view] setFrame:syncPromoPlaceholderFrame];
-  } else {
-    [promoContainer_ setHidden:YES];
-  }
-
-  // First part of extension installed message, the heading.
-  base::string16 extension_name =
-      base::UTF8ToUTF16([self extension]->name().c_str());
-  base::i18n::AdjustStringForLocaleDirection(&extension_name);
-  [heading_ setStringValue:l10n_util::GetNSStringF(
-      IDS_EXTENSION_INSTALLED_HEADING, extension_name)];
-  [GTMUILocalizerAndLayoutTweaker
-      sizeToFitFixedWidthTextField:heading_];
-  contentColumnHeight += NSHeight([heading_ frame]);
-
-  if (installedBubble_->options() & ExtensionInstalledBubble::HOW_TO_USE) {
-    [howToUse_ setStringValue:base::SysUTF16ToNSString(
-         installedBubble_->GetHowToUseDescription())];
-    [howToUse_ setHidden:NO];
-    [[howToUse_ cell]
-        setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
-    [GTMUILocalizerAndLayoutTweaker
-        sizeToFitFixedWidthTextField:howToUse_];
-    contentColumnHeight += NSHeight([howToUse_ frame]) +
-        extension_installed_bubble::kInnerVerticalMargin;
-  }
-
-  // If type is app, hide howToManage_, and include a "show me" link in the
-  // bubble.
-  if (type_ == extension_installed_bubble::kApp) {
-    [howToManage_ setHidden:YES];
-    [appShortcutLink_ setHidden:NO];
-    contentColumnHeight += 2 * extension_installed_bubble::kInnerVerticalMargin;
-    contentColumnHeight += NSHeight([appShortcutLink_ frame]);
-  } else if (installedBubble_->options() &
-                 ExtensionInstalledBubble::HOW_TO_MANAGE) {
-    // Second part of extension installed message.
-    [[howToManage_ cell]
-        setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
-    [GTMUILocalizerAndLayoutTweaker
-        sizeToFitFixedWidthTextField:howToManage_];
-    contentColumnHeight += NSHeight([howToManage_ frame]) +
-        extension_installed_bubble::kInnerVerticalMargin;
-  } else {
-    [howToManage_ setHidden:YES];
-  }
-
-  // Sync sign-in promo, if any.
-  if (syncPromoHeight > 0) {
-    // The sync promo goes at the bottom of the window and includes its own
-    // bottom margin. Thus, we subtract off the one of the outer margins, and
-    // apply it to both the icon area and content area.
-    int syncPromoDelta = extension_installed_bubble::kInnerVerticalMargin +
-                         syncPromoHeight -
-                         extension_installed_bubble::kOuterVerticalMargin;
-    contentColumnHeight += syncPromoDelta;
-    iconColumnHeight += syncPromoDelta;
-  }
-
-  if (installedBubble_->options() & ExtensionInstalledBubble::SHOW_KEYBINDING) {
-    [manageShortcutLink_ setHidden:NO];
-    [[manageShortcutLink_ cell]
-        setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
-    [[manageShortcutLink_ cell]
-        setTextColor:skia::SkColorToCalibratedNSColor(
-            chrome_style::GetLinkColor())];
-    [GTMUILocalizerAndLayoutTweaker sizeToFitView:manageShortcutLink_];
-    contentColumnHeight += extension_installed_bubble::kInnerVerticalMargin;
-    contentColumnHeight += NSHeight([manageShortcutLink_ frame]);
-  }
-
-  return std::max(contentColumnHeight, iconColumnHeight);
-}
-
-// Adjust y-position of messages to sit properly in new window height.
-- (void)setMessageFrames:(int)newWindowHeight {
-  NSRect headingFrame = [heading_ frame];
-  headingFrame.origin.y = newWindowHeight - (
-      NSHeight(headingFrame) +
-      extension_installed_bubble::kOuterVerticalMargin);
-  [heading_ setFrame:headingFrame];
-  int nextY = NSMinY(headingFrame);
-
-  auto adjustView = [](NSView* view, int* nextY) {
-    DCHECK(nextY);
-    NSRect frame = [view frame];
-    frame.origin.y = *nextY -
-        (NSHeight(frame) + extension_installed_bubble::kInnerVerticalMargin);
-    [view setFrame:frame];
-    *nextY = NSMinY(frame);
-  };
-
-  if (installedBubble_->options() & ExtensionInstalledBubble::HOW_TO_USE)
-    adjustView(howToUse_, &nextY);
-
-  if (installedBubble_->options() & ExtensionInstalledBubble::HOW_TO_MANAGE)
-    adjustView(howToManage_, &nextY);
-
-  if (installedBubble_->options() & ExtensionInstalledBubble::SHOW_KEYBINDING)
-    adjustView(manageShortcutLink_, &nextY);
-
-  if (installedBubble_->options() & ExtensionInstalledBubble::SIGN_IN_PROMO) {
-    // The sync promo goes at the bottom of the bubble, but that might be
-    // different than directly below the previous content if the icon is larger
-    // than the messages. Workaround by just always setting nextY to be at the
-    // bottom.
-    nextY = NSHeight([promoContainer_ frame]) +
-            extension_installed_bubble::kInnerVerticalMargin;
-    adjustView(promoContainer_, &nextY);
-  }
-}
-
-- (void)updateAnchorPosition {
-  self.anchorPoint = [self calculateArrowPoint];
-}
-
-- (IBAction)onManageShortcutClicked:(id)sender {
-  DCHECK([self bubbleReference]);
-  bool didClose = [self bubbleReference]->CloseBubble(BUBBLE_CLOSE_ACCEPTED);
-  DCHECK(didClose);
-  std::string configure_url = chrome::kChromeUIExtensionsURL;
-  configure_url += chrome::kExtensionConfigureCommandsSubPage;
-  NavigateParams params(
-      GetSingletonTabNavigateParams(browser_, GURL(configure_url)));
-  Navigate(&params);
-}
-
-- (IBAction)onAppShortcutClicked:(id)sender {
-  std::unique_ptr<extensions::ExtensionInstallUI> install_ui(
-      extensions::CreateExtensionInstallUI(browser_->profile()));
-  install_ui->OpenAppInstalledUI([self extension]->id());
-}
-
-- (void)doClose {
-  installedBubble_ = nullptr;
-  [self close];
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller_unittest.mm b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller_unittest.mm
deleted file mode 100644
index b6e35bf..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller_unittest.mm
+++ /dev/null
@@ -1,367 +0,0 @@
-// Copyright (c) 2012 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 "chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.h"
-
-#import <Cocoa/Cocoa.h>
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/macros.h"
-#include "base/path_service.h"
-#include "base/values.h"
-#include "chrome/browser/extensions/api/commands/command_service.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/test_extension_system.h"
-#include "chrome/browser/ui/browser_window.h"
-#import "chrome/browser/ui/cocoa/info_bubble_window.h"
-#import "chrome/browser/ui/cocoa/test/cocoa_profile_test.h"
-#include "chrome/browser/ui/extensions/extension_installed_bubble.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/crx_file/id_util.h"
-#include "content/public/browser/site_instance.h"
-#include "content/public/browser/web_contents.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_builder.h"
-#include "extensions/common/feature_switch.h"
-#include "extensions/common/manifest_constants.h"
-#include "extensions/common/value_builder.h"
-#import "third_party/ocmock/OCMock/OCMock.h"
-#include "third_party/ocmock/gtest_support.h"
-#include "ui/gfx/codec/png_codec.h"
-
-using extensions::Extension;
-using extensions::DictionaryBuilder;
-
-class ExtensionInstalledBubbleControllerTest : public CocoaProfileTest {
- protected:
-  ExtensionInstalledBubbleControllerTest() {}
-  ~ExtensionInstalledBubbleControllerTest() override {}
-
-  enum ExtensionType {
-    BROWSER_ACTION,
-    PAGE_ACTION,
-    APP,
-  };
-
-  void SetUp() override {
-    CocoaProfileTest::SetUp();
-    ASSERT_TRUE(browser());
-    window_ = browser()->window()->GetNativeWindow();
-    icon_ = LoadTestIcon();
-    base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
-    extensionService_ = static_cast<extensions::TestExtensionSystem*>(
-        extensions::ExtensionSystem::Get(profile()))->CreateExtensionService(
-            &command_line, base::FilePath(), false);
-  }
-
-  // Adds a WebContents to the tab strip.
-  void AddWebContents() {
-    std::unique_ptr<content::WebContents> web_contents = base::WrapUnique(
-        content::WebContents::Create(content::WebContents::CreateParams(
-            profile(), content::SiteInstance::Create(profile()))));
-    browser()->tab_strip_model()->AppendWebContents(std::move(web_contents),
-                                                    true);
-  }
-
-  // Create a simple extension of the given |type| and manifest |location|, and
-  // optionally with an associated keybinding.
-  void CreateExtension(ExtensionType type,
-                       bool has_keybinding,
-                       extensions::Manifest::Location location) {
-    DictionaryBuilder manifest;
-    manifest.Set("version", "1.0");
-    manifest.Set("name", "extension");
-    manifest.Set("manifest_version", 2);
-    switch (type) {
-      case PAGE_ACTION:
-        manifest.Set("page_action", DictionaryBuilder().Build());
-        break;
-      case BROWSER_ACTION:
-        manifest.Set("browser_action", DictionaryBuilder().Build());
-        break;
-      case APP:
-        manifest.Set(
-            "app",
-            DictionaryBuilder()
-                .Set("launch", DictionaryBuilder()
-                                   .Set("web_url", "http://www.example.com")
-                                   .Build())
-                .Build());
-        break;
-    }
-
-    if (has_keybinding) {
-      DictionaryBuilder command;
-      command.Set(type == PAGE_ACTION ? "_execute_page_action"
-                                      : "_execute_browser_action",
-                  DictionaryBuilder()
-                      .Set("suggested_key", DictionaryBuilder()
-                                                .Set("mac", "MacCtrl+Shift+E")
-                                                .Set("default", "Ctrl+Shift+E")
-                                                .Build())
-                      .Build());
-      manifest.Set("commands", command.Build());
-    }
-
-    extension_ = extensions::ExtensionBuilder()
-                     .SetManifest(manifest.Build())
-                     .SetID(crx_file::id_util::GenerateId("foo"))
-                     .SetLocation(location)
-                     .Build();
-    extensionService_->AddExtension(extension_.get());
-    if (has_keybinding) {
-      // Slight hack: manually notify the command service of the extension since
-      // it doesn't go through the normal installation flow.
-      extensions::CommandService::Get(profile())->UpdateKeybindingsForTest(
-          extension_.get());
-    }
-  }
-  void CreateExtension(ExtensionType type, bool has_keybinding) {
-    CreateExtension(type, has_keybinding, extensions::Manifest::INTERNAL);
-  }
-
-  // Create and return an ExtensionInstalledBubbleController and instruct it to
-  // show itself.
-  ExtensionInstalledBubbleController* CreateController() {
-    extensionBubble_.reset(
-        new ExtensionInstalledBubble(extension_.get(), browser(), icon_));
-    extensionBubble_->Initialize();
-    ExtensionInstalledBubbleController* controller =
-        [[ExtensionInstalledBubbleController alloc]
-            initWithParentWindow:window_
-                 extensionBubble:extensionBubble_.get()];
-
-    // Bring up the window and disable close animation.
-    [controller showWindow:nil];
-    NSWindow* bubbleWindow = [controller window];
-    CHECK([bubbleWindow isKindOfClass:[InfoBubbleWindow class]]);
-    [static_cast<InfoBubbleWindow*>(bubbleWindow)
-        setAllowedAnimations:info_bubble::kAnimateNone];
-
-    return controller;
-  }
-
-  NSWindow* window() { return window_; }
-
- private:
-  // Load test icon from extension test directory.
-  SkBitmap LoadTestIcon() {
-    base::FilePath path;
-    base::PathService::Get(chrome::DIR_TEST_DATA, &path);
-    path = path.AppendASCII("extensions").AppendASCII("icon1.png");
-
-    std::string file_contents;
-    base::ReadFileToString(path, &file_contents);
-    const unsigned char* data =
-        reinterpret_cast<const unsigned char*>(file_contents.data());
-
-    SkBitmap bitmap;
-    gfx::PNGCodec::Decode(data, file_contents.length(), &bitmap);
-    return bitmap;
-  }
-
-  // Required to initialize the extension installed bubble.
-  NSWindow* window_;  // weak, owned by CocoaProfileTest.
-
-  // The associated ExtensionService, owned by the ExtensionSystem.
-  ExtensionService* extensionService_;
-
-  // Skeleton extension to be tested; reinitialized for each test.
-  scoped_refptr<Extension> extension_;
-
-  // The bubble that tests are run on.
-  std::unique_ptr<ExtensionInstalledBubble> extensionBubble_;
-
-  // The icon_ to be loaded into the bubble window.
-  SkBitmap icon_;
-
-  DISALLOW_COPY_AND_ASSIGN(ExtensionInstalledBubbleControllerTest);
-};
-
-// We don't want to just test the bounds of these frames, because that results
-// in a change detector test (and just duplicates the logic in the class).
-// Instead, we do a few sanity checks.
-void SanityCheckFrames(NSRect frames[], size_t size) {
-  for (size_t i = 0; i < size; ++i) {
-    // Check 1: Non-hidden views should have a non-empty frame.
-    EXPECT_FALSE(NSIsEmptyRect(frames[i])) <<
-        "Frame at index " << i << " is empty";
-    // Check 2: No frames should overlap.
-    for (size_t j = 0; j < i; ++j) {
-      EXPECT_FALSE(NSIntersectsRect(frames[i], frames[j])) <<
-          "Frame at index " << i << " intersects frame at index " << j;
-    }
-  }
-}
-
-// Test the basic layout of the bubble for an extension that is from the store.
-TEST_F(ExtensionInstalledBubbleControllerTest,
-       BubbleLayoutFromStoreNoKeybinding) {
-  CreateExtension(BROWSER_ACTION, false);
-  ExtensionInstalledBubbleController* controller = CreateController();
-  ASSERT_TRUE(controller);
-
-  // The extension bubble should have the "how to use", "how to manage", and
-  // "sign in promo" areas. Since it doesn't have an associated keybinding, it
-  // shouldn't have the "manage shortcut" view.
-  EXPECT_FALSE([[controller howToUse] isHidden]);
-  EXPECT_FALSE([[controller howToManage] isHidden]);
-  EXPECT_FALSE([[controller promoContainer] isHidden]);
-  EXPECT_TRUE([[controller manageShortcutLink] isHidden]);
-
-  NSRect headingFrame = [[controller heading] frame];
-  NSRect closeFrame = [[controller closeButton] frame];
-  NSRect howToUseFrame = [[controller howToUse] frame];
-  NSRect howToManageFrame = [[controller howToManage] frame];
-  NSRect syncPromoFrame = [[controller promoContainer] frame];
-  NSRect iconFrame = [[controller iconImage] frame];
-
-  NSRect frames[] = {headingFrame, closeFrame, howToUseFrame, howToManageFrame,
-                     syncPromoFrame, iconFrame};
-  SanityCheckFrames(frames, arraysize(frames));
-
-  // Check the overall layout of the bubble; it should be:
-  // |------| | Heading        |
-  // | icon | | How to Use     |
-  // |------| | How to Manage  |
-  // |-------------------------|
-  // |       Sync Promo        |
-  // |-------------------------|
-  EXPECT_GT(NSMinY(headingFrame), NSMinY(howToUseFrame));
-  EXPECT_GT(NSMinY(howToUseFrame), NSMinY(howToManageFrame));
-  EXPECT_GT(NSMinY(howToManageFrame), NSMinY(syncPromoFrame));
-  EXPECT_GT(NSMinY(iconFrame), NSMinY(syncPromoFrame));
-  EXPECT_GT(NSMinY(iconFrame), 0);
-  EXPECT_EQ(NSMinY(syncPromoFrame), 0);
-
-  [controller close];
-}
-
-// Test the layout of a bubble for an extension that is from the store with an
-// associated keybinding.
-TEST_F(ExtensionInstalledBubbleControllerTest,
-       BubbleLayoutFromStoreWithKeybinding) {
-  CreateExtension(BROWSER_ACTION, true);
-  ExtensionInstalledBubbleController* controller = CreateController();
-  ASSERT_TRUE(controller);
-
-  // Since the extension has a keybinding, the "how to manage" section is
-  // hidden. The other fields are present.
-  EXPECT_FALSE([[controller howToUse] isHidden]);
-  EXPECT_TRUE([[controller howToManage] isHidden]);
-  EXPECT_FALSE([[controller manageShortcutLink] isHidden]);
-  EXPECT_FALSE([[controller promoContainer] isHidden]);
-
-  NSRect headingFrame = [[controller heading] frame];
-  NSRect closeFrame = [[controller closeButton] frame];
-  NSRect howToUseFrame = [[controller howToUse] frame];
-  NSRect manageShortcutFrame = [[controller manageShortcutLink] frame];
-  NSRect syncPromoFrame = [[controller promoContainer] frame];
-  NSRect iconFrame = [[controller iconImage] frame];
-
-  NSRect frames[] = {headingFrame, closeFrame, howToUseFrame,
-                     manageShortcutFrame, syncPromoFrame, iconFrame};
-  SanityCheckFrames(frames, arraysize(frames));
-
-  // Layout should be:
-  // |------| | Heading         |
-  // | icon | | How to Use      |
-  // |------| | Manage shortcut |
-  // |--------------------------|
-  // |       Sync Promo         |
-  // |--------------------------|
-  EXPECT_GT(NSMinY(headingFrame), NSMinY(howToUseFrame));
-  EXPECT_GT(NSMinY(howToUseFrame), NSMinY(manageShortcutFrame));
-  EXPECT_GT(NSMinY(manageShortcutFrame), NSMinY(syncPromoFrame));
-  EXPECT_GT(NSMinY(iconFrame), NSMinY(syncPromoFrame));
-  EXPECT_GT(NSMinY(iconFrame), 0);
-  EXPECT_EQ(NSMinY(syncPromoFrame), 0);
-
-  [controller close];
-}
-
-// Test the layout of a bubble for an unpacked extension (which is not
-// syncable).
-TEST_F(ExtensionInstalledBubbleControllerTest,
-       BubbleLayoutBrowserActionUnpacked) {
-  CreateExtension(BROWSER_ACTION, true, extensions::Manifest::UNPACKED);
-  ExtensionInstalledBubbleController* controller = CreateController();
-  ASSERT_TRUE(controller);
-
-  // The extension has a keybinding (so the "how to manage" view is hidden) and
-  // is an unpacked extension (so the "sign in promo" view is also hidden).
-  EXPECT_FALSE([[controller howToUse] isHidden]);
-  EXPECT_TRUE([[controller howToManage] isHidden]);
-  EXPECT_TRUE([[controller promoContainer] isHidden]);
-  EXPECT_FALSE([[controller manageShortcutLink] isHidden]);
-
-  NSRect headingFrame = [[controller heading] frame];
-  NSRect closeFrame = [[controller closeButton] frame];
-  NSRect howToUseFrame = [[controller howToUse] frame];
-  NSRect howToManageFrame = [[controller howToManage] frame];
-  NSRect iconFrame = [[controller iconImage] frame];
-
-  NSRect frames[] = {headingFrame, closeFrame, howToUseFrame, howToManageFrame,
-                     iconFrame};
-  SanityCheckFrames(frames, arraysize(frames));
-
-  // Layout should be:
-  // |------| | Heading         |
-  // | icon | | How to Use      |
-  // |------| | Manage shortcut |
-  EXPECT_FALSE(NSIntersectsRect(howToUseFrame, howToManageFrame));
-  EXPECT_GT(NSMinY(headingFrame), NSMinY(howToManageFrame));
-  EXPECT_GT(NSMinY(howToUseFrame), NSMinY(howToManageFrame));
-  EXPECT_GT(NSMinY(iconFrame), 0);
-}
-
-TEST_F(ExtensionInstalledBubbleControllerTest, ParentClose) {
-  CreateExtension(BROWSER_ACTION, false);
-  ExtensionInstalledBubbleController* controller = CreateController();
-  EXPECT_TRUE(controller);
-
-  NSWindow* bubbleWindow = [controller window];
-
-  // Observe whether the bubble window closes.
-  NSString* notification = NSWindowWillCloseNotification;
-  id observer = [OCMockObject observerMock];
-  [[observer expect] notificationWithName:notification object:bubbleWindow];
-  [[NSNotificationCenter defaultCenter]
-    addMockObserver:observer name:notification object:bubbleWindow];
-
-  // The bubble window goes from visible to not-visible.
-  EXPECT_TRUE([bubbleWindow isVisible]);
-  [window() close];
-  EXPECT_FALSE([bubbleWindow isVisible]);
-
-  [[NSNotificationCenter defaultCenter] removeObserver:observer];
-
-  // Check that the appropriate notification was received.
-  EXPECT_OCMOCK_VERIFY(observer);
-}
-
-TEST_F(ExtensionInstalledBubbleControllerTest, AppTest) {
-  CreateExtension(APP, false);
-  ExtensionInstalledBubbleController* controller = CreateController();
-  EXPECT_TRUE(controller);
-
-  int height = NSHeight([[controller window] frame]);
-
-  // Make sure there is always enough room for the icon and margin.
-  int minHeight = extension_installed_bubble::kIconSize +
-    (2 * extension_installed_bubble::kOuterVerticalMargin);
-  EXPECT_GT(height, minHeight);
-
-  // Make sure the "show me" link is visible.
-  EXPECT_FALSE([[controller appInstalledShortcutLink] isHidden]);
-
-  [controller close];
-}
diff --git a/chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller.h b/chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller.h
deleted file mode 100644
index 9cbae5e..0000000
--- a/chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_EXTENSIONS_WINDOWED_INSTALL_DIALOG_CONTROLLER_H_
-#define CHROME_BROWSER_UI_COCOA_EXTENSIONS_WINDOWED_INSTALL_DIALOG_CONTROLLER_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/gtest_prod_util.h"
-#include "base/mac/scoped_nsobject.h"
-#include "base/macros.h"
-#include "chrome/browser/extensions/extension_install_prompt.h"
-#import "chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h"
-
-class ExtensionInstallPromptShowParams;
-@class ExtensionInstallViewController;
-@class WindowedInstallController;
-
-// Displays an app or extension install or permissions prompt as a standalone
-// NSPanel.
-class WindowedInstallDialogController : public ExtensionInstallViewDelegate {
- public:
-  // Initializes the ExtensionInstallViewController and shows the window. This
-  // object will delete itself when the window is closed.
-  WindowedInstallDialogController(
-      ExtensionInstallPromptShowParams* show_params,
-      const ExtensionInstallPrompt::DoneCallback& callback,
-      std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt);
-  ~WindowedInstallDialogController() override;
-
-  // Invoked by the -[NSWindow windowWillClose:] notification after a dialog
-  // choice is invoked. Releases owned resources, then deletes |this|.
-  void OnWindowClosing();
-
-  // ExtensionInstallViewDelegate:
-  void OnOkButtonClicked() override;
-  void OnCancelButtonClicked() override;
-  void OnStoreLinkClicked() override;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(WindowedInstallDialogControllerBrowserTest,
-                           ShowInstallDialog);
-  ExtensionInstallViewController* GetViewController();
-
-  ExtensionInstallPrompt::DoneCallback done_callback_;
-  base::scoped_nsobject<WindowedInstallController> install_controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(WindowedInstallDialogController);
-};
-
-#endif  // CHROME_BROWSER_UI_COCOA_EXTENSIONS_WINDOWED_INSTALL_DIALOG_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller.mm b/chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller.mm
deleted file mode 100644
index 62f7a5a..0000000
--- a/chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller.mm
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller.h"
-
-#include <utility>
-
-#import "base/callback_helpers.h"
-#import "base/mac/sdk_forward_declarations.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/extensions/extension_install_prompt_show_params.h"
-#include "chrome/browser/profiles/profile.h"
-#import "chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h"
-#include "content/public/browser/web_contents.h"
-#include "ui/base/cocoa/window_size_constants.h"
-
-@interface WindowedInstallController
-    : NSWindowController<NSWindowDelegate> {
- @private
-  base::scoped_nsobject<ExtensionInstallViewController> installViewController_;
-  WindowedInstallDialogController* dialogController_;  // Weak. Owns us.
-}
-
-@property(readonly, nonatomic) ExtensionInstallViewController* viewController;
-
-- (id)initWithProfile:(Profile*)profile
-            navigator:(content::PageNavigator*)navigator
-             delegate:(WindowedInstallDialogController*)delegate
-               prompt:(std::unique_ptr<ExtensionInstallPrompt::Prompt>)prompt;
-
-@end
-
-WindowedInstallDialogController::WindowedInstallDialogController(
-    ExtensionInstallPromptShowParams* show_params,
-    const ExtensionInstallPrompt::DoneCallback& done_callback,
-    std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt)
-    : done_callback_(done_callback) {
-  install_controller_.reset([[WindowedInstallController alloc]
-      initWithProfile:show_params->profile()
-            navigator:show_params->GetParentWebContents()
-             delegate:this
-               prompt:std::move(prompt)]);
-  [[install_controller_ window] makeKeyAndOrderFront:nil];
-}
-
-WindowedInstallDialogController::~WindowedInstallDialogController() {
-  DCHECK(!install_controller_);
-  DCHECK(done_callback_.is_null());
-}
-
-void WindowedInstallDialogController::OnWindowClosing() {
-  install_controller_.reset();
-  if (!done_callback_.is_null()) {
-    base::ResetAndReturn(&done_callback_).Run(
-        ExtensionInstallPrompt::Result::ABORTED);
-  }
-  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
-}
-
-ExtensionInstallViewController*
-WindowedInstallDialogController::GetViewController() {
-  return [install_controller_ viewController];
-}
-
-void WindowedInstallDialogController::OnOkButtonClicked() {
-  base::ResetAndReturn(&done_callback_).Run(
-      ExtensionInstallPrompt::Result::ACCEPTED);
-  [[install_controller_ window] close];
-}
-
-void WindowedInstallDialogController::OnCancelButtonClicked() {
-  base::ResetAndReturn(&done_callback_).Run(
-      ExtensionInstallPrompt::Result::USER_CANCELED);
-  [[install_controller_ window] close];
-}
-
-void WindowedInstallDialogController::OnStoreLinkClicked() {
-  base::ResetAndReturn(&done_callback_).Run(
-      ExtensionInstallPrompt::Result::USER_CANCELED);
-  [[install_controller_ window] close];
-}
-
-@implementation WindowedInstallController
-
-- (id)initWithProfile:(Profile*)profile
-            navigator:(content::PageNavigator*)navigator
-             delegate:(WindowedInstallDialogController*)delegate
-               prompt:(std::unique_ptr<ExtensionInstallPrompt::Prompt>)prompt {
-  base::scoped_nsobject<NSWindow> controlledPanel(
-      [[NSPanel alloc] initWithContentRect:ui::kWindowSizeDeterminedLater
-                                 styleMask:NSTitledWindowMask
-                                   backing:NSBackingStoreBuffered
-                                     defer:NO]);
-  if ((self = [super initWithWindow:controlledPanel])) {
-    dialogController_ = delegate;
-    ExtensionInstallPrompt::Prompt* weakPrompt = prompt.get();
-    installViewController_.reset([[ExtensionInstallViewController alloc]
-        initWithProfile:profile
-              navigator:navigator
-               delegate:delegate
-                 prompt:std::move(prompt)]);
-    NSWindow* window = [self window];
-
-    // Ensure the window does not display behind the app launcher window, and is
-    // otherwise hard to lose behind other windows (since it is not modal).
-    [window setLevel:NSDockWindowLevel];
-
-    // Animate the window when ordered in, the same way as an NSAlert.
-    if ([window respondsToSelector:@selector(setAnimationBehavior:)])
-      [window setAnimationBehavior:NSWindowAnimationBehaviorAlertPanel];
-
-    [window setTitle:base::SysUTF16ToNSString(weakPrompt->GetDialogTitle())];
-    NSRect viewFrame = [[installViewController_ view] frame];
-    [window setFrame:[window frameRectForContentRect:viewFrame]
-             display:NO];
-    [window setContentView:[installViewController_ view]];
-    [window setDelegate:self];
-    [window center];
-  }
-  return self;
-}
-
-- (ExtensionInstallViewController*)viewController {
-  return installViewController_;
-}
-
-- (void)windowWillClose:(NSNotification*)notification {
-  [[self window] setDelegate:nil];
-  dialogController_->OnWindowClosing();
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller_browsertest.mm b/chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller_browsertest.mm
deleted file mode 100644
index bda2d0b..0000000
--- a/chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller_browsertest.mm
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller.h"
-
-#include <utility>
-
-#include "base/run_loop.h"
-#include "chrome/browser/extensions/extension_install_prompt_test_helper.h"
-#include "chrome/browser/ui/browser.h"
-#import "chrome/browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h"
-#import "chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/test/test_utils.h"
-#include "extensions/common/extension.h"
-
-namespace {
-
-// Similar to ShowExtensionInstallDialogImpl except this allows the created
-// dialog controller to be captured and manipulated for tests.
-void TestingShowAppListInstallDialogController(
-    WindowedInstallDialogController** controller,
-    ExtensionInstallPromptShowParams* show_params,
-    const ExtensionInstallPrompt::DoneCallback& done_callback,
-    std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt) {
-  *controller =
-      new WindowedInstallDialogController(show_params, done_callback,
-                                          std::move(prompt));
-}
-
-typedef InProcessBrowserTest WindowedInstallDialogControllerBrowserTest;
-
-}  // namespace
-
-// Test for showing an extension install prompt with no parent WebContents.
-IN_PROC_BROWSER_TEST_F(WindowedInstallDialogControllerBrowserTest,
-                       ShowInstallDialog) {
-  // Construct a prompt with a NULL parent window, the way ExtensionEnableFlow
-  // will for the Mac app list. For testing, sets a NULL PageNavigator as well.
-  std::unique_ptr<ExtensionInstallPrompt> prompt(
-      new ExtensionInstallPrompt(browser()->profile(), NULL));
-
-  WindowedInstallDialogController* controller = NULL;
-  ExtensionInstallPromptTestHelper test_helper;
-  scoped_refptr<extensions::Extension> extension =
-      chrome::LoadInstallPromptExtension("permissions", "many-apis.json");
-  prompt->ShowDialog(
-      test_helper.GetCallback(), extension.get(), nullptr,
-      base::Bind(&TestingShowAppListInstallDialogController, &controller));
-
-  // The prompt needs to load the image, which happens on the blocking pool.
-  content::RunAllTasksUntilIdle();
-  ASSERT_TRUE(controller);
-
-  base::scoped_nsobject<NSWindow> window(
-      [[[controller->GetViewController() view] window] retain]);
-  EXPECT_TRUE([window isVisible]);
-  EXPECT_TRUE([window delegate]);
-  EXPECT_FALSE(test_helper.has_result());
-
-  // Press cancel to close the window.
-  [[controller->GetViewController() cancelButton] performClick:nil];
-  EXPECT_FALSE([window delegate]);
-  EXPECT_EQ(ExtensionInstallPrompt::Result::USER_CANCELED,
-            test_helper.result());
-
-  // Ensure the window is closed.
-  EXPECT_FALSE([window isVisible]);
-}
diff --git a/chrome/browser/ui/search/local_ntp_one_google_bar_browsertest.cc b/chrome/browser/ui/search/local_ntp_one_google_bar_browsertest.cc
index 8cb32cc..d16cea69 100644
--- a/chrome/browser/ui/search/local_ntp_one_google_bar_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_one_google_bar_browsertest.cc
@@ -59,8 +59,7 @@
 
  private:
   void SetUp() override {
-    feature_list_.InitWithFeatures(
-        {features::kUseGoogleLocalNtp, features::kOneGoogleBarOnLocalNtp}, {});
+    feature_list_.InitWithFeatures({features::kUseGoogleLocalNtp}, {});
     InProcessBrowserTest::SetUp();
   }
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index f99066b..9f6fe14 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -647,10 +647,8 @@
 
 void BrowserNonClientFrameViewAsh::ChildPreferredSizeChanged(
     views::View* child) {
-  // TODO(estade): Do we need this in a world where Ash provides the header?
-  if (IsMash())
-    return;
-
+  // This is required even when Ash provides the header (as in Mash) for the
+  // |hosted_app_button_container_|.
   if (browser_view()->initialized()) {
     InvalidateLayout();
     frame()->GetRootView()->Layout();
diff --git a/chrome/browser/ui/views/hover_button.cc b/chrome/browser/ui/views/hover_button.cc
index 66d3d6f..4612b43 100644
--- a/chrome/browser/ui/views/hover_button.cc
+++ b/chrome/browser/ui/views/hover_button.cc
@@ -72,6 +72,7 @@
       icon_view_(nullptr),
       secondary_icon_view_(nullptr),
       listener_(button_listener) {
+  SetInstallFocusRingOnFocus(false);
   SetFocusBehavior(FocusBehavior::ALWAYS);
   SetFocusPainter(nullptr);
 
diff --git a/chrome/browser/ui/webui/media/webrtc_logs_ui.cc b/chrome/browser/ui/webui/media/webrtc_logs_ui.cc
index e1f8d88..748f45c 100644
--- a/chrome/browser/ui/webui/media/webrtc_logs_ui.cc
+++ b/chrome/browser/ui/webui/media/webrtc_logs_ui.cc
@@ -191,8 +191,8 @@
 
   base::Value version(version_info::GetVersionNumber());
 
-  web_ui()->CallJavascriptFunctionUnsafe("updateWebRtcLogsList", upload_list,
-                                         version);
+  AllowJavascript();
+  CallJavascriptFunction("updateWebRtcLogsList", upload_list, version);
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
index a9a50cf..7a93363 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
@@ -18,33 +18,11 @@
 namespace {
 
 const char kPageContentDataModeKey[] = "mode";
-const char kPageContentDataHostDeviceKey[] = "hostDevice";
-
-const char kRemoteDeviceNameKey[] = "name";
-const char kRemoteDeviceSoftwareFeaturesKey[] = "softwareFeatures";
-
-std::unique_ptr<base::DictionaryValue> GeneratePageContentDataDictionary(
-    multidevice_setup::mojom::HostStatus host_status,
-    const base::Optional<cryptauth::RemoteDeviceRef>& host_device) {
-  auto page_content_dictionary = std::make_unique<base::DictionaryValue>();
-  page_content_dictionary->SetInteger(kPageContentDataModeKey,
-                                      static_cast<int32_t>(host_status));
-
-  if (host_device) {
-    auto device_dictionary = std::make_unique<base::DictionaryValue>();
-    device_dictionary->SetString(kRemoteDeviceNameKey, host_device->name());
-
-    // TODO(khorimoto): Send actual feature dictionary. Currently, an empty
-    // dictionary is passed.
-    device_dictionary->SetDictionary(kRemoteDeviceSoftwareFeaturesKey,
-                                     std::make_unique<base::DictionaryValue>());
-
-    page_content_dictionary->SetDictionary(kPageContentDataHostDeviceKey,
-                                           std::move(device_dictionary));
-  }
-
-  return page_content_dictionary;
-}
+const char kPageContentDataHostDeviceNameKey[] = "hostDeviceName";
+const char kPageContentDataBetterTogetherStateKey[] = "betterTogetherState";
+const char kPageContentDataInstantTetheringStateKey[] = "instantTetheringState";
+const char kPageContentDataMessagesStateKey[] = "messagesState";
+const char kPageContentDataSmartLockStateKey[] = "smartLockState";
 
 void OnRetrySetHostNowResult(bool success) {
   if (success)
@@ -93,9 +71,36 @@
 void MultideviceHandler::OnHostStatusChanged(
     multidevice_setup::mojom::HostStatus host_status,
     const base::Optional<cryptauth::RemoteDeviceRef>& host_device) {
-  FireWebUIListener(
-      "settings.updateMultidevicePageContentData",
-      *GeneratePageContentDataDictionary(host_status, host_device));
+  last_host_status_update_ = std::make_pair(host_status, host_device);
+  AttemptUpdatePageContent();
+}
+
+void MultideviceHandler::OnFeatureStatesChanged(
+    const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
+        feature_states_map) {
+  last_feature_states_update_ = feature_states_map;
+  AttemptUpdatePageContent();
+}
+
+void MultideviceHandler::AttemptGetPageContentResponse(
+    const std::string& js_callback_id) {
+  std::unique_ptr<base::DictionaryValue> page_content_dictionary =
+      GeneratePageContentDataDictionary();
+  if (!page_content_dictionary)
+    return;
+
+  ResolveJavascriptCallback(base::Value(js_callback_id),
+                            *page_content_dictionary);
+}
+
+void MultideviceHandler::AttemptUpdatePageContent() {
+  std::unique_ptr<base::DictionaryValue> page_content_dictionary =
+      GeneratePageContentDataDictionary();
+  if (!page_content_dictionary)
+    return;
+
+  FireWebUIListener("settings.updateMultidevicePageContentData",
+                    *page_content_dictionary);
 }
 
 void MultideviceHandler::HandleShowMultiDeviceSetupDialog(
@@ -113,9 +118,17 @@
   bool result = args->GetString(0, &callback_id);
   DCHECK(result);
 
+  if (last_host_status_update_ && last_feature_states_update_) {
+    AttemptGetPageContentResponse(callback_id);
+    return;
+  }
+
   multidevice_setup_client_->GetHostStatus(
       base::BindOnce(&MultideviceHandler::OnHostStatusFetched,
                      callback_weak_ptr_factory_.GetWeakPtr(), callback_id));
+  multidevice_setup_client_->GetFeatureStates(
+      base::BindOnce(&MultideviceHandler::OnFeatureStatesFetched,
+                     callback_weak_ptr_factory_.GetWeakPtr(), callback_id));
 }
 
 void MultideviceHandler::HandleRetryPendingHostSetup(
@@ -129,9 +142,56 @@
     const std::string& js_callback_id,
     multidevice_setup::mojom::HostStatus host_status,
     const base::Optional<cryptauth::RemoteDeviceRef>& host_device) {
-  ResolveJavascriptCallback(
-      base::Value(js_callback_id),
-      *GeneratePageContentDataDictionary(host_status, host_device));
+  last_host_status_update_ = std::make_pair(host_status, host_device);
+  AttemptGetPageContentResponse(js_callback_id);
+}
+
+void MultideviceHandler::OnFeatureStatesFetched(
+    const std::string& js_callback_id,
+    const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
+        feature_states_map) {
+  last_feature_states_update_ = feature_states_map;
+  AttemptGetPageContentResponse(js_callback_id);
+}
+
+std::unique_ptr<base::DictionaryValue>
+MultideviceHandler::GeneratePageContentDataDictionary() {
+  // Cannot generate page contents without all required data.
+  if (!last_host_status_update_ || !last_feature_states_update_)
+    return nullptr;
+
+  auto page_content_dictionary = std::make_unique<base::DictionaryValue>();
+
+  page_content_dictionary->SetInteger(
+      kPageContentDataModeKey,
+      static_cast<int32_t>(last_host_status_update_->first));
+  page_content_dictionary->SetInteger(
+      kPageContentDataBetterTogetherStateKey,
+      static_cast<int32_t>(
+          (*last_feature_states_update_)
+              [multidevice_setup::mojom::Feature::kBetterTogetherSuite]));
+  page_content_dictionary->SetInteger(
+      kPageContentDataInstantTetheringStateKey,
+      static_cast<int32_t>(
+          (*last_feature_states_update_)
+              [multidevice_setup::mojom::Feature::kInstantTethering]));
+  page_content_dictionary->SetInteger(
+      kPageContentDataMessagesStateKey,
+      static_cast<int32_t>((*last_feature_states_update_)
+                               [multidevice_setup::mojom::Feature::kMessages]));
+  page_content_dictionary->SetInteger(
+      kPageContentDataSmartLockStateKey,
+      static_cast<int32_t>(
+          (*last_feature_states_update_)
+              [multidevice_setup::mojom::Feature::kSmartLock]));
+
+  if (last_host_status_update_->second) {
+    page_content_dictionary->SetString(
+        kPageContentDataHostDeviceNameKey,
+        last_host_status_update_->second->name());
+  }
+
+  return page_content_dictionary;
 }
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
index 571820a5..4f93a0eb 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
@@ -39,6 +39,17 @@
   void OnHostStatusChanged(
       multidevice_setup::mojom::HostStatus host_status,
       const base::Optional<cryptauth::RemoteDeviceRef>& host_device) override;
+  void OnFeatureStatesChanged(
+      const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
+          feature_states_map) override;
+
+  // Attempts to send the most recent PageContentData dictionary to the WebUI
+  // page as a response to a getPageContent() request.
+  void AttemptGetPageContentResponse(const std::string& js_callback_id);
+
+  // Attempts to send the most recent PageContentData dictionary to the WebUI
+  // page as an update (e.g., not due to a getPageCOntent() request.
+  void AttemptUpdatePageContent();
 
   void HandleShowMultiDeviceSetupDialog(const base::ListValue* args);
   void HandleGetPageContent(const base::ListValue* args);
@@ -48,6 +59,15 @@
       const std::string& js_callback_id,
       multidevice_setup::mojom::HostStatus host_status,
       const base::Optional<cryptauth::RemoteDeviceRef>& host_device);
+  void OnFeatureStatesFetched(
+      const std::string& js_callback_id,
+      const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
+          feature_states_map);
+
+  // Returns null if requisite data has not yet been fetched (i.e., if one or
+  // both of |last_host_status_update_| and |last_feature_states_update_| is
+  // null).
+  std::unique_ptr<base::DictionaryValue> GeneratePageContentDataDictionary();
 
   multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
 
@@ -55,6 +75,12 @@
                  multidevice_setup::MultiDeviceSetupClient::Observer>
       multidevice_setup_observer_;
 
+  base::Optional<std::pair<multidevice_setup::mojom::HostStatus,
+                           base::Optional<cryptauth::RemoteDeviceRef>>>
+      last_host_status_update_;
+  base::Optional<multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap>
+      last_feature_states_update_;
+
   // Used to cancel callbacks when JavaScript becomes disallowed.
   base::WeakPtrFactory<MultideviceHandler> callback_weak_ptr_factory_;
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
index 9dd5dbf..363c22f 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
@@ -34,7 +34,9 @@
 void VerifyPageContentDict(
     const base::Value* value,
     multidevice_setup::mojom::HostStatus expected_host_status,
-    const base::Optional<cryptauth::RemoteDeviceRef>& expected_host_device) {
+    const base::Optional<cryptauth::RemoteDeviceRef>& expected_host_device,
+    const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
+        feature_states_map) {
   const base::DictionaryValue* page_content_dict;
   EXPECT_TRUE(value->GetAsDictionary(&page_content_dict));
 
@@ -42,16 +44,39 @@
   EXPECT_TRUE(page_content_dict->GetInteger("mode", &mode));
   EXPECT_EQ(static_cast<int>(expected_host_status), mode);
 
-  if (expected_host_device) {
-    const base::DictionaryValue* remote_device_dict;
-    EXPECT_TRUE(
-        page_content_dict->GetDictionary("hostDevice", &remote_device_dict));
+  int better_together_state;
+  EXPECT_TRUE(page_content_dict->GetInteger("betterTogetherState",
+                                            &better_together_state));
+  auto it = feature_states_map.find(
+      multidevice_setup::mojom::Feature::kBetterTogetherSuite);
+  EXPECT_EQ(static_cast<int>(it->second), better_together_state);
 
-    std::string name;
-    EXPECT_TRUE(remote_device_dict->GetString("name", &name));
-    EXPECT_EQ(expected_host_device->name(), name);
+  int instant_tethering_state;
+  EXPECT_TRUE(page_content_dict->GetInteger("instantTetheringState",
+                                            &instant_tethering_state));
+  it = feature_states_map.find(
+      multidevice_setup::mojom::Feature::kInstantTethering);
+  EXPECT_EQ(static_cast<int>(it->second), instant_tethering_state);
+
+  int messages_state;
+  EXPECT_TRUE(page_content_dict->GetInteger("messagesState", &messages_state));
+  it = feature_states_map.find(multidevice_setup::mojom::Feature::kMessages);
+  EXPECT_EQ(static_cast<int>(it->second), messages_state);
+
+  int smart_lock_state;
+  EXPECT_TRUE(
+      page_content_dict->GetInteger("smartLockState", &smart_lock_state));
+  it = feature_states_map.find(multidevice_setup::mojom::Feature::kSmartLock);
+  EXPECT_EQ(static_cast<int>(it->second), smart_lock_state);
+
+  std::string host_device_name;
+  if (expected_host_device) {
+    EXPECT_TRUE(
+        page_content_dict->GetString("hostDeviceName", &host_device_name));
+    EXPECT_EQ(expected_host_device->name(), host_device_name);
   } else {
-    EXPECT_FALSE(page_content_dict->GetDictionary("hostDevice", nullptr));
+    EXPECT_FALSE(
+        page_content_dict->GetString("hostDeviceName", &host_device_name));
   }
 }
 
@@ -77,21 +102,40 @@
     handler_->AllowJavascript();
   }
 
-  void CallGetPageContentData(
-      multidevice_setup::mojom::HostStatus expected_host_status,
-      const base::Optional<cryptauth::RemoteDeviceRef>& expected_host_device) {
+  void SetPageContent(
+      multidevice_setup::mojom::HostStatus host_status,
+      const base::Optional<cryptauth::RemoteDeviceRef>& host_device,
+      const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
+          feature_states_map) {
+    current_host_status_with_device_ = std::make_pair(host_status, host_device);
+    current_feature_states_map_ = feature_states_map;
+  }
+
+  void CallGetPageContentData(bool expected_to_request_data_from_device_sync) {
+    EXPECT_TRUE(current_host_status_with_device_);
+    EXPECT_TRUE(current_host_status_with_device_);
+
     size_t call_data_count_before_call = test_web_ui()->call_data().size();
 
     base::ListValue args;
     args.AppendString("handlerFunctionName");
     test_web_ui()->HandleReceivedMessage("getPageContentData", &args);
 
-    // The callback did not complete yet, so no call should have been made.
-    EXPECT_EQ(call_data_count_before_call, test_web_ui()->call_data().size());
+    if (expected_to_request_data_from_device_sync) {
+      // The callback did not complete yet, so no call should have been made.
+      EXPECT_EQ(call_data_count_before_call, test_web_ui()->call_data().size());
 
-    // Invoke the callback; this should trigger the event to be sent to JS.
-    fake_multidevice_setup_client()->InvokePendingGetHostStatusCallback(
-        expected_host_status, expected_host_device);
+      // Invoke the host status callback.
+      fake_multidevice_setup_client()->InvokePendingGetHostStatusCallback(
+          current_host_status_with_device_->first /* host_status */,
+          current_host_status_with_device_->second /* host_device */);
+
+      // Invoke the feature states callback; this should trigger the event to be
+      // sent to JS.
+      fake_multidevice_setup_client()->InvokePendingGetFeatureStatesCallback(
+          *current_feature_states_map_);
+    }
+
     EXPECT_EQ(call_data_count_before_call + 1u,
               test_web_ui()->call_data().size());
 
@@ -100,13 +144,13 @@
     EXPECT_EQ("cr.webUIResponse", call_data.function_name());
     EXPECT_EQ("handlerFunctionName", call_data.arg1()->GetString());
     EXPECT_TRUE(call_data.arg2()->GetBool());
-    VerifyPageContentDict(call_data.arg3(), expected_host_status,
-                          expected_host_device);
+    VerifyPageContent(call_data.arg3());
   }
 
   void SimulateHostStatusUpdate(
       multidevice_setup::mojom::HostStatus host_status,
       const base::Optional<cryptauth::RemoteDeviceRef>& host_device) {
+    current_host_status_with_device_ = std::make_pair(host_status, host_device);
     size_t call_data_count_before_call = test_web_ui()->call_data().size();
 
     fake_multidevice_setup_client_->NotifyHostStatusChanged(host_status,
@@ -119,7 +163,26 @@
     EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
     EXPECT_EQ("settings.updateMultidevicePageContentData",
               call_data.arg1()->GetString());
-    VerifyPageContentDict(call_data.arg2(), host_status, host_device);
+    VerifyPageContent(call_data.arg2());
+  }
+
+  void SimulateFeatureStatesUpdate(
+      const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
+          feature_states_map) {
+    current_feature_states_map_ = feature_states_map;
+    size_t call_data_count_before_call = test_web_ui()->call_data().size();
+
+    fake_multidevice_setup_client_->NotifyFeatureStateChanged(
+        feature_states_map);
+    EXPECT_EQ(call_data_count_before_call + 1u,
+              test_web_ui()->call_data().size());
+
+    const content::TestWebUI::CallData& call_data =
+        CallDataAtIndex(call_data_count_before_call);
+    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
+    EXPECT_EQ("settings.updateMultidevicePageContentData",
+              call_data.arg1()->GetString());
+    VerifyPageContent(call_data.arg2());
   }
 
   void CallRetryPendingHostSetup(bool success) {
@@ -143,34 +206,45 @@
   const cryptauth::RemoteDeviceRef test_device_;
 
  private:
+  void VerifyPageContent(const base::Value* value) {
+    VerifyPageContentDict(value, current_host_status_with_device_->first,
+                          current_host_status_with_device_->second,
+                          *current_feature_states_map_);
+  }
+
   std::unique_ptr<content::TestWebUI> test_web_ui_;
   std::unique_ptr<multidevice_setup::FakeMultiDeviceSetupClient>
       fake_multidevice_setup_client_;
   std::unique_ptr<TestMultideviceHandler> handler_;
 
+  base::Optional<std::pair<multidevice_setup::mojom::HostStatus,
+                           base::Optional<cryptauth::RemoteDeviceRef>>>
+      current_host_status_with_device_;
+  base::Optional<multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap>
+      current_feature_states_map_;
+
   DISALLOW_COPY_AND_ASSIGN(MultideviceHandlerTest);
 };
 
 TEST_F(MultideviceHandlerTest, PageContentData) {
-  CallGetPageContentData(multidevice_setup::mojom::HostStatus::kNoEligibleHosts,
-                         base::nullopt /* host_device */);
-  CallGetPageContentData(
-      multidevice_setup::mojom::HostStatus::kEligibleHostExistsButNoHostSet,
-      base::nullopt /* host_device */);
-  CallGetPageContentData(multidevice_setup::mojom::HostStatus::
-                             kHostSetLocallyButWaitingForBackendConfirmation,
-                         test_device_);
-  CallGetPageContentData(
-      multidevice_setup::mojom::HostStatus::kHostSetButNotYetVerified,
-      test_device_);
-  CallGetPageContentData(multidevice_setup::mojom::HostStatus::kHostVerified,
-                         test_device_);
-}
+  static multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
+      feature_states_map{
+          {multidevice_setup::mojom::Feature::kBetterTogetherSuite,
+           multidevice_setup::mojom::FeatureState::kUnavailableNoVerifiedHost},
+          {multidevice_setup::mojom::Feature::kInstantTethering,
+           multidevice_setup::mojom::FeatureState::kUnavailableNoVerifiedHost},
+          {multidevice_setup::mojom::Feature::kMessages,
+           multidevice_setup::mojom::FeatureState::kUnavailableNoVerifiedHost},
+          {multidevice_setup::mojom::Feature::kSmartLock,
+           multidevice_setup::mojom::FeatureState::kUnavailableNoVerifiedHost}};
+  // multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
+  //     feature_states_map = GenerateUnverifiedFeatureStatesMap();
 
-TEST_F(MultideviceHandlerTest, HostStatusUpdates) {
-  SimulateHostStatusUpdate(
-      multidevice_setup::mojom::HostStatus::kNoEligibleHosts,
-      base::nullopt /* host_device */);
+  SetPageContent(multidevice_setup::mojom::HostStatus::kNoEligibleHosts,
+                 base::nullopt /* host_device */, feature_states_map);
+  CallGetPageContentData(true /* expected_to_request_data_from_device_sync */);
+  CallGetPageContentData(false /* expected_to_request_data_from_device_sync */);
+
   SimulateHostStatusUpdate(
       multidevice_setup::mojom::HostStatus::kEligibleHostExistsButNoHostSet,
       base::nullopt /* host_device */);
@@ -182,6 +256,14 @@
       test_device_);
   SimulateHostStatusUpdate(multidevice_setup::mojom::HostStatus::kHostVerified,
                            test_device_);
+
+  feature_states_map[multidevice_setup::mojom::Feature::kBetterTogetherSuite] =
+      multidevice_setup::mojom::FeatureState::kEnabledByUser;
+  SimulateFeatureStatesUpdate(feature_states_map);
+
+  feature_states_map[multidevice_setup::mojom::Feature::kBetterTogetherSuite] =
+      multidevice_setup::mojom::FeatureState::kDisabledByUser;
+  SimulateFeatureStatesUpdate(feature_states_map);
 }
 
 TEST_F(MultideviceHandlerTest, RetryPendingHostSetup) {
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
index 1d48096..d6285df2e 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -351,8 +351,8 @@
     if (!oauth_client_) {
       oauth_client_.reset(new gaia::GaiaOAuthClient(
           content::BrowserContext::GetDefaultStoragePartition(
-              web_ui()->GetWebContents()->GetBrowserContext())->
-                  GetURLRequestContext()));
+              web_ui()->GetWebContents()->GetBrowserContext())
+              ->GetURLLoaderFactoryForBrowserProcess()));
     }
 
     const std::string token = entry->GetPasswordChangeDetectionToken();
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index b3f3f3d..677b96ce 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -424,12 +424,6 @@
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
-#if !defined(OS_ANDROID)
-// Enables or disabled the OneGoogleBar on the local NTP.
-const base::Feature kOneGoogleBarOnLocalNtp{"OneGoogleBarOnLocalNtp",
-                                            base::FEATURE_ENABLED_BY_DEFAULT};
-#endif
-
 #if defined(OS_CHROMEOS)
 // Enables the Recommend Apps screen in OOBE.
 // TODO(https://crbug.com/862774): Remove this after the feature is fully
@@ -662,6 +656,10 @@
 // Enable USBGuard at the lockscreen on Chrome OS.
 // TODO(crbug.com/874630): Remove this kill-switch
 const base::Feature kUsbguard{"USBGuard", base::FEATURE_ENABLED_BY_DEFAULT};
+
+// Enable running shill in a minijail sandbox on Chrome OS.
+const base::Feature kShillSandboxing{"ShillSandboxing",
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // defined(OS_CHROMEOS)
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 1b56c28..bf26b67 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -226,10 +226,6 @@
 extern const base::Feature kOomIntervention;
 #endif
 
-#if !defined(OS_ANDROID)
-extern const base::Feature kOneGoogleBarOnLocalNtp;
-#endif
-
 #if defined(OS_CHROMEOS)
 extern const base::Feature kOobeRecommendAppsScreen;
 #endif
@@ -357,6 +353,8 @@
 extern const base::Feature kMachineLearningService;
 
 extern const base::Feature kUsbguard;
+
+extern const base::Feature kShillSandboxing;
 #endif  // defined(OS_CHROMEOS)
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/common/page_load_metrics/page_load_metrics.mojom b/chrome/common/page_load_metrics/page_load_metrics.mojom
index 4842669..9fcd500 100644
--- a/chrome/common/page_load_metrics/page_load_metrics.mojom
+++ b/chrome/common/page_load_metrics/page_load_metrics.mojom
@@ -152,6 +152,8 @@
 };
 
 // Data used for the page load.
+// TODO(johnidel): Remove/deprecate this struct once per resource updates are
+// being sent to the browser
 struct PageLoadDataUse {
   // Network bytes received for the page load.
   int64 received_data_length = 0;
@@ -162,11 +164,28 @@
   int64 data_reduction_proxy_bytes_saved = 0;
 };
 
+struct ResourceDataUpdate {
+  // The id for the resource request.
+  int32 request_id = 0;
+
+  // Network bytes received for the resource since the last timing update
+  // from renderer to browser.
+  int64 delta_bytes = 0;
+
+  // Total network bytes received for the resource across timing updates. This
+  // is the aggregate of the |delta_bytes| from each timing update.
+  int64 received_data_length = 0;
+
+  // Whether this resource load has completed.
+  bool is_complete;
+};
+
 // Sent from renderer to browser process when the PageLoadTiming for the
 // associated frame changed.
 interface PageLoadMetrics {
   UpdateTiming(PageLoadTiming page_load_timing,
                PageLoadMetadata page_load_metadata,
                PageLoadFeatures new_features,
-               PageLoadDataUse data_use);
+               PageLoadDataUse data_use,
+               array<ResourceDataUpdate> resources);
 };
diff --git a/chrome/renderer/page_load_metrics/fake_page_timing_sender.cc b/chrome/renderer/page_load_metrics/fake_page_timing_sender.cc
index 1026984cb..80c3472 100644
--- a/chrome/renderer/page_load_metrics/fake_page_timing_sender.cc
+++ b/chrome/renderer/page_load_metrics/fake_page_timing_sender.cc
@@ -18,8 +18,10 @@
     const mojom::PageLoadTimingPtr& timing,
     const mojom::PageLoadMetadataPtr& metadata,
     mojom::PageLoadFeaturesPtr new_features,
-    mojom::PageLoadDataUsePtr new_data_use) {
-  validator_->UpdateTiming(timing, metadata, new_features, new_data_use);
+    mojom::PageLoadDataUsePtr new_data_use,
+    std::vector<mojom::ResourceDataUpdatePtr> resources) {
+  validator_->UpdateTiming(timing, metadata, new_features, new_data_use,
+                           resources);
 }
 
 FakePageTimingSender::PageTimingValidator::PageTimingValidator() {}
@@ -95,7 +97,8 @@
     const mojom::PageLoadTimingPtr& timing,
     const mojom::PageLoadMetadataPtr& metadata,
     const mojom::PageLoadFeaturesPtr& new_features,
-    const mojom::PageLoadDataUsePtr& new_data_use) {
+    const mojom::PageLoadDataUsePtr& new_data_use,
+    const std::vector<mojom::ResourceDataUpdatePtr>& resources) {
   actual_timings_.push_back(timing.Clone());
   for (const auto feature : new_features->features) {
     EXPECT_EQ(actual_features_.find(feature), actual_features_.end())
diff --git a/chrome/renderer/page_load_metrics/fake_page_timing_sender.h b/chrome/renderer/page_load_metrics/fake_page_timing_sender.h
index 910e700..1d873a74 100644
--- a/chrome/renderer/page_load_metrics/fake_page_timing_sender.h
+++ b/chrome/renderer/page_load_metrics/fake_page_timing_sender.h
@@ -68,10 +68,12 @@
       return actual_timings_;
     }
 
-    void UpdateTiming(const mojom::PageLoadTimingPtr& timing,
-                      const mojom::PageLoadMetadataPtr& metadata,
-                      const mojom::PageLoadFeaturesPtr& new_features,
-                      const mojom::PageLoadDataUsePtr& new_data_use);
+    void UpdateTiming(
+        const mojom::PageLoadTimingPtr& timing,
+        const mojom::PageLoadMetadataPtr& metadata,
+        const mojom::PageLoadFeaturesPtr& new_features,
+        const mojom::PageLoadDataUsePtr& new_data_use,
+        const std::vector<mojom::ResourceDataUpdatePtr>& resources);
 
    private:
     std::vector<mojom::PageLoadTimingPtr> expected_timings_;
@@ -88,7 +90,8 @@
   void SendTiming(const mojom::PageLoadTimingPtr& timing,
                   const mojom::PageLoadMetadataPtr& metadata,
                   mojom::PageLoadFeaturesPtr new_features,
-                  mojom::PageLoadDataUsePtr new_data_use) override;
+                  mojom::PageLoadDataUsePtr new_data_use,
+                  std::vector<mojom::ResourceDataUpdatePtr> resources) override;
 
  private:
   PageTimingValidator* const validator_;
diff --git a/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc b/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
index 40131f3..0e861b3 100644
--- a/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
+++ b/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
@@ -38,14 +38,16 @@
         &page_load_metrics_);
   }
   ~MojoPageTimingSender() override {}
-  void SendTiming(const mojom::PageLoadTimingPtr& timing,
-                  const mojom::PageLoadMetadataPtr& metadata,
-                  mojom::PageLoadFeaturesPtr new_features,
-                  mojom::PageLoadDataUsePtr new_data_use) override {
+  void SendTiming(
+      const mojom::PageLoadTimingPtr& timing,
+      const mojom::PageLoadMetadataPtr& metadata,
+      mojom::PageLoadFeaturesPtr new_features,
+      mojom::PageLoadDataUsePtr new_data_use,
+      std::vector<mojom::ResourceDataUpdatePtr> resources) override {
     DCHECK(page_load_metrics_);
-    page_load_metrics_->UpdateTiming(timing->Clone(), metadata->Clone(),
-                                     std::move(new_features),
-                                     std::move(new_data_use));
+    page_load_metrics_->UpdateTiming(
+        timing->Clone(), metadata->Clone(), std::move(new_features),
+        std::move(new_data_use), std::move(resources));
   }
 
  private:
diff --git a/chrome/renderer/page_load_metrics/page_resource_data_use.cc b/chrome/renderer/page_load_metrics/page_resource_data_use.cc
index 02ba8714..3d1903c2 100644
--- a/chrome/renderer/page_load_metrics/page_resource_data_use.cc
+++ b/chrome/renderer/page_load_metrics/page_resource_data_use.cc
@@ -14,7 +14,10 @@
 PageResourceDataUse::PageResourceDataUse()
     : resource_id_(-1),
       data_reduction_proxy_compression_ratio_estimate_(1.0),
-      total_received_bytes_(0) {}
+      total_received_bytes_(0),
+      last_update_bytes_(0),
+      is_complete_(false),
+      is_canceled_(false) {}
 
 PageResourceDataUse::~PageResourceDataUse() = default;
 
@@ -25,6 +28,7 @@
   data_reduction_proxy_compression_ratio_estimate_ =
       data_reduction_proxy::EstimateCompressionRatioFromHeaders(&response_head);
   total_received_bytes_ = 0;
+  last_update_bytes_ = 0;
 }
 
 void PageResourceDataUse::DidReceiveTransferSizeUpdate(
@@ -41,8 +45,10 @@
     const network::URLLoaderCompletionStatus& status,
     mojom::PageLoadDataUse* delta_data_use) {
   // Report the difference in received bytes.
+  is_complete_ = true;
   int64_t delta_bytes = status.encoded_data_length - total_received_bytes_;
   if (delta_bytes > 0) {
+    total_received_bytes_ += delta_bytes;
     delta_data_use->received_data_length += delta_bytes;
     delta_data_use->data_reduction_proxy_bytes_saved +=
         delta_bytes * (data_reduction_proxy_compression_ratio_estimate_ - 1.0);
@@ -51,4 +57,28 @@
   return false;
 }
 
+void PageResourceDataUse::DidCancelResponse() {
+  is_canceled_ = true;
+}
+
+bool PageResourceDataUse::IsFinishedLoading() {
+  return is_complete_ || is_canceled_;
+}
+
+int PageResourceDataUse::CalculateNewlyReceivedBytes() {
+  int newly_received_bytes = total_received_bytes_ - last_update_bytes_;
+  last_update_bytes_ = total_received_bytes_;
+  DCHECK(newly_received_bytes >= 0);
+  return newly_received_bytes;
+}
+
+mojom::ResourceDataUpdatePtr PageResourceDataUse::GetResourceDataUpdate() {
+  mojom::ResourceDataUpdatePtr resource_data_update =
+      mojom::ResourceDataUpdate::New();
+  resource_data_update->request_id = resource_id();
+  resource_data_update->received_data_length = total_received_bytes_;
+  resource_data_update->delta_bytes = CalculateNewlyReceivedBytes();
+  resource_data_update->is_complete = is_complete_;
+  return resource_data_update;
+}
 }  // namespace page_load_metrics
diff --git a/chrome/renderer/page_load_metrics/page_resource_data_use.h b/chrome/renderer/page_load_metrics/page_resource_data_use.h
index c115fb6..7391762 100644
--- a/chrome/renderer/page_load_metrics/page_resource_data_use.h
+++ b/chrome/renderer/page_load_metrics/page_resource_data_use.h
@@ -34,9 +34,25 @@
   bool DidCompleteResponse(const network::URLLoaderCompletionStatus& status,
                            mojom::PageLoadDataUse* delta_data_use);
 
+  // Flags the resource as canceled.
+  void DidCancelResponse();
+
+  // Checks if the resource has completed loading or if the response was
+  // cancelled.
+  bool IsFinishedLoading();
+
   int resource_id() const { return resource_id_; }
 
+  // Creates a ResourceDataUpdate mojo for this resource. This page resource
+  // contains information since the last time update. Should be called at most
+  // once once per timing update.
+  mojom::ResourceDataUpdatePtr GetResourceDataUpdate();
+
  private:
+  // Calculates the difference between |total_received_bytes_| and
+  // |last_update_bytes_|, returns it, and updates |last_update_bytes_|.
+  int CalculateNewlyReceivedBytes();
+
   int resource_id_;
 
   // Compression ratio estimated from the response headers if data saver was
@@ -44,6 +60,10 @@
   double data_reduction_proxy_compression_ratio_estimate_;
 
   uint64_t total_received_bytes_;
+  uint64_t last_update_bytes_;
+
+  bool is_complete_;
+  bool is_canceled_;
 
   DISALLOW_ASSIGN(PageResourceDataUse);
 };
diff --git a/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc b/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc
index f2dd9ec..974ad11 100644
--- a/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc
+++ b/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc
@@ -61,8 +61,9 @@
 
 void PageTimingMetricsSender::DidObserveLoadingBehavior(
     blink::WebLoadingBehaviorFlag behavior) {
-  if (behavior & metadata_->behavior_flags)
+  if (behavior & metadata_->behavior_flags) {
     return;
+  }
   metadata_->behavior_flags |= behavior;
   EnsureSendTimer();
 }
@@ -70,8 +71,9 @@
 void PageTimingMetricsSender::DidObserveNewFeatureUsage(
     blink::mojom::WebFeature feature) {
   int32_t feature_id = static_cast<int32_t>(feature);
-  if (features_sent_.test(feature_id))
+  if (features_sent_.test(feature_id)) {
     return;
+  }
   features_sent_.set(feature_id);
   new_features_->features.push_back(feature);
   EnsureSendTimer();
@@ -109,11 +111,13 @@
 
   // It is possible that resources are not in the map, if response headers were
   // not received or for failed/cancelled resources.
-  if (resource_it == page_resource_data_use_.end())
+  if (resource_it == page_resource_data_use_.end()) {
     return;
+  }
 
   resource_it->second.DidReceiveTransferSizeUpdate(received_data_length,
                                                    new_data_use_.get());
+  modified_resources_.insert(&resource_it->second);
   EnsureSendTimer();
 }
 
@@ -132,17 +136,24 @@
     resource_it = new_resource_it.first;
   }
 
-  if (resource_it->second.DidCompleteResponse(status, new_data_use_.get()))
+  if (resource_it->second.DidCompleteResponse(status, new_data_use_.get())) {
     EnsureSendTimer();
+  }
+  modified_resources_.insert(&resource_it->second);
 }
 
 void PageTimingMetricsSender::DidCancelResponse(int resource_id) {
-  page_resource_data_use_.erase(resource_id);
+  auto resource_it = page_resource_data_use_.find(resource_id);
+  if (resource_it == page_resource_data_use_.end()) {
+    return;
+  }
+  resource_it->second.DidCancelResponse();
 }
 
 void PageTimingMetricsSender::Send(mojom::PageLoadTimingPtr timing) {
-  if (last_timing_->Equals(*timing))
+  if (last_timing_->Equals(*timing)) {
     return;
+  }
 
   // We want to make sure that each PageTimingMetricsSender is associated
   // with a distinct page navigation. Because we reset the object on commit,
@@ -170,10 +181,18 @@
 
 void PageTimingMetricsSender::SendNow() {
   have_sent_ipc_ = true;
+  std::vector<mojom::ResourceDataUpdatePtr> resources;
+  for (auto* resource : modified_resources_) {
+    resources.push_back(resource->GetResourceDataUpdate());
+    if (resource->IsFinishedLoading()) {
+      page_resource_data_use_.erase(resource->resource_id());
+    }
+  }
   sender_->SendTiming(last_timing_, metadata_, std::move(new_features_),
-                      std::move(new_data_use_));
+                      std::move(new_data_use_), std::move(resources));
   new_features_ = mojom::PageLoadFeatures::New();
   new_data_use_ = mojom::PageLoadDataUse::New();
+  modified_resources_.clear();
 }
 
 }  // namespace page_load_metrics
diff --git a/chrome/renderer/page_load_metrics/page_timing_metrics_sender.h b/chrome/renderer/page_load_metrics/page_timing_metrics_sender.h
index 15529ed1..4f0b611 100644
--- a/chrome/renderer/page_load_metrics/page_timing_metrics_sender.h
+++ b/chrome/renderer/page_load_metrics/page_timing_metrics_sender.h
@@ -8,6 +8,7 @@
 #include <bitset>
 #include <memory>
 
+#include "base/containers/flat_set.h"
 #include "base/containers/small_map.h"
 #include "base/macros.h"
 #include "chrome/common/page_load_metrics/page_load_timing.h"
@@ -83,9 +84,15 @@
 
   bool have_sent_ipc_ = false;
 
+  // The page's resources that are currently loading,  or were completed after
+  // the last timing update.
   base::small_map<std::map<int, PageResourceDataUse>, 16>
       page_resource_data_use_;
 
+  // Set of all resources that have completed or received a transfer
+  // size update since the last timimg update.
+  base::flat_set<PageResourceDataUse*> modified_resources_;
+
   // Field trial for alternating page timing metrics sender buffer timer delay.
   // https://crbug.com/847269.
   int buffer_timer_delay_ms_;
diff --git a/chrome/renderer/page_load_metrics/page_timing_sender.h b/chrome/renderer/page_load_metrics/page_timing_sender.h
index b0e59214..3ab07ea9 100644
--- a/chrome/renderer/page_load_metrics/page_timing_sender.h
+++ b/chrome/renderer/page_load_metrics/page_timing_sender.h
@@ -14,10 +14,12 @@
 class PageTimingSender {
  public:
   virtual ~PageTimingSender() {}
-  virtual void SendTiming(const mojom::PageLoadTimingPtr& timing,
-                          const mojom::PageLoadMetadataPtr& metadata,
-                          mojom::PageLoadFeaturesPtr new_features,
-                          mojom::PageLoadDataUsePtr new_data_use) = 0;
+  virtual void SendTiming(
+      const mojom::PageLoadTimingPtr& timing,
+      const mojom::PageLoadMetadataPtr& metadata,
+      mojom::PageLoadFeaturesPtr new_features,
+      mojom::PageLoadDataUsePtr new_data_use,
+      std::vector<mojom::ResourceDataUpdatePtr> resources) = 0;
 };
 
 }  // namespace page_load_metrics
diff --git a/chrome/service/cloud_print/DEPS b/chrome/service/cloud_print/DEPS
index 9e66b99..01a9fac1 100644
--- a/chrome/service/cloud_print/DEPS
+++ b/chrome/service/cloud_print/DEPS
@@ -5,4 +5,5 @@
   "+google_apis",
   # sync notifier depends on the common jingle notifier classes.
   "+jingle/notifier",
+  "+services/network",
 ]
diff --git a/chrome/service/cloud_print/cloud_print_auth.cc b/chrome/service/cloud_print/cloud_print_auth.cc
index 3a2ba1b9..8acf7d6 100644
--- a/chrome/service/cloud_print/cloud_print_auth.cc
+++ b/chrome/service/cloud_print/cloud_print_auth.cc
@@ -14,8 +14,8 @@
 #include "chrome/common/cloud_print/cloud_print_helpers.h"
 #include "chrome/service/cloud_print/cloud_print_token_store.h"
 #include "chrome/service/net/service_url_request_context_getter.h"
-#include "chrome/service/service_process.h"
 #include "google_apis/gaia/gaia_urls.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace cloud_print {
 
@@ -95,8 +95,8 @@
 
   robot_email_ = robot_email;
   // Now that we have an auth code we need to get the refresh and access tokens.
-  oauth_client_.reset(new gaia::GaiaOAuthClient(
-      g_service_process->GetServiceURLRequestContextGetter()));
+  oauth_client_.reset(
+      new gaia::GaiaOAuthClient(client_->GetURLLoaderFactory()));
   oauth_client_->GetTokensFromAuthCode(oauth_client_info_,
                                        robot_oauth_auth_code,
                                        kCloudPrintAuthMaxRetryCount,
@@ -106,8 +106,8 @@
 void CloudPrintAuth::RefreshAccessToken() {
   UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", AUTH_EVENT_REFRESH_REQUEST,
                             AUTH_EVENT_MAX);
-  oauth_client_.reset(new gaia::GaiaOAuthClient(
-      g_service_process->GetServiceURLRequestContextGetter()));
+  oauth_client_.reset(
+      new gaia::GaiaOAuthClient(client_->GetURLLoaderFactory()));
   std::vector<std::string> empty_scope_list;  // (Use scope from refresh token.)
   oauth_client_->RefreshToken(oauth_client_info_,
                               refresh_token_,
@@ -191,8 +191,8 @@
 
   json_data->GetString(kXMPPJidValue, &robot_email_);
   // Now that we have an auth code we need to get the refresh and access tokens.
-  oauth_client_.reset(new gaia::GaiaOAuthClient(
-      g_service_process->GetServiceURLRequestContextGetter()));
+  oauth_client_.reset(
+      new gaia::GaiaOAuthClient(client_->GetURLLoaderFactory()));
   oauth_client_->GetTokensFromAuthCode(oauth_client_info_,
                                        auth_code,
                                        kCloudPrintAPIMaxRetryCount,
diff --git a/chrome/service/cloud_print/cloud_print_auth.h b/chrome/service/cloud_print/cloud_print_auth.h
index d19dafb..a295398 100644
--- a/chrome/service/cloud_print/cloud_print_auth.h
+++ b/chrome/service/cloud_print/cloud_print_auth.h
@@ -15,6 +15,10 @@
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "url/gurl.h"
 
+namespace network {
+class SharedURLLoaderFactory;
+}
+
 namespace cloud_print {
 
 // CloudPrintAuth is a class to handle login, token refresh, and other
@@ -35,6 +39,9 @@
         const std::string& robot_email,
         const std::string& user_email) = 0;
     virtual void OnInvalidCredentials() = 0;
+    virtual scoped_refptr<network::SharedURLLoaderFactory>
+    GetURLLoaderFactory() = 0;
+
    protected:
      virtual ~Client() {}
   };
diff --git a/chrome/service/cloud_print/cloud_print_proxy_backend.cc b/chrome/service/cloud_print/cloud_print_proxy_backend.cc
index f8b4da12..d2d5729 100644
--- a/chrome/service/cloud_print/cloud_print_proxy_backend.cc
+++ b/chrome/service/cloud_print/cloud_print_proxy_backend.cc
@@ -34,6 +34,8 @@
 #include "jingle/notifier/listener/push_client.h"
 #include "jingle/notifier/listener/push_client_observer.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/transitional_url_loader_factory_owner.h"
 #include "url/gurl.h"
 
 namespace cloud_print {
@@ -104,6 +106,7 @@
                                 const std::string& robot_email,
                                 const std::string& user_email) override;
   void OnInvalidCredentials() override;
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
 
   // CloudPrintConnector::Client implementation.
   void OnAuthFailed() override;
@@ -165,6 +168,10 @@
   // Our parent CloudPrintProxyBackend
   CloudPrintProxyBackend* const backend_;
 
+  // Provides access to networking APIs for auth_.
+  std::unique_ptr<network::TransitionalURLLoaderFactoryOwner>
+      url_loader_factory_owner_;
+
   // Cloud Print authenticator.
   scoped_refptr<CloudPrintAuth> auth_;
 
@@ -267,13 +274,13 @@
     const ConnectorSettings& settings,
     const gaia::OAuthClientInfo& oauth_client_info,
     bool enable_job_poll)
-      : backend_(backend),
-        oauth_client_info_(oauth_client_info),
-        notifications_enabled_(false),
-        job_poll_scheduled_(false),
-        enable_job_poll_(enable_job_poll),
-        xmpp_ping_scheduled_(false),
-        pending_xmpp_pings_(0) {
+    : backend_(backend),
+      oauth_client_info_(oauth_client_info),
+      notifications_enabled_(false),
+      job_poll_scheduled_(false),
+      enable_job_poll_(enable_job_poll),
+      xmpp_ping_scheduled_(false),
+      pending_xmpp_pings_(0) {
   settings_.CopyFrom(settings);
 }
 
@@ -381,6 +388,18 @@
                    base::Bind(&Core::NotifyAuthenticationFailed, this));
 }
 
+scoped_refptr<network::SharedURLLoaderFactory>
+CloudPrintProxyBackend::Core::GetURLLoaderFactory() {
+  DCHECK(CurrentlyOnCoreThread());
+  if (!url_loader_factory_owner_) {
+    url_loader_factory_owner_ =
+        std::make_unique<network::TransitionalURLLoaderFactoryOwner>(
+            g_service_process->GetServiceURLRequestContextGetter());
+  }
+
+  return url_loader_factory_owner_->GetURLLoaderFactory();
+}
+
 void CloudPrintProxyBackend::Core::OnAuthFailed() {
   VLOG(1) << "CP_CONNECTOR: Authentication failed in connector.";
   // Let's stop connecter and refresh token. We'll restart connecter once
@@ -438,6 +457,7 @@
   notifications_enabled_ = false;
   notifications_enabled_since_ = base::TimeTicks();
   token_store_.reset();
+  url_loader_factory_owner_.reset();
 
   DestroyAuthAndConnector();
 }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 092c8d5c..3d8d7b4b 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1972,8 +1972,6 @@
         "../browser/ui/cocoa/apps/app_shim_menu_controller_mac_browsertest.mm",
         "../browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm",
         "../browser/ui/cocoa/dev_tools_controller_browsertest.mm",
-        "../browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h",
-        "../browser/ui/cocoa/extensions/extension_install_prompt_test_utils.mm",
         "../browser/ui/cocoa/extensions/media_galleries_dialog_cocoa_browsertest.mm",
         "../browser/ui/cocoa/find_bar/find_bar_browsertest.mm",
         "../browser/ui/cocoa/location_bar/content_setting_decoration_browsertest.mm",
@@ -1998,8 +1996,6 @@
         ## not compile and should be removed (along with test file) when the
         ## corresponding release code is deleted.
         # "../browser/ui/cocoa/constrained_window/constrained_window_mac_browsertest.mm",
-        # "../browser/ui/cocoa/extensions/extension_install_dialog_controller_browsertest.mm",
-        # "../browser/ui/cocoa/extensions/windowed_install_dialog_controller_browsertest.mm",
         # "../browser/ui/cocoa/page_info/page_info_bubble_views_mac_browsertest.mm",
       ]
     }
@@ -4160,10 +4156,6 @@
         "../browser/ui/cocoa/draggable_button_unittest.mm",
         "../browser/ui/cocoa/extensions/browser_actions_container_view_unittest.mm",
         "../browser/ui/cocoa/extensions/chooser_dialog_cocoa_controller_unittest.mm",
-        "../browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h",
-        "../browser/ui/cocoa/extensions/extension_install_prompt_test_utils.mm",
-        "../browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm",
-        "../browser/ui/cocoa/extensions/extension_installed_bubble_controller_unittest.mm",
         "../browser/ui/cocoa/extensions/media_galleries_dialog_cocoa_unittest.mm",
         "../browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac_unittest.mm",
         "../browser/ui/cocoa/find_bar/find_bar_bridge_unittest.mm",
@@ -4264,8 +4256,6 @@
     if (mac_views_browser) {
       sources -= [
         "../browser/ui/cocoa/extensions/chooser_dialog_cocoa_controller_unittest.mm",
-        "../browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm",
-        "../browser/ui/cocoa/extensions/extension_installed_bubble_controller_unittest.mm",
         "../browser/ui/cocoa/extensions/media_galleries_dialog_cocoa_unittest.mm",
         "../browser/ui/cocoa/page_info/page_info_bubble_controller_unittest.mm",
       ]
diff --git a/chrome/test/chromedriver/BUILD.gn b/chrome/test/chromedriver/BUILD.gn
index cb16c26..b27dd4a 100644
--- a/chrome/test/chromedriver/BUILD.gn
+++ b/chrome/test/chromedriver/BUILD.gn
@@ -150,6 +150,8 @@
     "chrome/web_view.h",
     "chrome/web_view_impl.cc",
     "chrome/web_view_impl.h",
+    "log_replay/devtools_log_reader.cc",
+    "log_replay/devtools_log_reader.h",
     "net/adb_client_socket.cc",
     "net/adb_client_socket.h",
     "net/net_util.cc",
@@ -386,6 +388,7 @@
     "chrome_launcher_unittest.cc",
     "command_listener_proxy_unittest.cc",
     "commands_unittest.cc",
+    "log_replay/devtools_log_reader_unittest.cc",
     "logging_unittest.cc",
     "net/adb_client_socket_unittest.cc",
     "net/timeout_unittest.cc",
@@ -402,6 +405,7 @@
 
   data = [
     "//chrome/test/data/chromedriver/",
+    "//chrome/test/chromedriver/log_replay/test_data",
   ]
 
   deps = [
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl.cc b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
index d6372ee..dde4f5ca 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl.cc
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
@@ -310,8 +310,10 @@
   command.SetKey("params", params.Clone());
   std::string message = SerializeValue(&command);
   if (IsVLogOn(1)) {
-    VLOG(1) << "DEVTOOLS COMMAND " << method << " (id=" << command_id << ") "
-            << FormatValueForDisplay(params);
+    // Note: ChromeDriver log-replay depends on the format of this logging.
+    // see chromedriver/log_replay/devtools_log_reader.cc.
+    VLOG(1) << "DevTools WebSocket Command: " << method << " (id=" << command_id
+            << ") " << FormatValueForDisplay(params);
   }
   if (parent_ != nullptr) {
     base::DictionaryValue params2;
@@ -433,7 +435,9 @@
 
 Status DevToolsClientImpl::ProcessEvent(const internal::InspectorEvent& event) {
   if (IsVLogOn(1)) {
-    VLOG(1) << "DEVTOOLS EVENT " << event.method << " "
+    // Note: ChromeDriver log-replay depends on the format of this logging.
+    // see chromedriver/log_replay/devtools_log_reader.cc.
+    VLOG(1) << "DevTools WebSocket Event: " << event.method << " "
             << FormatValueForDisplay(*event.params);
   }
   unnotified_event_listeners_ = listeners_;
@@ -507,8 +511,10 @@
       result = FormatValueForDisplay(*response.result);
     else
       result = response.error;
-    VLOG(1) << "DEVTOOLS RESPONSE " << method << " (id=" << response.id
-            << ") " << result;
+    // Note: ChromeDriver log-replay depends on the format of this logging.
+    // see chromedriver/log_replay/devtools_log_reader.cc.
+    VLOG(1) << "DevTools WebSocket Response: " << method
+            << " (id=" << response.id << ") " << result;
   }
 
   if (iter == response_info_map_.end())
diff --git a/chrome/test/chromedriver/chrome/devtools_http_client.cc b/chrome/test/chromedriver/chrome/devtools_http_client.cc
index 760da07..7c9d0e9 100644
--- a/chrome/test/chromedriver/chrome/devtools_http_client.cc
+++ b/chrome/test/chromedriver/chrome/devtools_http_client.cc
@@ -241,12 +241,12 @@
 bool DevToolsHttpClient::FetchUrlAndLog(const std::string& url,
                                         URLRequestContextGetter* getter,
                                         std::string* response) {
-  VLOG(1) << "DevTools request: " << url;
+  VLOG(1) << "DevTools HTTP Request: " << url;
   bool ok = FetchUrl(url, getter, response);
   if (ok) {
-    VLOG(1) << "DevTools response: " << *response;
+    VLOG(1) << "DevTools HTTP Response: " << *response;
   } else {
-    VLOG(1) << "DevTools request failed";
+    VLOG(1) << "DevTools HTTP Request failed";
   }
   return ok;
 }
diff --git a/chrome/test/chromedriver/commands.cc b/chrome/test/chromedriver/commands.cc
index 553ef3d..cc8dc833 100644
--- a/chrome/test/chromedriver/commands.cc
+++ b/chrome/test/chromedriver/commands.cc
@@ -220,6 +220,8 @@
   if (IsVLogOn(0)) {
     if (!session->driver_log ||
         session->driver_log->min_level() != Log::Level::kOff) {
+      // Note: ChromeDriver log-replay depends on the format of this logging.
+      // see chromedriver/log_replay/client_replay.py
       VLOG(0) << "[" << session->id << "] "
               << "COMMAND " << command_name << " "
               << FormatValueForDisplay(*params);
@@ -277,6 +279,8 @@
       }
       if (!session->driver_log ||
           session->driver_log->min_level() != Log::Level::kOff) {
+        // Note: ChromeDriver log-replay depends on the format of this logging.
+        // see chromedriver/log_replay/client_replay.py
         VLOG(0) << "[" << session->id << "] "
                 << "RESPONSE " << command_name
                 << (result.length() ? " " + result : "");
diff --git a/chrome/test/chromedriver/log_replay/devtools_log_reader.cc b/chrome/test/chromedriver/log_replay/devtools_log_reader.cc
new file mode 100644
index 0000000..58986b28
--- /dev/null
+++ b/chrome/test/chromedriver/log_replay/devtools_log_reader.cc
@@ -0,0 +1,164 @@
+// 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/chromedriver/log_replay/devtools_log_reader.h"
+
+#include <iostream>
+#include <string>
+
+#include "base/logging.h"
+#include "base/strings/pattern.h"
+
+namespace {
+// Parses the word (id=X) and just returns the id number
+int GetId(std::istringstream& header_stream) {
+  int id = 0;
+  header_stream.ignore(5);  // ignore the " (id=" characters
+  header_stream >> id;
+  header_stream.ignore(1);  // ignore the final parenthesis
+  return id;
+}
+}  // namespace
+
+LogEntry::LogEntry(std::istringstream& header_stream) {
+  error = false;
+  std::string protocol_type_string;
+  header_stream >> protocol_type_string;  // "HTTP" or "WebSocket"
+  if (protocol_type_string == "HTTP") {
+    protocol_type = HTTP;
+  } else if (protocol_type_string == "WebSocket") {
+    protocol_type = WebSocket;
+  } else {
+    error = true;
+    LOG(ERROR) << "Could not read protocol from log entry header.";
+    return;
+  }
+
+  std::string event_type_string;
+  header_stream >> event_type_string;
+
+  if (event_type_string == "Response:") {
+    event_type = response;
+  } else if (event_type_string == "Command:" ||
+             event_type_string == "Request:") {
+    event_type = request;
+  } else if (event_type_string == "Event:") {
+    event_type = event;
+  } else {
+    error = true;
+    LOG(ERROR) << "Could not read event type from log entry header.";
+    return;
+  }
+
+  if (!(protocol_type == HTTP && event_type == response)) {
+    header_stream >> command_name;
+    if (command_name == "") {
+      error = true;
+      LOG(ERROR) << "Could not read command name from log entry header";
+      return;
+    }
+    if (protocol_type != HTTP) {
+      id = GetId(header_stream);
+      if (id == 0) {
+        error = true;
+        LOG(ERROR) << "Could not read sequential id from log entry header.";
+        return;
+      }
+    }
+  }
+}
+
+LogEntry::~LogEntry() {}
+
+DevToolsLogReader::DevToolsLogReader(const base::FilePath& log_path)
+    : log_file(log_path.value().c_str(), std::ios::in) {}
+
+DevToolsLogReader::~DevToolsLogReader() {}
+
+bool DevToolsLogReader::IsHeader(std::istringstream& header_stream) {
+  std::string word;
+  header_stream >> word;  // preamble
+  if (!base::MatchPattern(word, "[??????????.???][DEBUG]:")) {
+    return false;
+  }
+  header_stream >> word;  // "DevTools" for DevTools commands/responses/events
+  bool result = word == "DevTools";
+  return result;
+}
+
+std::unique_ptr<LogEntry> DevToolsLogReader::GetNext(
+    LogEntry::Protocol protocol_type) {
+  std::string next_line;
+  while (true) {
+    if (log_file.eof())
+      return nullptr;
+    std::getline(log_file, next_line);
+
+    std::istringstream next_line_stream(next_line);
+    if (IsHeader(next_line_stream)) {
+      std::unique_ptr<LogEntry> log_entry =
+          std::make_unique<LogEntry>(next_line_stream);
+      if (log_entry->error) {
+        return nullptr;  // helpful error message already logged
+      }
+      if (log_entry->protocol_type != protocol_type)
+        continue;
+      if (!(log_entry->event_type == LogEntry::EventType::request &&
+            log_entry->protocol_type == LogEntry::Protocol::HTTP)) {
+        log_entry->payload = GetJSONString(next_line_stream);
+        if (log_entry->payload == "") {
+          LOG(ERROR) << "Problem parsing JSON from log file";
+          return nullptr;
+        }
+      }
+      return log_entry;
+    }
+  }
+}
+
+std::string DevToolsLogReader::GetJSONString(
+    std::istringstream& header_stream) {
+  std::string next_line, json;
+
+  int opening_char_count = 0;
+  std::getline(header_stream, next_line);
+  next_line = next_line.substr(1);
+  char opening_char = next_line[0];
+  char closing_char;
+  switch (opening_char) {
+    case '{':
+      closing_char = '}';
+      break;
+    case '[':
+      closing_char = ']';
+      break;
+    default:
+      return "";
+  }
+  while (true) {
+    json += next_line;
+    opening_char_count += CountChar(next_line, opening_char, closing_char);
+    if (opening_char_count == 0)
+      break;
+    if (log_file.eof())
+      return "";
+    getline(log_file, next_line);
+  }
+  return json;
+}
+
+int DevToolsLogReader::CountChar(const std::string& line,
+                                 char opening_char,
+                                 char closing_char) const {
+  bool in_quote = false;
+  int total = 0;
+  for (size_t i = 0; i < line.length(); i++) {
+    if (!in_quote && line[i] == opening_char)
+      total++;
+    if (!in_quote && line[i] == closing_char)
+      total--;
+    if (line[i] == '"' && (i == 0 || line[i - 1] != '\\'))
+      in_quote = !in_quote;
+  }
+  return total;
+}
diff --git a/chrome/test/chromedriver/log_replay/devtools_log_reader.h b/chrome/test/chromedriver/log_replay/devtools_log_reader.h
new file mode 100644
index 0000000..0fb734d8d
--- /dev/null
+++ b/chrome/test/chromedriver/log_replay/devtools_log_reader.h
@@ -0,0 +1,95 @@
+// 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_CHROMEDRIVER_LOG_REPLAY_DEVTOOLS_LOG_READER_H_
+#define CHROME_TEST_CHROMEDRIVER_LOG_REPLAY_DEVTOOLS_LOG_READER_H_
+
+#include <fstream>
+#include <memory>
+#include <string>
+
+#include "base/files/file_path.h"
+
+// Represents one DevTools entry (command or response) in the log.
+//
+// These appear in the log in the following format:
+// [<timestamp>][DEBUG]: DevTools <protocol_type> <event_type> <command_name>
+//     (id=<id>) <payload>
+// where:
+//
+// <protocol_type> is either HTTP or WebSocket
+//
+// <event_type> is either "Command:" (for WebSocket only), "Request:" (for HTTP
+//   only), "Response:", or "Event:"
+//
+// <command_name> is the command for WebSocket (like "DOM.getDocument") or a url
+// for HTTP (like "http://localhost:38845/json")
+//
+// <id> is a sequential number to identify WebSocket commands with their
+// responses
+//
+// <payload> is either the parameters in case of a WebSocket command, or the
+// response in case of any response. It is always a JSON, and always spans
+// multiple lines.
+class LogEntry {
+ public:
+  // Build this LogEntry using the header line of an entry in the log. This
+  // doesn't initialize the payload member. The payload must be parsed from
+  // lines after the header.
+  explicit LogEntry(std::istringstream& header_stream);
+  ~LogEntry();
+
+  enum EventType {
+    request,  // Command or Request depending on HTTP or WebSocket client
+    response,
+    event
+  };
+  enum Protocol { HTTP, WebSocket };
+
+  EventType event_type;
+  Protocol protocol_type;
+  std::string command_name;
+  std::string payload;
+  int id;
+  bool error;
+};
+
+// Reads a log file for DevTools entries.
+class DevToolsLogReader {
+ public:
+  // Initialize the log reader using a path to a log file to read from.
+  explicit DevToolsLogReader(const base::FilePath& log_path);
+  ~DevToolsLogReader();
+
+  // Get the next DevTools entry in the log of the specified protocol type.
+  //
+  // This returns commands, responses, and events separately. If there are
+  // no remaining entries of the specified type, or if there is some other
+  // problem encountered like a truncated JSON, nullptr is returned.
+  std::unique_ptr<LogEntry> GetNext(LogEntry::Protocol protocol_type);
+
+ private:
+  std::ifstream log_file;
+  // Starting with |header_line|, parse a JSON string out of the log file.
+  //
+  // will parse either list or dictionary-type JSON strings, depending on the
+  // starting character.
+  std::string GetJSONString(std::istringstream& header_line);
+
+  // Return whether |line| is a header of a DevTools entry.
+  //
+  // This only parses out the first two words of the line, meaning that the
+  // stream can be re-used to parse the specifics of the entry after calling
+  // this.
+  bool IsHeader(std::istringstream& line);
+
+  // Count (number of opening_char) - (number of closing_char) in |line|.
+  //
+  // Used to check for the end of JSON parameters. Ignores characters inside of
+  // non-escaped quotes.
+  int CountChar(const std::string& line,
+                char opening_char,
+                char closing_char) const;
+};
+
+#endif  // CHROME_TEST_CHROMEDRIVER_LOG_REPLAY_DEVTOOLS_LOG_READER_H_
diff --git a/chrome/test/chromedriver/log_replay/devtools_log_reader_unittest.cc b/chrome/test/chromedriver/log_replay/devtools_log_reader_unittest.cc
new file mode 100644
index 0000000..d653c88
--- /dev/null
+++ b/chrome/test/chromedriver/log_replay/devtools_log_reader_unittest.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/test/chromedriver/log_replay/devtools_log_reader.h"
+
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+// Log files to test the reader against
+const char* const kTestDataPath[] = {"chrome", "test", "chromedriver",
+                                     "log_replay", "test_data"};
+const char kTestGetTitlePath[] = "testGetTitle_simple.log";
+const char kOneEntryPath[] = "oneDevToolsEntry.log";
+const char kTruncatedJSONPath[] = "truncatedJSON.log";
+
+base::FilePath GetLogFileFromLiteral(const char literal[]) {
+  base::FilePath root_dir;
+  CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &root_dir));
+  for (int i = 0; i < 5; i++)
+    root_dir = root_dir.AppendASCII(kTestDataPath[i]);
+  base::FilePath result = root_dir.AppendASCII(literal);
+  CHECK(base::PathExists(result));
+  return result;
+}
+}  // namespace
+
+TEST(DevToolsLogReaderTest, Basic) {
+  base::FilePath path = GetLogFileFromLiteral(kTestGetTitlePath);
+  DevToolsLogReader reader(path);
+  std::unique_ptr<LogEntry> next = reader.GetNext(LogEntry::Protocol::HTTP);
+  EXPECT_TRUE(next != nullptr);
+  EXPECT_EQ(next->protocol_type, LogEntry::Protocol::HTTP);
+  EXPECT_EQ(next->command_name, "http://localhost:38845/json/version");
+  next = reader.GetNext(LogEntry::Protocol::HTTP);
+  EXPECT_TRUE(next != nullptr);
+  EXPECT_EQ(next->payload, "{   \"string_key\": \"string_value\"}");
+}
+
+TEST(DevToolsLogReaderTest, Multiple) {
+  base::FilePath path = GetLogFileFromLiteral(kTestGetTitlePath);
+  DevToolsLogReader reader(path);
+  std::unique_ptr<LogEntry> next;
+  for (int i = 0; i < 3; i++)
+    next = reader.GetNext(LogEntry::Protocol::HTTP);
+
+  EXPECT_TRUE(next != nullptr);
+  EXPECT_EQ(next->command_name, "http://localhost:38845/json");
+  next = reader.GetNext(LogEntry::Protocol::HTTP);
+  EXPECT_EQ(next->payload,
+            "[ {   \"string_key1\": \"string_value1\"}, {   "
+            "\"string_key2\": \"string_value2\"} ]");
+}
+
+TEST(DevToolsLogReaderTest, EndOfFile) {
+  base::FilePath path = GetLogFileFromLiteral(kOneEntryPath);
+  DevToolsLogReader reader(path);
+  std::unique_ptr<LogEntry> next = reader.GetNext(LogEntry::Protocol::HTTP);
+  EXPECT_TRUE(next != nullptr);
+  next = reader.GetNext(LogEntry::Protocol::HTTP);
+  EXPECT_TRUE(next == nullptr);
+}
+
+TEST(DevToolsLogReaderTest, WebSocketBasic) {
+  base::FilePath path = GetLogFileFromLiteral(kTestGetTitlePath);
+  DevToolsLogReader reader(path);
+  std::unique_ptr<LogEntry> next =
+      reader.GetNext(LogEntry::Protocol::WebSocket);
+  EXPECT_TRUE(next != nullptr);
+  EXPECT_EQ(next->protocol_type, LogEntry::Protocol::WebSocket);
+  EXPECT_EQ(next->event_type, LogEntry::EventType::request);
+  EXPECT_EQ(next->command_name, "Log.enable");
+  EXPECT_EQ(next->id, 1);
+}
+
+TEST(DevToolsLogReaderTest, WebSocketMultiple) {
+  base::FilePath path = GetLogFileFromLiteral(kTestGetTitlePath);
+  DevToolsLogReader reader(path);
+  std::unique_ptr<LogEntry> next =
+      reader.GetNext(LogEntry::Protocol::WebSocket);
+  next = reader.GetNext(LogEntry::Protocol::WebSocket);
+  EXPECT_TRUE(next != nullptr);
+  EXPECT_EQ(next->event_type, LogEntry::EventType::request);
+  EXPECT_EQ(next->command_name, "DOM.getDocument");
+  EXPECT_EQ(next->id, 2);
+}
+
+TEST(DevToolsLogReaderTest, WebSocketPayload) {
+  base::FilePath path = GetLogFileFromLiteral(kTestGetTitlePath);
+  DevToolsLogReader reader(path);
+  std::unique_ptr<LogEntry> next;
+  for (int i = 0; i < 3; i++)
+    next = reader.GetNext(LogEntry::Protocol::WebSocket);
+  EXPECT_TRUE(next != nullptr);
+  EXPECT_EQ(next->command_name, "Target.setAutoAttach");
+  EXPECT_EQ(next->id, 3);
+  EXPECT_EQ(next->payload,
+            "{   \"autoAttach\": true,   \"waitForDebuggerOnStart\": false}");
+}
+
+TEST(DevToolsLogReaderTest, TruncatedJSON) {
+  base::FilePath path = GetLogFileFromLiteral(kTruncatedJSONPath);
+  DevToolsLogReader reader(path);
+  std::unique_ptr<LogEntry> next =
+      reader.GetNext(LogEntry::Protocol::WebSocket);
+  EXPECT_TRUE(next == nullptr);
+}
diff --git a/chrome/test/chromedriver/log_replay/test_data/oneDevToolsEntry.log b/chrome/test/chromedriver/log_replay/test_data/oneDevToolsEntry.log
new file mode 100644
index 0000000..df207fe
--- /dev/null
+++ b/chrome/test/chromedriver/log_replay/test_data/oneDevToolsEntry.log
@@ -0,0 +1 @@
+[1533082321.074][DEBUG]: DevTools HTTP Request: http://localhost:38845/json/version
\ No newline at end of file
diff --git a/chrome/test/chromedriver/log_replay/test_data/testGetTitle_simple.log b/chrome/test/chromedriver/log_replay/test_data/testGetTitle_simple.log
new file mode 100644
index 0000000..26a190b
--- /dev/null
+++ b/chrome/test/chromedriver/log_replay/test_data/testGetTitle_simple.log
@@ -0,0 +1,480 @@
+[1533082318.791][INFO]: [37ab6acf4be79d7e368e3260482cd6a2] COMMAND InitSession {
+   "desiredCapabilities": {
+      "chromeOptions": {
+         "args": [ "no-sandbox", "disable-gpu" ],
+         "binary": "/usr/local/google/home/cwinstanley/chromium/src/out/Default/chrome"
+      },
+      "goog:testName": "__main__.ChromeDriverTest.testGetTitle",
+      "loggingPrefs": {
+
+      }
+   }
+}
+[1533082318.792][INFO]: Populating Preferences file: {
+   "alternate_error_pages": {
+      "enabled": false
+   },
+   "autofill": {
+      "enabled": false
+   },
+   "browser": {
+      "check_default_browser": false
+   },
+   "distribution": {
+      "import_bookmarks": false,
+      "import_history": false,
+      "import_search_engine": false,
+      "make_chrome_default_for_user": false,
+      "skip_first_run_ui": true
+   },
+   "dns_prefetching": {
+      "enabled": false
+   },
+   "profile": {
+      "content_settings": {
+         "pattern_pairs": {
+            "https://*,*": {
+               "media-stream": {
+                  "audio": "Default",
+                  "video": "Default"
+               }
+            }
+         }
+      },
+      "default_content_setting_values": {
+         "geolocation": 1
+      },
+      "default_content_settings": {
+         "geolocation": 1,
+         "mouselock": 1,
+         "notifications": 1,
+         "popups": 1,
+         "ppapi-broker": 1
+      },
+      "password_manager_enabled": false
+   },
+   "safebrowsing": {
+      "enabled": false
+   },
+   "search": {
+      "suggest_enabled": false
+   },
+   "translate": {
+      "enabled": false
+   }
+}
+[1533082318.792][INFO]: Populating Local State file: {
+   "background_mode": {
+      "enabled": false
+   },
+   "ssl": {
+      "rev_checking": {
+         "enabled": false
+      }
+   }
+}
+[1533082318.793][INFO]: Launching chrome: /usr/local/google/home/cwinstanley/chromium/src/out/Default/chrome --disable-background-networking --disable-client-side-phishing-detection --disable-default-apps --disable-gpu --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --disable-web-resources --enable-automation --enable-logging --force-fieldtrials=SiteIsolationExtensions/Control --ignore-certificate-errors --load-extension=/tmp/.org.chromium.Chromium.EUcaSh/internal --log-level=0 --metrics-recording-only --no-first-run --no-sandbox --password-store=basic --remote-debugging-port=0 --test-type=webdriver --use-mock-keychain --user-data-dir=/tmp/.org.chromium.Chromium.E0v4OG data:,
+[47071:47071:0731/171200.838032:ERROR:gpu_process_transport_factory.cc(642)] Switching to software compositing.
+[47071:47071:0731/171200.838096:ERROR:gpu_process_transport_factory.cc(1007)] Lost UI shared context.
+[47071:47071:0731/171200.917972:WARNING:account_consistency_mode_manager.cc(290)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured.
+[47071:47071:0731/171200.918086:WARNING:account_consistency_mode_manager.cc(290)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured.
+[47071:47071:0731/171200.918141:WARNING:account_consistency_mode_manager.cc(290)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured.
+[47071:47071:0731/171200.918194:WARNING:account_consistency_mode_manager.cc(290)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured.
+[47071:47071:0731/171200.919405:WARNING:account_consistency_mode_manager.cc(290)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured.
+[47071:47071:0731/171200.926168:WARNING:account_consistency_mode_manager.cc(290)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured.
+[47071:47071:0731/171200.968513:WARNING:account_consistency_mode_manager.cc(290)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured.
+[47071:47071:0731/171200.998394:WARNING:account_consistency_mode_manager.cc(290)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured.
+[47071:47071:0731/171200.998472:WARNING:account_consistency_mode_manager.cc(290)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured.
+
+DevTools listening on ws://127.0.0.1:38845/devtools/browser/1ea90138-ad2b-4281-a528-a07943a64ba8
+[1533082321.074][DEBUG]: DevTools HTTP Request: http://localhost:38845/json/version
+[47071:47071:0731/171201.142350:WARNING:password_store_factory.cc(253)] Using basic (unencrypted) store for password storage. See https://chromium.googlesource.com/chromium/src/+/master/docs/linux_password_storage.md for more information about password storage options.
+[47071:47071:0731/171201.145247:WARNING:account_consistency_mode_manager.cc(290)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured.
+[1533082321.383][DEBUG]: DevTools HTTP Response: {
+   "string_key": "string_value"
+}
+
+[1533082321.434][DEBUG]: DevTools HTTP Request: http://localhost:38845/json
+[1533082321.483][DEBUG]: DevTools HTTP Response: [ {
+   "string_key1": "string_value1"
+}, {
+   "string_key2": "string_value2"
+} ]
+
+[1533082321.484][DEBUG]: DevTools HTTP Request: http://localhost:38845/json
+[1533082321.489][DEBUG]: DevTools HTTP Response: [ {
+   "description": "",
+   "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:38845/devtools/page/AA1E6CEE168988EC3C52DF53EAE73BD6",
+   "id": "AA1E6CEE168988EC3C52DF53EAE73BD6",
+   "title": "Chrome Automation Extension",
+   "type": "background_page",
+   "url": "chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/_generated_background_page.html",
+   "webSocketDebuggerUrl": "ws://localhost:38845/devtools/page/AA1E6CEE168988EC3C52DF53EAE73BD6"
+}, {
+   "description": "",
+   "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:38845/devtools/page/68B1DB93B848C3F618D9304F7FE26D99",
+   "id": "68B1DB93B848C3F618D9304F7FE26D99",
+   "title": "",
+   "type": "page",
+   "url": "data:,",
+   "webSocketDebuggerUrl": "ws://localhost:38845/devtools/page/68B1DB93B848C3F618D9304F7FE26D99"
+} ]
+
+[1533082321.491][INFO]: resolved localhost to ["::1","127.0.0.1"]
+[1533082321.495][DEBUG]: DevTools WebSocket Command: Log.enable (id=1) {
+
+}
+[1533082321.495][DEBUG]: DevTools WebSocket Command: DOM.getDocument (id=2) {
+
+}
+[1533082321.495][DEBUG]: DevTools WebSocket Command: Target.setAutoAttach (id=3) {
+   "autoAttach": true,
+   "waitForDebuggerOnStart": false
+}
+[1533082321.495][DEBUG]: DevTools WebSocket Command: Page.enable (id=4) {
+
+}
+[1533082321.496][DEBUG]: DevTools WebSocket Command: Page.enable (id=5) {
+
+}
+[1533082321.587][DEBUG]: DevTools WebSocket Response: Log.enable (id=1) {
+
+}
+[1533082321.588][DEBUG]: DevTools WebSocket Response: DOM.getDocument (id=2) {
+   "root": {
+      "backendNodeId": 2,
+      "baseURL": "data:,",
+      "childNodeCount": 1,
+      "children": [ {
+         "attributes": [  ],
+         "backendNodeId": 3,
+         "childNodeCount": 2,
+         "children": [ {
+            "attributes": [  ],
+            "backendNodeId": 4,
+            "childNodeCount": 0,
+            "localName": "head",
+            "nodeId": 3,
+            "nodeName": "HEAD",
+            "nodeType": 1,
+            "nodeValue": "",
+            "parentId": 2
+         }, {
+            "attributes": [  ],
+            "backendNodeId": 5,
+            "childNodeCount": 0,
+            "localName": "body",
+            "nodeId": 4,
+            "nodeName": "BODY",
+            "nodeType": 1,
+            "nodeValue": "",
+            "parentId": 2
+         } ],
+         "frameId": "68B1DB93B848C3F618D9304F7FE26D99",
+         "localName": "html",
+         "nodeId": 2,
+         "nodeName": "HTML",
+         "nodeType": 1,
+         "nodeValue": "",
+         "parentId": 1
+      } ],
+      "documentURL": "data:,",
+      "localName": "",
+      "nodeId": 1,
+      "nodeName": "#document",
+      "nodeType": 9,
+      "nodeValue": "",
+      "xmlVersion": ""
+   }
+}
+[1533082321.588][DEBUG]: DevTools WebSocket Response: Target.setAutoAttach (id=3) {
+
+}
+[1533082321.588][DEBUG]: DevTools WebSocket Response: Page.enable (id=4) {
+
+}
+[1533082321.589][DEBUG]: DevTools WebSocket Response: Page.enable (id=5) {
+
+}
+[1533082321.589][DEBUG]: DevTools WebSocket Command: Runtime.enable (id=6) {
+
+}
+[1533082321.589][DEBUG]: DevTools WebSocket Event: Page.frameResized {
+
+}
+[1533082321.631][DEBUG]: DevTools WebSocket Event: Runtime.executionContextCreated {
+   "context": {
+      "auxData": {
+         "frameId": "68B1DB93B848C3F618D9304F7FE26D99",
+         "isDefault": true
+      },
+      "id": 1,
+      "name": "",
+      "origin": "://"
+   }
+}
+[1533082321.632][DEBUG]: DevTools WebSocket Response: Runtime.enable (id=6) {
+
+}
+[1533082321.632][DEBUG]: DevTools WebSocket Command: Page.enable (id=7) {
+
+}
+[1533082321.653][DEBUG]: DevTools WebSocket Response: Page.enable (id=7) {
+
+}
+[1533082321.653][DEBUG]: DevTools WebSocket Command: Runtime.enable (id=8) {
+
+}
+[1533082321.655][DEBUG]: DevTools WebSocket Response: Runtime.enable (id=8) {
+
+}
+[1533082321.656][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=9) {
+   "expression": "(function() { // Copyright (c) 2012 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n/**\n * Enum f...",
+   "returnByValue": true
+}
+[1533082321.684][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=9) {
+   "result": {
+      "type": "object",
+      "value": {
+         "status": 0,
+         "value": 1
+      }
+   }
+}
+[1533082321.684][INFO]: [37ab6acf4be79d7e368e3260482cd6a2] RESPONSE InitSession {
+   "acceptInsecureCerts": false,
+   "acceptSslCerts": false,
+   "applicationCacheEnabled": false,
+   "browserConnectionEnabled": false,
+   "browserName": "chrome",
+   "chrome": {
+      "chromedriverVersion": "2.40 (2e6092eb527be36e741f68341db300e257ec4c6d)",
+      "userDataDir": "/tmp/.org.chromium.Chromium.E0v4OG"
+   },
+   "cssSelectorsEnabled": true,
+   "databaseEnabled": false,
+   "goog:chromeOptions": {
+      "debuggerAddress": "localhost:38845"
+   },
+   "handlesAlerts": true,
+   "hasTouchScreen": false,
+   "javascriptEnabled": true,
+   "locationContextEnabled": true,
+   "mobileEmulationEnabled": false,
+   "nativeEvents": true,
+   "networkConnectionEnabled": false,
+   "pageLoadStrategy": "normal",
+   "platform": "Linux",
+   "rotatable": false,
+   "~~~": "..."
+}
+[1533082321.686][INFO]: [37ab6acf4be79d7e368e3260482cd6a2] COMMAND ExecuteScript {
+   "args": [  ],
+   "script": "document.title = \"title\"; return 1;"
+}
+[1533082321.686][INFO]: Waiting for pending navigations...
+[1533082321.686][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=10) {
+   "expression": "1"
+}
+[1533082321.697][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=10) {
+   "result": {
+      "description": "1",
+      "type": "number",
+      "value": 1
+   }
+}
+[1533082321.697][DEBUG]: DevTools WebSocket Command: DOM.getDocument (id=11) {
+
+}
+[1533082321.701][DEBUG]: DevTools WebSocket Response: DOM.getDocument (id=11) {
+   "root": {
+      "backendNodeId": 2,
+      "baseURL": "data:,",
+      "childNodeCount": 1,
+      "children": [ {
+         "attributes": [  ],
+         "backendNodeId": 3,
+         "childNodeCount": 2,
+         "children": [ {
+            "attributes": [  ],
+            "backendNodeId": 4,
+            "childNodeCount": 0,
+            "localName": "head",
+            "nodeId": 7,
+            "nodeName": "HEAD",
+            "nodeType": 1,
+            "nodeValue": "",
+            "parentId": 6
+         }, {
+            "attributes": [  ],
+            "backendNodeId": 5,
+            "childNodeCount": 0,
+            "localName": "body",
+            "nodeId": 8,
+            "nodeName": "BODY",
+            "nodeType": 1,
+            "nodeValue": "",
+            "parentId": 6
+         } ],
+         "frameId": "68B1DB93B848C3F618D9304F7FE26D99",
+         "localName": "html",
+         "nodeId": 6,
+         "nodeName": "HTML",
+         "nodeType": 1,
+         "nodeValue": "",
+         "parentId": 5
+      } ],
+      "documentURL": "data:,",
+      "localName": "",
+      "nodeId": 5,
+      "nodeName": "#document",
+      "nodeType": 9,
+      "nodeValue": "",
+      "xmlVersion": ""
+   }
+}
+[1533082321.701][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=12) {
+   "expression": "var frame = document.createElement('iframe');frame.name = 'chromedriver dummy frame';frame.src = 'about:blank';document.body.appendChild(frame);window.setTimeout(function() {  document.body.removeC..."
+}
+[1533082321.716][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated {
+   "childNodeCount": 1,
+   "nodeId": 8
+}
+[1533082321.716][DEBUG]: DevTools WebSocket Event: Page.frameAttached {
+   "frameId": "F5F7DB3E16F8067CB249702C3F197C2F",
+   "parentFrameId": "68B1DB93B848C3F618D9304F7FE26D99",
+   "stack": {
+      "callFrames": [ {
+         "columnNumber": 125,
+         "functionName": "",
+         "lineNumber": 0,
+         "scriptId": "20",
+         "url": ""
+      } ]
+   }
+}
+[1533082321.751][DEBUG]: DevTools WebSocket Event: Page.frameStartedLoading {
+   "frameId": "F5F7DB3E16F8067CB249702C3F197C2F"
+}
+[1533082321.751][DEBUG]: DevTools WebSocket Event: Page.frameNavigated {
+   "frame": {
+      "id": "F5F7DB3E16F8067CB249702C3F197C2F",
+      "loaderId": "C386F2CD5A0B681ED28D85257B599C1B",
+      "mimeType": "text/html",
+      "name": "chromedriver dummy frame",
+      "parentId": "68B1DB93B848C3F618D9304F7FE26D99",
+      "securityOrigin": "://",
+      "url": "about:blank"
+   }
+}
+[1533082321.751][DEBUG]: DevTools WebSocket Event: Runtime.executionContextCreated {
+   "context": {
+      "auxData": {
+         "frameId": "F5F7DB3E16F8067CB249702C3F197C2F",
+         "isDefault": true
+      },
+      "id": 2,
+      "name": "",
+      "origin": "://"
+   }
+}
+[1533082321.751][DEBUG]: DevTools WebSocket Event: Page.frameStoppedLoading {
+   "frameId": "F5F7DB3E16F8067CB249702C3F197C2F"
+}
+[1533082321.752][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=12) {
+   "result": {
+      "description": "1",
+      "type": "number",
+      "value": 1
+   }
+}
+[1533082321.752][INFO]: Done waiting for pending navigations. Status: ok
+[1533082321.753][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=13) {
+   "expression": "(function() { // Copyright (c) 2012 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n/**\n * Enum f...",
+   "returnByValue": true
+}
+[1533082321.931][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated {
+   "childNodeCount": 0,
+   "nodeId": 8
+}
+[1533082321.931][DEBUG]: DevTools WebSocket Event: Runtime.executionContextDestroyed {
+   "executionContextId": 2
+}
+[1533082321.931][DEBUG]: DevTools WebSocket Event: Page.frameDetached {
+   "frameId": "F5F7DB3E16F8067CB249702C3F197C2F"
+}
+[1533082321.935][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated {
+   "childNodeCount": 1,
+   "nodeId": 7
+}
+[1533082321.935][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=13) {
+   "result": {
+      "type": "object",
+      "value": {
+         "status": 0,
+         "value": 1
+      }
+   }
+}
+[1533082321.935][INFO]: Waiting for pending navigations...
+[1533082321.935][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=14) {
+   "expression": "1"
+}
+[1533082321.952][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=14) {
+   "result": {
+      "description": "1",
+      "type": "number",
+      "value": 1
+   }
+}
+[1533082321.952][INFO]: Done waiting for pending navigations. Status: ok
+[1533082321.952][INFO]: [37ab6acf4be79d7e368e3260482cd6a2] RESPONSE ExecuteScript 1
+[1533082321.954][INFO]: [37ab6acf4be79d7e368e3260482cd6a2] COMMAND GetTitle {
+
+}
+[1533082321.954][INFO]: Waiting for pending navigations...
+[1533082321.954][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=15) {
+   "expression": "1"
+}
+[1533082321.955][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=15) {
+   "result": {
+      "description": "1",
+      "type": "number",
+      "value": 1
+   }
+}
+[1533082321.955][INFO]: Done waiting for pending navigations. Status: ok
+[1533082321.956][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=16) {
+   "expression": "(function() { // Copyright (c) 2012 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n/**\n * Enum f...",
+   "returnByValue": true
+}
+[1533082321.961][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=16) {
+   "result": {
+      "type": "object",
+      "value": {
+         "status": 0,
+         "value": "title"
+      }
+   }
+}
+[1533082321.961][INFO]: Waiting for pending navigations...
+[1533082321.961][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=17) {
+   "expression": "1"
+}
+[1533082321.962][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=17) {
+   "result": {
+      "description": "1",
+      "type": "number",
+      "value": 1
+   }
+}
+[1533082321.963][INFO]: Done waiting for pending navigations. Status: ok
+[1533082321.963][INFO]: [37ab6acf4be79d7e368e3260482cd6a2] RESPONSE GetTitle "title"
+[1533082321.964][INFO]: [37ab6acf4be79d7e368e3260482cd6a2] COMMAND Quit {
+
+}
+[0731/171201.978986:ERROR:nacl_helper_linux.cc(310)] NaCl helper process running without a sandbox!
+Most likely you need to configure your SUID sandbox correctly
+[1533082322.014][INFO]: [37ab6acf4be79d7e368e3260482cd6a2] RESPONSE Quit
+[1533082322.014][DEBUG]: Log type 'driver' lost 0 entries on destruction
+[1533082322.014][DEBUG]: Log type 'browser' lost 0 entries on destruction
diff --git a/chrome/test/chromedriver/log_replay/test_data/truncatedJSON.log b/chrome/test/chromedriver/log_replay/test_data/truncatedJSON.log
new file mode 100644
index 0000000..6f94031
--- /dev/null
+++ b/chrome/test/chromedriver/log_replay/test_data/truncatedJSON.log
@@ -0,0 +1,3 @@
+[1533082321.495][DEBUG]: DevTools WebSocket Command: Target.setAutoAttach (id=3) {
+   "autoAttach": true,
+   "waitForDebuggerOnStart": false,
\ No newline at end of file
diff --git a/chrome/test/data/webui/settings/multidevice_page_container_tests.js b/chrome/test/data/webui/settings/multidevice_page_container_tests.js
index 952fc9373..5166ba57 100644
--- a/chrome/test/data/webui/settings/multidevice_page_container_tests.js
+++ b/chrome/test/data/webui/settings/multidevice_page_container_tests.js
@@ -36,7 +36,7 @@
   function getFakePageContentData(mode) {
     return {
       mode: mode,
-      hostDevice: HOST_SET_MODES.includes(mode) ? {name: 'Pixel XL'} : null,
+      hostDeviceName: HOST_SET_MODES.includes(mode) ? 'Pixel XL' : undefined,
     };
   }
 
diff --git a/chrome/test/data/webui/settings/multidevice_page_tests.js b/chrome/test/data/webui/settings/multidevice_page_tests.js
index 2083422..4ebeabb 100644
--- a/chrome/test/data/webui/settings/multidevice_page_tests.js
+++ b/chrome/test/data/webui/settings/multidevice_page_tests.js
@@ -25,14 +25,12 @@
   let multidevicePage = null;
   let browserProxy = null;
   let HOST_SET_MODES;
-  const HOST_DEVICE = {
-    name: 'Pixel XL',
-  };
+  const HOST_DEVICE = 'Pixel XL';
 
-  function setPageContentData(newMode, newHostDevice) {
+  function setPageContentData(newMode, newHostDeviceName) {
     multidevicePage.pageContentData = {
       mode: newMode,
-      hostDevice: newHostDevice,
+      hostDeviceName: newHostDeviceName,
     };
     Polymer.dom.flush();
   }
@@ -66,7 +64,7 @@
   const getSubpage = () => multidevicePage.$$('settings-multidevice-subpage');
 
   test('clicking setup shows multidevice setup dialog', function() {
-    setPageContentData(settings.MultiDeviceSettingsMode.NO_HOST_SET, null);
+    setPageContentData(settings.MultiDeviceSettingsMode.NO_HOST_SET, undefined);
     const button = multidevicePage.$$('paper-button');
     assertTrue(!!button);
     button.click();
@@ -76,25 +74,24 @@
   test('headings render based on mode and host', function() {
     for (let mode of HOST_SET_MODES) {
       setPageContentData(mode, HOST_DEVICE);
-      assertEquals(getLabel(), HOST_DEVICE.name);
+      assertEquals(getLabel(), HOST_DEVICE);
     }
-    setPageContentData(settings.MultiDeviceSettingsMode.NO_HOST_SET, null);
-    assertNotEquals(getLabel(), HOST_DEVICE.name);
+    setPageContentData(settings.MultiDeviceSettingsMode.NO_HOST_SET, undefined);
+    assertNotEquals(getLabel(), HOST_DEVICE);
   });
 
   test('changing host device and fixing mode changes header', function() {
     setPageContentData(
         settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED, HOST_DEVICE);
-    assertEquals(getLabel(), HOST_DEVICE.name);
-    const anotherHost =
-        Object.assign(HOST_DEVICE, {name: 'Super Duper ' + HOST_DEVICE.name});
+    assertEquals(getLabel(), HOST_DEVICE);
+    const anotherHost = 'Super Duper ' + HOST_DEVICE;
     setPageContentData(
         settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED, anotherHost);
-    assertEquals(getLabel(), anotherHost.name);
+    assertEquals(getLabel(), anotherHost);
   });
 
   test('item is actionable if and only if a host is set', function() {
-    setPageContentData(settings.MultiDeviceSettingsMode.NO_HOST_SET, null);
+    setPageContentData(settings.MultiDeviceSettingsMode.NO_HOST_SET, undefined);
     assertFalse(
         multidevicePage.$$('#multidevice-item').hasAttribute('actionable'));
     for (let mode of HOST_SET_MODES) {
diff --git a/chrome/test/data/webui/settings/multidevice_subpage_tests.js b/chrome/test/data/webui/settings/multidevice_subpage_tests.js
index e2998aa..aa566783 100644
--- a/chrome/test/data/webui/settings/multidevice_subpage_tests.js
+++ b/chrome/test/data/webui/settings/multidevice_subpage_tests.js
@@ -8,14 +8,11 @@
   // enum settings.MultiDeviceSettingsMode from here so its initialization is
   // deferred to the suiteSetup function.
   let HOST_SET_MODES;
-  const HOST_DEVICE = {
-    name: 'Pixel XL',
-  };
 
   function setPageContentData(newMode) {
     multideviceSubpage.pageContentData = {
       mode: newMode,
-      hostDevice: HOST_DEVICE,
+      hostDeviceName: 'Pixel XL',
     };
     Polymer.dom.flush();
   }
diff --git a/chromecast/browser/accessibility/accessibility_manager.cc b/chromecast/browser/accessibility/accessibility_manager.cc
index 0afd97f4..446972a 100644
--- a/chromecast/browser/accessibility/accessibility_manager.cc
+++ b/chromecast/browser/accessibility/accessibility_manager.cc
@@ -75,6 +75,16 @@
 
 void AccessibilityManager::SetScreenReader(bool enable) {
   touch_exploration_manager_->Enable(enable);
+
+  // TODO(rdaum): Until we can fix triple-tap and two finger gesture conflicts
+  // between TouchExplorationController, FullscreenMagnifier, and
+  // TripleTapDetector, we have to make sure magnification is not on while
+  // screenreader is active.
+  // The triple-tap gesture can still be enabled, but will not do anything until
+  // screenreader is disabled again.
+  if (enable) {
+    magnification_controller_->SetEnabled(false);
+  }
 }
 
 void AccessibilityManager::SetTouchAccessibilityAnchorPoint(
diff --git a/chromecast/browser/cast_web_view.h b/chromecast/browser/cast_web_view.h
index 415b41e..99d746b 100644
--- a/chromecast/browser/cast_web_view.h
+++ b/chromecast/browser/cast_web_view.h
@@ -101,6 +101,9 @@
     // True if this CastWebView is for running a remote control app.
     bool is_remote_control_mode = false;
 
+    // Whether this CastWebView should be managed by web ui window manager.
+    bool managed = true;
+
     CreateParams();
   };
 
diff --git a/chromecast/graphics/cast_window_manager.h b/chromecast/graphics/cast_window_manager.h
index b17de4f3..906ae276 100644
--- a/chromecast/graphics/cast_window_manager.h
+++ b/chromecast/graphics/cast_window_manager.h
@@ -27,7 +27,10 @@
   // Note: these window IDs are ordered by z-order.
   enum WindowId {
     BOTTOM = -1,
+    // Base layer for WebUiManager and apps and activities managed by it.
     APP = BOTTOM,
+    // Apps running in this layer won't be managed by WebUiManager.
+    UNMANAGED_APP,
     DEBUG_OVERLAY,
     INFO_OVERLAY,
     SOFT_KEYBOARD,
diff --git a/chromeos/dbus/services/chrome_features_service_provider.cc b/chromeos/dbus/services/chrome_features_service_provider.cc
index 52c0e4be9..4cd0d1f 100644
--- a/chromeos/dbus/services/chrome_features_service_provider.cc
+++ b/chromeos/dbus/services/chrome_features_service_provider.cc
@@ -35,6 +35,14 @@
                           weak_ptr_factory_.GetWeakPtr()),
       base::BindRepeating(&ChromeFeaturesServiceProvider::OnExported,
                           weak_ptr_factory_.GetWeakPtr()));
+  exported_object->ExportMethod(
+      kChromeFeaturesServiceInterface,
+      kChromeFeaturesServiceIsShillSandboxingEnabledMethod,
+      base::BindRepeating(
+          &ChromeFeaturesServiceProvider::IsShillSandboxingEnabled,
+          weak_ptr_factory_.GetWeakPtr()),
+      base::BindRepeating(&ChromeFeaturesServiceProvider::OnExported,
+                          weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ChromeFeaturesServiceProvider::OnExported(
@@ -76,4 +84,14 @@
   response_sender.Run(std::move(response));
 }
 
+void ChromeFeaturesServiceProvider::IsShillSandboxingEnabled(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  std::unique_ptr<dbus::Response> response =
+      dbus::Response::FromMethodCall(method_call);
+  dbus::MessageWriter writer(response.get());
+  writer.AppendBool(delegate_->IsShillSandboxingEnabled());
+  response_sender.Run(std::move(response));
+}
+
 }  // namespace chromeos
diff --git a/chromeos/dbus/services/chrome_features_service_provider.h b/chromeos/dbus/services/chrome_features_service_provider.h
index b6cc6fa..d668064 100644
--- a/chromeos/dbus/services/chrome_features_service_provider.h
+++ b/chromeos/dbus/services/chrome_features_service_provider.h
@@ -42,6 +42,7 @@
 
     virtual bool IsCrostiniEnabled(const std::string& user_id_hash) = 0;
     virtual bool IsUsbguardEnabled() = 0;
+    virtual bool IsShillSandboxingEnabled() = 0;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(Delegate);
@@ -65,6 +66,9 @@
                          dbus::ExportedObject::ResponseSender response_sender);
   void IsUsbguardEnabled(dbus::MethodCall* method_call,
                          dbus::ExportedObject::ResponseSender response_sender);
+  void IsShillSandboxingEnabled(
+      dbus::MethodCall* method_call,
+      dbus::ExportedObject::ResponseSender response_sender);
 
   std::unique_ptr<Delegate> delegate_;
   // Keep this last so that all weak pointers will be invalidated at the
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
index 42d59f5..0d0191da 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
@@ -45,15 +45,18 @@
 MultiDeviceSetupImpl::Factory::BuildInstance(
     PrefService* pref_service,
     device_sync::DeviceSyncClient* device_sync_client,
-    secure_channel::SecureChannelClient* secure_channel_client) {
-  return base::WrapUnique(new MultiDeviceSetupImpl(
-      pref_service, device_sync_client, secure_channel_client));
+    secure_channel::SecureChannelClient* secure_channel_client,
+    AuthTokenValidator* auth_token_validator) {
+  return base::WrapUnique(
+      new MultiDeviceSetupImpl(pref_service, device_sync_client,
+                               secure_channel_client, auth_token_validator));
 }
 
 MultiDeviceSetupImpl::MultiDeviceSetupImpl(
     PrefService* pref_service,
     device_sync::DeviceSyncClient* device_sync_client,
-    secure_channel::SecureChannelClient* secure_channel_client)
+    secure_channel::SecureChannelClient* secure_channel_client,
+    AuthTokenValidator* auth_token_validator)
     : eligible_host_devices_provider_(
           EligibleHostDevicesProviderImpl::Factory::Get()->BuildInstance(
               device_sync_client)),
@@ -124,6 +127,9 @@
 
 void MultiDeviceSetupImpl::SetHostDevice(const std::string& host_device_id,
                                          SetHostDeviceCallback callback) {
+  // TODO(crbug.com/870122): Use AuthTokenValidator to verify that the
+  // user is authenticated.
+
   cryptauth::RemoteDeviceRefList eligible_devices =
       eligible_host_devices_provider_->GetEligibleHostDevices();
   auto it =
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.h b/chromeos/services/multidevice_setup/multidevice_setup_impl.h
index 6194779..9a06e57 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl.h
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.h
@@ -29,6 +29,7 @@
 namespace multidevice_setup {
 
 class AccountStatusChangeDelegateNotifier;
+class AuthTokenValidator;
 class HostBackendDelegate;
 class HostStatusProvider;
 class HostVerifier;
@@ -48,7 +49,8 @@
     virtual std::unique_ptr<mojom::MultiDeviceSetup> BuildInstance(
         PrefService* pref_service,
         device_sync::DeviceSyncClient* device_sync_client,
-        secure_channel::SecureChannelClient* secure_channel_client);
+        secure_channel::SecureChannelClient* secure_channel_client,
+        AuthTokenValidator* auth_token_validator);
 
    private:
     static Factory* test_factory_;
@@ -62,7 +64,8 @@
   MultiDeviceSetupImpl(
       PrefService* pref_service,
       device_sync::DeviceSyncClient* device_sync_client,
-      secure_channel::SecureChannelClient* secure_channel_client);
+      secure_channel::SecureChannelClient* secure_channel_client,
+      AuthTokenValidator* auth_token_validator);
 
   // mojom::MultiDeviceSetup:
   void SetAccountStatusChangeDelegate(
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
index abd860a..d607c6cf 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
@@ -26,6 +26,7 @@
 #include "chromeos/services/multidevice_setup/host_status_provider_impl.h"
 #include "chromeos/services/multidevice_setup/host_verifier_impl.h"
 #include "chromeos/services/multidevice_setup/multidevice_setup_impl.h"
+#include "chromeos/services/multidevice_setup/public/cpp/fake_auth_token_validator.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "chromeos/services/multidevice_setup/setup_flow_completion_recorder_impl.h"
 #include "chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
@@ -361,6 +362,7 @@
         std::make_unique<device_sync::FakeDeviceSyncClient>();
     fake_secure_channel_client_ =
         std::make_unique<secure_channel::FakeSecureChannelClient>();
+    fake_auth_token_validator_ = std::make_unique<FakeAuthTokenValidator>();
 
     fake_eligible_host_devices_provider_factory_ =
         std::make_unique<FakeEligibleHostDevicesProviderFactory>(
@@ -411,7 +413,7 @@
 
     multidevice_setup_ = MultiDeviceSetupImpl::Factory::Get()->BuildInstance(
         test_pref_service_.get(), fake_device_sync_client_.get(),
-        fake_secure_channel_client_.get());
+        fake_secure_channel_client_.get(), fake_auth_token_validator_.get());
   }
 
   void TearDown() override {
@@ -662,6 +664,7 @@
   std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
   std::unique_ptr<secure_channel::FakeSecureChannelClient>
       fake_secure_channel_client_;
+  std::unique_ptr<FakeAuthTokenValidator> fake_auth_token_validator_;
 
   std::unique_ptr<FakeEligibleHostDevicesProviderFactory>
       fake_eligible_host_devices_provider_factory_;
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc b/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
index 308e7777..320cbc4 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
@@ -40,18 +40,22 @@
 MultiDeviceSetupInitializer::Factory::BuildInstance(
     PrefService* pref_service,
     device_sync::DeviceSyncClient* device_sync_client,
-    secure_channel::SecureChannelClient* secure_channel_client) {
+    secure_channel::SecureChannelClient* secure_channel_client,
+    AuthTokenValidator* auth_token_validator) {
   return base::WrapUnique(new MultiDeviceSetupInitializer(
-      pref_service, device_sync_client, secure_channel_client));
+      pref_service, device_sync_client, secure_channel_client,
+      auth_token_validator));
 }
 
 MultiDeviceSetupInitializer::MultiDeviceSetupInitializer(
     PrefService* pref_service,
     device_sync::DeviceSyncClient* device_sync_client,
-    secure_channel::SecureChannelClient* secure_channel_client)
+    secure_channel::SecureChannelClient* secure_channel_client,
+    AuthTokenValidator* auth_token_validator)
     : pref_service_(pref_service),
       device_sync_client_(device_sync_client),
-      secure_channel_client_(secure_channel_client) {
+      secure_channel_client_(secure_channel_client),
+      auth_token_validator_(auth_token_validator) {
   if (device_sync_client_->is_ready()) {
     InitializeImplementation();
     return;
@@ -205,7 +209,8 @@
   DCHECK(!multidevice_setup_impl_);
 
   multidevice_setup_impl_ = MultiDeviceSetupImpl::Factory::Get()->BuildInstance(
-      pref_service_, device_sync_client_, secure_channel_client_);
+      pref_service_, device_sync_client_, secure_channel_client_,
+      auth_token_validator_);
 
   if (pending_delegate_) {
     multidevice_setup_impl_->SetAccountStatusChangeDelegate(
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_initializer.h b/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
index ad44cea7..baf7f1a 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
+++ b/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
@@ -24,6 +24,8 @@
 
 namespace multidevice_setup {
 
+class AuthTokenValidator;
+
 // Initializes the MultiDeviceSetup service. This class is responsible for
 // waiting for asynchronous initialization steps to complete before creating
 // the concrete implementation of the mojom::MultiDeviceSetup interface.
@@ -39,7 +41,8 @@
     virtual std::unique_ptr<MultiDeviceSetupBase> BuildInstance(
         PrefService* pref_service,
         device_sync::DeviceSyncClient* device_sync_client,
-        secure_channel::SecureChannelClient* secure_channel_client);
+        secure_channel::SecureChannelClient* secure_channel_client,
+        AuthTokenValidator* auth_token_validator);
 
    private:
     static Factory* test_factory_;
@@ -51,7 +54,8 @@
   MultiDeviceSetupInitializer(
       PrefService* pref_service,
       device_sync::DeviceSyncClient* device_sync_client,
-      secure_channel::SecureChannelClient* secure_channel_client);
+      secure_channel::SecureChannelClient* secure_channel_client,
+      AuthTokenValidator* auth_token_validator);
 
   // mojom::MultiDeviceSetup:
   void SetAccountStatusChangeDelegate(
@@ -81,6 +85,7 @@
   PrefService* pref_service_;
   device_sync::DeviceSyncClient* device_sync_client_;
   secure_channel::SecureChannelClient* secure_channel_client_;
+  AuthTokenValidator* auth_token_validator_;
 
   std::unique_ptr<mojom::MultiDeviceSetup> multidevice_setup_impl_;
 
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service.cc b/chromeos/services/multidevice_setup/multidevice_setup_service.cc
index 6974980f..b3e45c3 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_service.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service.cc
@@ -28,12 +28,14 @@
 MultiDeviceSetupService::MultiDeviceSetupService(
     PrefService* pref_service,
     device_sync::DeviceSyncClient* device_sync_client,
-    secure_channel::SecureChannelClient* secure_channel_client)
+    secure_channel::SecureChannelClient* secure_channel_client,
+    AuthTokenValidator* auth_token_validator)
     : multidevice_setup_(
           MultiDeviceSetupInitializer::Factory::Get()->BuildInstance(
               pref_service,
               device_sync_client,
-              secure_channel_client)) {}
+              secure_channel_client,
+              auth_token_validator)) {}
 
 MultiDeviceSetupService::~MultiDeviceSetupService() = default;
 
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service.h b/chromeos/services/multidevice_setup/multidevice_setup_service.h
index f3ae2e8d..820dd0f 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_service.h
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service.h
@@ -26,6 +26,7 @@
 
 namespace multidevice_setup {
 
+class AuthTokenValidator;
 class MultiDeviceSetupBase;
 
 // Service which provides an implementation for mojom::MultiDeviceSetup. This
@@ -36,7 +37,8 @@
   MultiDeviceSetupService(
       PrefService* pref_service,
       device_sync::DeviceSyncClient* device_sync_client,
-      secure_channel::SecureChannelClient* secure_channel_client);
+      secure_channel::SecureChannelClient* secure_channel_client,
+      AuthTokenValidator* auth_token_validator);
   ~MultiDeviceSetupService() override;
 
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
index ca55e78..1832d47 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
@@ -11,6 +11,7 @@
 #include "chromeos/services/multidevice_setup/fake_host_status_observer.h"
 #include "chromeos/services/multidevice_setup/multidevice_setup_impl.h"
 #include "chromeos/services/multidevice_setup/multidevice_setup_service.h"
+#include "chromeos/services/multidevice_setup/public/cpp/fake_auth_token_validator.h"
 #include "chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup.h"
 #include "chromeos/services/multidevice_setup/public/mojom/constants.mojom.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
@@ -34,10 +35,12 @@
       sync_preferences::TestingPrefServiceSyncable*
           expected_testing_pref_service,
       device_sync::FakeDeviceSyncClient* expected_device_sync_client,
-      secure_channel::FakeSecureChannelClient* expected_secure_channel_client)
+      secure_channel::FakeSecureChannelClient* expected_secure_channel_client,
+      FakeAuthTokenValidator* expected_auth_token_validator)
       : expected_testing_pref_service_(expected_testing_pref_service),
         expected_device_sync_client_(expected_device_sync_client),
-        expected_secure_channel_client_(expected_secure_channel_client) {}
+        expected_secure_channel_client_(expected_secure_channel_client),
+        expected_auth_token_validator_(expected_auth_token_validator) {}
 
   ~FakeMultiDeviceSetupFactory() override = default;
 
@@ -47,11 +50,13 @@
   std::unique_ptr<mojom::MultiDeviceSetup> BuildInstance(
       PrefService* pref_service,
       device_sync::DeviceSyncClient* device_sync_client,
-      secure_channel::SecureChannelClient* secure_channel_client) override {
+      secure_channel::SecureChannelClient* secure_channel_client,
+      AuthTokenValidator* auth_token_validator) override {
     EXPECT_FALSE(instance_);
     EXPECT_EQ(expected_testing_pref_service_, pref_service);
     EXPECT_EQ(expected_device_sync_client_, device_sync_client);
     EXPECT_EQ(expected_secure_channel_client_, secure_channel_client);
+    EXPECT_EQ(expected_auth_token_validator_, auth_token_validator);
 
     auto instance = std::make_unique<FakeMultiDeviceSetup>();
     instance_ = instance.get();
@@ -61,6 +66,7 @@
   sync_preferences::TestingPrefServiceSyncable* expected_testing_pref_service_;
   device_sync::FakeDeviceSyncClient* expected_device_sync_client_;
   secure_channel::FakeSecureChannelClient* expected_secure_channel_client_;
+  FakeAuthTokenValidator* expected_auth_token_validator_;
 
   FakeMultiDeviceSetup* instance_ = nullptr;
 
@@ -84,11 +90,13 @@
         std::make_unique<device_sync::FakeDeviceSyncClient>();
     fake_secure_channel_client_ =
         std::make_unique<secure_channel::FakeSecureChannelClient>();
+    fake_auth_token_validator_ = std::make_unique<FakeAuthTokenValidator>();
 
     fake_multidevice_setup_factory_ =
         std::make_unique<FakeMultiDeviceSetupFactory>(
             test_pref_service_.get(), fake_device_sync_client_.get(),
-            fake_secure_channel_client_.get());
+            fake_secure_channel_client_.get(),
+            fake_auth_token_validator_.get());
     MultiDeviceSetupImpl::Factory::SetFactoryForTesting(
         fake_multidevice_setup_factory_.get());
 
@@ -96,7 +104,8 @@
         service_manager::TestConnectorFactory::CreateForUniqueService(
             std::make_unique<MultiDeviceSetupService>(
                 test_pref_service_.get(), fake_device_sync_client_.get(),
-                fake_secure_channel_client_.get()));
+                fake_secure_channel_client_.get(),
+                fake_auth_token_validator_.get()));
 
     auto connector = connector_factory_->CreateConnector();
     connector->BindInterface(mojom::kServiceName, &multidevice_setup_ptr_);
@@ -152,6 +161,7 @@
   std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
   std::unique_ptr<secure_channel::FakeSecureChannelClient>
       fake_secure_channel_client_;
+  std::unique_ptr<FakeAuthTokenValidator> fake_auth_token_validator_;
 
   std::unique_ptr<FakeMultiDeviceSetupFactory> fake_multidevice_setup_factory_;
 
diff --git a/chromeos/services/multidevice_setup/public/cpp/BUILD.gn b/chromeos/services/multidevice_setup/public/cpp/BUILD.gn
index 337bc13..3f12e322 100644
--- a/chromeos/services/multidevice_setup/public/cpp/BUILD.gn
+++ b/chromeos/services/multidevice_setup/public/cpp/BUILD.gn
@@ -20,6 +20,16 @@
   ]
 }
 
+source_set("auth_token_validator") {
+  sources = [
+    "auth_token_validator.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
+
 source_set("prefs") {
   sources = [
     "prefs.cc",
@@ -36,6 +46,8 @@
   testonly = true
 
   sources = [
+    "fake_auth_token_validator.cc",
+    "fake_auth_token_validator.h",
     "fake_multidevice_setup.cc",
     "fake_multidevice_setup.h",
     "fake_multidevice_setup_client.cc",
@@ -43,6 +55,7 @@
   ]
 
   public_deps = [
+    ":auth_token_validator",
     ":cpp",
     "//chromeos/services/multidevice_setup",
     "//chromeos/services/multidevice_setup/public/mojom",
diff --git a/chromeos/services/multidevice_setup/public/cpp/auth_token_validator.h b/chromeos/services/multidevice_setup/public/cpp/auth_token_validator.h
new file mode 100644
index 0000000..2618c49
--- /dev/null
+++ b/chromeos/services/multidevice_setup/public/cpp/auth_token_validator.h
@@ -0,0 +1,32 @@
+// 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_MULTIDEVICE_SETUP_PUBLIC_CPP_AUTH_TOKEN_VALIDATOR_H_
+#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_PUBLIC_CPP_AUTH_TOKEN_VALIDATOR_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+// Validates a given auth token.
+class AuthTokenValidator {
+ public:
+  AuthTokenValidator() = default;
+  virtual ~AuthTokenValidator() = default;
+
+  virtual bool IsAuthTokenValid(const std::string& auth_token) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AuthTokenValidator);
+};
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_PUBLIC_CPP_AUTH_TOKEN_VALIDATOR_H_
diff --git a/chromeos/services/multidevice_setup/public/cpp/fake_auth_token_validator.cc b/chromeos/services/multidevice_setup/public/cpp/fake_auth_token_validator.cc
new file mode 100644
index 0000000..2e209a7f
--- /dev/null
+++ b/chromeos/services/multidevice_setup/public/cpp/fake_auth_token_validator.cc
@@ -0,0 +1,24 @@
+// 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/multidevice_setup/public/cpp/fake_auth_token_validator.h"
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+FakeAuthTokenValidator::FakeAuthTokenValidator() = default;
+
+FakeAuthTokenValidator::~FakeAuthTokenValidator() = default;
+
+bool FakeAuthTokenValidator::IsAuthTokenValid(const std::string& auth_token) {
+  if (!expected_auth_token_)
+    return false;
+
+  return *expected_auth_token_ == auth_token;
+}
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/public/cpp/fake_auth_token_validator.h b/chromeos/services/multidevice_setup/public/cpp/fake_auth_token_validator.h
new file mode 100644
index 0000000..7ecff04d
--- /dev/null
+++ b/chromeos/services/multidevice_setup/public/cpp/fake_auth_token_validator.h
@@ -0,0 +1,41 @@
+// 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_MULTIDEVICE_SETUP_PUBLIC_CPP_FAKE_AUTH_TOKEN_VALIDATOR_H_
+#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_PUBLIC_CPP_FAKE_AUTH_TOKEN_VALIDATOR_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "chromeos/services/multidevice_setup/public/cpp/auth_token_validator.h"
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+// Fake AuthTokenValidator implementation for tests.
+class FakeAuthTokenValidator : public AuthTokenValidator {
+ public:
+  FakeAuthTokenValidator();
+  ~FakeAuthTokenValidator() override;
+
+  // AuthTokenValidator:
+  bool IsAuthTokenValid(const std::string& auth_token) override;
+
+  void set_expected_auth_token(const std::string& expected_auth_token) {
+    expected_auth_token_ = expected_auth_token;
+  }
+
+ private:
+  base::Optional<std::string> expected_auth_token_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeAuthTokenValidator);
+};
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_PUBLIC_CPP_AUTH_TOKEN_VALIDATOR_H_
diff --git a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
index 3cecf25..483140fe 100644
--- a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
+++ b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
@@ -42,7 +42,8 @@
   std::unique_ptr<MultiDeviceSetupBase> BuildInstance(
       PrefService* pref_service,
       device_sync::DeviceSyncClient* device_sync_client,
-      secure_channel::SecureChannelClient* secure_channel_client) override {
+      secure_channel::SecureChannelClient* secure_channel_client,
+      AuthTokenValidator* auth_token_validator) override {
     EXPECT_TRUE(fake_multidevice_setup_);
     return std::move(fake_multidevice_setup_);
   }
@@ -97,7 +98,8 @@
 
     auto multidevice_setup_service = std::make_unique<MultiDeviceSetupService>(
         nullptr /* pref_service */, nullptr /* device_sync_client */,
-        nullptr /* secure_channel_client */);
+        nullptr /* secure_channel_client */,
+        nullptr /* auth_token_validator */);
 
     connector_factory_ =
         service_manager::TestConnectorFactory::CreateForUniqueService(
diff --git a/components/crash/core/browser/resources/crashes.css b/components/crash/core/browser/resources/crashes.css
index 05687c1..f923db7 100644
--- a/components/crash/core/browser/resources/crashes.css
+++ b/components/crash/core/browser/resources/crashes.css
@@ -7,6 +7,7 @@
 }
 
 h1 {
+  -webkit-padding-start: 75px;
   background-image: url(../../../../resources/sadtab.svg);
   background-position: left;
   background-repeat: no-repeat;
diff --git a/components/gcm_driver/account_tracker.cc b/components/gcm_driver/account_tracker.cc
index d95e936..254c65a 100644
--- a/components/gcm_driver/account_tracker.cc
+++ b/components/gcm_driver/account_tracker.cc
@@ -8,15 +8,15 @@
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
 #include "base/trace_event/trace_event.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace gcm {
 
 AccountTracker::AccountTracker(
     identity::IdentityManager* identity_manager,
-    net::URLRequestContextGetter* request_context_getter)
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
     : identity_manager_(identity_manager),
-      request_context_getter_(request_context_getter),
+      url_loader_factory_(std::move(url_loader_factory)),
       shutdown_called_(false) {
   identity_manager_->AddObserver(this);
 }
@@ -169,7 +169,7 @@
 
   DVLOG(1) << "StartFetching " << account_key;
   AccountIdFetcher* fetcher = new AccountIdFetcher(
-      identity_manager_, request_context_getter_.get(), this, account_key);
+      identity_manager_, url_loader_factory_, this, account_key);
   user_info_requests_[account_key] = base::WrapUnique(fetcher);
   fetcher->Start();
 }
@@ -205,11 +205,11 @@
 
 AccountIdFetcher::AccountIdFetcher(
     identity::IdentityManager* identity_manager,
-    net::URLRequestContextGetter* request_context_getter,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     AccountTracker* tracker,
     const std::string& account_key)
     : identity_manager_(identity_manager),
-      request_context_getter_(request_context_getter),
+      url_loader_factory_(std::move(url_loader_factory)),
       tracker_(tracker),
       account_key_(account_key) {
   TRACE_EVENT_ASYNC_BEGIN1("identity", "AccountIdFetcher", this, "account_key",
@@ -247,7 +247,7 @@
   TRACE_EVENT_ASYNC_STEP_PAST0("identity", "AccountIdFetcher", this,
                                "AccessTokenFetched");
 
-  gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(request_context_getter_));
+  gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(url_loader_factory_));
 
   const int kMaxGetUserIdRetries = 3;
   gaia_oauth_client_->GetUserId(access_token_info.token, kMaxGetUserIdRetries,
diff --git a/components/gcm_driver/account_tracker.h b/components/gcm_driver/account_tracker.h
index 2eb3a63..7b670aa 100644
--- a/components/gcm_driver/account_tracker.h
+++ b/components/gcm_driver/account_tracker.h
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "base/memory/scoped_refptr.h"
 #include "base/observer_list.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "services/identity/public/cpp/access_token_fetcher.h"
@@ -17,8 +18,8 @@
 
 class GoogleServiceAuthError;
 
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
 }
 
 namespace gcm {
@@ -42,8 +43,9 @@
 // 4. If there is no primary account, there are no other accounts.
 class AccountTracker : public identity::IdentityManager::Observer {
  public:
-  AccountTracker(identity::IdentityManager* identity_manager,
-                 net::URLRequestContextGetter* request_context_getter);
+  AccountTracker(
+      identity::IdentityManager* identity_manager,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
   ~AccountTracker() override;
 
   class Observer {
@@ -103,7 +105,7 @@
   void DeleteFetcher(AccountIdFetcher* fetcher);
 
   identity::IdentityManager* identity_manager_;
-  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   std::map<std::string, std::unique_ptr<AccountIdFetcher>> user_info_requests_;
   std::map<std::string, AccountState> accounts_;
   base::ObserverList<Observer> observer_list_;
@@ -112,10 +114,11 @@
 
 class AccountIdFetcher : public gaia::GaiaOAuthClient::Delegate {
  public:
-  AccountIdFetcher(identity::IdentityManager* identity_manager,
-                   net::URLRequestContextGetter* request_context_getter,
-                   AccountTracker* tracker,
-                   const std::string& account_key);
+  AccountIdFetcher(
+      identity::IdentityManager* identity_manager,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      AccountTracker* tracker,
+      const std::string& account_key);
   ~AccountIdFetcher() override;
 
   const std::string& account_key() { return account_key_; }
@@ -132,7 +135,7 @@
 
  private:
   identity::IdentityManager* identity_manager_;
-  net::URLRequestContextGetter* request_context_getter_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   AccountTracker* tracker_;
   const std::string account_key_;
 
diff --git a/components/gcm_driver/account_tracker_unittest.cc b/components/gcm_driver/account_tracker_unittest.cc
index d12abec2..9e8bd38 100644
--- a/components/gcm_driver/account_tracker_unittest.cc
+++ b/components/gcm_driver/account_tracker_unittest.cc
@@ -12,14 +12,15 @@
 #include "build/build_config.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "net/http/http_status_code.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_test_util.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
+const char kOAuthURL[] = "https://www.googleapis.com/oauth2/v1/userinfo";
 const char kPrimaryAccountEmail[] = "primary_account@example.com";
 
 enum TrackingEventType { SIGN_IN, SIGN_OUT };
@@ -243,7 +244,8 @@
   void SetUp() override {
     account_tracker_.reset(new AccountTracker(
         identity_test_env_.identity_manager(),
-        new net::TestURLRequestContextGetter(message_loop_.task_runner())));
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_)));
     account_tracker_->AddObserver(&observer_);
   }
 
@@ -308,8 +310,7 @@
            "\" }";
   }
 
-  void ReturnOAuthUrlFetchResults(int fetcher_id,
-                                  net::HttpStatusCode response_code,
+  void ReturnOAuthUrlFetchResults(net::HttpStatusCode response_code,
                                   const std::string& response_string);
 
   void ReturnOAuthUrlFetchSuccess(const std::string& account_key);
@@ -325,9 +326,13 @@
     return primary_account_id;
   }
 
+  network::TestURLLoaderFactory* test_url_loader_factory() {
+    return &test_url_loader_factory_;
+  }
+
  private:
   base::MessageLoopForIO message_loop_;  // net:: stuff needs IO message loop.
-  net::TestURLFetcherFactory test_fetcher_factory_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
   identity::IdentityTestEnvironment identity_test_env_;
 
   std::unique_ptr<AccountTracker> account_tracker_;
@@ -335,29 +340,24 @@
 };
 
 void AccountTrackerTest::ReturnOAuthUrlFetchResults(
-    int fetcher_id,
     net::HttpStatusCode response_code,
     const std::string& response_string) {
-  net::TestURLFetcher* fetcher =
-      test_fetcher_factory_.GetFetcherByID(fetcher_id);
-  ASSERT_TRUE(fetcher);
-  fetcher->set_response_code(response_code);
-  fetcher->SetResponseString(response_string);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_TRUE(test_url_loader_factory()->SimulateResponseForPendingRequest(
+      GURL(kOAuthURL), network::URLLoaderCompletionStatus(net::OK),
+      network::CreateResourceResponseHead(response_code), response_string));
 }
 
 void AccountTrackerTest::ReturnOAuthUrlFetchSuccess(
     const std::string& account_id) {
   IssueAccessToken(account_id);
-  ReturnOAuthUrlFetchResults(gaia::GaiaOAuthClient::kUrlFetcherId, net::HTTP_OK,
+  ReturnOAuthUrlFetchResults(net::HTTP_OK,
                              GetValidTokenInfoResponse(account_id));
 }
 
 void AccountTrackerTest::ReturnOAuthUrlFetchFailure(
     const std::string& account_id) {
   IssueAccessToken(account_id);
-  ReturnOAuthUrlFetchResults(gaia::GaiaOAuthClient::kUrlFetcherId,
-                             net::HTTP_BAD_REQUEST, "");
+  ReturnOAuthUrlFetchResults(net::HTTP_BAD_REQUEST, "");
 }
 
 // Primary tests just involve the Active account
diff --git a/components/gcm_driver/gcm_account_tracker_unittest.cc b/components/gcm_driver/gcm_account_tracker_unittest.cc
index 2abba16..8fbfd44 100644
--- a/components/gcm_driver/gcm_account_tracker_unittest.cc
+++ b/components/gcm_driver/gcm_account_tracker_unittest.cc
@@ -13,16 +13,20 @@
 #include "base/message_loop/message_loop.h"
 #include "components/gcm_driver/fake_gcm_driver.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "net/base/ip_endpoint.h"
 #include "net/http/http_status_code.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace gcm {
 
 namespace {
 
+const char kOAuthURL[] = "https://www.googleapis.com/oauth2/v1/userinfo";
+
 const char kEmail1[] = "account_1@me.com";
 const char kEmail2[] = "account_2@me.com";
 
@@ -194,19 +198,25 @@
   bool IsTokenReportingRequired() const;
   base::TimeDelta GetTimeToNextTokenReporting() const;
 
+  network::TestURLLoaderFactory* test_url_loader_factory() {
+    return &test_url_loader_factory_;
+  }
+
  private:
   CustomFakeGCMDriver driver_;
 
   base::MessageLoop message_loop_;
-  net::TestURLFetcherFactory test_fetcher_factory_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
   identity::IdentityTestEnvironment identity_test_env_;
+
   std::unique_ptr<GCMAccountTracker> tracker_;
 };
 
 GCMAccountTrackerTest::GCMAccountTrackerTest() {
   std::unique_ptr<AccountTracker> gaia_account_tracker(new AccountTracker(
       identity_test_env_.identity_manager(),
-      new net::TestURLRequestContextGetter(message_loop_.task_runner())));
+      base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+          &test_url_loader_factory_)));
 
   tracker_.reset(new GCMAccountTracker(std::move(gaia_account_tracker),
                                        identity_test_env_.identity_manager(),
@@ -239,12 +249,13 @@
     const std::string& account_id) {
   IssueAccessToken(account_id);
 
-  net::TestURLFetcher* fetcher = test_fetcher_factory_.GetFetcherByID(
-      gaia::GaiaOAuthClient::kUrlFetcherId);
-  ASSERT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenInfoResponse(account_id));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_TRUE(test_url_loader_factory()->IsPending(kOAuthURL));
+  test_url_loader_factory()->SimulateResponseForPendingRequest(
+      GURL(kOAuthURL), network::URLLoaderCompletionStatus(net::OK),
+      network::CreateResourceResponseHead(net::HTTP_OK),
+      GetValidTokenInfoResponse(account_id));
+
+  GetValidTokenInfoResponse(account_id);
 }
 
 std::string GCMAccountTrackerTest::AddPrimaryAccount(const std::string& email) {
diff --git a/components/gcm_driver/gcm_profile_service.cc b/components/gcm_driver/gcm_profile_service.cc
index 47bd3d00..26f18ce 100644
--- a/components/gcm_driver/gcm_profile_service.cc
+++ b/components/gcm_driver/gcm_profile_service.cc
@@ -28,7 +28,6 @@
 #include "components/gcm_driver/gcm_client_factory.h"
 #include "components/gcm_driver/gcm_desktop_utils.h"
 #include "components/gcm_driver/gcm_driver_desktop.h"
-#include "net/url_request/url_request_context_getter.h"
 #include "services/identity/public/cpp/identity_manager.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #endif
@@ -41,9 +40,10 @@
 class GCMProfileService::IdentityObserver
     : public identity::IdentityManager::Observer {
  public:
-  IdentityObserver(identity::IdentityManager* identity_manager,
-                   net::URLRequestContextGetter* request_context,
-                   GCMDriver* driver);
+  IdentityObserver(
+      identity::IdentityManager* identity_manager,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      GCMDriver* driver);
   ~IdentityObserver() override;
 
   // identity::IdentityManager::Observer:
@@ -52,7 +52,8 @@
       const AccountInfo& previous_primary_account_info) override;
 
  private:
-  void StartAccountTracker(net::URLRequestContextGetter* request_context);
+  void StartAccountTracker(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 
   GCMDriver* driver_;
   identity::IdentityManager* identity_manager_;
@@ -69,7 +70,7 @@
 
 GCMProfileService::IdentityObserver::IdentityObserver(
     identity::IdentityManager* identity_manager,
-    net::URLRequestContextGetter* request_context,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     GCMDriver* driver)
     : driver_(driver),
       identity_manager_(identity_manager),
@@ -77,7 +78,7 @@
   identity_manager_->AddObserver(this);
 
   OnPrimaryAccountSet(identity_manager_->GetPrimaryAccountInfo());
-  StartAccountTracker(request_context);
+  StartAccountTracker(std::move(url_loader_factory));
 }
 
 GCMProfileService::IdentityObserver::~IdentityObserver() {
@@ -106,12 +107,12 @@
 }
 
 void GCMProfileService::IdentityObserver::StartAccountTracker(
-    net::URLRequestContextGetter* request_context) {
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
   if (gcm_account_tracker_)
     return;
 
   std::unique_ptr<AccountTracker> gaia_account_tracker(
-      new AccountTracker(identity_manager_, request_context));
+      new AccountTracker(identity_manager_, std::move(url_loader_factory)));
 
   gcm_account_tracker_.reset(new GCMAccountTracker(
       std::move(gaia_account_tracker), identity_manager_, driver_));
@@ -154,7 +155,8 @@
     const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
     const scoped_refptr<base::SequencedTaskRunner>& io_task_runner,
     scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
-    : identity_manager_(identity_manager), request_context_(request_context) {
+    : identity_manager_(identity_manager),
+      url_loader_factory_(url_loader_factory) {
   driver_ = CreateGCMDriverDesktop(
       std::move(gcm_client_factory), prefs,
       path.Append(gcm_driver::kGCMStoreDirname),
@@ -163,8 +165,8 @@
       url_loader_factory, channel, product_category_for_subtypes,
       ui_task_runner, io_task_runner, blocking_task_runner);
 
-  identity_observer_.reset(
-      new IdentityObserver(identity_manager_, request_context_, driver_.get()));
+  identity_observer_.reset(new IdentityObserver(
+      identity_manager_, url_loader_factory, driver_.get()));
 }
 #endif  // BUILDFLAG(USE_GCM_FROM_PLATFORM)
 
@@ -188,7 +190,7 @@
 #if !BUILDFLAG(USE_GCM_FROM_PLATFORM)
   if (identity_observer_) {
     identity_observer_ = std::make_unique<IdentityObserver>(
-        identity_manager_, request_context_, driver.get());
+        identity_manager_, url_loader_factory_, driver.get());
   }
 #endif  // !BUILDFLAG(USE_GCM_FROM_PLATFORM)
 }
diff --git a/components/gcm_driver/gcm_profile_service.h b/components/gcm_driver/gcm_profile_service.h
index 0da5c055..90b40e1b 100644
--- a/components/gcm_driver/gcm_profile_service.h
+++ b/components/gcm_driver/gcm_profile_service.h
@@ -94,7 +94,7 @@
 #if !BUILDFLAG(USE_GCM_FROM_PLATFORM)
   identity::IdentityManager* identity_manager_;
 
-  net::URLRequestContextGetter* request_context_ = nullptr;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   // Used for both account tracker and GCM.UserSignedIn UMA.
   class IdentityObserver;
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 7c72336..55cf1e69 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -106,6 +106,11 @@
     "OmniboxUIExperimentMaxAutocompleteMatches",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Feature used to display the search terms instead of the URL in the Omnibox
+// when the user is on the search results page of the default search provider.
+const base::Feature kQueryInOmnibox{"QueryInOmnibox",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Feature used for eliding the suggestion URL after the host as a UI
 // experiment.
 const base::Feature kUIExperimentElideSuggestionUrlAfterHost{
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 2c2a83f..a92568f3 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -35,6 +35,7 @@
 extern const base::Feature kZeroSuggestRedirectToChrome;
 extern const base::Feature kZeroSuggestSwapTitleAndUrl;
 extern const base::Feature kDisplayTitleForCurrentUrl;
+extern const base::Feature kQueryInOmnibox;
 extern const base::Feature kUIExperimentElideSuggestionUrlAfterHost;
 extern const base::Feature kUIExperimentHideSteadyStateUrlSchemeAndSubdomains;
 extern const base::Feature kUIExperimentJogTextfieldOnPopup;
diff --git a/components/signin/core/browser/account_fetcher_service.cc b/components/signin/core/browser/account_fetcher_service.cc
index 66dc8bee..e830cb0f 100644
--- a/components/signin/core/browser/account_fetcher_service.cc
+++ b/components/signin/core/browser/account_fetcher_service.cc
@@ -20,7 +20,6 @@
 #include "components/signin/core/browser/child_account_info_fetcher.h"
 #include "components/signin/core/browser/signin_client.h"
 #include "components/signin/core/browser/signin_switches.h"
-#include "net/url_request/url_request_context_getter.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace {
@@ -210,7 +209,7 @@
     DVLOG(1) << "StartFetching " << account_id;
     std::unique_ptr<AccountInfoFetcher> fetcher =
         std::make_unique<AccountInfoFetcher>(
-            token_service_, signin_client_->GetURLRequestContext(), this,
+            token_service_, signin_client_->GetURLLoaderFactory(), this,
             account_id);
     request = std::move(fetcher);
     request->Start();
diff --git a/components/signin/core/browser/account_info_fetcher.cc b/components/signin/core/browser/account_info_fetcher.cc
index 75575bd..33e601d 100644
--- a/components/signin/core/browser/account_info_fetcher.cc
+++ b/components/signin/core/browser/account_info_fetcher.cc
@@ -9,15 +9,16 @@
 #include "base/trace_event/trace_event.h"
 #include "components/signin/core/browser/account_fetcher_service.h"
 #include "google_apis/gaia/gaia_constants.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 AccountInfoFetcher::AccountInfoFetcher(
     OAuth2TokenService* token_service,
-    net::URLRequestContextGetter* request_context_getter,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     AccountFetcherService* service,
     const std::string& account_id)
     : OAuth2TokenService::Consumer("gaia_account_tracker"),
       token_service_(token_service),
-      request_context_getter_(request_context_getter),
+      url_loader_factory_(std::move(url_loader_factory)),
       service_(service),
       account_id_(account_id) {
   TRACE_EVENT_ASYNC_BEGIN1("AccountFetcherService", "AccountIdFetcher", this,
@@ -44,7 +45,7 @@
                                this, "OnGetTokenSuccess");
   DCHECK_EQ(request, login_token_request_.get());
 
-  gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(request_context_getter_));
+  gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(url_loader_factory_));
   const int kMaxRetries = 3;
   gaia_oauth_client_->GetUserInfo(access_token, kMaxRetries, this);
 }
diff --git a/components/signin/core/browser/account_info_fetcher.h b/components/signin/core/browser/account_info_fetcher.h
index 071b790f..89bc919 100644
--- a/components/signin/core/browser/account_info_fetcher.h
+++ b/components/signin/core/browser/account_info_fetcher.h
@@ -8,12 +8,13 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "google_apis/gaia/oauth2_token_service.h"
 
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
 }
 
 class AccountFetcherService;
@@ -24,10 +25,11 @@
 class AccountInfoFetcher : public OAuth2TokenService::Consumer,
                            public gaia::GaiaOAuthClient::Delegate {
  public:
-  AccountInfoFetcher(OAuth2TokenService* token_service,
-                     net::URLRequestContextGetter* request_context_getter,
-                     AccountFetcherService* service,
-                     const std::string& account_id);
+  AccountInfoFetcher(
+      OAuth2TokenService* token_service,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      AccountFetcherService* service,
+      const std::string& account_id);
   ~AccountInfoFetcher() override;
 
   const std::string& account_id() { return account_id_; }
@@ -50,7 +52,7 @@
 
  private:
   OAuth2TokenService* token_service_;
-  net::URLRequestContextGetter* request_context_getter_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   AccountFetcherService* service_;
   const std::string account_id_;
 
diff --git a/components/signin/core/browser/account_tracker_service_unittest.cc b/components/signin/core/browser/account_tracker_service_unittest.cc
index 9d0d5ad..9f13eb0 100644
--- a/components/signin/core/browser/account_tracker_service_unittest.cc
+++ b/components/signin/core/browser/account_tracker_service_unittest.cc
@@ -25,10 +25,9 @@
 #include "components/signin/core/browser/test_signin_client.h"
 #include "google_apis/gaia/fake_oauth2_token_service.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
+#include "google_apis/gaia/gaia_urls.h"
 #include "net/http/http_status_code.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_test_util.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -350,9 +349,6 @@
   void ReturnAccountImageFetchSuccess(const std::string& account_id);
   void ReturnAccountImageFetchFailure(const std::string& account_id);
 
-  net::TestURLFetcherFactory* test_fetcher_factory() {
-    return &test_fetcher_factory_;
-  }
   AccountFetcherService* account_fetcher() { return account_fetcher_.get(); }
   AccountTrackerService* account_tracker() { return account_tracker_.get(); }
   OAuth2TokenService* token_service() {
@@ -360,8 +356,6 @@
   }
   SigninClient* signin_client() { return signin_client_.get(); }
 
-  // Images go through test_url_loader_factory(); others use
-  // |test_fetcher_factory_| for now.
   network::TestURLLoaderFactory* test_url_loader_factory() {
     return signin_client_->test_url_loader_factory();
   }
@@ -370,11 +364,9 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
  private:
-  void ReturnFetchResults(int fetcher_id,
-                          net::HttpStatusCode response_code,
+  void ReturnFetchResults(net::HttpStatusCode response_code,
                           const std::string& response_string);
 
-  net::TestURLFetcherFactory test_fetcher_factory_;
   std::unique_ptr<FakeOAuth2TokenService> fake_oauth2_token_service_;
   TestingPrefServiceSimple pref_service_;
   std::unique_ptr<AccountFetcherService> account_fetcher_;
@@ -383,36 +375,40 @@
 };
 
 void AccountTrackerServiceTest::ReturnFetchResults(
-    int fetcher_id,
     net::HttpStatusCode response_code,
     const std::string& response_string) {
-  net::TestURLFetcher* fetcher =
-      test_fetcher_factory_.GetFetcherByID(fetcher_id);
-  ASSERT_TRUE(fetcher);
-  fetcher->set_response_code(response_code);
-  fetcher->SetResponseString(response_string);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  GURL url = GaiaUrls::GetInstance()->oauth_user_info_url();
+  EXPECT_TRUE(test_url_loader_factory()->IsPending(url.spec()));
+
+  // It's possible for multiple requests to be pending, since some tests have
+  // a local AccountFetcherService which shares token_service() with fixture's
+  // |account_fetcher_service_|, so when they make tokens available, both end up
+  // doing fetches. Respond to all of them.
+  while (test_url_loader_factory()->IsPending(url.spec())) {
+    test_url_loader_factory()->SimulateResponseForPendingRequest(
+        url, network::URLLoaderCompletionStatus(net::OK),
+        network::CreateResourceResponseHead(response_code), response_string,
+        network::TestURLLoaderFactory::kMostRecentMatch);
+  }
 }
 
 void AccountTrackerServiceTest::ReturnAccountInfoFetchSuccess(
     const std::string& account_id) {
   IssueAccessToken(account_id);
-  ReturnFetchResults(gaia::GaiaOAuthClient::kUrlFetcherId, net::HTTP_OK,
-                     GenerateValidTokenInfoResponse(account_id));
+  ReturnFetchResults(net::HTTP_OK, GenerateValidTokenInfoResponse(account_id));
 }
 
 void AccountTrackerServiceTest::ReturnAccountInfoFetchSuccessIncomplete(
     const std::string& account_id) {
   IssueAccessToken(account_id);
-  ReturnFetchResults(gaia::GaiaOAuthClient::kUrlFetcherId, net::HTTP_OK,
+  ReturnFetchResults(net::HTTP_OK,
                      GenerateIncompleteTokenInfoResponse(account_id));
 }
 
 void AccountTrackerServiceTest::ReturnAccountInfoFetchFailure(
     const std::string& account_id) {
   IssueAccessToken(account_id);
-  ReturnFetchResults(gaia::GaiaOAuthClient::kUrlFetcherId,
-                     net::HTTP_BAD_REQUEST, "");
+  ReturnFetchResults(net::HTTP_BAD_REQUEST, "");
 }
 
 void AccountTrackerServiceTest::ReturnAccountImageFetchSuccess(
@@ -616,9 +612,7 @@
   SimulateTokenAvailable("alpha");
   IssueAccessToken("alpha");
   // No fetcher has been created yet.
-  net::TestURLFetcher* fetcher = test_fetcher_factory()->GetFetcherByID(
-      gaia::GaiaOAuthClient::kUrlFetcherId);
-  ASSERT_FALSE(fetcher);
+  EXPECT_EQ(0, test_url_loader_factory()->NumPending());
 
   // Enable the network to create the fetcher then issue the access token.
   fetcher_service.EnableNetworkFetchesForTest();
diff --git a/components/sync/base/enum_set.h b/components/sync/base/enum_set.h
index a6c14c0..9aa7554 100644
--- a/components/sync/base/enum_set.h
+++ b/components/sync/base/enum_set.h
@@ -8,6 +8,7 @@
 #include <bitset>
 #include <cstddef>
 #include <string>
+#include <utility>
 
 #include "base/logging.h"
 
@@ -52,31 +53,34 @@
   using EnumBitSet = std::bitset<kValueCount>;
 
  public:
-  // Iterator is a forward-only read-only iterator for EnumSet.  Its
-  // interface is deliberately distinct from an STL iterator as its
-  // semantics are substantially different.
+  // Iterator is a forward-only read-only iterator for EnumSet. It follows the
+  // common STL input iterator interface (like std::unordered_set).
   //
-  // Example usage:
+  // Example usage, using a range-based for loop:
   //
-  // for (EnumSet<...>::Iterator it = enums.First(); it.Good(); it.Inc()) {
-  //   Process(it.Get());
+  // EnumSet<SomeType> enums;
+  // for (SomeType val : enums) {
+  //   Process(val);
   // }
   //
-  // The iterator must not be outlived by the set.  In particular, the
-  // following is an error:
+  // Or using an explicit iterator (not recommended):
+  //
+  // for (EnumSet<...>::Iterator it = enums.begin(); it != enums.end(); it++) {
+  //   Process(*it);
+  // }
+  //
+  // The iterator must not be outlived by the set. In particular, the following
+  // is an error:
   //
   // EnumSet<...> SomeFn() { ... }
   //
   // /* ERROR */
-  // for (EnumSet<...>::Iterator it = SomeFun().First(); ...
+  // for (EnumSet<...>::Iterator it = SomeFun().begin(); ...
   //
   // Also, there are no guarantees as to what will happen if you
   // modify an EnumSet while traversing it with an iterator.
   class Iterator {
    public:
-    // A default-constructed iterator can't do anything except check
-    // Good().  You need to call First() on an EnumSet to get a usable
-    // iterator.
     Iterator() : enums_(nullptr), i_(kValueCount) {}
     ~Iterator() {}
 
@@ -93,15 +97,45 @@
       return FromIndex(i_);
     }
 
-    // Moves the iterator to the next value in the EnumSet.  Good()
-    // must hold.  Takes linear time.
+    // Moves the iterator to the next value in the EnumSet. Good()
+    // must hold. Takes linear time.
     void Inc() {
       DCHECK(Good());
       i_ = FindNext(i_ + 1);
     }
 
+    bool operator==(const Iterator& other) const { return i_ == other.i_; }
+
+    bool operator!=(const Iterator& other) const { return !(*this == other); }
+
+    E operator*() const {
+      DCHECK(Good());
+      return FromIndex(i_);
+    }
+
+    Iterator& operator++() {
+      DCHECK(Good());
+      // If there are no more set elements in the bitset, this will result in an
+      // index equal to kValueCount, which is equivalent to EnumSet.end().
+      i_ = FindNext(i_ + 1);
+
+      return *this;
+    }
+
+    Iterator operator++(int) {
+      DCHECK(Good());
+      Iterator old(*this);
+
+      // If there are no more set elements in the bitset, this will result in an
+      // index equal to kValueCount, which is equivalent to EnumSet.end().
+      i_ = FindNext(i_ + 1);
+
+      return std::move(old);
+    }
+
    private:
     friend Iterator EnumSet::First() const;
+    friend Iterator EnumSet::begin() const;
 
     explicit Iterator(const EnumBitSet& enums)
         : enums_(&enums), i_(FindNext(0)) {}
@@ -209,6 +243,12 @@
   // Returns an iterator pointing to the first element (if any).
   Iterator First() const { return Iterator(enums_); }
 
+  Iterator begin() const { return Iterator(enums_); }
+
+  // Returns an iterator that does not point to any element, but to the position
+  // that follows the last element in the set.
+  Iterator end() const { return Iterator(); }
+
   // Returns true iff our set and the given set contain exactly the same values.
   bool operator==(const EnumSet& other) const { return enums_ == other.enums_; }
 
diff --git a/components/sync/base/enum_set_unittest.cc b/components/sync/base/enum_set_unittest.cc
index 1865f34..b8cf466 100644
--- a/components/sync/base/enum_set_unittest.cc
+++ b/components/sync/base/enum_set_unittest.cc
@@ -203,6 +203,67 @@
   EXPECT_EQ(enums2, enums1);
 }
 
+TEST_F(EnumSetTest, RangeBasedForLoop) {
+  const TestEnumSet enums1(TEST_1, TEST_4, TEST_5);
+  TestEnumSet enums2;
+  for (TestEnum e : enums1) {
+    enums2.Put(e);
+  }
+  EXPECT_EQ(enums2, enums1);
+}
+
+TEST_F(EnumSetTest, IteratorComparisonOperators) {
+  const TestEnumSet enums(TEST_1, TEST_3, TEST_5);
+  const auto first_it = enums.begin();
+  const auto second_it = ++enums.begin();
+
+  // Copy for equality testing.
+  const auto first_it_copy = first_it;
+
+  // Sanity check, as the rest of the test relies on |first_it| and
+  // |first_it_copy| pointing to the same element and |first_it| and |second_it|
+  // pointing to different elements.
+  ASSERT_EQ(*first_it, *first_it_copy);
+  ASSERT_NE(*first_it, *second_it);
+
+  EXPECT_TRUE(first_it == first_it_copy);
+  EXPECT_FALSE(first_it != first_it_copy);
+
+  EXPECT_TRUE(first_it != second_it);
+  EXPECT_FALSE(first_it == second_it);
+}
+
+TEST_F(EnumSetTest, IteratorIncrementOperators) {
+  const TestEnumSet enums(TEST_1, TEST_3, TEST_5);
+  const auto begin = enums.begin();
+
+  auto post_inc_it = begin;
+  auto pre_inc_it = begin;
+
+  auto post_inc_return_it = post_inc_it++;
+  auto pre_inc_return_it = ++pre_inc_it;
+
+  // |pre_inc_it| and |post_inc_it| should point to the same element.
+  EXPECT_EQ(pre_inc_it, post_inc_it);
+  EXPECT_EQ(*pre_inc_it, *post_inc_it);
+
+  // |pre_inc_it| should NOT point to the first element.
+  EXPECT_NE(begin, pre_inc_it);
+  EXPECT_NE(*begin, *pre_inc_it);
+
+  // |post_inc_it| should NOT point to the first element.
+  EXPECT_NE(begin, post_inc_it);
+  EXPECT_NE(*begin, *post_inc_it);
+
+  // Prefix increment should return new iterator.
+  EXPECT_EQ(pre_inc_return_it, post_inc_it);
+  EXPECT_EQ(*pre_inc_return_it, *post_inc_it);
+
+  // Postfix increment should return original iterator.
+  EXPECT_EQ(post_inc_return_it, begin);
+  EXPECT_EQ(*post_inc_return_it, *begin);
+}
+
 TEST_F(EnumSetTest, Union) {
   const TestEnumSet enums1(TEST_3, TEST_4);
   const TestEnumSet enums2(TEST_2, TEST_3);
diff --git a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
index 56808832..596b7d7e 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
@@ -56,6 +56,43 @@
   acc_obj->GetNode()->AddAccessibilityTreeProperties(dict);
 }
 
+const char* const ATK_OBJECT_ATTRIBUTES[] = {
+    "atomic",
+    "autocomplete",
+    "busy",
+    "checkable",
+    "class",
+    "colcount",
+    "colindex",
+    "container-atomic",
+    "container-busy",
+    "container-live",
+    "container-relevant",
+    "display",
+    "explicit-name",
+    "haspopup",
+    "id",
+    "keyshortcuts",
+    "level",
+    "live",
+    "placeholder",
+    "posinset",
+    "relevant",
+    "roledescription",
+    "rowcount",
+    "rowindex",
+    "setsize",
+    "src",
+    "table-cell-index",
+    "tag",
+    "text-input-type",
+    "valuemin",
+    "valuemax",
+    "valuenow",
+    "valuetext",
+    "xml-roles",
+};
+
 base::string16 AccessibilityTreeFormatterAuraLinux::ProcessTreeForOutput(
     const base::DictionaryValue& node,
     base::DictionaryValue* filtered_dict_result) {
@@ -87,14 +124,24 @@
        it != states_value->end(); ++it) {
     std::string state_value;
     if (it->GetAsString(&state_value))
-      WriteAttribute(true, state_value, &line);
+      WriteAttribute(false, state_value, &line);
   }
 
   int id_value;
   node.GetInteger("id", &id_value);
   WriteAttribute(false, base::StringPrintf("id=%d", id_value), &line);
 
-  return line + base::ASCIIToUTF16("\n");
+  for (const char* attribute_name : ATK_OBJECT_ATTRIBUTES) {
+    std::string attribute_value;
+    if (node.GetString(attribute_name, &attribute_value)) {
+      WriteAttribute(
+          false,
+          base::StringPrintf("%s:%s", attribute_name, attribute_value.c_str()),
+          &line);
+    }
+  }
+
+  return line;
 }
 
 const base::FilePath::StringType
diff --git a/content/browser/bluetooth/bluetooth_metrics.cc b/content/browser/bluetooth/bluetooth_metrics.cc
index d6368e6..8b68152 100644
--- a/content/browser/bluetooth/bluetooth_metrics.cc
+++ b/content/browser/bluetooth/bluetooth_metrics.cc
@@ -33,8 +33,7 @@
   // should be migrated to a dedicated histogram macro for hashed strings.
   uint32_t data = base::PersistentHash(canonical_uuid);
 
-  // Strip off the sign bit because UMA doesn't support negative values,
-  // but takes a signed int as input.
+  // Strip off the sign bit to make the hash look nicer.
   return static_cast<int>(data & 0x7fffffff);
 }
 
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index b199dab..faf2e2d 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -178,6 +178,7 @@
 
   // Create a persistent memory segment for subprocess histograms.
   CreateMetricsAllocator();
+  ShareMetricsAllocatorToProcess();
 }
 
 BrowserChildProcessHostImpl::~BrowserChildProcessHostImpl() {
@@ -389,7 +390,6 @@
   delegate_->OnChannelConnected(peer_pid);
 
   if (IsProcessLaunched()) {
-    ShareMetricsAllocatorToProcess();
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
         base::BindOnce(&NotifyProcessLaunchedAndConnected, data_.Duplicate()));
@@ -608,7 +608,6 @@
   delegate_->OnProcessLaunched();
 
   if (is_channel_connected_) {
-    ShareMetricsAllocatorToProcess();
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
         base::BindOnce(&NotifyProcessLaunchedAndConnected, data_.Duplicate()));
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 30a40a9..b04021fe 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1213,7 +1213,7 @@
   // CompositingModeReporter.
   return;
 #else
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
     // Mash == ChromeOS, which doesn't support software compositing, so no need
     // to report compositing mode.
     return;
@@ -1248,7 +1248,7 @@
   InitializeMojo();
 
 #if BUILDFLAG(ENABLE_MUS)
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kEnableSurfaceSynchronization);
   }
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index a58286b..55d20d95 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -408,7 +408,7 @@
 
 void BrowserPluginGuest::FirstSurfaceActivation(
     const viz::SurfaceInfo& surface_info) {
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     SendMessageToEmbedder(
         std::make_unique<BrowserPluginMsg_FirstSurfaceActivation>(
             browser_plugin_instance_id(), surface_info));
@@ -665,7 +665,7 @@
   // In case we've created a new guest render process after a crash, let the
   // associated BrowserPlugin know. We only need to send this if we're attached,
   // as guest_crashed_ is cleared automatically on attach anyways.
-  if (attached() && features::IsAshInBrowserProcess()) {
+  if (attached() && !features::IsUsingWindowService()) {
     RenderWidgetHostViewGuest* rwhv = static_cast<RenderWidgetHostViewGuest*>(
         web_contents()->GetRenderWidgetHostView());
     if (rwhv) {
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 21171afa..7936dc67 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -217,7 +217,7 @@
     return base::WrapUnique(new viz::SoftwareOutputDevice);
 
 #if defined(USE_AURA)
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
     NOTREACHED();
     return nullptr;
   }
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index b881d77b..57bd126 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -118,7 +118,7 @@
     if (is_hidden_)
       OnVisibilityChanged(false);
     FrameMsg_ViewChanged_Params params;
-    if (features::IsAshInBrowserProcess())
+    if (!features::IsUsingWindowService())
       params.frame_sink_id = view_->GetFrameSinkId();
     frame_proxy_in_parent_renderer_->Send(new FrameMsg_ViewChanged(
         frame_proxy_in_parent_renderer_->GetRoutingID(), params));
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 51bfe73..d8aca9e2 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -1022,6 +1022,10 @@
       return NAVIGATION_TYPE_NAV_IGNORE;
 
     // This is history.replaceState() or history.reload().
+    // TODO(nasko): With error page isolation, reloading an existing session
+    // history entry can result in change of SiteInstance. Check for such a case
+    // here and classify it as NEW_PAGE, as such navigations should be treated
+    // as new with replacement.
     return NAVIGATION_TYPE_EXISTING_PAGE;
   }
 
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index b20dcd6..2484bfa 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -1138,22 +1138,35 @@
   // If the entry has an instance already we should use it, unless it is no
   // longer suitable.
   if (dest_instance) {
-    SiteInstanceImpl* dest_instance_impl =
-        static_cast<SiteInstanceImpl*>(dest_instance);
-    // TODO(nasko,creis): The check whether data: or about: URLs are allowed
-    // to commit in the current process should be in HasWrongProcessForURL.
-    // However, making this change has further implications and needs more
-    // investigation of what behavior changes. For now, use a conservative
-    // approach and explicitly check before calling HasWrongProcessForURL.
-    if (IsDataOrAbout(dest_url) ||
-        !dest_instance_impl->HasWrongProcessForURL(dest_url)) {
-      // If we are forcing a swap, this should be in a different
-      // BrowsingInstance.
-      if (force_browsing_instance_swap) {
-        CHECK(!dest_instance->IsRelatedSiteInstance(
-            render_frame_host_->GetSiteInstance()));
+    // When error page isolation is enabled, don't reuse |dest_instance| if it's
+    // an error page SiteInstance, but the navigation will no longer fail.
+    // Similarly, don't reuse |dest_instance| if it's not an error page
+    // SiteInstance but the navigation will fail and actually need an error page
+    // SiteInstance.
+    // Note: The later call to HasWrongProcessForURL does not have context about
+    // error page navigaions, so we cannot rely on it to return correct value
+    // when error pages are involved.
+    if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(
+            frame_tree_node_->IsMainFrame()) ||
+        ((dest_instance->GetSiteURL() == GURL(kUnreachableWebDataURL)) ==
+         is_failure)) {
+      // TODO(nasko,creis): The check whether data: or about: URLs are allowed
+      // to commit in the current process should be in HasWrongProcessForURL.
+      // However, making this change has further implications and needs more
+      // investigation of what behavior changes. For now, use a conservative
+      // approach and explicitly check before calling HasWrongProcessForURL.
+      SiteInstanceImpl* dest_instance_impl =
+          static_cast<SiteInstanceImpl*>(dest_instance);
+      if (IsDataOrAbout(dest_url) ||
+          !dest_instance_impl->HasWrongProcessForURL(dest_url)) {
+        // If we are forcing a swap, this should be in a different
+        // BrowsingInstance.
+        if (force_browsing_instance_swap) {
+          CHECK(!dest_instance->IsRelatedSiteInstance(
+              render_frame_host_->GetSiteInstance()));
+        }
+        return SiteInstanceDescriptor(dest_instance);
       }
-      return SiteInstanceDescriptor(dest_instance);
     }
   }
 
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index f97ec5a..986d87e8 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -4312,45 +4312,87 @@
     return;
 
   StartEmbeddedServer();
-  GURL url(embedded_test_server()->GetURL("/title1.html"));
+  GURL start_url(embedded_test_server()->GetURL("/title1.html"));
   GURL error_url(embedded_test_server()->GetURL("/empty.html"));
-  std::unique_ptr<URLLoaderInterceptor> url_interceptor =
-      SetupRequestFailForURL(error_url);
+  GURL end_url(embedded_test_server()->GetURL("/title2.html"));
   NavigationControllerImpl& nav_controller =
       static_cast<NavigationControllerImpl&>(
           shell()->web_contents()->GetController());
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
 
-  // Start with a successful navigation to a document and verify there is
-  // only one entry in session history.
-  EXPECT_TRUE(NavigateToURL(shell(), url));
+  // Build session history with three entries, where the middle one will be
+  // tested for successful and failed reloads. This allows checking whether
+  // reload accidentally clears the forward session history if it is
+  // incorrectly classified.
+  EXPECT_TRUE(NavigateToURL(shell(), start_url));
+  EXPECT_TRUE(NavigateToURL(shell(), error_url));
+  EXPECT_TRUE(NavigateToURL(shell(), end_url));
+  {
+    TestNavigationObserver back_observer(shell()->web_contents());
+    shell()->web_contents()->GetController().GoBack();
+    back_observer.Wait();
+    EXPECT_TRUE(back_observer.last_navigation_succeeded());
+  }
+  EXPECT_EQ(3, nav_controller.GetEntryCount());
+  EXPECT_EQ(1, nav_controller.GetLastCommittedEntryIndex());
+  EXPECT_EQ(error_url, shell()->web_contents()->GetLastCommittedURL());
+
   scoped_refptr<SiteInstance> success_site_instance =
       shell()->web_contents()->GetMainFrame()->GetSiteInstance();
-  EXPECT_EQ(1, nav_controller.GetEntryCount());
 
-  // Navigate to an url resulting in an error page and ensure a new entry
-  // was added to session history.
-  EXPECT_FALSE(NavigateToURL(shell(), error_url));
-  EXPECT_EQ(2, nav_controller.GetEntryCount());
+  // Install an interceptor which will cause network failure for |error_url|,
+  // reload the existing entry and verify.
+  std::unique_ptr<URLLoaderInterceptor> url_interceptor =
+      SetupRequestFailForURL(error_url);
+  {
+    TestNavigationObserver reload_observer(shell()->web_contents());
+    shell()->web_contents()->GetController().Reload(ReloadType::NORMAL, false);
+    reload_observer.Wait();
+    EXPECT_FALSE(reload_observer.last_navigation_succeeded());
+    // TODO(nasko): Investigate making a failing reload of a successful
+    // navigation be classified as NEW_PAGE instead, since with error page
+    // isolation it involves a SiteInstance swap.
+    EXPECT_EQ(NavigationType::NAVIGATION_TYPE_EXISTING_PAGE,
+              reload_observer.last_navigation_type());
+  }
+  EXPECT_EQ(3, nav_controller.GetEntryCount());
+  EXPECT_EQ(1, nav_controller.GetLastCommittedEntryIndex());
   EXPECT_EQ(
       GURL(kUnreachableWebDataURL),
       shell()->web_contents()->GetMainFrame()->GetSiteInstance()->GetSiteURL());
-  EXPECT_EQ(
-      GURL(kUnreachableWebDataURL),
-      policy->GetOriginLock(
-          shell()->web_contents()->GetSiteInstance()->GetProcess()->GetID()));
+  int process_id =
+      shell()->web_contents()->GetMainFrame()->GetProcess()->GetID();
+  EXPECT_EQ(GURL(kUnreachableWebDataURL), policy->GetOriginLock(process_id));
+
+  // Reload while it will still fail to ensure it stays in the same process.
+  {
+    TestNavigationObserver reload_observer(shell()->web_contents());
+    shell()->web_contents()->GetController().Reload(ReloadType::NORMAL, false);
+    reload_observer.Wait();
+    EXPECT_FALSE(reload_observer.last_navigation_succeeded());
+    EXPECT_EQ(NavigationType::NAVIGATION_TYPE_EXISTING_PAGE,
+              reload_observer.last_navigation_type());
+  }
+  EXPECT_EQ(process_id,
+            shell()->web_contents()->GetMainFrame()->GetProcess()->GetID());
 
   // Reload the error page after clearing the error condition, such that the
   // navigation is successful and verify that no new entry was added to
-  // session history.
+  // session history and forward history is not pruned.
   url_interceptor.reset();
   {
     TestNavigationObserver reload_observer(shell()->web_contents());
     shell()->web_contents()->GetController().Reload(ReloadType::NORMAL, false);
     reload_observer.Wait();
     EXPECT_TRUE(reload_observer.last_navigation_succeeded());
+    // The successful reload should be classified as a NEW_PAGE navigation
+    // with replacement, since it needs to stay at the same entry in session
+    // history, but needs a new entry because of the change in SiteInstance.
+    EXPECT_EQ(NavigationType::NAVIGATION_TYPE_NEW_PAGE,
+              reload_observer.last_navigation_type());
   }
-  EXPECT_EQ(2, nav_controller.GetEntryCount());
+  EXPECT_EQ(3, nav_controller.GetEntryCount());
+  EXPECT_EQ(1, nav_controller.GetLastCommittedEntryIndex());
   EXPECT_NE(
       GURL(kUnreachableWebDataURL),
       shell()->web_contents()->GetMainFrame()->GetSiteInstance()->GetSiteURL());
@@ -4362,8 +4404,19 @@
   // Test the same scenario as above, but this time initiated by the
   // renderer process.
   url_interceptor = SetupRequestFailForURL(error_url);
-  EXPECT_FALSE(NavigateToURL(shell(), error_url));
-  EXPECT_EQ(2, nav_controller.GetEntryCount());
+  {
+    TestNavigationObserver reload_observer(shell()->web_contents());
+    shell()->web_contents()->GetController().Reload(ReloadType::NORMAL, false);
+    reload_observer.Wait();
+    EXPECT_FALSE(reload_observer.last_navigation_succeeded());
+    // TODO(nasko): Investigate making a failing reload of a successful
+    // navigation be classified as NEW_PAGE instead, since with error page
+    // isolation it involves a SiteInstance swap.
+    EXPECT_EQ(NavigationType::NAVIGATION_TYPE_EXISTING_PAGE,
+              reload_observer.last_navigation_type());
+  }
+  EXPECT_EQ(3, nav_controller.GetEntryCount());
+  EXPECT_EQ(1, nav_controller.GetLastCommittedEntryIndex());
   EXPECT_EQ(
       GURL(kUnreachableWebDataURL),
       shell()->web_contents()->GetMainFrame()->GetSiteInstance()->GetSiteURL());
@@ -4378,8 +4431,13 @@
     EXPECT_TRUE(ExecuteScript(shell(), "location.reload();"));
     reload_observer.Wait();
     EXPECT_TRUE(reload_observer.last_navigation_succeeded());
+    // TODO(nasko): Investigate making renderer initiated reloads that change
+    // SiteInstance be classified as NEW_PAGE as well.
+    EXPECT_EQ(NavigationType::NAVIGATION_TYPE_EXISTING_PAGE,
+              reload_observer.last_navigation_type());
   }
-  EXPECT_EQ(2, nav_controller.GetEntryCount());
+  EXPECT_EQ(3, nav_controller.GetEntryCount());
+  EXPECT_EQ(1, nav_controller.GetLastCommittedEntryIndex());
   EXPECT_NE(
       GURL(kUnreachableWebDataURL),
       shell()->web_contents()->GetMainFrame()->GetSiteInstance()->GetSiteURL());
@@ -4459,6 +4517,64 @@
       shell()->web_contents()->GetMainFrame()->GetSiteInstance()->GetSiteURL());
 }
 
+// Test to verify that when an error page is hit and its process is terminated,
+// a successful reload correctly commits in a different process.
+// See https://crbug.com/866549.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
+                       ErrorPageNavigationReloadWithTerminatedProcess) {
+  // This test is only valid if error page isolation is enabled.
+  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
+    return;
+
+  StartEmbeddedServer();
+  GURL url(embedded_test_server()->GetURL("/title1.html"));
+  GURL error_url(embedded_test_server()->GetURL("/empty.html"));
+  std::unique_ptr<URLLoaderInterceptor> url_interceptor =
+      SetupRequestFailForURL(error_url);
+  WebContents* web_contents = shell()->web_contents();
+  NavigationControllerImpl& nav_controller =
+      static_cast<NavigationControllerImpl&>(web_contents->GetController());
+
+  // Start with a successful navigation to a document.
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+  scoped_refptr<SiteInstance> success_site_instance =
+      web_contents->GetMainFrame()->GetSiteInstance();
+  EXPECT_EQ(1, nav_controller.GetEntryCount());
+
+  // Navigate to an url resulting in an error page.
+  EXPECT_FALSE(NavigateToURL(shell(), error_url));
+  EXPECT_EQ(GURL(kUnreachableWebDataURL),
+            web_contents->GetMainFrame()->GetSiteInstance()->GetSiteURL());
+  EXPECT_EQ(GURL(kUnreachableWebDataURL),
+            ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(
+                web_contents->GetSiteInstance()->GetProcess()->GetID()));
+  EXPECT_EQ(2, nav_controller.GetEntryCount());
+
+  // Terminate the renderer process.
+  {
+    RenderProcessHostWatcher termination_observer(
+        web_contents->GetMainFrame()->GetProcess(),
+        RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+    web_contents->GetMainFrame()->GetProcess()->Shutdown(0);
+    termination_observer.Wait();
+  }
+
+  // Clear the interceptor so the navigation will succeed on reload.
+  url_interceptor.reset();
+
+  // Reload the URL and execute a Javascript statement to verify that the
+  // renderer process is still around and responsive.
+  TestNavigationObserver reload_observer(shell()->web_contents());
+  nav_controller.Reload(ReloadType::NORMAL, false);
+  reload_observer.Wait();
+  EXPECT_TRUE(reload_observer.last_navigation_succeeded());
+
+  std::string result;
+  EXPECT_TRUE(ExecuteScriptAndExtractString(
+      shell(), "window.domAutomationController.send(location.href);", &result));
+  EXPECT_EQ(error_url.spec(), result);
+}
+
 // A NavigationThrottle implementation that blocks all outgoing navigation
 // requests for a specific WebContents. It is used to block navigations to
 // WebUI URLs in the following test.
diff --git a/content/browser/frame_host/render_widget_host_view_guest.cc b/content/browser/frame_host/render_widget_host_view_guest.cc
index 09d512f..0cd7cef 100644
--- a/content/browser/frame_host/render_widget_host_view_guest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest.cc
@@ -396,7 +396,7 @@
 void RenderWidgetHostViewGuest::OnAttached() {
   RegisterFrameSinkId();
 #if defined(USE_AURA)
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
     aura::Env::GetInstance()->ScheduleEmbed(
         GetWindowTreeClientFromRenderer(),
         base::BindOnce(&RenderWidgetHostViewGuest::OnGotEmbedToken,
diff --git a/content/browser/frame_host/render_widget_host_view_guest_unittest.cc b/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
index 4864b1b..bfa0b5f3c 100644
--- a/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
@@ -201,7 +201,7 @@
   // Early out because RenderWidgetHostViewChildFrame::SendSurfaceInfoToEmbedder
   // is no-op on mash and the test expects it call into FirstSurfaceActivation
   // of BrowserPluginGuest.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   gfx::Size view_size(100, 100);
diff --git a/content/browser/oop_browsertest.cc b/content/browser/oop_browsertest.cc
index 390c7340..eaf658ca 100644
--- a/content/browser/oop_browsertest.cc
+++ b/content/browser/oop_browsertest.cc
@@ -38,7 +38,7 @@
     command_line->AppendSwitch(switches::kEnablePixelOutputInTests);
     command_line->AppendSwitch(switches::kEnableOopRasterization);
 
-    const bool use_gpu_in_tests = features::IsAshInBrowserProcess();
+    const bool use_gpu_in_tests = !features::IsUsingWindowService();
     if (use_gpu_in_tests)
       command_line->AppendSwitch(switches::kUseGpuInTests);
   }
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 7bc55c57..e07459a 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1463,7 +1463,7 @@
 
   InitializeChannelProxy();
 
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     const int id = GetID();
     const uint64_t tracing_id =
         ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(id);
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 ecb3cda..e78fc2b 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -364,7 +364,7 @@
       is_guest_view_hack_(is_guest_view_hack),
       device_scale_factor_(0.0f),
       event_handler_(new RenderWidgetHostViewEventHandler(host(), this, this)),
-      frame_sink_id_(!features::IsAshInBrowserProcess()
+      frame_sink_id_(features::IsUsingWindowService()
                          ? viz::FrameSinkId()
                          : is_guest_view_hack_
                                ? AllocateFrameSinkIdForGuestViewHack()
@@ -1699,7 +1699,7 @@
 void RenderWidgetHostViewAura::ScheduleEmbed(
     ui::mojom::WindowTreeClientPtr client,
     base::OnceCallback<void(const base::UnguessableToken&)> callback) {
-  DCHECK(!features::IsAshInBrowserProcess());
+  DCHECK(features::IsUsingWindowService());
   aura::Env::GetInstance()->ScheduleEmbed(std::move(client),
                                           std::move(callback));
 }
@@ -1921,7 +1921,7 @@
   if (frame_sink_id_.is_valid())
     window_->SetEmbedFrameSinkId(frame_sink_id_);
 
-  if (features::IsAshInBrowserProcess())
+  if (!features::IsUsingWindowService())
     return;
 
   // Embed the renderer into the Window.
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 68888a5..421d192 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -3044,7 +3044,7 @@
 // This test verifies that returned resources do not require a pending ack.
 TEST_F(RenderWidgetHostViewAuraTest, ReturnedResources) {
   // TODO: fix for mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   gfx::Size view_size(100, 100);
@@ -3078,7 +3078,7 @@
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
   if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
-      !features::IsAshInBrowserProcess()) {
+      features::IsUsingWindowService()) {
     return;
   }
 
@@ -3220,7 +3220,7 @@
 
 TEST_F(RenderWidgetHostViewAuraTest, BackgroundColorMatchesCompositorFrame) {
   // TODO: fix for mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   gfx::Size frame_size(100, 100);
@@ -3385,7 +3385,7 @@
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
   if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
-      !features::IsAshInBrowserProcess()) {
+      features::IsUsingWindowService()) {
     return;
   }
 
@@ -3446,7 +3446,7 @@
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        DropFallbackWhenHidden) {
   // Early out because DelegatedFrameHost is not used in mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   view_->InitAsChild(nullptr);
@@ -3474,7 +3474,7 @@
 // the fallback SurfaceId is populated in OnFirstSurfaceActivation.
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest, SurfaceChanges) {
   // Early out because DelegatedFrameHost is not used in mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   view_->InitAsChild(nullptr);
@@ -3513,7 +3513,7 @@
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        DeviceScaleFactorChanges) {
   // TODO: fix for mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   view_->InitAsChild(nullptr);
@@ -3543,7 +3543,7 @@
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
   if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
-      !features::IsAshInBrowserProcess()) {
+      features::IsUsingWindowService()) {
     return;
   }
 
@@ -3578,7 +3578,7 @@
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        DiscardDelegatedFrames) {
   // Early out because DelegatedFrameHost is not used in mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   view_->InitAsChild(nullptr);
@@ -3718,7 +3718,7 @@
 
 TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFramesWithLocking) {
   // Early out because DelegatedFrameHost is not used in mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   view_->InitAsChild(nullptr);
@@ -3792,7 +3792,7 @@
 // only applies to ChromeOS.
 TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFramesWithMemoryPressure) {
   // Early out because DelegatedFrameHost is not used in mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   view_->InitAsChild(nullptr);
@@ -3899,7 +3899,7 @@
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
   if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
-      !features::IsAshInBrowserProcess()) {
+      features::IsUsingWindowService()) {
     return;
   }
 
@@ -5840,7 +5840,7 @@
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
   if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
-      !features::IsAshInBrowserProcess()) {
+      features::IsUsingWindowService()) {
     return;
   }
 
@@ -5882,7 +5882,7 @@
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        NewContentRenderingTimeout) {
   // TODO: fix for mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   constexpr base::TimeDelta kTimeout = base::TimeDelta::FromMicroseconds(10);
@@ -5960,7 +5960,7 @@
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        AllocateLocalSurfaceIdOnEviction) {
   // TODO: fix for mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   view_->InitAsChild(nullptr);
@@ -5983,7 +5983,7 @@
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        DropFallbackIfResizedWhileHidden) {
   // Early out because DelegatedFrameHost is not used in mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   view_->InitAsChild(nullptr);
@@ -6006,7 +6006,7 @@
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        DontDropFallbackIfNotResizedWhileHidden) {
   // Early out because DelegatedFrameHost is not used in mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   view_->InitAsChild(nullptr);
@@ -6028,7 +6028,7 @@
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        TakeFallbackContent) {
   // Early out because DelegatedFrameHost is not used in mash.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   // Initialize the first view.
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index 9ececcc3..45dd9ba4 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -74,7 +74,7 @@
       enable_viz_(
           base::FeatureList::IsEnabled(features::kVizDisplayCompositor)),
       weak_factory_(this) {
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
     // In Mus the RenderFrameProxy will eventually assign a viz::FrameSinkId
     // until then set ours invalid, as operations using it will be disregarded.
     frame_sink_id_ = viz::FrameSinkId();
@@ -93,7 +93,7 @@
   if (frame_connector_)
     DetachFromTouchSelectionClientManagerIfNecessary();
 
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     ResetCompositorFrameSinkSupport();
     if (GetHostFrameSinkManager())
       GetHostFrameSinkManager()->InvalidateFrameSinkId(frame_sink_id_);
@@ -147,7 +147,7 @@
 
   if (parent_view) {
     DCHECK(parent_view->GetFrameSinkId().is_valid() ||
-           !features::IsAshInBrowserProcess());
+           features::IsUsingWindowService());
     SetParentFrameSinkId(parent_view->GetFrameSinkId());
   }
 
@@ -168,7 +168,7 @@
   }
 
 #if defined(USE_AURA)
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
     frame_connector_->EmbedRendererWindowTreeClientInParent(
         GetWindowTreeClientFromRenderer());
   }
@@ -180,7 +180,7 @@
 #if defined(USE_AURA)
 void RenderWidgetHostViewChildFrame::SetFrameSinkId(
     const viz::FrameSinkId& frame_sink_id) {
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     frame_sink_id_ = frame_sink_id;
 }
 #endif  // defined(USE_AURA)
@@ -582,7 +582,7 @@
 void RenderWidgetHostViewChildFrame::SetParentFrameSinkId(
     const viz::FrameSinkId& parent_frame_sink_id) {
   if (parent_frame_sink_id_ == parent_frame_sink_id ||
-      !features::IsAshInBrowserProcess())
+      features::IsUsingWindowService())
     return;
 
   auto* host_frame_sink_manager = GetHostFrameSinkManager();
@@ -603,7 +603,7 @@
 }
 
 void RenderWidgetHostViewChildFrame::SendSurfaceInfoToEmbedder() {
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
   if (!last_activated_surface_info_.is_valid())
     return;
@@ -1070,7 +1070,7 @@
 }
 
 void RenderWidgetHostViewChildFrame::CreateCompositorFrameSinkSupport() {
-  if (!features::IsAshInBrowserProcess() || enable_viz_)
+  if (features::IsUsingWindowService() || enable_viz_)
     return;
 
   DCHECK(!support_);
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
index 6c9a0a1b..96d0d448 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
@@ -160,7 +160,7 @@
 IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameTest, ChildFrameSinkId) {
   // Only when mus hosts viz do we expect a RenderFrameProxy to provide the
   // FrameSinkId.
-  if (features::IsAshInBrowserProcess())
+  if (!features::IsUsingWindowService())
     return;
 
   GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
diff --git a/content/browser/service_manager/common_browser_interfaces.cc b/content/browser/service_manager/common_browser_interfaces.cc
index bb155a0..86941e4 100644
--- a/content/browser/service_manager/common_browser_interfaces.cc
+++ b/content/browser/service_manager/common_browser_interfaces.cc
@@ -51,7 +51,7 @@
 #elif defined(OS_MACOSX)
     registry_.AddInterface(base::BindRepeating(&FontLoaderDispatcher::Create));
 #endif
-    if (features::IsAshInBrowserProcess()) {
+    if (!features::IsUsingWindowService()) {
       // For mus, the mojom::discardable_memory::DiscardableSharedMemoryManager
       // is exposed from ui::Service. So we don't need bind the interface here.
       auto* browser_main_loop = BrowserMainLoop::GetInstance();
@@ -63,8 +63,6 @@
               base::Unretained(manager)));
         }
       }
-    }
-    if (features::IsAshInBrowserProcess()) {
       registry_.AddInterface(base::BindRepeating(
           &ConnectionFilterImpl::BindGpuRequest, base::Unretained(this)));
     }
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 68cdf8a..b46ebd6 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -1328,15 +1328,8 @@
   RunTest(ShowPressHasTouchID);
 }
 
-#if defined(OS_CHROMEOS)
-// Flaky: https://crbug.com/833380
-#define MAYBE_EmulatedTouchScrollBubbles DISABLED_EmulatedTouchScrollBubbles
-#else
-#define MAYBE_EmulatedTouchScrollBubbles EmulatedTouchScrollBubbles
-#endif
-
 IN_PROC_BROWSER_TEST_P(SitePerProcessEmulatedTouchBrowserTest,
-                       MAYBE_EmulatedTouchScrollBubbles) {
+                       EmulatedTouchScrollBubbles) {
   RunTest(ScrollBubbling);
 }
 
@@ -1345,15 +1338,8 @@
   RunTest(PinchGoesToMainFrame);
 }
 
-#if defined(OS_CHROMEOS)
-// Flaky timeouts: https://crbug.com/833380
-#define MAYBE_EmulatedGestureScrollBubbles DISABLED_EmulatedGestureScrollBubbles
-#else
-#define MAYBE_EmulatedGestureScrollBubbles EmulatedGestureScrollBubbles
-#endif
-
 IN_PROC_BROWSER_TEST_P(SitePerProcessEmulatedTouchBrowserTest,
-                       MAYBE_EmulatedGestureScrollBubbles) {
+                       EmulatedGestureScrollBubbles) {
   RunTest(TouchActionBubbling);
 }
 
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index 8aff250..732c011 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -467,7 +467,7 @@
                                          WebContentsViewDelegate* delegate)
     : is_mus_browser_plugin_guest_(web_contents->GetBrowserPluginGuest() !=
                                        nullptr &&
-                                   !features::IsAshInBrowserProcess()),
+                                   features::IsUsingWindowService()),
       web_contents_(web_contents),
       delegate_(delegate),
       current_drag_op_(blink::kWebDragOperationNone),
diff --git a/content/browser/web_contents/web_contents_view_guest.cc b/content/browser/web_contents/web_contents_view_guest.cc
index 44c68ed..12f6817 100644
--- a/content/browser/web_contents/web_contents_view_guest.cc
+++ b/content/browser/web_contents/web_contents_view_guest.cc
@@ -72,14 +72,14 @@
   // view hierarchy. We add this view as embedder's child here.
   // This would go in WebContentsViewGuest::CreateView, but that is too early to
   // access embedder_web_contents(). Therefore, we do it here.
-  if (features::IsAshInBrowserProcess())
+  if (!features::IsUsingWindowService())
     parent_view->GetNativeView()->AddChild(platform_view_->GetNativeView());
 #endif  // defined(USE_AURA)
 }
 
 void WebContentsViewGuest::OnGuestDetached(WebContentsView* old_parent_view) {
 #if defined(USE_AURA)
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     old_parent_view->GetNativeView()->RemoveChild(
         platform_view_->GetNativeView());
   }
diff --git a/content/browser/webauth/authenticator_impl.cc b/content/browser/webauth/authenticator_impl.cc
index b6978d1..04da603 100644
--- a/content/browser/webauth/authenticator_impl.cc
+++ b/content/browser/webauth/authenticator_impl.cc
@@ -379,38 +379,16 @@
 std::string AuthenticatorImpl::SerializeCollectedClientDataToJson(
     const std::string& type,
     const url::Origin& origin,
-    base::span<const uint8_t> challenge,
-    base::Optional<base::span<const uint8_t>> token_binding) {
+    base::span<const uint8_t> challenge) {
   static constexpr char kTypeKey[] = "type";
   static constexpr char kChallengeKey[] = "challenge";
   static constexpr char kOriginKey[] = "origin";
-  static constexpr char kTokenBindingKey[] = "tokenBinding";
 
   base::DictionaryValue client_data;
   client_data.SetKey(kTypeKey, base::Value(type));
   client_data.SetKey(kChallengeKey, base::Value(Base64UrlEncode(challenge)));
   client_data.SetKey(kOriginKey, base::Value(origin.Serialize()));
 
-  if (token_binding) {
-    base::DictionaryValue token_binding_dict;
-    static constexpr char kTokenBindingStatusKey[] = "status";
-    static constexpr char kTokenBindingIdKey[] = "id";
-    static constexpr char kTokenBindingSupportedStatus[] = "supported";
-    static constexpr char kTokenBindingPresentStatus[] = "present";
-
-    if (token_binding->empty()) {
-      token_binding_dict.SetKey(kTokenBindingStatusKey,
-                                base::Value(kTokenBindingSupportedStatus));
-    } else {
-      token_binding_dict.SetKey(kTokenBindingStatusKey,
-                                base::Value(kTokenBindingPresentStatus));
-      token_binding_dict.SetKey(kTokenBindingIdKey,
-                                base::Value(Base64UrlEncode(*token_binding)));
-    }
-
-    client_data.SetKey(kTokenBindingKey, std::move(token_binding_dict));
-  }
-
   if (base::RandDouble() < 0.2) {
     // An extra key is sometimes added to ensure that RPs do not make
     // unreasonably specific assumptions about the clientData JSON. This is
@@ -488,8 +466,7 @@
   // TODO(kpaulhamus): Fetch and add the Token Binding ID public key used to
   // communicate with the origin.
   client_data_json_ = SerializeCollectedClientDataToJson(
-      client_data::kCreateType, caller_origin, std::move(options->challenge),
-      base::nullopt);
+      client_data::kCreateType, caller_origin, std::move(options->challenge));
 
   const bool individual_attestation =
       options->attestation ==
@@ -587,8 +564,7 @@
   // TODO(kpaulhamus): Fetch and add the Token Binding ID public key used to
   // communicate with the origin.
   client_data_json_ = SerializeCollectedClientDataToJson(
-      client_data::kGetType, caller_origin, std::move(options->challenge),
-      base::nullopt);
+      client_data::kGetType, caller_origin, std::move(options->challenge));
 
   request_ = std::make_unique<device::GetAssertionRequestHandler>(
       connector_, protocols_,
diff --git a/content/browser/webauth/authenticator_impl.h b/content/browser/webauth/authenticator_impl.h
index 9637a3b..50b4c55a 100644
--- a/content/browser/webauth/authenticator_impl.h
+++ b/content/browser/webauth/authenticator_impl.h
@@ -104,8 +104,7 @@
   static std::string SerializeCollectedClientDataToJson(
       const std::string& type,
       const url::Origin& origin,
-      base::span<const uint8_t> challenge,
-      base::Optional<base::span<const uint8_t>> token_binding);
+      base::span<const uint8_t> challenge);
 
   // mojom:Authenticator
   void MakeCredential(
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 80cbb9c..8208ae3 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -361,15 +361,7 @@
 
   std::string GetTestClientDataJSON(std::string type) {
     return AuthenticatorImpl::SerializeCollectedClientDataToJson(
-        std::move(type), GetTestOrigin(), GetTestChallengeBytes(),
-        base::nullopt);
-  }
-
-  std::string GetTokenBindingTestClientDataJSON(
-      base::Optional<base::span<const uint8_t>> token_binding) {
-    return AuthenticatorImpl::SerializeCollectedClientDataToJson(
-        client_data::kGetType, GetTestOrigin(), GetTestChallengeBytes(),
-        token_binding);
+        std::move(type), GetTestOrigin(), GetTestChallengeBytes());
   }
 
   AuthenticatorStatus TryAuthenticationWithAppId(const std::string& origin,
@@ -611,34 +603,6 @@
                           GetTestClientDataJSON(client_data::kGetType));
 }
 
-TEST_F(AuthenticatorImplTest, TestTokenBindingClientData) {
-  const std::vector<
-      std::pair<base::Optional<std::vector<uint8_t>>, const char*>>
-      kTestCases = {
-          std::make_pair(base::nullopt, ""),
-          std::make_pair(std::vector<uint8_t>{},
-                         R"({"tokenBinding":{"status":"supported"}})"),
-          std::make_pair(
-              std::vector<uint8_t>{1, 2, 3, 4},
-              R"({"tokenBinding":{"status":"present","id":"AQIDBA"}})"),
-      };
-
-  for (const auto& test : kTestCases) {
-    const auto& token_binding = test.first;
-    const std::string expected_json_subset = test.second;
-    SCOPED_TRACE(expected_json_subset);
-    const std::string client_data =
-        GetTokenBindingTestClientDataJSON(token_binding);
-
-    if (!expected_json_subset.empty()) {
-      CheckJSONIsSubsetOfJSON(expected_json_subset, client_data);
-    } else {
-      EXPECT_TRUE(client_data.find("tokenBinding") == std::string::npos)
-          << client_data;
-    }
-  }
-}
-
 TEST_F(AuthenticatorImplTest, TestMakeCredentialTimeout) {
   SimulateNavigation(GURL(kTestOrigin1));
   PublicKeyCredentialCreationOptionsPtr options =
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 10776fb..6d7d038 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -343,9 +343,6 @@
   if (base::FeatureList::IsEnabled(network::features::kOutOfBlinkCORS))
     WebRuntimeFeatures::EnableOutOfBlinkCORS(true);
 
-  if (base::FeatureList::IsEnabled(features::kOriginPolicy))
-    WebRuntimeFeatures::EnableOriginPolicy(true);
-
   WebRuntimeFeatures::EnableMediaCastOverlayButton(
       base::FeatureList::IsEnabled(media::kMediaCastOverlayButton));
 
@@ -490,6 +487,9 @@
        FeaturesFromSwitch(command_line, switches::kDisableBlinkFeatures)) {
     WebRuntimeFeatures::EnableFeatureFromString(feature, false);
   }
+
+  WebRuntimeFeatures::EnablePortals(
+      base::FeatureList::IsEnabled(blink::features::kPortals));
 };
 
 }  // namespace content
diff --git a/content/common/content_param_traits.cc b/content/common/content_param_traits.cc
index ad9a9821..cac6c15 100644
--- a/content/common/content_param_traits.cc
+++ b/content/common/content_param_traits.cc
@@ -148,7 +148,7 @@
 void ParamTraits<content::FrameMsg_ViewChanged_Params>::Write(
     base::Pickle* m,
     const param_type& p) {
-  DCHECK(!features::IsAshInBrowserProcess() ||
+  DCHECK(features::IsUsingWindowService() ||
          (p.frame_sink_id.has_value() && p.frame_sink_id->is_valid()));
   WriteParam(m, p.frame_sink_id);
 }
@@ -159,7 +159,7 @@
     param_type* r) {
   if (!ReadParam(m, iter, &(r->frame_sink_id)))
     return false;
-  if (features::IsAshInBrowserProcess() &&
+  if (!features::IsUsingWindowService() &&
       (!r->frame_sink_id || !r->frame_sink_id->is_valid())) {
     NOTREACHED();
     return false;
diff --git a/content/ppapi_plugin/ppapi_thread.cc b/content/ppapi_plugin/ppapi_thread.cc
index 40926064..8b4fe965 100644
--- a/content/ppapi_plugin/ppapi_thread.cc
+++ b/content/ppapi_plugin/ppapi_thread.cc
@@ -120,7 +120,7 @@
   // allocator.
   if (!command_line.HasSwitch(switches::kSingleProcess)) {
     discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr;
-    if (!features::IsAshInBrowserProcess()) {
+    if (features::IsUsingWindowService()) {
 #if defined(USE_AURA)
       GetServiceManagerConnection()->GetConnector()->BindInterface(
           ui::mojom::kServiceName, &manager_ptr);
diff --git a/content/public/test/navigation_simulator.cc b/content/public/test/navigation_simulator.cc
index 4c0b09d..436f1b4 100644
--- a/content/public/test/navigation_simulator.cc
+++ b/content/public/test/navigation_simulator.cc
@@ -512,6 +512,7 @@
   params.origin = url::Origin::Create(navigation_url_);
   params.referrer = referrer_;
   params.transition = transition_;
+  params.redirects.push_back(navigation_url_);
   params.should_update_history = true;
   params.did_create_new_entry = DidCreateNewEntry();
   params.gesture =
diff --git a/content/public/test/test_navigation_observer.cc b/content/public/test/test_navigation_observer.cc
index fbebfa0..0def396 100644
--- a/content/public/test/test_navigation_observer.cc
+++ b/content/public/test/test_navigation_observer.cc
@@ -5,8 +5,8 @@
 #include "content/public/test/test_navigation_observer.h"
 
 #include "base/bind.h"
+#include "content/browser/frame_host/navigation_handle_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents_observer.h"
 
 namespace content {
@@ -186,6 +186,8 @@
   last_navigation_url_ = navigation_handle->GetURL();
   last_navigation_succeeded_ = !navigation_handle->IsErrorPage();
   last_net_error_code_ = navigation_handle->GetNetErrorCode();
+  last_navigation_type_ =
+      static_cast<NavigationHandleImpl*>(navigation_handle)->navigation_type();
 
   if (wait_event_ == WaitEvent::kNavigationFinished)
     EventTriggered();
diff --git a/content/public/test/test_navigation_observer.h b/content/public/test/test_navigation_observer.h
index d2f9c84..fdbfae4 100644
--- a/content/public/test/test_navigation_observer.h
+++ b/content/public/test/test_navigation_observer.h
@@ -10,6 +10,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "content/public/browser/navigation_type.h"
 #include "content/public/test/test_utils.h"
 #include "net/base/net_errors.h"
 #include "url/gurl.h"
@@ -68,6 +69,8 @@
 
   net::Error last_net_error_code() const { return last_net_error_code_; }
 
+  NavigationType last_navigation_type() const { return last_navigation_type_; }
+
  protected:
   // Register this TestNavigationObserver as an observer of the |web_contents|.
   void RegisterAsObserver(WebContents* web_contents);
@@ -123,6 +126,9 @@
   // The net error code of the last navigation.
   net::Error last_net_error_code_;
 
+  // The NavigationType of the last navigation.
+  NavigationType last_navigation_type_;
+
   // The MessageLoopRunner used to spin the message loop.
   scoped_refptr<MessageLoopRunner> message_loop_runner_;
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 5b73e127..b383c5f 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -382,8 +382,6 @@
     "media/webrtc/peer_connection_remote_audio_source.h",
     "media/webrtc/peer_connection_tracker.cc",
     "media/webrtc/peer_connection_tracker.h",
-    "media/webrtc/rtc_certificate.cc",
-    "media/webrtc/rtc_certificate.h",
     "media/webrtc/rtc_certificate_generator.cc",
     "media/webrtc/rtc_certificate_generator.h",
     "media/webrtc/rtc_data_channel_handler.cc",
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 4d82510..963702e 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -145,7 +145,7 @@
 void BrowserPlugin::OnFirstSurfaceActivation(
     int browser_plugin_instance_id,
     const viz::SurfaceInfo& surface_info) {
-  if (!attached() || !features::IsAshInBrowserProcess())
+  if (!attached() || features::IsUsingWindowService())
     return;
 
   if (!enable_surface_synchronization_) {
@@ -320,7 +320,7 @@
     sent_visual_properties_ = pending_visual_properties_;
 
 #if defined(USE_AURA)
-  if (!features::IsAshInBrowserProcess() && mus_embedded_frame_) {
+  if (features::IsUsingWindowService() && mus_embedded_frame_) {
     mus_embedded_frame_->SetWindowBounds(GetLocalSurfaceId(),
                                          FrameRectInPixels());
   }
@@ -414,7 +414,7 @@
 void BrowserPlugin::OnSetMusEmbedToken(
     int instance_id,
     const base::UnguessableToken& embed_token) {
-  DCHECK(!features::IsAshInBrowserProcess());
+  DCHECK(features::IsUsingWindowService());
   if (!attached_) {
     pending_embed_token_ = embed_token;
   } else {
@@ -861,7 +861,7 @@
 void BrowserPlugin::OnMusEmbeddedFrameSinkIdAllocated(
     const viz::FrameSinkId& frame_sink_id) {
   // RendererWindowTreeClient should only call this when mus is hosting viz.
-  DCHECK(!features::IsAshInBrowserProcess());
+  DCHECK(features::IsUsingWindowService());
   OnGuestReady(browser_plugin_instance_id_, frame_sink_id);
 }
 #endif
diff --git a/content/renderer/media/webrtc/rtc_certificate.cc b/content/renderer/media/webrtc/rtc_certificate.cc
deleted file mode 100644
index 316b602..0000000
--- a/content/renderer/media/webrtc/rtc_certificate.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 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 "content/renderer/media/webrtc/rtc_certificate.h"
-
-#include <vector>
-
-#include "base/memory/ptr_util.h"
-#include "base/strings/string_util.h"
-#include "third_party/webrtc/rtc_base/sslidentity.h"
-#include "url/gurl.h"
-
-namespace content {
-
-RTCCertificate::RTCCertificate(
-    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate)
-    : certificate_(certificate) {
-  DCHECK(certificate_);
-}
-
-RTCCertificate::~RTCCertificate() {
-}
-
-std::unique_ptr<blink::WebRTCCertificate> RTCCertificate::ShallowCopy() const {
-  return base::WrapUnique(new RTCCertificate(certificate_));
-}
-
-uint64_t RTCCertificate::Expires() const {
-  return certificate_->Expires();
-}
-
-blink::WebVector<blink::WebRTCDtlsFingerprint> RTCCertificate::GetFingerprints()
-    const {
-  std::vector<blink::WebRTCDtlsFingerprint> fingerprints;
-  std::unique_ptr<rtc::SSLCertificateStats> first_certificate_stats =
-      certificate_->identity()->certificate().GetStats();
-  for (rtc::SSLCertificateStats* certificate_stats =
-           first_certificate_stats.get();
-       certificate_stats; certificate_stats = certificate_stats->issuer.get()) {
-    fingerprints.push_back(blink::WebRTCDtlsFingerprint(
-        blink::WebString::FromUTF8(certificate_stats->fingerprint_algorithm),
-        blink::WebString::FromUTF8(
-            base::ToLowerASCII(certificate_stats->fingerprint))));
-  }
-  return blink::WebVector<blink::WebRTCDtlsFingerprint>(fingerprints);
-}
-
-blink::WebRTCCertificatePEM RTCCertificate::ToPEM() const {
-  rtc::RTCCertificatePEM pem = certificate_->ToPEM();
-  return blink::WebRTCCertificatePEM(
-      blink::WebString::FromUTF8(pem.private_key()),
-      blink::WebString::FromUTF8(pem.certificate()));
-}
-
-bool RTCCertificate::Equals(const blink::WebRTCCertificate& other) const {
-  return *certificate_ ==
-         *static_cast<const RTCCertificate&>(other).certificate_;
-}
-
-const rtc::scoped_refptr<rtc::RTCCertificate>&
-RTCCertificate::rtcCertificate() const {
-  return certificate_;
-}
-
-}  // namespace content
diff --git a/content/renderer/media/webrtc/rtc_certificate.h b/content/renderer/media/webrtc/rtc_certificate.h
deleted file mode 100644
index 5210e8f4..0000000
--- a/content/renderer/media/webrtc/rtc_certificate.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 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 CONTENT_RENDERER_MEDIA_WEBRTC_RTC_CERTIFICATE_H_
-#define CONTENT_RENDERER_MEDIA_WEBRTC_RTC_CERTIFICATE_H_
-
-#include <stdint.h>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "content/common/content_export.h"
-#include "third_party/blink/public/platform/web_rtc_certificate.h"
-#include "third_party/webrtc/rtc_base/rtccertificate.h"
-#include "third_party/webrtc/rtc_base/scoped_ref_ptr.h"
-
-namespace content {
-
-// Chromium's WebRTCCertificate implementation; wraps a rtc::scoped_refptr to an
-// rtc::RTCCertificate. This abstraction layer is necessary because blink does
-// not have direct access to WebRTC.
-class CONTENT_EXPORT RTCCertificate : public blink::WebRTCCertificate {
- public:
-  RTCCertificate(const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
-  ~RTCCertificate() override;
-
-  // blink::WebRTCCertificate implementation.
-  std::unique_ptr<blink::WebRTCCertificate> ShallowCopy() const override;
-  uint64_t Expires() const override;
-  blink::WebVector<blink::WebRTCDtlsFingerprint> GetFingerprints()
-      const override;
-  blink::WebRTCCertificatePEM ToPEM() const override;
-  bool Equals(const blink::WebRTCCertificate& other) const override;
-
-  const rtc::scoped_refptr<rtc::RTCCertificate>& rtcCertificate() const;
-
- private:
-  rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
-
-  DISALLOW_COPY_AND_ASSIGN(RTCCertificate);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_MEDIA_WEBRTC_RTC_CERTIFICATE_H_
diff --git a/content/renderer/media/webrtc/rtc_certificate_generator.cc b/content/renderer/media/webrtc/rtc_certificate_generator.cc
index 4f5ff47..2b3924b 100644
--- a/content/renderer/media/webrtc/rtc_certificate_generator.cc
+++ b/content/renderer/media/webrtc/rtc_certificate_generator.cc
@@ -12,7 +12,6 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/renderer/media/webrtc/peer_connection_dependency_factory.h"
-#include "content/renderer/media/webrtc/rtc_certificate.h"
 #include "content/renderer/render_thread_impl.h"
 #include "media/media_buildflags.h"
 #include "third_party/webrtc/rtc_base/rtccertificate.h"
@@ -91,15 +90,12 @@
     main_thread_->PostTask(
         FROM_HERE,
         base::BindOnce(&RTCCertificateGeneratorRequest::DoCallbackOnMainThread,
-                       this, std::move(observer),
-                       certificate
-                           ? std::make_unique<RTCCertificate>(certificate)
-                           : nullptr));
+                       this, std::move(observer), certificate));
   }
 
   void DoCallbackOnMainThread(
       CertificateCallbackPtr observer,
-      std::unique_ptr<blink::WebRTCCertificate> certificate) {
+      rtc::scoped_refptr<rtc::RTCCertificate> certificate) {
     DCHECK(main_thread_->BelongsToCurrentThread());
     DCHECK(observer);
     if (certificate)
@@ -155,7 +151,7 @@
   return WebRTCKeyParamsToKeyParams(key_params).IsValid();
 }
 
-std::unique_ptr<blink::WebRTCCertificate> RTCCertificateGenerator::FromPEM(
+rtc::scoped_refptr<rtc::RTCCertificate> RTCCertificateGenerator::FromPEM(
     blink::WebString pem_private_key,
     blink::WebString pem_certificate) {
   rtc::scoped_refptr<rtc::RTCCertificate> certificate =
@@ -163,7 +159,7 @@
           pem_private_key.Utf8(), pem_certificate.Utf8()));
   if (!certificate)
     return nullptr;
-  return std::make_unique<RTCCertificate>(certificate);
+  return certificate;
 }
 
 }  // namespace content
diff --git a/content/renderer/media/webrtc/rtc_certificate_generator.h b/content/renderer/media/webrtc/rtc_certificate_generator.h
index 5e29c7f..f3fa2ce 100644
--- a/content/renderer/media/webrtc/rtc_certificate_generator.h
+++ b/content/renderer/media/webrtc/rtc_certificate_generator.h
@@ -7,9 +7,9 @@
 
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
-#include "third_party/blink/public/platform/web_rtc_certificate.h"
 #include "third_party/blink/public/platform/web_rtc_certificate_generator.h"
 #include "third_party/blink/public/platform/web_rtc_key_params.h"
+#include "third_party/webrtc/api/peerconnectioninterface.h"
 
 namespace content {
 
@@ -32,7 +32,7 @@
       std::unique_ptr<blink::WebRTCCertificateCallback> observer,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
   bool IsSupportedKeyParams(const blink::WebRTCKeyParams& key_params) override;
-  std::unique_ptr<blink::WebRTCCertificate> FromPEM(
+  rtc::scoped_refptr<rtc::RTCCertificate> FromPEM(
       blink::WebString pem_private_key,
       blink::WebString pem_certificate) override;
 
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index e9b8755e..b41ed0ff 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -30,7 +30,6 @@
 #include "content/renderer/media/stream/media_stream_track.h"
 #include "content/renderer/media/webrtc/peer_connection_dependency_factory.h"
 #include "content/renderer/media/webrtc/peer_connection_tracker.h"
-#include "content/renderer/media/webrtc/rtc_certificate.h"
 #include "content/renderer/media/webrtc/rtc_data_channel_handler.h"
 #include "content/renderer/media/webrtc/rtc_dtmf_sender_handler.h"
 #include "content/renderer/media/webrtc/rtc_event_log_output_sink.h"
@@ -214,11 +213,8 @@
   }
 
   webrtc_config->certificates.clear();
-  for (const std::unique_ptr<blink::WebRTCCertificate>& blink_certificate :
-       blink_config.certificates) {
-    webrtc_config->certificates.push_back(
-        static_cast<RTCCertificate*>(blink_certificate.get())
-            ->rtcCertificate());
+  for (const auto& blink_certificate : blink_config.certificates) {
+    webrtc_config->certificates.push_back(blink_certificate);
   }
 
   webrtc_config->ice_candidate_pool_size = blink_config.ice_candidate_pool_size;
diff --git a/content/renderer/mus/renderer_window_tree_client.cc b/content/renderer/mus/renderer_window_tree_client.cc
index 516c37f6b..50d6f1b 100644
--- a/content/renderer/mus/renderer_window_tree_client.cc
+++ b/content/renderer/mus/renderer_window_tree_client.cc
@@ -32,7 +32,7 @@
 
 // static
 void RendererWindowTreeClient::CreateIfNecessary(int routing_id) {
-  if (features::IsAshInBrowserProcess() || Get(routing_id))
+  if (!features::IsUsingWindowService() || Get(routing_id))
     return;
   RendererWindowTreeClient* connection =
       new RendererWindowTreeClient(routing_id);
@@ -237,7 +237,7 @@
     const viz::FrameSinkId& frame_sink_id) {
   // When mus is not hosting viz FrameSinkIds come from the browser, so we
   // ignore them here.
-  if (features::IsAshInBrowserProcess())
+  if (!features::IsUsingWindowService())
     return;
 
   for (MusEmbeddedFrame* embedded_frame : embedded_frames_) {
@@ -331,7 +331,7 @@
 void RendererWindowTreeClient::OnWindowSurfaceChanged(
     ui::Id window_id,
     const viz::SurfaceInfo& surface_info) {
-  DCHECK(!features::IsAshInBrowserProcess());
+  DCHECK(features::IsUsingWindowService());
   for (MusEmbeddedFrame* embedded_frame : embedded_frames_) {
     if (embedded_frame->window_id_ == window_id) {
       embedded_frame->delegate_->OnMusEmbeddedFrameSurfaceChanged(surface_info);
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 9cc1fdc..183177b 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -249,7 +249,7 @@
       render_widget_->GetOriginalScreenInfo();
 
 #if defined(USE_AURA)
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
     RendererWindowTreeClient* renderer_window_tree_client =
         RendererWindowTreeClient::Get(render_widget_->routing_id());
     // It's possible a MusEmbeddedFrame has already been scheduled for creation
@@ -491,7 +491,7 @@
     const FrameMsg_ViewChanged_Params& params) {
   crashed_ = false;
   // In mash the FrameSinkId comes from RendererWindowTreeClient.
-  if (features::IsAshInBrowserProcess())
+  if (!features::IsUsingWindowService())
     frame_sink_id_ = *params.frame_sink_id;
 
   // Resend the FrameRects and allocate a new viz::LocalSurfaceId when the view
@@ -883,7 +883,7 @@
 void RenderFrameProxy::OnMusEmbeddedFrameSinkIdAllocated(
     const viz::FrameSinkId& frame_sink_id) {
   // RendererWindowTreeClient should only call this when mus is hosting viz.
-  DCHECK(!features::IsAshInBrowserProcess());
+  DCHECK(features::IsUsingWindowService());
   frame_sink_id_ = frame_sink_id;
   // Resend the FrameRects and allocate a new viz::LocalSurfaceId when the view
   // changes.
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index db2edf8..6357e06 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -745,9 +745,9 @@
                           main_thread_runner(), GetConnector()));
 
   gpu_ = ui::Gpu::Create(GetConnector(),
-                         features::IsAshInBrowserProcess()
-                             ? mojom::kBrowserServiceName
-                             : ui::mojom::kServiceName,
+                         features::IsUsingWindowService()
+                             ? ui::mojom::kServiceName
+                             : mojom::kBrowserServiceName,
                          GetIOTaskRunner());
 
   resource_dispatcher_.reset(new ResourceDispatcher());
@@ -801,7 +801,7 @@
   AddFilter(midi_message_filter_.get());
 
 #if defined(USE_AURA)
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     CreateRenderWidgetWindowTreeClientFactory(GetServiceManagerConnection());
 #endif
 
@@ -956,7 +956,7 @@
   categorized_worker_pool_->Start(num_raster_threads);
 
   discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr;
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
 #if defined(USE_AURA)
     GetServiceManagerConnection()->GetConnector()->BindInterface(
         ui::mojom::kServiceName, &manager_ptr);
@@ -1948,7 +1948,7 @@
     params.synthetic_begin_frame_source = CreateSyntheticBeginFrameSource();
 
 #if defined(USE_AURA)
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
     if (!RendererWindowTreeClient::Get(routing_id)) {
       std::move(callback).Run(nullptr);
       return;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index bf4fc27..3f35799 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -452,7 +452,7 @@
   }
 #if defined(USE_AURA)
   RendererWindowTreeClient::CreateIfNecessary(routing_id_);
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     RendererWindowTreeClient::Get(routing_id_)->SetVisible(!is_hidden_);
 #endif
 
@@ -2244,7 +2244,7 @@
   is_hidden_ = hidden;
 
 #if defined(USE_AURA)
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     RendererWindowTreeClient::Get(routing_id_)->SetVisible(!hidden);
 #endif
 
diff --git a/content/test/data/accessibility/aria/aria-blockquote-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-blockquote-expected-auralinux.txt
index 1b90db26..8997e80f 100644
--- a/content/test/data/accessibility/aria/aria-blockquote-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-blockquote-expected-auralinux.txt
@@ -1,3 +1,3 @@
-[document web] enabled focusable focused sensitive showing visible<newline>
-++[block quote] enabled sensitive showing visible<newline>
-++++[text] name='Blockquote' enabled sensitive showing visible<newline>
+[document web]
+++[block quote] xml-roles:blockquote
+++++[text] name='Blockquote'
diff --git a/content/test/data/accessibility/aria/aria-blockquote.html b/content/test/data/accessibility/aria/aria-blockquote.html
index 895bbfa..0b325ca 100644
--- a/content/test/data/accessibility/aria/aria-blockquote.html
+++ b/content/test/data/accessibility/aria/aria-blockquote.html
@@ -1,6 +1,7 @@
 <!--
 @MAC-ALLOW:AXRole*
 @WIN-ALLOW:xml-roles*
+@AURALINUX-ALLOW:xml-roles*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/aria/aria-caption-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-caption-expected-auralinux.txt
index a652cae..8370b6a 100644
--- a/content/test/data/accessibility/aria/aria-caption-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-caption-expected-auralinux.txt
@@ -1,3 +1,3 @@
-[document web] enabled focusable focused sensitive showing visible<newline>
-++[caption] enabled sensitive showing visible<newline>
-++++[text] name='Caption' enabled sensitive showing visible<newline>
+[document web]
+++[caption] xml-roles:caption
+++++[text] name='Caption'
diff --git a/content/test/data/accessibility/aria/aria-caption.html b/content/test/data/accessibility/aria/aria-caption.html
index c353764..c2cd66b78 100644
--- a/content/test/data/accessibility/aria/aria-caption.html
+++ b/content/test/data/accessibility/aria/aria-caption.html
@@ -1,6 +1,7 @@
 <!--
 @MAC-ALLOW:AXRole*
 @WIN-ALLOW:xml-roles*
+@AURALINUX-ALLOW:xml-roles*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/aria/aria-paragraph-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-paragraph-expected-auralinux.txt
index b1fe1873..8d532173 100644
--- a/content/test/data/accessibility/aria/aria-paragraph-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-paragraph-expected-auralinux.txt
@@ -1,3 +1,3 @@
-[document web] enabled focusable focused sensitive showing visible<newline>
-++[paragraph] enabled sensitive showing visible<newline>
-++++[text] name='Paragraph' enabled sensitive showing visible<newline>
+[document web]
+++[paragraph] xml-roles:paragraph
+++++[text] name='Paragraph'
diff --git a/content/test/data/accessibility/aria/aria-paragraph.html b/content/test/data/accessibility/aria/aria-paragraph.html
index ca5a12b..df762fdd 100644
--- a/content/test/data/accessibility/aria/aria-paragraph.html
+++ b/content/test/data/accessibility/aria/aria-paragraph.html
@@ -1,6 +1,7 @@
 <!--
 @MAC-ALLOW:AXRole*
 @WIN-ALLOW:xml-roles*
+@AURALINUX-ALLOW:xml-roles*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/aria/dpub-roles-expected-auralinux.txt b/content/test/data/accessibility/aria/dpub-roles-expected-auralinux.txt
index e0e10c8..22ef39e 100644
--- a/content/test/data/accessibility/aria/dpub-roles-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/dpub-roles-expected-auralinux.txt
@@ -1,40 +1,40 @@
-[document web] enabled focusable focused sensitive showing visible<newline>
-++[section] name='doc-abstract' enabled sensitive showing visible<newline>
-++[landmark] name='doc-acknowledgments' enabled sensitive showing visible<newline>
-++[landmark] name='doc-afterword' enabled sensitive showing visible<newline>
-++[landmark] name='doc-appendix' enabled sensitive showing visible<newline>
-++[link] name='doc-backlink' enabled sensitive showing visible<newline>
-++[list item] name='doc-biblioentry' enabled sensitive showing visible<newline>
-++[landmark] name='doc-bibliography' enabled sensitive showing visible<newline>
-++[link] name='doc-biblioref' enabled sensitive showing visible<newline>
-++[landmark] name='doc-chapter' enabled sensitive showing visible<newline>
-++[section] name='doc-colophon' enabled sensitive showing visible<newline>
-++[landmark] name='doc-conclusion' enabled sensitive showing visible<newline>
-++[image] name='doc-cover' enabled sensitive showing visible<newline>
-++[section] name='doc-credit' enabled sensitive showing visible<newline>
-++[landmark] name='doc-credits' enabled sensitive showing visible<newline>
-++[section] name='doc-dedication' enabled sensitive showing visible<newline>
-++[list item] name='doc-endnote' enabled sensitive showing visible<newline>
-++[landmark] name='doc-endnotes' enabled sensitive showing visible<newline>
-++[section] name='doc-epigraph' enabled sensitive showing visible<newline>
-++[landmark] name='doc-epilogue' enabled sensitive showing visible<newline>
-++[landmark] name='doc-errata' enabled sensitive showing visible<newline>
-++[section] name='doc-example' enabled sensitive showing visible<newline>
-++[footnote] name='doc-footnote' enabled sensitive showing visible<newline>
-++[landmark] name='doc-foreword' enabled sensitive showing visible<newline>
-++[landmark] name='doc-glossary' enabled sensitive showing visible<newline>
-++[link] name='doc-glossref' enabled sensitive showing visible<newline>
-++[landmark] name='doc-index' enabled sensitive showing visible<newline>
-++[landmark] name='doc-introduction' enabled sensitive showing visible<newline>
-++[link] name='doc-noteref' enabled sensitive showing visible<newline>
-++[comment] name='doc-notice' enabled sensitive showing visible<newline>
-++[separator] name='doc-pagebreak' enabled sensitive showing visible<newline>
-++[landmark] name='doc-pagelist' enabled sensitive showing visible<newline>
-++[landmark] name='doc-part' enabled sensitive showing visible<newline>
-++[landmark] name='doc-preface' enabled sensitive showing visible<newline>
-++[landmark] name='doc-prologue' enabled sensitive showing visible<newline>
-++[section] name='doc-pullquote' enabled sensitive showing visible<newline>
-++[section] name='doc-qna' enabled sensitive showing visible<newline>
-++[heading] name='doc-subtitle' enabled sensitive showing visible<newline>
-++[comment] name='doc-tip' enabled sensitive showing visible<newline>
-++[landmark] name='doc-toc' enabled sensitive showing visible<newline>
+[document web]
+++[section] name='doc-abstract' xml-roles:doc-abstract
+++[landmark] name='doc-acknowledgments' xml-roles:doc-acknowledgments
+++[landmark] name='doc-afterword' xml-roles:doc-afterword
+++[landmark] name='doc-appendix' xml-roles:doc-appendix
+++[link] name='doc-backlink' xml-roles:doc-backlink
+++[list item] name='doc-biblioentry' xml-roles:doc-biblioentry
+++[landmark] name='doc-bibliography' xml-roles:doc-bibliography
+++[link] name='doc-biblioref' xml-roles:doc-biblioref
+++[landmark] name='doc-chapter' xml-roles:doc-chapter
+++[section] name='doc-colophon' xml-roles:doc-colophon
+++[landmark] name='doc-conclusion' xml-roles:doc-conclusion
+++[image] name='doc-cover' xml-roles:doc-cover
+++[section] name='doc-credit' xml-roles:doc-credit
+++[landmark] name='doc-credits' xml-roles:doc-credits
+++[section] name='doc-dedication' xml-roles:doc-dedication
+++[list item] name='doc-endnote' xml-roles:doc-endnote
+++[landmark] name='doc-endnotes' xml-roles:doc-endnotes
+++[section] name='doc-epigraph' xml-roles:doc-epigraph
+++[landmark] name='doc-epilogue' xml-roles:doc-epilogue
+++[landmark] name='doc-errata' xml-roles:doc-errata
+++[section] name='doc-example' xml-roles:doc-example
+++[footnote] name='doc-footnote' xml-roles:doc-footnote
+++[landmark] name='doc-foreword' xml-roles:doc-foreword
+++[landmark] name='doc-glossary' xml-roles:doc-glossary
+++[link] name='doc-glossref' xml-roles:doc-glossref
+++[landmark] name='doc-index' xml-roles:doc-index
+++[landmark] name='doc-introduction' xml-roles:doc-introduction
+++[link] name='doc-noteref' xml-roles:doc-noteref
+++[comment] name='doc-notice' xml-roles:doc-notice
+++[separator] name='doc-pagebreak' xml-roles:doc-pagebreak
+++[landmark] name='doc-pagelist' xml-roles:doc-pagelist
+++[landmark] name='doc-part' xml-roles:doc-part
+++[landmark] name='doc-preface' xml-roles:doc-preface
+++[landmark] name='doc-prologue' xml-roles:doc-prologue
+++[section] name='doc-pullquote' xml-roles:doc-pullquote
+++[section] name='doc-qna' xml-roles:doc-qna
+++[heading] name='doc-subtitle' xml-roles:doc-subtitle
+++[comment] name='doc-tip' xml-roles:doc-tip
+++[landmark] name='doc-toc' xml-roles:doc-toc
diff --git a/content/test/data/accessibility/aria/dpub-roles.html b/content/test/data/accessibility/aria/dpub-roles.html
index 28c142de..a6699d13 100644
--- a/content/test/data/accessibility/aria/dpub-roles.html
+++ b/content/test/data/accessibility/aria/dpub-roles.html
@@ -2,6 +2,7 @@
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXSubrole*
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/aria/graphics-roles-expected-auralinux.txt b/content/test/data/accessibility/aria/graphics-roles-expected-auralinux.txt
index 9372606f..25541cc3 100644
--- a/content/test/data/accessibility/aria/graphics-roles-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/graphics-roles-expected-auralinux.txt
@@ -1,4 +1,4 @@
-[document web] enabled focusable focused sensitive showing visible<newline>
-++[document web] name='graphics-document' enabled sensitive showing visible<newline>
-++[panel] name='graphics-object' enabled sensitive showing visible<newline>
-++[image] name='graphics-symbol' enabled sensitive showing visible<newline>
+[document web]
+++[document web] name='graphics-document' xml-roles:graphics-document
+++[panel] name='graphics-object' xml-roles:graphics-object
+++[image] name='graphics-symbol' xml-roles:graphics-symbol
diff --git a/content/test/data/accessibility/aria/graphics-roles.html b/content/test/data/accessibility/aria/graphics-roles.html
index cdde367..457e605 100644
--- a/content/test/data/accessibility/aria/graphics-roles.html
+++ b/content/test/data/accessibility/aria/graphics-roles.html
@@ -2,6 +2,7 @@
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXSubrole*
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/a-expected-auralinux.txt b/content/test/data/accessibility/html/a-expected-auralinux.txt
index cb5e5d3a..d4be624a 100644
--- a/content/test/data/accessibility/html/a-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/a-expected-auralinux.txt
@@ -1,4 +1,4 @@
-[document web] enabled focusable focused sensitive showing visible<newline>
-++[panel] enabled sensitive showing visible<newline>
-++++[link] name='normal link' enabled focusable sensitive showing visible<newline>
-++++++[text] name='normal link' enabled sensitive showing visible<newline>
+[document web] focusable focused
+++[section]
+++++[link] name='normal link' focusable
+++++++[text] name='normal link'
diff --git a/content/test/data/accessibility/html/a-name-calc-expected-auralinux.txt b/content/test/data/accessibility/html/a-name-calc-expected-auralinux.txt
new file mode 100644
index 0000000..afdb01b
--- /dev/null
+++ b/content/test/data/accessibility/html/a-name-calc-expected-auralinux.txt
@@ -0,0 +1,15 @@
+[document web]
+++[link] name='InnerText0'
+++++[text] name='InnerText0'
+++[text] name=' '
+++[link] name='InnerText1' description='Title1'
+++++[text] name='InnerText1'
+++[text] name=' '
+++[link] name='Title2' explicit-name:true
+++++[text] name='InnerText2'
+++[text] name=' '
+++[link] name='LabelledBy3' explicit-name:true
+++++[text] name='InnerText3'
+++[link] name='Title4' explicit-name:true
+++[link] name='Label5' explicit-name:true
+++[link] name='LabelledBy6' explicit-name:true
diff --git a/content/test/data/accessibility/html/a-name-calc.html b/content/test/data/accessibility/html/a-name-calc.html
index 10467ad..ce2e7875 100644
--- a/content/test/data/accessibility/html/a-name-calc.html
+++ b/content/test/data/accessibility/html/a-name-calc.html
@@ -5,6 +5,8 @@
 @MAC-ALLOW:AXHelp*
 @MAC-DENY:AXValue*
 @BLINK-ALLOW:nameFrom*
+@AURALINUX-ALLOW:description*
+@AURALINUX-ALLOW:explicit-name*
 -->
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/a-name-expected-auralinux.txt b/content/test/data/accessibility/html/a-name-expected-auralinux.txt
new file mode 100644
index 0000000..4b4260bb6
--- /dev/null
+++ b/content/test/data/accessibility/html/a-name-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[link] name='named anchor'
+++++[text] name='named anchor'
+++[link] name='both a named anchor and a link'
+++++[text] name='both a named anchor and a link'
diff --git a/content/test/data/accessibility/html/a-with-img-expected-auralinux.txt b/content/test/data/accessibility/html/a-with-img-expected-auralinux.txt
new file mode 100644
index 0000000..515332a
--- /dev/null
+++ b/content/test/data/accessibility/html/a-with-img-expected-auralinux.txt
@@ -0,0 +1,19 @@
+[document web]
+++[section]
+++++[link] name='Link with image at start.'
+++++++[image] name='Link'
+++++++[text] name=' with image at start.'
+++++[text] name=' '
+++++[link] name='Link with image in the middle.'
+++++++[text] name='Link with '
+++++++[image] name='image'
+++++++[text] name=' in the middle.'
+++++[text] name=' '
+++++[link] name='Link with broken in the middle.'
+++++++[text] name='Link with '
+++++++[image] name='broken'
+++++++[text] name=' in the middle.'
+++++[text] name=' '
+++++[link] name='Link with image at the end'
+++++++[text] name='Link with image at the '
+++++++[image] name='end'
diff --git a/content/test/data/accessibility/html/a.html b/content/test/data/accessibility/html/a.html
index 7b0bf21..4d164f3 100644
--- a/content/test/data/accessibility/html/a.html
+++ b/content/test/data/accessibility/html/a.html
@@ -2,6 +2,7 @@
 @BLINK-ALLOW:inPageLinkTargetId=*
 @BLINK-ALLOW:linked
 @WIN-ALLOW:LINKED
+@AURALINUX-ALLOW:focus*
 -->
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/abbr-expected-auralinux.txt b/content/test/data/accessibility/html/abbr-expected-auralinux.txt
new file mode 100644
index 0000000..1b168654
--- /dev/null
+++ b/content/test/data/accessibility/html/abbr-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[paragraph]
+++++[text] name='The '
+++++[static] name='World Health Organization'
+++++++[text] name='WHO'
+++++[text] name=' was founded in 1948.'
diff --git a/content/test/data/accessibility/html/address-expected-auralinux.txt b/content/test/data/accessibility/html/address-expected-auralinux.txt
new file mode 100644
index 0000000..1216a6af
--- /dev/null
+++ b/content/test/data/accessibility/html/address-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[landmark]
+++++[text] name='Please contact John Citizen for more information.'
diff --git a/content/test/data/accessibility/html/area-expected-auralinux.txt b/content/test/data/accessibility/html/area-expected-auralinux.txt
new file mode 100644
index 0000000..a7e76218
--- /dev/null
+++ b/content/test/data/accessibility/html/area-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[section]
+++++[image map] name='pipe'
+++++++[link] name='pipe1'
+++++++[link] name='pipe2'
diff --git a/content/test/data/accessibility/html/article-expected-auralinux.txt b/content/test/data/accessibility/html/article-expected-auralinux.txt
new file mode 100644
index 0000000..2ab5917
--- /dev/null
+++ b/content/test/data/accessibility/html/article-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[article] xml-roles:article
+++++[text] name='This is an article element.'
diff --git a/content/test/data/accessibility/html/article.html b/content/test/data/accessibility/html/article.html
index 532f72a73..10af48b 100644
--- a/content/test/data/accessibility/html/article.html
+++ b/content/test/data/accessibility/html/article.html
@@ -3,6 +3,7 @@
 @MAC-ALLOW:AXSubrole=AXDocumentArticle
 @MAC-DENY:AXTitle*
 @WIN-ALLOW:xml-roles:article
+@AURALINUX-ALLOW:xml-roles:article
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/aside-expected-auralinux.txt b/content/test/data/accessibility/html/aside-expected-auralinux.txt
new file mode 100644
index 0000000..b3c13bf
--- /dev/null
+++ b/content/test/data/accessibility/html/aside-expected-auralinux.txt
@@ -0,0 +1,8 @@
+[document web]
+++[paragraph]
+++++[text] name='The aside tag defines some content aside from the content it is placed in.'
+++[landmark] xml-roles:complementary
+++++[heading] name='Aside tag' xml-roles:heading
+++++++[text] name='Aside tag'
+++++[paragraph]
+++++++[text] name='The aside content should be related to the surrounding content.'
diff --git a/content/test/data/accessibility/html/aside.html b/content/test/data/accessibility/html/aside.html
index 37e3e9a..be71103d 100644
--- a/content/test/data/accessibility/html/aside.html
+++ b/content/test/data/accessibility/html/aside.html
@@ -2,6 +2,7 @@
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXSubrole=*
 @WIN-ALLOW:xml-roles*
+@AURALINUX-ALLOW:xml-roles*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/audio-expected-auralinux.txt b/content/test/data/accessibility/html/audio-expected-auralinux.txt
new file mode 100644
index 0000000..a0d4367
--- /dev/null
+++ b/content/test/data/accessibility/html/audio-expected-auralinux.txt
@@ -0,0 +1,11 @@
+[document web]
+++[section]
+++++[audio]
+++++++[section]
+++++++++[tool bar] name='audio' description='audio' horizontal
+++++++++++[tool bar] name='audio' description='audio' horizontal
+++++++++++++[push button] name='play' description='begin playback' xml-roles:button
+++++++++++++[text] name='0:00'
+++++++++++++[text] name='/ 0:00'
+++++++++++++[slider] description='audio time scrubber' horizontal xml-roles:slider
+++++++++++++[push button] name='mute' description='mute audio track' xml-roles:button
diff --git a/content/test/data/accessibility/html/audio.html b/content/test/data/accessibility/html/audio.html
index 4ae0f3b..105c1da 100644
--- a/content/test/data/accessibility/html/audio.html
+++ b/content/test/data/accessibility/html/audio.html
@@ -1,6 +1,10 @@
 <!--
 @WIN-ALLOW:xml-roles*
 @WIN-ALLOW:description*
+@AURALINUX-ALLOW:xml-roles*
+@AURALINUX-ALLOW:description*
+@AURALINUX-ALLOW:horizontal
+@AURALINUX-ALLOW:vertical
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/b-expected-auralinux.txt b/content/test/data/accessibility/html/b-expected-auralinux.txt
new file mode 100644
index 0000000..644c967
--- /dev/null
+++ b/content/test/data/accessibility/html/b-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[paragraph]
+++++[text] name='Some '
+++++[text] name='bold'
+++++[text] name=' text'
diff --git a/content/test/data/accessibility/html/base-expected-auralinux.txt b/content/test/data/accessibility/html/base-expected-auralinux.txt
new file mode 100644
index 0000000..19b9c9f
--- /dev/null
+++ b/content/test/data/accessibility/html/base-expected-auralinux.txt
@@ -0,0 +1 @@
+[document web]
diff --git a/content/test/data/accessibility/html/bdo-expected-auralinux.txt b/content/test/data/accessibility/html/bdo-expected-auralinux.txt
new file mode 100644
index 0000000..17fc34d
--- /dev/null
+++ b/content/test/data/accessibility/html/bdo-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[section]
+++++[text] name='Some LTR text'
+++++[text] name=' '
+++++[text] name='Some RTL text '
+++++[text] name='with some LTR text embedded'
diff --git a/content/test/data/accessibility/html/blockquote-expected-auralinux.txt b/content/test/data/accessibility/html/blockquote-expected-auralinux.txt
index 2b0a264..6266276 100644
--- a/content/test/data/accessibility/html/blockquote-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/blockquote-expected-auralinux.txt
@@ -1,6 +1,6 @@
-[document web] enabled focusable focused sensitive showing visible<newline>
-++[block quote] enabled sensitive showing visible<newline>
-++++[paragraph] enabled sensitive showing visible<newline>
-++++++[text] name='First blockquote has a child element.' enabled sensitive showing visible<newline>
-++[block quote] enabled sensitive showing visible<newline>
-++++[text] name='Second blockquote has no child.' enabled sensitive showing visible<newline>
+[document web]
+++[block quote]
+++++[paragraph]
+++++++[text] name='First blockquote has a child element.'
+++[block quote]
+++++[text] name='Second blockquote has no child.'
diff --git a/content/test/data/accessibility/html/body-expected-auralinux.txt b/content/test/data/accessibility/html/body-expected-auralinux.txt
new file mode 100644
index 0000000..89b5fee
--- /dev/null
+++ b/content/test/data/accessibility/html/body-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[paragraph]
+++++[text] name='This test is for body tag'
diff --git a/content/test/data/accessibility/html/br-expected-auralinux.txt b/content/test/data/accessibility/html/br-expected-auralinux.txt
new file mode 100644
index 0000000..b8f61444
--- /dev/null
+++ b/content/test/data/accessibility/html/br-expected-auralinux.txt
@@ -0,0 +1,8 @@
+[document web]
+++[text] name='<newline>'
+++[text] name='Text line 1'
+++[paragraph]
+++++[text] name='Text line 2'
+++++[text] name='<newline>'
+++++[text] name='Text line 3'
+++[text] name='<newline>'
diff --git a/content/test/data/accessibility/html/button-expected-auralinux.txt b/content/test/data/accessibility/html/button-expected-auralinux.txt
new file mode 100644
index 0000000..574bb6ef7
--- /dev/null
+++ b/content/test/data/accessibility/html/button-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[push button] name='Click me!'
diff --git a/content/test/data/accessibility/html/button-name-calc-expected-auralinux.txt b/content/test/data/accessibility/html/button-name-calc-expected-auralinux.txt
new file mode 100644
index 0000000..39002bd
--- /dev/null
+++ b/content/test/data/accessibility/html/button-name-calc-expected-auralinux.txt
@@ -0,0 +1,10 @@
+[document web]
+++[push button] name='InnerText0'
+++[push button] name='InnerText1' description='Title1'
+++[push button] name='AriaLabel2' description='Title2' explicit-name:true
+++[push button] name='LabelledBy3' description='Title3' explicit-name:true
+++[push button] name='LabelledBy4' description='DescribedBy4' explicit-name:true
+++[push button] name='InnerText5' description='DescribedBy5'
+++[push button] name='Outer inner'
+++[push button] name='Outer inner1'
+++[push button] name='Outer grandchild'
diff --git a/content/test/data/accessibility/html/button-name-calc.html b/content/test/data/accessibility/html/button-name-calc.html
index 7c783b9..bd8170c2 100644
--- a/content/test/data/accessibility/html/button-name-calc.html
+++ b/content/test/data/accessibility/html/button-name-calc.html
@@ -1,6 +1,8 @@
 <!--
 @WIN-ALLOW:description*
 @WIN-ALLOW:explicit-name*
+@AURALINUX-ALLOW:description*
+@AURALINUX-ALLOW:explicit-name*
 @MAC-ALLOW:AXDescription*
 @MAC-ALLOW:AXHelp*
 @MAC-DENY:AXValue*
diff --git a/content/test/data/accessibility/html/canvas-expected-auralinux.txt b/content/test/data/accessibility/html/canvas-expected-auralinux.txt
new file mode 100644
index 0000000..36378eb
--- /dev/null
+++ b/content/test/data/accessibility/html/canvas-expected-auralinux.txt
@@ -0,0 +1,7 @@
+[document web]
+++[section]
+++++[canvas]
+++++++[text] name='Static fallback'
+++++[canvas]
+++++++[link] name='Interactive fallback'
+++++++++[text] name='Interactive fallback'
diff --git a/content/test/data/accessibility/html/caption-expected-auralinux.txt b/content/test/data/accessibility/html/caption-expected-auralinux.txt
index 7f46e58..c238565 100644
--- a/content/test/data/accessibility/html/caption-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/caption-expected-auralinux.txt
@@ -1,19 +1,19 @@
-[document web] enabled focusable focused sensitive showing visible<newline>
-++[table] name='Browser and Engine' enabled sensitive showing visible<newline>
-++++[caption] enabled sensitive showing visible<newline>
-++++++[text] name='Browser and Engine' enabled sensitive showing visible<newline>
-++++[table row] enabled sensitive showing visible<newline>
-++++++[column header] name='Browser' enabled sensitive showing visible<newline>
-++++++++[text] name='Browser' enabled sensitive showing visible<newline>
-++++++[column header] name='Engine' enabled sensitive showing visible<newline>
-++++++++[text] name='Engine' enabled sensitive showing visible<newline>
-++++[table row] enabled sensitive showing visible<newline>
-++++++[table cell] name='Chrome' enabled sensitive showing visible<newline>
-++++++++[text] name='Chrome' enabled sensitive showing visible<newline>
-++++++[table cell] name='Blink' enabled sensitive showing visible<newline>
-++++++++[text] name='Blink' enabled sensitive showing visible<newline>
-++++[table row] enabled sensitive showing visible<newline>
-++++++[table cell] name='Safari' enabled sensitive showing visible<newline>
-++++++++[text] name='Safari' enabled sensitive showing visible<newline>
-++++++[table cell] name='WebKit' enabled sensitive showing visible<newline>
-++++++++[text] name='WebKit' enabled sensitive showing visible<newline>
+[document web]
+++[table] name='Browser and Engine'
+++++[caption]
+++++++[text] name='Browser and Engine'
+++++[table row]
+++++++[column header] name='Browser'
+++++++++[text] name='Browser'
+++++++[column header] name='Engine'
+++++++++[text] name='Engine'
+++++[table row]
+++++++[table cell] name='Chrome'
+++++++++[text] name='Chrome'
+++++++[table cell] name='Blink'
+++++++++[text] name='Blink'
+++++[table row]
+++++++[table cell] name='Safari'
+++++++++[text] name='Safari'
+++++++[table cell] name='WebKit'
+++++++++[text] name='WebKit'
diff --git a/content/test/data/accessibility/html/checkbox-name-calc-expected-auralinux.txt b/content/test/data/accessibility/html/checkbox-name-calc-expected-auralinux.txt
new file mode 100644
index 0000000..6a5596a5
--- /dev/null
+++ b/content/test/data/accessibility/html/checkbox-name-calc-expected-auralinux.txt
@@ -0,0 +1,7 @@
+[document web]
+++[check box] name='Title0' checkable:true explicit-name:true
+++[check box] name='Label1' description='Title1' checkable:true explicit-name:true
+++[check box] name='AriaLabel2' description='Title2' checkable:true explicit-name:true
+++[check box] name='LabelledBy3' description='Title3' checkable:true explicit-name:true
+++[check box] name='LabelledBy4' description='DescribedBy4' checkable:true explicit-name:true
+++[check box] description='DescribedBy5' checkable:true
diff --git a/content/test/data/accessibility/html/checkbox-name-calc.html b/content/test/data/accessibility/html/checkbox-name-calc.html
index c1c96d3b..7d78725 100644
--- a/content/test/data/accessibility/html/checkbox-name-calc.html
+++ b/content/test/data/accessibility/html/checkbox-name-calc.html
@@ -1,6 +1,10 @@
 <!--
 @WIN-ALLOW:description*
 @WIN-ALLOW:explicit-name*
+@AURALINUX-ALLOW:description*
+@AURALINUX-ALLOW:explicit-name*
+@AURALINUX-DENY:checkable
+@AURALINUX-DENY:last-defined
 @MAC-ALLOW:AXDescription*
 @MAC-ALLOW:AXHelp*
 @MAC-DENY:AXValue*
diff --git a/content/test/data/accessibility/html/cite-expected-auralinux.txt b/content/test/data/accessibility/html/cite-expected-auralinux.txt
new file mode 100644
index 0000000..29e849c2
--- /dev/null
+++ b/content/test/data/accessibility/html/cite-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[image] name='Pipe'
+++[paragraph]
+++++[text] name='The pipe'
+++++[text] name=' clicked by SomeOne.'
diff --git a/content/test/data/accessibility/html/code-expected-auralinux.txt b/content/test/data/accessibility/html/code-expected-auralinux.txt
new file mode 100644
index 0000000..c1366e3
--- /dev/null
+++ b/content/test/data/accessibility/html/code-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[text] name='A piece of computer code'
diff --git a/content/test/data/accessibility/html/col-expected-auralinux.txt b/content/test/data/accessibility/html/col-expected-auralinux.txt
new file mode 100644
index 0000000..6481413
--- /dev/null
+++ b/content/test/data/accessibility/html/col-expected-auralinux.txt
@@ -0,0 +1,12 @@
+[document web]
+++[table]
+++++[table row]
+++++++[column header] name='Browser'
+++++++++[text] name='Browser'
+++++++[column header] name='Rendering Engine'
+++++++++[text] name='Rendering Engine'
+++++[table row]
+++++++[table cell] name='Chrome'
+++++++++[text] name='Chrome'
+++++++[table cell] name='Blink'
+++++++++[text] name='Blink'
diff --git a/content/test/data/accessibility/html/colgroup-expected-auralinux.txt b/content/test/data/accessibility/html/colgroup-expected-auralinux.txt
new file mode 100644
index 0000000..8861fab
--- /dev/null
+++ b/content/test/data/accessibility/html/colgroup-expected-auralinux.txt
@@ -0,0 +1,12 @@
+[document web]
+++[table]
+++++[table row]
+++++++[column header] name='Single'
+++++++++[text] name='Single'
+++++++[column header] name='Pair'
+++++++++[text] name='Pair'
+++++[table row]
+++++++[table cell] name='A'
+++++++++[text] name='A'
+++++++[table cell] name='AA'
+++++++++[text] name='AA'
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-expected-auralinux.txt b/content/test/data/accessibility/html/contenteditable-descendants-expected-auralinux.txt
new file mode 100644
index 0000000..05caaf2
--- /dev/null
+++ b/content/test/data/accessibility/html/contenteditable-descendants-expected-auralinux.txt
@@ -0,0 +1,23 @@
+[document web]
+++[section] editable multi-line selectable-text
+++++[paragraph] editable
+++++++[text] name='A contenteditable with a ' editable
+++++++[link] name='link' editable
+++++++++[text] name='link' editable
+++++++[text] name=' and an ' editable
+++++++[image] name='Image' editable
+++++++[text] name=' and a ' editable
+++++++[push button] name='Button' editable
+++++++[text] name='.' editable
+++++[table] editable
+++++++[table row] editable
+++++++++[table cell] name='Always expose editable tables as tables.' editable
+++++++++++[text] name='Always expose editable tables as tables.' editable
+++++[list] editable
+++++++[list item] editable
+++++++++[static] name='1. '
+++++++++[text] name='Editable list item.' editable
+++[paragraph]
+++++[text] name='Non-editable paragraph.'
+++[paragraph] editable multi-line selectable-text
+++++[text] name='Should keep the role but change the state.' editable
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-auralinux.txt b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-auralinux.txt
new file mode 100644
index 0000000..ee77eb7
--- /dev/null
+++ b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-auralinux.txt
@@ -0,0 +1,19 @@
+[document web]
+++[section] editable multi-line selectable-text
+++++[paragraph] editable
+++++++[text] name='A contenteditable with a ' editable
+++++++[link] name='link' editable
+++++++++[text] name='link' editable
+++++++[text] name=' and an ' editable
+++++++[image] name='Image' editable
+++++++[text] name=' and a ' editable
+++++++[push button] name='Button' editable
+++++++[text] name='.' editable
+++++[table] editable
+++++++[table row] editable
+++++++++[table cell] name='Always expose editable tables as tables.' editable
+++++++++++[text] name='Always expose editable tables as tables.' editable
+++++[list] editable
+++++++[list item] editable
+++++++++[static] name='1. '
+++++++++[text] name='Editable list item.' editable
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-with-selection.html b/content/test/data/accessibility/html/contenteditable-descendants-with-selection.html
index ee1d1931..3f91d75 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants-with-selection.html
+++ b/content/test/data/accessibility/html/contenteditable-descendants-with-selection.html
@@ -11,6 +11,8 @@
 @BLINK-ALLOW:editable*
 @BLINK-ALLOW:richlyEditable*
 @BLINK-ALLOW:*textSel*
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:multi-line
 -->
 <div id="contenteditable" tabindex="0" contenteditable>
   <p>A contenteditable with a
diff --git a/content/test/data/accessibility/html/contenteditable-descendants.html b/content/test/data/accessibility/html/contenteditable-descendants.html
index 93a6e4e..c95f25d 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants.html
+++ b/content/test/data/accessibility/html/contenteditable-descendants.html
@@ -14,6 +14,8 @@
 @BLINK-ALLOW:editable*
 @BLINK-ALLOW:richlyEditable*
 @BLINK-ALLOW:textSel*
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:multi-line
 -->
 <div contenteditable>
   <p>A contenteditable with a
diff --git a/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-auralinux.txt b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-auralinux.txt
new file mode 100644
index 0000000..e2aa623
--- /dev/null
+++ b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-auralinux.txt
@@ -0,0 +1,10 @@
+[document web]
+++[section] editable multi-line selectable-text
+++++[paragraph] editable
+++++++[text] name='This is editable.' editable
+++++[text] name='This is not editable.'
+++++[text] name='<newline>'
+++++[paragraph] editable multi-line selectable-text
+++++++[text] name='But this one is.' editable
+++++[paragraph] editable
+++++++[text] name='So is this one.' editable
diff --git a/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables.html b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables.html
index 47a9ff5..aaa3c91 100644
--- a/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables.html
+++ b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables.html
@@ -8,6 +8,8 @@
 @BLINK-ALLOW:editable*
 @BLINK-ALLOW:richlyEditable*
 @BLINK-ALLOW:textSel*
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:multi-line
 -->
 <div contenteditable>
   <p>This is editable.</p>
diff --git a/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-auralinux.txt b/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-auralinux.txt
new file mode 100644
index 0000000..38f5649
--- /dev/null
+++ b/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[section] name='label' editable selectable-text
+++[section] description='description' editable selectable-text
+++[section] name='title' editable selectable-text
+++[paragraph]
+++++[text] name='description'
diff --git a/content/test/data/accessibility/html/contenteditable-with-no-descendants.html b/content/test/data/accessibility/html/contenteditable-with-no-descendants.html
index 21693473..7236b6c9 100644
--- a/content/test/data/accessibility/html/contenteditable-with-no-descendants.html
+++ b/content/test/data/accessibility/html/contenteditable-with-no-descendants.html
@@ -2,8 +2,10 @@
 @WIN-ALLOW:description*
 @WIN-ALLOW:IA2_STATE_EDITABLE
 @WIN-ALLOW:ia2_hypertext=*
+@AURALINUX-ALLOW:description*
 @BLINK-ALLOW:editable
 @BLINK-ALLOW:richlyEditable
+@AURALINUX-ALLOW:editable
 -->
 <div contenteditable aria-label="label"></div>
 <div contenteditable aria-describedby="description"></div>
diff --git a/content/test/data/accessibility/html/dd-expected-auralinux.txt b/content/test/data/accessibility/html/dd-expected-auralinux.txt
new file mode 100644
index 0000000..083192e
--- /dev/null
+++ b/content/test/data/accessibility/html/dd-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[description list]
+++++[description term]
+++++++[text] name='Coffee'
+++++[description value]
+++++++[text] name='Black hot drink'
diff --git a/content/test/data/accessibility/html/del-expected-auralinux.txt b/content/test/data/accessibility/html/del-expected-auralinux.txt
new file mode 100644
index 0000000..83ed3cd
--- /dev/null
+++ b/content/test/data/accessibility/html/del-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[paragraph]
+++++[text] name='I am '
+++++[section]
+++++++[text] name='vegetarian'
diff --git a/content/test/data/accessibility/html/details-expected-auralinux.txt b/content/test/data/accessibility/html/details-expected-auralinux.txt
new file mode 100644
index 0000000..5bb55e5
--- /dev/null
+++ b/content/test/data/accessibility/html/details-expected-auralinux.txt
@@ -0,0 +1,9 @@
+[document web] focusable focused
+++[panel]
+++++[toggle button] name='details tag' expandable focusable
+++++++[text] name='details tag'
+++[panel]
+++++[toggle button] name='details tag open' expandable expanded focusable
+++++++[text] name='details tag open'
+++++[paragraph]
+++++++[text] name='The details tag with open specifies that the details should be visible (open) to the user.'
diff --git a/content/test/data/accessibility/html/details.html b/content/test/data/accessibility/html/details.html
index b0612d5..d65dcaa7 100644
--- a/content/test/data/accessibility/html/details.html
+++ b/content/test/data/accessibility/html/details.html
@@ -3,6 +3,8 @@
 @MAC-ALLOW:AXExpanded=*
 @WIN-ALLOW:EXPANDED
 @WIN-ALLOW:COLLAPSED
+@AURALINUX-ALLOW:expand*
+@AURALINUX-ALLOW:focus*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/dfn-expected-auralinux.txt b/content/test/data/accessibility/html/dfn-expected-auralinux.txt
new file mode 100644
index 0000000..1563d67
--- /dev/null
+++ b/content/test/data/accessibility/html/dfn-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web] tag:#document
+++[section] tag:body
+++++[text] name='Web Browser'
+++++[text] name=' A computer program with a graphical user interface for displaying HTML files, used to navigate the World Wide Web.'
diff --git a/content/test/data/accessibility/html/dfn.html b/content/test/data/accessibility/html/dfn.html
index a453e4c..b2511922 100644
--- a/content/test/data/accessibility/html/dfn.html
+++ b/content/test/data/accessibility/html/dfn.html
@@ -1,3 +1,7 @@
+<!--
+@AURALINUX-ALLOW:tag*
+@AURALINUX-ALLOW:xml-roles*
+-->
 <!DOCTYPE html>
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/dialog-expected-auralinux.txt b/content/test/data/accessibility/html/dialog-expected-auralinux.txt
new file mode 100644
index 0000000..82d3f36
--- /dev/null
+++ b/content/test/data/accessibility/html/dialog-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[dialog]
+++++[text] name='Text in dialog'
diff --git a/content/test/data/accessibility/html/div-expected-auralinux.txt b/content/test/data/accessibility/html/div-expected-auralinux.txt
new file mode 100644
index 0000000..a7e4408
--- /dev/null
+++ b/content/test/data/accessibility/html/div-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[section]
+++++[text] name='Unfocusable div'
+++[section] name='Focusable div'
+++++[text] name='Focusable div'
diff --git a/content/test/data/accessibility/html/dl-expected-auralinux.txt b/content/test/data/accessibility/html/dl-expected-auralinux.txt
new file mode 100644
index 0000000..959d890
--- /dev/null
+++ b/content/test/data/accessibility/html/dl-expected-auralinux.txt
@@ -0,0 +1,8 @@
+[document web]
+++[description list]
+++++[description term]
+++++++[text] name='Term'
+++++[description value]
+++++++[text] name='Description'
+++[description value]
+++++[text] name='Definition'
diff --git a/content/test/data/accessibility/html/dt-expected-auralinux.txt b/content/test/data/accessibility/html/dt-expected-auralinux.txt
new file mode 100644
index 0000000..083192e
--- /dev/null
+++ b/content/test/data/accessibility/html/dt-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[description list]
+++++[description term]
+++++++[text] name='Coffee'
+++++[description value]
+++++++[text] name='Black hot drink'
diff --git a/content/test/data/accessibility/html/element-class-id-src-attr-expected-auralinux.txt b/content/test/data/accessibility/html/element-class-id-src-attr-expected-auralinux.txt
new file mode 100644
index 0000000..b8a871ce
--- /dev/null
+++ b/content/test/data/accessibility/html/element-class-id-src-attr-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web] tag:#document
+++[heading] name='Image' class:headerClass id:headerID tag:h1
+++++[text] name='Image'
+++[image] name='ImageAlt' class:imageClass id:imageID src:greenbox.png tag:img
diff --git a/content/test/data/accessibility/html/element-class-id-src-attr.html b/content/test/data/accessibility/html/element-class-id-src-attr.html
index bd77a95..a3194c1e 100644
--- a/content/test/data/accessibility/html/element-class-id-src-attr.html
+++ b/content/test/data/accessibility/html/element-class-id-src-attr.html
@@ -3,6 +3,10 @@
 @WIN-ALLOW:id:*
 @WIN-ALLOW:src:*
 @WIN-ALLOW:tag:*
+@AURALINUX-ALLOW:class:*
+@AURALINUX-ALLOW:id:*
+@AURALINUX-ALLOW:src:*
+@AURALINUX-ALLOW:tag:*
 @BLINK-ALLOW:htmlTag*
 -->
 <!DOCTYPE html>
@@ -11,4 +15,4 @@
     <h1 id="headerID" class="headerClass">Image</h1>
     <img id="imageID" class="imageClass" src="greenbox.png" alt="ImageAlt" height="100" width="200">
   </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/content/test/data/accessibility/html/em-expected-auralinux.txt b/content/test/data/accessibility/html/em-expected-auralinux.txt
new file mode 100644
index 0000000..8d7fdb04
--- /dev/null
+++ b/content/test/data/accessibility/html/em-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[section]
+++++[text] name='One word is '
+++++[text] name='emphasized'
+++++[text] name='.'
diff --git a/content/test/data/accessibility/html/embed-expected-auralinux.txt b/content/test/data/accessibility/html/embed-expected-auralinux.txt
new file mode 100644
index 0000000..1b84d9f1
--- /dev/null
+++ b/content/test/data/accessibility/html/embed-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[embedded component]
diff --git a/content/test/data/accessibility/html/fieldset-expected-auralinux.txt b/content/test/data/accessibility/html/fieldset-expected-auralinux.txt
new file mode 100644
index 0000000..8a630803
--- /dev/null
+++ b/content/test/data/accessibility/html/fieldset-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[form]
+++++[panel] name='Browser Engines:'
+++++++[label]
+++++++++[text] name='Browser Engines:'
diff --git a/content/test/data/accessibility/html/figcaption-expected-auralinux.txt b/content/test/data/accessibility/html/figcaption-expected-auralinux.txt
new file mode 100644
index 0000000..5667be1
--- /dev/null
+++ b/content/test/data/accessibility/html/figcaption-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[panel] name='Fig.1 - A green Box'
+++++[image] name='This is a green box.'
+++++[caption]
+++++++[text] name='Fig.1 - A green Box'
diff --git a/content/test/data/accessibility/html/figure-expected-auralinux.txt b/content/test/data/accessibility/html/figure-expected-auralinux.txt
new file mode 100644
index 0000000..0803594
--- /dev/null
+++ b/content/test/data/accessibility/html/figure-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[panel] xml-roles:figure
+++++[image] name='Sunspots' xml-roles:img
diff --git a/content/test/data/accessibility/html/figure.html b/content/test/data/accessibility/html/figure.html
index 43f60b8..49dfa3550 100644
--- a/content/test/data/accessibility/html/figure.html
+++ b/content/test/data/accessibility/html/figure.html
@@ -1,6 +1,7 @@
 <!--
 @MAC-ALLOW:AXRole*
 @WIN-ALLOW:xml-roles*
+@AURALINUX-ALLOW:xml-roles*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/footer-expected-auralinux.txt b/content/test/data/accessibility/html/footer-expected-auralinux.txt
new file mode 100644
index 0000000..a127933
--- /dev/null
+++ b/content/test/data/accessibility/html/footer-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[footer]
+++++[text] name='Footer element'
diff --git a/content/test/data/accessibility/html/footer-inside-other-section-expected-auralinux.txt b/content/test/data/accessibility/html/footer-inside-other-section-expected-auralinux.txt
new file mode 100644
index 0000000..f5d6a1b
--- /dev/null
+++ b/content/test/data/accessibility/html/footer-inside-other-section-expected-auralinux.txt
@@ -0,0 +1,13 @@
+[document web]
+++[article] xml-roles:article
+++++[section]
+++++++[paragraph]
+++++++++[text] name='footer inside article.'
+++[section] xml-roles:region
+++++[section]
+++++++[paragraph]
+++++++++[text] name='footer inside section.'
+++[landmark] xml-roles:main
+++++[section]
+++++++[paragraph]
+++++++++[text] name='footer inside main.'
diff --git a/content/test/data/accessibility/html/footer-inside-other-section.html b/content/test/data/accessibility/html/footer-inside-other-section.html
index 6012e34..428f727 100644
--- a/content/test/data/accessibility/html/footer-inside-other-section.html
+++ b/content/test/data/accessibility/html/footer-inside-other-section.html
@@ -3,6 +3,7 @@
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXSubrole*
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
 -->
 <article>
   <footer>
diff --git a/content/test/data/accessibility/html/form-expected-auralinux.txt b/content/test/data/accessibility/html/form-expected-auralinux.txt
new file mode 100644
index 0000000..4300939
--- /dev/null
+++ b/content/test/data/accessibility/html/form-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[form]
+++++[push button] name='Submit'
diff --git a/content/test/data/accessibility/html/frameset-expected-auralinux.txt b/content/test/data/accessibility/html/frameset-expected-auralinux.txt
new file mode 100644
index 0000000..f4e80c4b
--- /dev/null
+++ b/content/test/data/accessibility/html/frameset-expected-auralinux.txt
@@ -0,0 +1,18 @@
+[document web]
+++[internal frame]
+++++[document web]
+++++++[paragraph]
+++++++++[text] name='My favorite browser is '
+++++++++[section]
+++++++++++[text] name='ABC'
+++++++++[text] name=' '
+++++++++[section]
+++++++++++[text] name='Chrome'
+++++++++[text] name='!'
+++[internal frame]
+++++[document web]
+++++++[paragraph]
+++++++++[text] name='This test is to check '
+++++++++[static]
+++++++++++[text] name='mark tag'
+++++++++[text] name='.'
diff --git a/content/test/data/accessibility/html/head-expected-auralinux.txt b/content/test/data/accessibility/html/head-expected-auralinux.txt
new file mode 100644
index 0000000..19b9c9f
--- /dev/null
+++ b/content/test/data/accessibility/html/head-expected-auralinux.txt
@@ -0,0 +1 @@
+[document web]
diff --git a/content/test/data/accessibility/html/header-expected-auralinux.txt b/content/test/data/accessibility/html/header-expected-auralinux.txt
new file mode 100644
index 0000000..228a02f
--- /dev/null
+++ b/content/test/data/accessibility/html/header-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[landmark] xml-roles:banner
+++++[text] name='Chromium Browser'
diff --git a/content/test/data/accessibility/html/header-inside-other-section-expected-auralinux.txt b/content/test/data/accessibility/html/header-inside-other-section-expected-auralinux.txt
new file mode 100644
index 0000000..77cbdf4
--- /dev/null
+++ b/content/test/data/accessibility/html/header-inside-other-section-expected-auralinux.txt
@@ -0,0 +1,13 @@
+[document web]
+++[article] xml-roles:article
+++++[section]
+++++++[paragraph]
+++++++++[text] name='Header inside article.'
+++[section] xml-roles:region
+++++[section]
+++++++[paragraph]
+++++++++[text] name='Header inside section.'
+++[landmark] xml-roles:main
+++++[section]
+++++++[paragraph]
+++++++++[text] name='Header inside main.'
diff --git a/content/test/data/accessibility/html/header-inside-other-section.html b/content/test/data/accessibility/html/header-inside-other-section.html
index 9cf97a0..0ef757fc 100644
--- a/content/test/data/accessibility/html/header-inside-other-section.html
+++ b/content/test/data/accessibility/html/header-inside-other-section.html
@@ -3,6 +3,7 @@
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXSubrole*
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
 -->
 <article>
   <header>
diff --git a/content/test/data/accessibility/html/header.html b/content/test/data/accessibility/html/header.html
index bef095c8..6234446 100644
--- a/content/test/data/accessibility/html/header.html
+++ b/content/test/data/accessibility/html/header.html
@@ -2,6 +2,7 @@
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXSubrole*
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/heading-expected-auralinux.txt b/content/test/data/accessibility/html/heading-expected-auralinux.txt
new file mode 100644
index 0000000..b67d197
--- /dev/null
+++ b/content/test/data/accessibility/html/heading-expected-auralinux.txt
@@ -0,0 +1,13 @@
+[document web]
+++[heading] name='Heading 1' level:1 xml-roles:heading
+++++[text] name='Heading 1'
+++[heading] name='Heading 2' level:2 xml-roles:heading
+++++[text] name='Heading 2'
+++[heading] name='Heading 3' level:3 xml-roles:heading
+++++[text] name='Heading 3'
+++[heading] name='Heading 4' level:4 xml-roles:heading
+++++[text] name='Heading 4'
+++[heading] name='Heading 5' level:5 xml-roles:heading
+++++[text] name='Heading 5'
+++[heading] name='Heading 6' level:6 xml-roles:heading
+++++[text] name='Heading 6'
diff --git a/content/test/data/accessibility/html/heading.html b/content/test/data/accessibility/html/heading.html
index 51e7ef53..bac08e4 100644
--- a/content/test/data/accessibility/html/heading.html
+++ b/content/test/data/accessibility/html/heading.html
@@ -1,6 +1,8 @@
 <!--
 @WIN-ALLOW:level:*
 @WIN-ALLOW:xml-roles*
+@AURALINUX-ALLOW:level:*
+@AURALINUX-ALLOW:xml-roles*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/hr-expected-auralinux.txt b/content/test/data/accessibility/html/hr-expected-auralinux.txt
new file mode 100644
index 0000000..91f6f2b1
--- /dev/null
+++ b/content/test/data/accessibility/html/hr-expected-auralinux.txt
@@ -0,0 +1,9 @@
+[document web]
+++[paragraph]
+++++[text] name='Before.'
+++[separator] name='Dividing line' horizontal
+++[paragraph]
+++++[text] name='Middle.'
+++[separator] horizontal
+++[paragraph]
+++++[text] name='After.'
diff --git a/content/test/data/accessibility/html/hr.html b/content/test/data/accessibility/html/hr.html
index c804cb71..78c177f 100644
--- a/content/test/data/accessibility/html/hr.html
+++ b/content/test/data/accessibility/html/hr.html
@@ -1,6 +1,8 @@
 <!--
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXDescription*
+@AURALINUX-ALLOW:horizontal
+@AURALINUX-ALLOW:vertical
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/html-expected-auralinux.txt b/content/test/data/accessibility/html/html-expected-auralinux.txt
new file mode 100644
index 0000000..19b9c9f
--- /dev/null
+++ b/content/test/data/accessibility/html/html-expected-auralinux.txt
@@ -0,0 +1 @@
+[document web]
diff --git a/content/test/data/accessibility/html/i-expected-auralinux.txt b/content/test/data/accessibility/html/i-expected-auralinux.txt
new file mode 100644
index 0000000..b65c3435
--- /dev/null
+++ b/content/test/data/accessibility/html/i-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[paragraph]
+++++[text] name='This is to check '
+++++[text] name='italic property'
+++++[text] name=' using i tag.'
diff --git a/content/test/data/accessibility/html/iframe-coordinates-cross-process.html b/content/test/data/accessibility/html/iframe-coordinates-cross-process.html
index 2c0cf087..3279f55 100644
--- a/content/test/data/accessibility/html/iframe-coordinates-cross-process.html
+++ b/content/test/data/accessibility/html/iframe-coordinates-cross-process.html
@@ -12,6 +12,8 @@
 @WIN-ALLOW:size=(150, 50)
 @WIN-ALLOW:IA2_STATE_EDITABLE
 
+@AURALINUX-ALLOW:location*
+
 @BLINK-ALLOW:location*
 @BLINK-ALLOW:scrollX=*
 @BLINK-ALLOW:scrollY=*
diff --git a/content/test/data/accessibility/html/iframe-cross-process-expected-auralinux.txt b/content/test/data/accessibility/html/iframe-cross-process-expected-auralinux.txt
new file mode 100644
index 0000000..c9c7795
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-cross-process-expected-auralinux.txt
@@ -0,0 +1,10 @@
+[document web]
+++[paragraph]
+++++[text] name='Before frame'
+++[section]
+++++[internal frame] name='Cross-process iframe'
+++++++[document web]
+++++++++[section]
+++++++++++[text] name='Text in iframe'
+++[paragraph]
+++++[text] name='After frame'
diff --git a/content/test/data/accessibility/html/iframe-expected-auralinux.txt b/content/test/data/accessibility/html/iframe-expected-auralinux.txt
new file mode 100644
index 0000000..b2783ab
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web]
+++[section]
+++++[internal frame] name='Empty iframe'
+++++++[document web]
diff --git a/content/test/data/accessibility/html/iframe-presentational-expected-auralinux.txt b/content/test/data/accessibility/html/iframe-presentational-expected-auralinux.txt
new file mode 100644
index 0000000..1b326de0
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-presentational-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web]
+++[section]
+++++[internal frame]
+++++++[document web]
diff --git a/content/test/data/accessibility/html/img-empty-alt-expected-auralinux.txt b/content/test/data/accessibility/html/img-empty-alt-expected-auralinux.txt
new file mode 100644
index 0000000..3fec5e3
--- /dev/null
+++ b/content/test/data/accessibility/html/img-empty-alt-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[section]
+++++[image] name=''
+++++[image] name=''
+++++[image]
+++++[image] name='full'
diff --git a/content/test/data/accessibility/html/img-expected-auralinux.txt b/content/test/data/accessibility/html/img-expected-auralinux.txt
new file mode 100644
index 0000000..24def55
--- /dev/null
+++ b/content/test/data/accessibility/html/img-expected-auralinux.txt
@@ -0,0 +1,7 @@
+[document web]
+++[section]
+++++[image] name='pipe' xml-roles:img
+++++[text] name=' '
+++++[image] name='' xml-roles:img
+++++[text] name=' '
+++++[image] name='  ' xml-roles:img
diff --git a/content/test/data/accessibility/html/img-link-empty-alt-expected-auralinux.txt b/content/test/data/accessibility/html/img-link-empty-alt-expected-auralinux.txt
new file mode 100644
index 0000000..abb430c6
--- /dev/null
+++ b/content/test/data/accessibility/html/img-link-empty-alt-expected-auralinux.txt
@@ -0,0 +1,13 @@
+[document web]
+++[section]
+++++[link] name='unread '
+++++++[text] name='unread '
+++++[link] name='read '
+++++++[image] name=''
+++++++[text] name='read '
+++++[link] name='read '
+++++++[image] name=''
+++++++[text] name='read '
+++++[link] name='read'
+++++++[image]
+++++++[text] name='read'
diff --git a/content/test/data/accessibility/html/img.html b/content/test/data/accessibility/html/img.html
index 8e7671f..39cea3a 100644
--- a/content/test/data/accessibility/html/img.html
+++ b/content/test/data/accessibility/html/img.html
@@ -1,6 +1,7 @@
 <!--
 @MAC-ALLOW:AXRoleDescription='image'
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
 @ANDROID-ALLOW:has_image
 -->
 <html>
diff --git a/content/test/data/accessibility/html/in-page-links-expected-auralinux.txt b/content/test/data/accessibility/html/in-page-links-expected-auralinux.txt
new file mode 100644
index 0000000..2ecd2ae
--- /dev/null
+++ b/content/test/data/accessibility/html/in-page-links-expected-auralinux.txt
@@ -0,0 +1,35 @@
+[document web]
+++[link] name='Empty anchor'
+++++[text] name='Empty anchor'
+++[text] name=' '
+++[link] name='Anchor with content'
+++++[text] name='Anchor with content'
+++[text] name=' '
+++[link] name='Anchor with ID'
+++++[text] name='Anchor with ID'
+++[text] name=' '
+++[link] name='Empty span'
+++++[text] name='Empty span'
+++[text] name=' '
+++[link] name='Span with content'
+++++[text] name='Span with content'
+++[text] name=' '
+++[link] name='Paragraph with content'
+++++[text] name='Paragraph with content'
+++[paragraph]
+++++[link]
+++++[text] name='After empty anchor'
+++[paragraph]
+++++[link] name='Anchor with content'
+++++++[text] name='Anchor with content'
+++[paragraph]
+++++[link] name='Anchor with ID'
+++++++[text] name='Anchor with ID'
+++[paragraph]
+++++[section]
+++++[text] name='After empty span'
+++[paragraph]
+++++[section]
+++++++[text] name='Span with content'
+++[paragraph]
+++++[text] name='Paragraph with content'
diff --git a/content/test/data/accessibility/html/input-button-expected-auralinux.txt b/content/test/data/accessibility/html/input-button-expected-auralinux.txt
new file mode 100644
index 0000000..9d338be1
--- /dev/null
+++ b/content/test/data/accessibility/html/input-button-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[push button] name='Button' xml-roles:button
diff --git a/content/test/data/accessibility/html/input-button-in-menu-expected-auralinux.txt b/content/test/data/accessibility/html/input-button-in-menu-expected-auralinux.txt
new file mode 100644
index 0000000..a597cac
--- /dev/null
+++ b/content/test/data/accessibility/html/input-button-in-menu-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[section]
+++++[menu item] name='Button in menu element'
+++[menu] vertical xml-roles:menu
+++++[menu item] name='Button in element with menu role'
diff --git a/content/test/data/accessibility/html/input-button-in-menu.html b/content/test/data/accessibility/html/input-button-in-menu.html
index 0b8834b8..4902889 100644
--- a/content/test/data/accessibility/html/input-button-in-menu.html
+++ b/content/test/data/accessibility/html/input-button-in-menu.html
@@ -1,6 +1,10 @@
 <!--
 @WIN-ALLOW:ia2_hypertext=*
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:horizontal
+@AURALINUX-ALLOW:vertical
+@AURALINUX-ALLOW:focus
 -->
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/input-button.html b/content/test/data/accessibility/html/input-button.html
index 4360fcd..23fd744 100644
--- a/content/test/data/accessibility/html/input-button.html
+++ b/content/test/data/accessibility/html/input-button.html
@@ -1,6 +1,7 @@
 <!--
 @WIN-ALLOW:ia2_hypertext=*
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-checkbox-expected-auralinux.txt b/content/test/data/accessibility/html/input-checkbox-expected-auralinux.txt
new file mode 100644
index 0000000..aafb5ef
--- /dev/null
+++ b/content/test/data/accessibility/html/input-checkbox-expected-auralinux.txt
@@ -0,0 +1,7 @@
+[document web]
+++[section]
+++++[check box] name='Checkbox0' checkable:true
+++++[check box] name='Checkbox1' checked checkable:true
+++++[check box] name='Checkbox2' checked checkable:true
+++++[check box] name='Checkbox3' checked checkable:true xml-roles:checkbox
+++++[check box] name='Checkbox4' indeterminate checkable:true
diff --git a/content/test/data/accessibility/html/input-checkbox-in-menu-expected-auralinux.txt b/content/test/data/accessibility/html/input-checkbox-in-menu-expected-auralinux.txt
new file mode 100644
index 0000000..79f1eb1
--- /dev/null
+++ b/content/test/data/accessibility/html/input-checkbox-in-menu-expected-auralinux.txt
@@ -0,0 +1,7 @@
+[document web]
+++[section]
+++++[check box] name='Checkbox1' checkable:true
+++++[check box] name='Checkbox2' checked checkable:true
+++[menu] xml-roles:menu
+++++[check box] name='Checkbox3' checked checkable:true
+++++[check box] name='Checkbox4' indeterminate checkable:true
diff --git a/content/test/data/accessibility/html/input-checkbox-in-menu.html b/content/test/data/accessibility/html/input-checkbox-in-menu.html
index f89cffe..f6649a1 100644
--- a/content/test/data/accessibility/html/input-checkbox-in-menu.html
+++ b/content/test/data/accessibility/html/input-checkbox-in-menu.html
@@ -5,6 +5,11 @@
 @WIN-ALLOW:IA2_STATE_CHECKABLE*
 @WIN-ALLOW:MIXED
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:indeterminate
+@AURALINUX-ALLOW:checked
+@AURALINUX-DENY:checkable
+@AURALINUX-DENY:last-defined
 -->
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/input-checkbox.html b/content/test/data/accessibility/html/input-checkbox.html
index e5ccb2e..7a0bc7c 100644
--- a/content/test/data/accessibility/html/input-checkbox.html
+++ b/content/test/data/accessibility/html/input-checkbox.html
@@ -5,6 +5,11 @@
 @WIN-ALLOW:IA2_STATE_*
 @WIN-ALLOW:MIXED
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:indeterminate
+@AURALINUX-ALLOW:checked
+@AURALINUX-DENY:checkable
+@AURALINUX-DENY:last-defined
 -->
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/input-color-expected-auralinux.txt b/content/test/data/accessibility/html/input-color-expected-auralinux.txt
new file mode 100644
index 0000000..5678eaf
--- /dev/null
+++ b/content/test/data/accessibility/html/input-color-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[push button]
diff --git a/content/test/data/accessibility/html/input-date-expected-auralinux.txt b/content/test/data/accessibility/html/input-date-expected-auralinux.txt
new file mode 100644
index 0000000..ab9ca6c
--- /dev/null
+++ b/content/test/data/accessibility/html/input-date-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[dateeditor] name='@NO_CHILDREN_DUMP'
diff --git a/content/test/data/accessibility/html/input-datetime-expected-auralinux.txt b/content/test/data/accessibility/html/input-datetime-expected-auralinux.txt
new file mode 100644
index 0000000..05223202
--- /dev/null
+++ b/content/test/data/accessibility/html/input-datetime-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable focused
+++[section]
+++++[entry] editable focusable selectable-text text-input-type:datetime
diff --git a/content/test/data/accessibility/html/input-datetime-local-expected-auralinux.txt b/content/test/data/accessibility/html/input-datetime-local-expected-auralinux.txt
new file mode 100644
index 0000000..697e413
--- /dev/null
+++ b/content/test/data/accessibility/html/input-datetime-local-expected-auralinux.txt
@@ -0,0 +1,23 @@
+[document web]
+++[section]
+++++[dateeditor]
+++++++[section]
+++++++++[section]
+++++++++++[spin button] name='Month'
+++++++++++++[text] name='mm'
+++++++++++[text] name='/'
+++++++++++[spin button] name='Day'
+++++++++++++[text] name='dd'
+++++++++++[text] name='/'
+++++++++++[spin button] name='Year'
+++++++++++++[text] name='yyyy'
+++++++++++[text] name=', '
+++++++++++[spin button] name='Hours'
+++++++++++++[text] name='--'
+++++++++++[text] name=':'
+++++++++++[spin button] name='Minutes'
+++++++++++++[text] name='--'
+++++++++++[text] name=' '
+++++++++++[spin button] name='AM/PM'
+++++++++++++[text] name='--'
+++++++[push button] name='Show date picker' haspopup:menu
diff --git a/content/test/data/accessibility/html/input-datetime-local.html b/content/test/data/accessibility/html/input-datetime-local.html
index 8bdddda..3e2e4f4 100644
--- a/content/test/data/accessibility/html/input-datetime-local.html
+++ b/content/test/data/accessibility/html/input-datetime-local.html
@@ -3,6 +3,8 @@
 @WIN-ALLOW:description=*
 @WIN-ALLOW:haspopup*
 @WIN-ALLOW:ia2_hypertext=*
+@AURALINUX-ALLOW:description=*
+@AURALINUX-ALLOW:haspopup*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-datetime.html b/content/test/data/accessibility/html/input-datetime.html
index 8ba14ff..4556e3a3 100644
--- a/content/test/data/accessibility/html/input-datetime.html
+++ b/content/test/data/accessibility/html/input-datetime.html
@@ -1,6 +1,10 @@
 <!--
 @MAC-ALLOW:AXRole*
 @WIN-ALLOW:ia2_hypertext=*
+@AURALINUX-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:text-input-type:*
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:editable
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-email-expected-auralinux.txt b/content/test/data/accessibility/html/input-email-expected-auralinux.txt
new file mode 100644
index 0000000..7c3849cb
--- /dev/null
+++ b/content/test/data/accessibility/html/input-email-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable focused
+++[section]
+++++[entry] editable focusable selectable-text text-input-type:email
diff --git a/content/test/data/accessibility/html/input-email.html b/content/test/data/accessibility/html/input-email.html
index e091778..8ec892b 100644
--- a/content/test/data/accessibility/html/input-email.html
+++ b/content/test/data/accessibility/html/input-email.html
@@ -7,6 +7,10 @@
 @WIN-ALLOW:selection_start*
 @WIN-ALLOW:selection_end*
 @WIN-ALLOW:text-input-type*
+@AURALINUX-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:text-input-type:*
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:editable
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-file-expected-auralinux.txt b/content/test/data/accessibility/html/input-file-expected-auralinux.txt
new file mode 100644
index 0000000..3e067c5c
--- /dev/null
+++ b/content/test/data/accessibility/html/input-file-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[push button] name='Choose File'
diff --git a/content/test/data/accessibility/html/input-hidden-expected-auralinux.txt b/content/test/data/accessibility/html/input-hidden-expected-auralinux.txt
new file mode 100644
index 0000000..19b9c9f
--- /dev/null
+++ b/content/test/data/accessibility/html/input-hidden-expected-auralinux.txt
@@ -0,0 +1 @@
+[document web]
diff --git a/content/test/data/accessibility/html/input-image-button-in-menu-expected-auralinux.txt b/content/test/data/accessibility/html/input-image-button-in-menu-expected-auralinux.txt
new file mode 100644
index 0000000..a8c5250
--- /dev/null
+++ b/content/test/data/accessibility/html/input-image-button-in-menu-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[section]
+++++[push button] name='Bullet'
+++[menu]
+++++[push button] name='Bullet'
diff --git a/content/test/data/accessibility/html/input-image-expected-auralinux.txt b/content/test/data/accessibility/html/input-image-expected-auralinux.txt
new file mode 100644
index 0000000..cadf6d6
--- /dev/null
+++ b/content/test/data/accessibility/html/input-image-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[push button] name='Submit' xml-roles:button
diff --git a/content/test/data/accessibility/html/input-image.html b/content/test/data/accessibility/html/input-image.html
index 12e7af7..eb09cbc4 100644
--- a/content/test/data/accessibility/html/input-image.html
+++ b/content/test/data/accessibility/html/input-image.html
@@ -1,6 +1,7 @@
 <!--
 @MAC-ALLOW:AXRole*
 @WIN-ALLOW:xml-roles*
+@AURALINUX-ALLOW:xml-roles*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-list-expected-auralinux.txt b/content/test/data/accessibility/html/input-list-expected-auralinux.txt
new file mode 100644
index 0000000..b326e16
--- /dev/null
+++ b/content/test/data/accessibility/html/input-list-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web] focusable focused
+++[section]
+++++[label]
+++++++[text] name='Choose a pokemon '
+++++++[combo box] name='Choose a pokemon ' editable focusable selectable-text autocomplete:list haspopup:listbox
diff --git a/content/test/data/accessibility/html/input-list.html b/content/test/data/accessibility/html/input-list.html
index c67d33d..b5f8c768 100644
--- a/content/test/data/accessibility/html/input-list.html
+++ b/content/test/data/accessibility/html/input-list.html
@@ -1,6 +1,10 @@
 <!--
 @WIN-ALLOW:autocomplete:*
 @WIN-ALLOW:haspopup*
+@AURALINUX-ALLOW:autocomplete*
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:haspopup*
 @BLINK-ALLOW:editable
 @BLINK-ALLOW:focusable
 @BLINK-ALLOW:haspopup*
diff --git a/content/test/data/accessibility/html/input-month-expected-auralinux.txt b/content/test/data/accessibility/html/input-month-expected-auralinux.txt
new file mode 100644
index 0000000..1e397ca
--- /dev/null
+++ b/content/test/data/accessibility/html/input-month-expected-auralinux.txt
@@ -0,0 +1,11 @@
+[document web]
+++[section]
+++++[dateeditor]
+++++++[section]
+++++++++[section]
+++++++++++[spin button] name='Month' valuetext:0
+++++++++++++[text] name='---------'
+++++++++++[text] name=' '
+++++++++++[spin button] name='Year' valuetext:0
+++++++++++++[text] name='----'
+++++++[push button] name='Show date picker' haspopup:menu
diff --git a/content/test/data/accessibility/html/input-month.html b/content/test/data/accessibility/html/input-month.html
index 9d262bc..66fff2fd 100644
--- a/content/test/data/accessibility/html/input-month.html
+++ b/content/test/data/accessibility/html/input-month.html
@@ -7,6 +7,9 @@
 @WIN-ALLOW:haspopup*
 @WIN-ALLOW:ia2_hypertext=*
 @WIN-ALLOW:value*
+@AURALINUX-ALLOW:description*
+@AURALINUX-ALLOW:haspopup*
+@AURALINUX-ALLOW:value*
 -->
 <html>
   <body>
diff --git a/content/test/data/accessibility/html/input-number-expected-auralinux.txt b/content/test/data/accessibility/html/input-number-expected-auralinux.txt
new file mode 100644
index 0000000..8eb0c9a
--- /dev/null
+++ b/content/test/data/accessibility/html/input-number-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web]
+++[section]
+++++[spin button] selectable-text text-input-type:number valuetext:1
+++++[spin button] selectable-text text-input-type:number valuetext:6
diff --git a/content/test/data/accessibility/html/input-number.html b/content/test/data/accessibility/html/input-number.html
index 78e7e8c..ca1dd90 100644
--- a/content/test/data/accessibility/html/input-number.html
+++ b/content/test/data/accessibility/html/input-number.html
@@ -11,6 +11,8 @@
 @WIN-ALLOW:maximumValue*
 @WIN-ALLOW:minimumValue*
 @WIN-ALLOW:valuetext*
+@AURALINUX-ALLOW:text-input-type*
+@AURALINUX-ALLOW:valuetext*
 -->
 <html>
   <body>
diff --git a/content/test/data/accessibility/html/input-password-expected-auralinux.txt b/content/test/data/accessibility/html/input-password-expected-auralinux.txt
new file mode 100644
index 0000000..d482f55d
--- /dev/null
+++ b/content/test/data/accessibility/html/input-password-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web] focusable
+++[section]
+++++[password text] editable focusable focused selectable-text
+
diff --git a/content/test/data/accessibility/html/input-password.html b/content/test/data/accessibility/html/input-password.html
index d765b46..73ee1c3 100644
--- a/content/test/data/accessibility/html/input-password.html
+++ b/content/test/data/accessibility/html/input-password.html
@@ -10,6 +10,8 @@
 @WIN-ALLOW:n_selections*
 @WIN-ALLOW:selection_start*
 @WIN-ALLOW:selection_end*
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:editable
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-radio-expected-auralinux.txt b/content/test/data/accessibility/html/input-radio-expected-auralinux.txt
new file mode 100644
index 0000000..8b447953
--- /dev/null
+++ b/content/test/data/accessibility/html/input-radio-expected-auralinux.txt
@@ -0,0 +1,13 @@
+[document web]
+++[form]
+++++[radio button] checkable:true
+++++[text] name='Radio1'
+++++[text] name='<newline>'
+++++[radio button] checkable:true
+++++[text] name='Radio2'
+++[form]
+++++[radio button] name='Radio3' checkable:true
+++++[radio button] name='Radio4' checked checkable:true
+++[form]
+++++[radio button] name='Radio5' checkable:true
+++++[radio button] name='Radio6' checked checkable:true
diff --git a/content/test/data/accessibility/html/input-radio-in-menu-expected-auralinux.txt b/content/test/data/accessibility/html/input-radio-in-menu-expected-auralinux.txt
new file mode 100644
index 0000000..93ae1dc
--- /dev/null
+++ b/content/test/data/accessibility/html/input-radio-in-menu-expected-auralinux.txt
@@ -0,0 +1,11 @@
+[document web]
+++[section]
+++++[radio menu item] checked checkable:true
+++++[text] name='Radio0 '
+++++[radio menu item] checkable:true
+++++[text] name='Radio1 '
+++++[radio button] name='Radio2' checkable:true xml-roles:radio
+++[menu] xml-roles:menu
+++++[radio button] name='Radio3' checkable:true xml-roles:radio
+++++[radio menu item] checkable:true
+++++[radio menu item] checked checkable:true
diff --git a/content/test/data/accessibility/html/input-radio-in-menu.html b/content/test/data/accessibility/html/input-radio-in-menu.html
index 3b82375..ef51ca2 100644
--- a/content/test/data/accessibility/html/input-radio-in-menu.html
+++ b/content/test/data/accessibility/html/input-radio-in-menu.html
@@ -3,6 +3,11 @@
 @WIN-ALLOW:MIXED*
 @WIN-ALLOW:checkable*
 @WIN-ALLOW:ia2_hypertext=*
+@AURALINUX-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:indeterminate
+@AURALINUX-ALLOW:checked
+@AURALINUX-DENY:checkable
+@AURALINUX-DENY:last-defined
 -->
 <html>
   <body>
diff --git a/content/test/data/accessibility/html/input-radio.html b/content/test/data/accessibility/html/input-radio.html
index dadba4d8..248ce2e 100644
--- a/content/test/data/accessibility/html/input-radio.html
+++ b/content/test/data/accessibility/html/input-radio.html
@@ -6,6 +6,8 @@
 @WIN-ALLOW:checkable:*
 @WIN-ALLOW:ia2_hypertext=*
 @WIN-ALLOW:IA2_STATE_CHECKABLE*
+@AURALINUX-DENY:checkable
+@AURALINUX-DENY:last-defined
 -->
 <html>
   <body>
diff --git a/content/test/data/accessibility/html/input-range-expected-auralinux.txt b/content/test/data/accessibility/html/input-range-expected-auralinux.txt
new file mode 100644
index 0000000..173a32b
--- /dev/null
+++ b/content/test/data/accessibility/html/input-range-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[slider] horizontal valuetext:5 xml-roles:slider
diff --git a/content/test/data/accessibility/html/input-range.html b/content/test/data/accessibility/html/input-range.html
index 4ac86e07..f976e79 100644
--- a/content/test/data/accessibility/html/input-range.html
+++ b/content/test/data/accessibility/html/input-range.html
@@ -11,6 +11,10 @@
 @WIN-ALLOW:ia2_hypertext=*
 @WIN-ALLOW:valuetext*
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:valuetext*
+@AURALINUX-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:horizontal
+@AURALINUX-ALLOW:vertical
 -->
 <html>
   <body>
diff --git a/content/test/data/accessibility/html/input-reset-expected-auralinux.txt b/content/test/data/accessibility/html/input-reset-expected-auralinux.txt
new file mode 100644
index 0000000..10272f2c
--- /dev/null
+++ b/content/test/data/accessibility/html/input-reset-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web] focusable focused
+++[section]
+++++[entry] editable focusable selectable-text
+++++[push button] name='Reset' focusable
diff --git a/content/test/data/accessibility/html/input-reset.html b/content/test/data/accessibility/html/input-reset.html
index 49771d63..7e61221c 100644
--- a/content/test/data/accessibility/html/input-reset.html
+++ b/content/test/data/accessibility/html/input-reset.html
@@ -1,5 +1,7 @@
 <!--
 @MAC-ALLOW:AXRole*
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:editable
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-search-expected-auralinux.txt b/content/test/data/accessibility/html/input-search-expected-auralinux.txt
new file mode 100644
index 0000000..6ff961eb
--- /dev/null
+++ b/content/test/data/accessibility/html/input-search-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable focused
+++[section]
+++++[entry] editable focusable selectable-text text-input-type:search
diff --git a/content/test/data/accessibility/html/input-search.html b/content/test/data/accessibility/html/input-search.html
index 8f1cadb9..8e8b20a 100644
--- a/content/test/data/accessibility/html/input-search.html
+++ b/content/test/data/accessibility/html/input-search.html
@@ -7,6 +7,9 @@
 @WIN-ALLOW:selection_start*
 @WIN-ALLOW:selection_end*
 @WIN-ALLOW:textSel*
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:text-input-type*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-submit-expected-auralinux.txt b/content/test/data/accessibility/html/input-submit-expected-auralinux.txt
new file mode 100644
index 0000000..d206ebc
--- /dev/null
+++ b/content/test/data/accessibility/html/input-submit-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web] focusable focused
+++[form]
+++++[entry] editable focusable selectable-text text-input-type:text
+++++[push button] name='Submit' focusable
diff --git a/content/test/data/accessibility/html/input-submit.html b/content/test/data/accessibility/html/input-submit.html
index 14be9210..34729f1 100644
--- a/content/test/data/accessibility/html/input-submit.html
+++ b/content/test/data/accessibility/html/input-submit.html
@@ -1,5 +1,8 @@
 <!--
 @MAC-ALLOW:AXRole*
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:text-input-type*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-suggestions-source-element-expected-auralinux.txt b/content/test/data/accessibility/html/input-suggestions-source-element-expected-auralinux.txt
new file mode 100644
index 0000000..498f9a0a
--- /dev/null
+++ b/content/test/data/accessibility/html/input-suggestions-source-element-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable focused
+++[section]
+++++[combo box] editable focusable supports-autocompletion selectable-text haspopup:listbox
diff --git a/content/test/data/accessibility/html/input-suggestions-source-element.html b/content/test/data/accessibility/html/input-suggestions-source-element.html
index f696935..038e853 100644
--- a/content/test/data/accessibility/html/input-suggestions-source-element.html
+++ b/content/test/data/accessibility/html/input-suggestions-source-element.html
@@ -2,6 +2,10 @@
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXHasPopup*
 @WIN-ALLOW:haspopup*
+@AURALINUX-ALLOW:haspopup*
+@AURALINUX-ALLOW:supports-autocompletion
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:focus*
 @BLINK-ALLOW:haspopup*
 -->
 <!DOCTYPE html>
diff --git a/content/test/data/accessibility/html/input-tel-expected-auralinux.txt b/content/test/data/accessibility/html/input-tel-expected-auralinux.txt
new file mode 100644
index 0000000..c488983
--- /dev/null
+++ b/content/test/data/accessibility/html/input-tel-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable focused
+++[section]
+++++[entry] editable focusable selectable-text text-input-type:tel
diff --git a/content/test/data/accessibility/html/input-tel.html b/content/test/data/accessibility/html/input-tel.html
index 1129b81..5e13ef03 100644
--- a/content/test/data/accessibility/html/input-tel.html
+++ b/content/test/data/accessibility/html/input-tel.html
@@ -6,6 +6,9 @@
 @WIN-ALLOW:selection_end*
 @WIN-ALLOW:text-input-type*
 @WIN-ALLOW:textSel*
+@AURALINUX-ALLOW:text-input-type*
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:focus*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-text-expected-auralinux.txt b/content/test/data/accessibility/html/input-text-expected-auralinux.txt
new file mode 100644
index 0000000..0b64ce28
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable
+++[section]
+++++[entry] name='Name' editable focusable focused single-line selectable-text text-input-type:text
diff --git a/content/test/data/accessibility/html/input-text-name-calc-expected-auralinux.txt b/content/test/data/accessibility/html/input-text-name-calc-expected-auralinux.txt
new file mode 100644
index 0000000..ec4da370
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-name-calc-expected-auralinux.txt
@@ -0,0 +1,10 @@
+[document web]
+++[entry] name='Title0' explicit-name:true
+++[entry] name='Label1' description='Title1' explicit-name:true
+++[entry] name='AriaLabel2' description='Title2' explicit-name:true
+++[entry] name='LabelledBy3' description='Title3' explicit-name:true
+++[entry] name='Placeholder4' explicit-name:true
+++[entry] name='ARIA Placeholder4a' explicit-name:true
+++[entry] name='Placeholder4b' explicit-name:true
+++[entry] name='Placeholder5' description='Title5' explicit-name:true
+++[entry] name='LabelledBy6' description='DescribedBy6' explicit-name:true
diff --git a/content/test/data/accessibility/html/input-text-name-calc.html b/content/test/data/accessibility/html/input-text-name-calc.html
index 04ca17a..cb62129 100644
--- a/content/test/data/accessibility/html/input-text-name-calc.html
+++ b/content/test/data/accessibility/html/input-text-name-calc.html
@@ -1,6 +1,9 @@
 <!--
 @WIN-ALLOW:description*
 @WIN-ALLOW:explicit-name*
+@AURALINUX-ALLOW:description*
+@AURALINUX-ALLOW:explicit-name*
+@AURALINUX-DENY:selectable-text
 -->
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/input-text-read-only-expected-auralinux.txt b/content/test/data/accessibility/html/input-text-read-only-expected-auralinux.txt
new file mode 100644
index 0000000..0fa1ad2
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-read-only-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable
+++[section]
+++++[entry] name='Name' focusable focused single-line text-input-type:text
diff --git a/content/test/data/accessibility/html/input-text-read-only.html b/content/test/data/accessibility/html/input-text-read-only.html
index 98d35e1..9622a7e 100644
--- a/content/test/data/accessibility/html/input-text-read-only.html
+++ b/content/test/data/accessibility/html/input-text-read-only.html
@@ -9,6 +9,12 @@
 @WIN-ALLOW:selection_start*
 @WIN-ALLOW:selection_end*
 @WIN-ALLOW:text-input-type*
+@AURALINUX-ALLOW:text-input-type*
+@AURALINUX-ALLOW:*-line
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:focus*
+@AURALINUX-DENY:read-only
+@AURALINUX-DENY:selectable-text
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-text-value-expected-auralinux.txt b/content/test/data/accessibility/html/input-text-value-expected-auralinux.txt
new file mode 100644
index 0000000..3c9a5e55
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-value-expected-auralinux.txt
@@ -0,0 +1,18 @@
+[document web]
+++[section]
+++++[label]
+++++++[text] name='l1'
+++++[entry] name='l1' single-line
+++++[label]
+++++++[text] name='l2'
+++++[entry] name='l2' single-line
+++++[entry] name='l2' single-line
+++++[entry] name='l2' single-line
+++++[label]
+++++++[text] name='Email'
+++++[section]
+++++[entry] name='Email' single-line
+++++[entry] name='Email' single-line
+++++[entry] name='l5' multi-line
+++++[entry] name='l6' multi-line
+++++[entry] name='Name' description='Description' single-line
diff --git a/content/test/data/accessibility/html/input-text-value.html b/content/test/data/accessibility/html/input-text-value.html
index 9db750a..687ace1d 100644
--- a/content/test/data/accessibility/html/input-text-value.html
+++ b/content/test/data/accessibility/html/input-text-value.html
@@ -1,9 +1,12 @@
 <!--
+@AURALINUX-ALLOW:description*
 @WIN-ALLOW:description*
 @WIN-ALLOW:IA2_STATE_MULTI_LINE
 @BLINK-ALLOW:description*
 @BLINK-ALLOW:focusable*
 @BLINK-ALLOW:multiline
+@AURALINUX-ALLOW:*-line
+@AURALINUX-DENY:selectable-text
 -->
 <html>
 <!-- Width/font adds consistency -->
diff --git a/content/test/data/accessibility/html/input-text-with-selection-expected-auralinux.txt b/content/test/data/accessibility/html/input-text-with-selection-expected-auralinux.txt
new file mode 100644
index 0000000..a09dae2
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-with-selection-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable
+++[section]
+++++[entry] editable focusable focused single-line selectable-text text-input-type:text
diff --git a/content/test/data/accessibility/html/input-text-with-selection.html b/content/test/data/accessibility/html/input-text-with-selection.html
index 60bcfb66..5ad4be4c 100644
--- a/content/test/data/accessibility/html/input-text-with-selection.html
+++ b/content/test/data/accessibility/html/input-text-with-selection.html
@@ -8,6 +8,10 @@
 @WIN-ALLOW:selection_start*
 @WIN-ALLOW:selection_end*
 @WIN-ALLOW:text-input-type*
+@AURALINUX-ALLOW:text-input-type*
+@AURALINUX-ALLOW:*-line
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:focus*
 @BLINK-ALLOW:textSel*
 -->
 <!DOCTYPE html>
diff --git a/content/test/data/accessibility/html/input-text.html b/content/test/data/accessibility/html/input-text.html
index 8c01462..eda91b0 100644
--- a/content/test/data/accessibility/html/input-text.html
+++ b/content/test/data/accessibility/html/input-text.html
@@ -12,6 +12,10 @@
 @WIN-ALLOW:selection_start*
 @WIN-ALLOW:selection_end*
 @WIN-ALLOW:text-input-type*
+@AURALINUX-ALLOW:text-input-type*
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:*-line
 -->
 <html>
   <body>
diff --git a/content/test/data/accessibility/html/input-time-expected-auralinux.txt b/content/test/data/accessibility/html/input-time-expected-auralinux.txt
new file mode 100644
index 0000000..5d169fbe
--- /dev/null
+++ b/content/test/data/accessibility/html/input-time-expected-auralinux.txt
@@ -0,0 +1,13 @@
+[document web]
+++[section]
+++++[dateeditor]
+++++++[section]
+++++++++[section]
+++++++++++[spin button] name='Hours' xml-roles:spinbutton
+++++++++++++[text] name='12'
+++++++++++[text] name=':'
+++++++++++[spin button] name='Minutes' xml-roles:spinbutton
+++++++++++++[text] name='00'
+++++++++++[text] name=' '
+++++++++++[spin button] name='AM/PM' xml-roles:spinbutton
+++++++++++++[text] name='AM'
diff --git a/content/test/data/accessibility/html/input-time.html b/content/test/data/accessibility/html/input-time.html
index af75a6b8..b31c121a 100644
--- a/content/test/data/accessibility/html/input-time.html
+++ b/content/test/data/accessibility/html/input-time.html
@@ -3,6 +3,8 @@
 @WIN-ALLOW:ia2_hypertext=*
 @WIN-ALLOW:xml-roles*
 @WIN-ALLOW:description*
+@AURALINUX-ALLOW:xml-roles*
+@AURALINUX-ALLOW:description*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-types-expected-auralinux.txt b/content/test/data/accessibility/html/input-types-expected-auralinux.txt
new file mode 100644
index 0000000..d67ced67
--- /dev/null
+++ b/content/test/data/accessibility/html/input-types-expected-auralinux.txt
@@ -0,0 +1,49 @@
+[document web]
+++[section]
+++++[label]
+++++++[text] name='Default: '
+++++++[entry] name='Default: ' selectable-text
+++++[label]
+++++++[text] name='Button: '
+++++++[push button] name='Button: '
+++++[check box] name='Checkbox: ' checkable:true
+++++[label]
+++++++[text] name='Color: '
+++++++[push button] name='Color: '
+++++[label]
+++++++[text] name='Email: '
+++++++[entry] name='Email: ' selectable-text text-input-type:email
+++++[label]
+++++++[text] name='File: '
+++++++[push button] name='File: '
+++++[label]
+++++++[text] name='Image: '
+++++++[push button] name='Image: '
+++++[label]
+++++++[text] name='Number: '
+++++++[spin button] name='Number: ' selectable-text text-input-type:number
+++++[label]
+++++++[text] name='Password: '
+++++++[password text] name='Password: ' selectable-text text-input-type:password
+++++[radio button] name='Radio: ' checkable:true
+++++[label]
+++++++[text] name='Range: '
+++++++[slider] name='Range: ' horizontal
+++++[label]
+++++++[text] name='Reset: '
+++++++[push button] name='Reset: '
+++++[label]
+++++++[text] name='Search: '
+++++++[entry] name='Search: ' selectable-text text-input-type:search
+++++[label]
+++++++[text] name='Submit: '
+++++++[push button] name='Submit: '
+++++[label]
+++++++[text] name='Tel: '
+++++++[entry] name='Tel: ' selectable-text text-input-type:tel
+++++[label]
+++++++[text] name='Text: '
+++++++[entry] name='Text: ' selectable-text text-input-type:text
+++++[label]
+++++++[text] name='Url: '
+++++++[entry] name='Url: ' selectable-text text-input-type:url
diff --git a/content/test/data/accessibility/html/input-types.html b/content/test/data/accessibility/html/input-types.html
index 22821bb4..78e1a5ef 100644
--- a/content/test/data/accessibility/html/input-types.html
+++ b/content/test/data/accessibility/html/input-types.html
@@ -1,5 +1,8 @@
 <!--
 @WIN-ALLOW:text-input-type*
+@AURALINUX-ALLOW:text-input-type*
+@AURALINUX-DENY:checkable
+@AURALINUX-DENY:last-defined
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/input-url-expected-auralinux.txt b/content/test/data/accessibility/html/input-url-expected-auralinux.txt
new file mode 100644
index 0000000..04cd01b
--- /dev/null
+++ b/content/test/data/accessibility/html/input-url-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable focused
+++[section]
+++++[entry] editable focusable selectable-text text-input-type:url
diff --git a/content/test/data/accessibility/html/input-url.html b/content/test/data/accessibility/html/input-url.html
index e8d6b59..fe496c5 100644
--- a/content/test/data/accessibility/html/input-url.html
+++ b/content/test/data/accessibility/html/input-url.html
@@ -6,6 +6,9 @@
 @WIN-ALLOW:selection_start*
 @WIN-ALLOW:selection_end*
 @WIN-ALLOW:text-input-type*
+@AURALINUX-ALLOW:text-input-type:*
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:editable
 @BLINK-ALLOW:textSel*
 -->
 <!DOCTYPE html>
diff --git a/content/test/data/accessibility/html/input-week-expected-auralinux.txt b/content/test/data/accessibility/html/input-week-expected-auralinux.txt
new file mode 100644
index 0000000..eb44d4f
--- /dev/null
+++ b/content/test/data/accessibility/html/input-week-expected-auralinux.txt
@@ -0,0 +1,12 @@
+[document web]
+++[section]
+++++[dateeditor]
+++++++[section]
+++++++++[section]
+++++++++++[text] name='Week '
+++++++++++[spin button] name='Week'
+++++++++++++[text] name='--'
+++++++++++[text] name=', '
+++++++++++[spin button] name='Year'
+++++++++++++[text] name='----'
+++++++[push button] name='Show date picker' haspopup:menu
diff --git a/content/test/data/accessibility/html/input-week.html b/content/test/data/accessibility/html/input-week.html
index 75c3a1c2..ba7e5e2 100644
--- a/content/test/data/accessibility/html/input-week.html
+++ b/content/test/data/accessibility/html/input-week.html
@@ -4,6 +4,8 @@
 @WIN-ALLOW:ia2_hypertext=*
 @WIN-ALLOW:description*
 @WIN-ALLOW:haspopup*
+@AURALINUX-ALLOW:description*
+@AURALINUX-ALLOW:haspopup*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/ins-expected-auralinux.txt b/content/test/data/accessibility/html/ins-expected-auralinux.txt
new file mode 100644
index 0000000..51661fa
--- /dev/null
+++ b/content/test/data/accessibility/html/ins-expected-auralinux.txt
@@ -0,0 +1,9 @@
+[document web]
+++[paragraph]
+++++[text] name='My favorite browser is '
+++++[section]
+++++++[text] name='ABC'
+++++[text] name=' '
+++++[section]
+++++++[text] name='Chrome'
+++++[text] name='!'
diff --git a/content/test/data/accessibility/html/label-expected-auralinux.txt b/content/test/data/accessibility/html/label-expected-auralinux.txt
new file mode 100644
index 0000000..ea3c90b
--- /dev/null
+++ b/content/test/data/accessibility/html/label-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web]
+++[section]
+++++[label]
+++++++[text] name='Label'
diff --git a/content/test/data/accessibility/html/landmark-expected-auralinux.txt b/content/test/data/accessibility/html/landmark-expected-auralinux.txt
new file mode 100644
index 0000000..185c954c
--- /dev/null
+++ b/content/test/data/accessibility/html/landmark-expected-auralinux.txt
@@ -0,0 +1,149 @@
+[document web]
+++[landmark] xml-roles:banner
+++++[text] name='This is a header element.'
+++[landmark] xml-roles:complementary
+++++[text] name='This is an aside element.'
+++[landmark]
+++++[text] name='This is an address element.'
+++[footer] xml-roles:contentinfo
+++++[text] name='This is a footer element.'
+++[form]
+++++[text] name='This is a form element.'
+++[landmark] xml-roles:main
+++++[text] name='This is a main element.'
+++[landmark] xml-roles:navigation
+++++[text] name='This is a nav element.'
+++[embedded component] xml-roles:application
+++++[text] name='This is an ARIA application landmark.'
+++[landmark] xml-roles:banner
+++++[text] name='This is an ARIA banner landmark.'
+++[landmark] xml-roles:complementary
+++++[text] name='This is an ARIA complementary landmark.'
+++[landmark] xml-roles:contentinfo
+++++[text] name='This is an ARIA contentinfo landmark.'
+++[form] xml-roles:form
+++++[text] name='This is an ARIA form landmark.'
+++[landmark] xml-roles:main
+++++[text] name='This is an ARIA main landmark.'
+++[landmark] xml-roles:navigation
+++++[text] name='This is an ARIA navigation landmark.'
+++[landmark] xml-roles:search
+++++[text] name='This is an ARIA search landmark.'
+++[article] xml-roles:article
+++++[section]
+++++++[text] name='This should NOT banner role.'
+++[landmark] xml-roles:complementary
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[landmark] xml-roles:navigation
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[section] xml-roles:region
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[block quote]
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[panel]
+++++[toggle button] name='Details'
+++++++[text] name='Details'
+++[panel]
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[panel] xml-roles:figure
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[panel] xml-roles:group
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[landmark] xml-roles:main
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[article] xml-roles:article
+++++[section]
+++++++[text] name='This should NOT banner role.'
+++[landmark] xml-roles:complementary
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[landmark] xml-roles:navigation
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[section] xml-roles:region
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[block quote]
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[panel]
+++++[toggle button] name='Details'
+++++++[text] name='Details'
+++[panel]
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[panel] xml-roles:figure
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[section]
+++++[text] name='This should NOT have banner role.'
+++[landmark] xml-roles:main
+++++[section]
+++++++[text] name='This should NOT have banner role.'
+++[article] xml-roles:article
+++++[section]
+++++++[text] name='This should NOT footer role.'
+++[landmark] xml-roles:complementary
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[landmark] xml-roles:navigation
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[section] xml-roles:region
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[block quote]
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[panel]
+++++[toggle button] name='Details'
+++++++[text] name='Details'
+++[panel]
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[panel] xml-roles:figure
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[section]
+++++[text] name='This should NOT have footer role.'
+++[landmark] xml-roles:main
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[article] xml-roles:article
+++++[section]
+++++++[text] name='This should NOT footer role.'
+++[landmark] xml-roles:complementary
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[landmark] xml-roles:navigation
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[section] xml-roles:region
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[block quote]
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[panel]
+++++[toggle button] name='Details'
+++++++[text] name='Details'
+++[panel]
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[panel] xml-roles:figure
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[panel] xml-roles:group
+++++[section]
+++++++[text] name='This should NOT have footer role.'
+++[landmark] xml-roles:main
+++++[section]
+++++++[text] name='This should NOT have footer role.'
diff --git a/content/test/data/accessibility/html/landmark.html b/content/test/data/accessibility/html/landmark.html
index 2d26215..3218097 100644
--- a/content/test/data/accessibility/html/landmark.html
+++ b/content/test/data/accessibility/html/landmark.html
@@ -2,6 +2,7 @@
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXSubrole=*
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/legend-expected-auralinux.txt b/content/test/data/accessibility/html/legend-expected-auralinux.txt
new file mode 100644
index 0000000..2368ebf
--- /dev/null
+++ b/content/test/data/accessibility/html/legend-expected-auralinux.txt
@@ -0,0 +1,9 @@
+[document web] focusable focused
+++[form]
+++++[panel] name='Browser Engines:'
+++++++[label]
+++++++++[text] name='Browser Engines:'
+++++++[text] name='Browser: '
+++++++[entry] editable focusable single-line selectable-text
+++++++[text] name=' Rendering Engine: '
+++++++[entry] editable focusable single-line selectable-text
diff --git a/content/test/data/accessibility/html/legend.html b/content/test/data/accessibility/html/legend.html
index b08b24a..96c06a2 100644
--- a/content/test/data/accessibility/html/legend.html
+++ b/content/test/data/accessibility/html/legend.html
@@ -1,5 +1,8 @@
 <!--
 @MAC-ALLOW:AXTitleUIElement*
+@AURALINUX-ALLOW:*-line
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:focus*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/li-expected-auralinux.txt b/content/test/data/accessibility/html/li-expected-auralinux.txt
new file mode 100644
index 0000000..ef3966a7
--- /dev/null
+++ b/content/test/data/accessibility/html/li-expected-auralinux.txt
@@ -0,0 +1,11 @@
+[document web]
+++[list] display:block
+++++[list item] display:list-item posinset:1 setsize:3
+++++++[static] name='• '
+++++++[text] name='Item 1' display:list-item
+++++[list item] display:list-item posinset:2 setsize:3
+++++++[static] name='• '
+++++++[text] name='Item 2' display:list-item
+++++[list item] display:list-item posinset:3 setsize:3
+++++++[static] name='• '
+++++++[text] name='Item 3' display:list-item
diff --git a/content/test/data/accessibility/html/li.html b/content/test/data/accessibility/html/li.html
index 93ea363..328a73f 100644
--- a/content/test/data/accessibility/html/li.html
+++ b/content/test/data/accessibility/html/li.html
@@ -1,6 +1,9 @@
 <!--
 @MAC-ALLOW:AXSubrole*
 @WIN-ALLOW:display*
+@AURALINUX-ALLOW:display*
+@AURALINUX-ALLOW:posinset*
+@AURALINUX-ALLOW:setsize*
 @BLINK-ALLOW:display*
 @BLINK-ALLOW:setSize*
 @BLINK-ALLOW:posInSet*
diff --git a/content/test/data/accessibility/html/link-expected-auralinux.txt b/content/test/data/accessibility/html/link-expected-auralinux.txt
new file mode 100644
index 0000000..19b9c9f
--- /dev/null
+++ b/content/test/data/accessibility/html/link-expected-auralinux.txt
@@ -0,0 +1 @@
+[document web]
diff --git a/content/test/data/accessibility/html/link-inside-heading-expected-auralinux.txt b/content/test/data/accessibility/html/link-inside-heading-expected-auralinux.txt
new file mode 100644
index 0000000..981ca104
--- /dev/null
+++ b/content/test/data/accessibility/html/link-inside-heading-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web]
+++[heading] name='Link In Heading'
+++++[link] name='Link In Heading'
+++++++[text] name='Link In Heading'
diff --git a/content/test/data/accessibility/html/list-expected-auralinux.txt b/content/test/data/accessibility/html/list-expected-auralinux.txt
new file mode 100644
index 0000000..5979c752
--- /dev/null
+++ b/content/test/data/accessibility/html/list-expected-auralinux.txt
@@ -0,0 +1,20 @@
+[document web]
+++[list]
+++++[list item]
+++++++[static] name='• '
+++++++[text] name='tic'
+++++[list item]
+++++++[static] name='• '
+++++++[text] name='tac'
+++++[list item]
+++++++[static] name='• '
+++++++[text] name='toe'
+++[list]
+++++[list item]
+++++++[text] name='tic'
+++++[text] name=' '
+++++[list item]
+++++++[text] name='tac'
+++++[text] name=' '
+++++[list item]
+++++++[text] name='toe'
diff --git a/content/test/data/accessibility/html/list-markers-expected-auralinux.txt b/content/test/data/accessibility/html/list-markers-expected-auralinux.txt
new file mode 100644
index 0000000..189d8a4
--- /dev/null
+++ b/content/test/data/accessibility/html/list-markers-expected-auralinux.txt
@@ -0,0 +1,17 @@
+[document web]
+++[list]
+++++[list item]
+++++++[static] name='• '
+++++++[text] name='First item properly groups itself despite '
+++++++[text] name='bolded'
+++++++[text] name=' text.'
+++++[list item]
+++++++[static] name='• '
+++++++[text] name='This should also be '
+++++++[text] name='seen'
+++++++[text] name=' as a group.'
+++++[list item]
+++++++[static] name='• '
+++++++[text] name='Some '
+++++++[text] name='more'
+++++++[text] name=' text.'
diff --git a/content/test/data/accessibility/html/main-expected-auralinux.txt b/content/test/data/accessibility/html/main-expected-auralinux.txt
new file mode 100644
index 0000000..385eb48
--- /dev/null
+++ b/content/test/data/accessibility/html/main-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[landmark] xml-roles:main
+++++[text] name='This is main element.'
+++[landmark] xml-roles:main
+++++[text] name='This is an ARIA role main.'
diff --git a/content/test/data/accessibility/html/main.html b/content/test/data/accessibility/html/main.html
index 1bb6060..d421ca1b 100644
--- a/content/test/data/accessibility/html/main.html
+++ b/content/test/data/accessibility/html/main.html
@@ -2,6 +2,7 @@
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXSubrole=*
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/mark-expected-auralinux.txt b/content/test/data/accessibility/html/mark-expected-auralinux.txt
new file mode 100644
index 0000000..6a5eeb9d
--- /dev/null
+++ b/content/test/data/accessibility/html/mark-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[paragraph]
+++++[text] name='This test is to check '
+++++[static]
+++++++[text] name='mark tag'
+++++[text] name='.'
diff --git a/content/test/data/accessibility/html/math-expected-auralinux.txt b/content/test/data/accessibility/html/math-expected-auralinux.txt
new file mode 100644
index 0000000..e9f3a6d
--- /dev/null
+++ b/content/test/data/accessibility/html/math-expected-auralinux.txt
@@ -0,0 +1,10 @@
+[document web]
+++[section]
+++++[math]
+++++++[text] name='a'
+++++++[text] name='2'
+++++++[text] name=' '
+++++++[text] name='+'
+++++++[text] name=' '
+++++++[text] name='b'
+++++++[text] name='2'
diff --git a/content/test/data/accessibility/html/meter-expected-auralinux.txt b/content/test/data/accessibility/html/meter-expected-auralinux.txt
new file mode 100644
index 0000000..808dec7
--- /dev/null
+++ b/content/test/data/accessibility/html/meter-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[level bar]
diff --git a/content/test/data/accessibility/html/modal-dialog-closed-expected-auralinux.txt b/content/test/data/accessibility/html/modal-dialog-closed-expected-auralinux.txt
new file mode 100644
index 0000000..8b14944
--- /dev/null
+++ b/content/test/data/accessibility/html/modal-dialog-closed-expected-auralinux.txt
@@ -0,0 +1,7 @@
+[document web]
+++[text] name='Test that elements respawn in the accessibility tree after a modal dialog closes.'
+++[section]
+++++[combo box] expandable haspopup:menu
+++++++[menu]
+++++++++[menu item] name='This should be in the tree.' selectable selected
+++[push button]
diff --git a/content/test/data/accessibility/html/modal-dialog-closed.html b/content/test/data/accessibility/html/modal-dialog-closed.html
index a870db86..04953d7 100644
--- a/content/test/data/accessibility/html/modal-dialog-closed.html
+++ b/content/test/data/accessibility/html/modal-dialog-closed.html
@@ -1,6 +1,9 @@
 <!--
 @MAC-ALLOW:AXSubrole=*
 @WIN-ALLOW:haspopup*
+@AURALINUX-ALLOW:haspopup*
+@AURALINUX-ALLOW:expand*
+@AURALINUX-ALLOW:select*
 @BLINK-ALLOW:haspopup*
 -->
 <html>
diff --git a/content/test/data/accessibility/html/modal-dialog-opened-expected-auralinux.txt b/content/test/data/accessibility/html/modal-dialog-opened-expected-auralinux.txt
new file mode 100644
index 0000000..6a184f3
--- /dev/null
+++ b/content/test/data/accessibility/html/modal-dialog-opened-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[section]
+++[dialog] modal
+++++[text] name='The dialog subtree should be the only text content in the accessibility tree. '
+++++[link] name='Link inside the dialog.'
+++++++[text] name='Link inside the dialog.'
diff --git a/content/test/data/accessibility/html/modal-dialog-opened.html b/content/test/data/accessibility/html/modal-dialog-opened.html
index 47da0581..92741e4 100644
--- a/content/test/data/accessibility/html/modal-dialog-opened.html
+++ b/content/test/data/accessibility/html/modal-dialog-opened.html
@@ -1,5 +1,6 @@
 <!--
 @MAC-ALLOW:AXSubrole=*
+@AURALINUX-ALLOW:modal
 -->
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-auralinux.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-auralinux.txt
new file mode 100644
index 0000000..7a0a189
--- /dev/null
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[section]
+++[dialog] modal
+++++[text] name='This is the now active dialog. Of course it should be in the tree. '
+++++[push button] name='This is in the active dialog and should be in the tree.'
diff --git a/content/test/data/accessibility/html/modal-dialog-stack.html b/content/test/data/accessibility/html/modal-dialog-stack.html
index 5e15665e..0f35db3 100644
--- a/content/test/data/accessibility/html/modal-dialog-stack.html
+++ b/content/test/data/accessibility/html/modal-dialog-stack.html
@@ -1,5 +1,6 @@
 <!--
 @MAC-ALLOW:AXSubrole=*
+@AURALINUX-ALLOW:modal
 -->
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/navigation-expected-auralinux.txt b/content/test/data/accessibility/html/navigation-expected-auralinux.txt
new file mode 100644
index 0000000..2e306a7a
--- /dev/null
+++ b/content/test/data/accessibility/html/navigation-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web]
+++[landmark] xml-roles:navigation
+++++[link] name='Don't click on me'
+++++++[text] name='Don't click on me'
diff --git a/content/test/data/accessibility/html/navigation.html b/content/test/data/accessibility/html/navigation.html
index 5c6ffb32..f71df39 100644
--- a/content/test/data/accessibility/html/navigation.html
+++ b/content/test/data/accessibility/html/navigation.html
@@ -2,6 +2,7 @@
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXSubrole=*
 @WIN-ALLOW:xml-roles*
+@AURALINUX-ALLOW:xml-roles*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/noscript-expected-auralinux.txt b/content/test/data/accessibility/html/noscript-expected-auralinux.txt
new file mode 100644
index 0000000..19b9c9f
--- /dev/null
+++ b/content/test/data/accessibility/html/noscript-expected-auralinux.txt
@@ -0,0 +1 @@
+[document web]
diff --git a/content/test/data/accessibility/html/object-expected-auralinux.txt b/content/test/data/accessibility/html/object-expected-auralinux.txt
new file mode 100644
index 0000000..1b84d9f1
--- /dev/null
+++ b/content/test/data/accessibility/html/object-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[embedded component]
diff --git a/content/test/data/accessibility/html/ol-expected-auralinux.txt b/content/test/data/accessibility/html/ol-expected-auralinux.txt
new file mode 100644
index 0000000..7ba3e99f
--- /dev/null
+++ b/content/test/data/accessibility/html/ol-expected-auralinux.txt
@@ -0,0 +1,21 @@
+[document web]
+++[list]
+++++[list item]
+++++++[static] name='1. '
+++++++[text] name='Chrome'
+++++[list item]
+++++++[static] name='2. '
+++++++[text] name='Safari'
+++++[list item]
+++++++[static] name='3. '
+++++++[text] name='IE'
+++[list]
+++++[list item]
+++++++[static] name='10. '
+++++++[text] name='Android'
+++++[list item]
+++++++[static] name='11. '
+++++++[text] name='Mac'
+++++[list item]
+++++++[static] name='12. '
+++++++[text] name='Windows'
diff --git a/content/test/data/accessibility/html/optgroup-expected-auralinux.txt b/content/test/data/accessibility/html/optgroup-expected-auralinux.txt
new file mode 100644
index 0000000..aecbdeed
--- /dev/null
+++ b/content/test/data/accessibility/html/optgroup-expected-auralinux.txt
@@ -0,0 +1,15 @@
+[document web] enabled
+++[section] enabled
+++++[list box] enabled
+++++++[panel] name='Enabled' enabled xml-roles:group
+++++++++[text] name='Enabled' enabled
+++++++[list item] name='One' enabled posinset:1 setsize:4
+++++++[list item] name='Two' enabled posinset:2 setsize:4
+++++++[list item] name='Three' enabled posinset:3 setsize:4
+++++++[list item] name='Four' enabled posinset:4 setsize:4
+++++++[panel] name='Disabled' enabled xml-roles:group
+++++++++[text] name='Disabled' enabled
+++++++[list item] name='One' posinset:1 setsize:4
+++++++[list item] name='Two' posinset:2 setsize:4
+++++++[list item] name='Three' posinset:3 setsize:4
+++++++[list item] name='Four' posinset:4 setsize:4
diff --git a/content/test/data/accessibility/html/optgroup.html b/content/test/data/accessibility/html/optgroup.html
index 4623396..1600fbc 100644
--- a/content/test/data/accessibility/html/optgroup.html
+++ b/content/test/data/accessibility/html/optgroup.html
@@ -5,6 +5,10 @@
 @WIN-ALLOW:SELECTABLE
 @BLINK-ALLOW:setSize*
 @BLINK-ALLOW:posInSet*
+@AURALINUX-ALLOW:xml-roles*
+@AURALINUX-ALLOW:posinset*
+@AURALINUX-ALLOW:setsize*
+@AURALINUX-ALLOW:enabled
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/p-expected-auralinux.txt b/content/test/data/accessibility/html/p-expected-auralinux.txt
index 86b022b..718bc482 100644
--- a/content/test/data/accessibility/html/p-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/p-expected-auralinux.txt
@@ -1,6 +1,5 @@
-[document web] enabled focusable focused sensitive showing visible<newline>
-++[text] name='Before' enabled sensitive showing visible<newline>
-++[paragraph] enabled sensitive showing visible<newline>
-++++[text] name='Paragraph' enabled sensitive showing visible<newline>
-++[text] name='After' enabled sensitive showing visible<newline>
-
+[document web]
+++[text] name='Before'
+++[paragraph]
+++++[text] name='Paragraph'
+++[text] name='After'
diff --git a/content/test/data/accessibility/html/param-expected-auralinux.txt b/content/test/data/accessibility/html/param-expected-auralinux.txt
new file mode 100644
index 0000000..1b84d9f1
--- /dev/null
+++ b/content/test/data/accessibility/html/param-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[embedded component]
diff --git a/content/test/data/accessibility/html/pre-expected-auralinux.txt b/content/test/data/accessibility/html/pre-expected-auralinux.txt
new file mode 100644
index 0000000..098f82cb
--- /dev/null
+++ b/content/test/data/accessibility/html/pre-expected-auralinux.txt
@@ -0,0 +1,9 @@
+[document web]
+++[section]
+++++[text] name='This test is to check   pre<newline>formatting.'
+++[section]
+++++[text] name='This test is to check   pre<newline>formatting'
+++[section]
+++++[text] name='This test is to check   pre<newline>formatting.'
+++[section]
+++++[text] name='This test is to check pre formatting.'
diff --git a/content/test/data/accessibility/html/progress-expected-auralinux.txt b/content/test/data/accessibility/html/progress-expected-auralinux.txt
new file mode 100644
index 0000000..6ed6be0
--- /dev/null
+++ b/content/test/data/accessibility/html/progress-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web]
+++[section]
+++++[progress bar] valuetext:22
+++++[progress bar]
diff --git a/content/test/data/accessibility/html/progress.html b/content/test/data/accessibility/html/progress.html
index 6bf6cdc1..13df22b4 100644
--- a/content/test/data/accessibility/html/progress.html
+++ b/content/test/data/accessibility/html/progress.html
@@ -4,6 +4,7 @@
 @WIN-ALLOW:currentValue=*
 @WIN-ALLOW:minimumValue=*
 @WIN-ALLOW:maximumValue=*
+@AURALINUX-ALLOW:valuetext*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/q-expected-auralinux.txt b/content/test/data/accessibility/html/q-expected-auralinux.txt
new file mode 100644
index 0000000..7100a91
--- /dev/null
+++ b/content/test/data/accessibility/html/q-expected-auralinux.txt
@@ -0,0 +1,7 @@
+[document web]
+++[paragraph]
+++++[text] name='This is '
+++++[text] name='"'
+++++[text] name='Chromium Blink'
+++++[text] name='"'
+++++[text] name=' based browser.'
diff --git a/content/test/data/accessibility/html/ruby-expected-auralinux.txt b/content/test/data/accessibility/html/ruby-expected-auralinux.txt
new file mode 100644
index 0000000..5c8f462
--- /dev/null
+++ b/content/test/data/accessibility/html/ruby-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[section]
+++++[static]
+++++++[panel]
+++++++++[text] name='ruby text'
+++++++[text] name='ruby base'
diff --git a/content/test/data/accessibility/html/s-expected-auralinux.txt b/content/test/data/accessibility/html/s-expected-auralinux.txt
new file mode 100644
index 0000000..39d97b1
--- /dev/null
+++ b/content/test/data/accessibility/html/s-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[text] name='My car is blue.'
diff --git a/content/test/data/accessibility/html/samp-expected-auralinux.txt b/content/test/data/accessibility/html/samp-expected-auralinux.txt
new file mode 100644
index 0000000..b2de3eff
--- /dev/null
+++ b/content/test/data/accessibility/html/samp-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[text] name='Sample output from a computer program'
diff --git a/content/test/data/accessibility/html/script-expected-auralinux.txt b/content/test/data/accessibility/html/script-expected-auralinux.txt
new file mode 100644
index 0000000..cbc7ff7c
--- /dev/null
+++ b/content/test/data/accessibility/html/script-expected-auralinux.txt
@@ -0,0 +1,2 @@
+[document web]
+
diff --git a/content/test/data/accessibility/html/section-expected-auralinux.txt b/content/test/data/accessibility/html/section-expected-auralinux.txt
new file mode 100644
index 0000000..4e52b05
--- /dev/null
+++ b/content/test/data/accessibility/html/section-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section] xml-roles:region
+++++[text] name='This is a section element.'
diff --git a/content/test/data/accessibility/html/section.html b/content/test/data/accessibility/html/section.html
index 281a61771..e5976bbe 100644
--- a/content/test/data/accessibility/html/section.html
+++ b/content/test/data/accessibility/html/section.html
@@ -2,6 +2,7 @@
 @MAC-ALLOW:AXRole*
 @MAC-ALLOW:AXSubrole*
 @WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles:*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/select-expected-auralinux.txt b/content/test/data/accessibility/html/select-expected-auralinux.txt
new file mode 100644
index 0000000..09d3988
--- /dev/null
+++ b/content/test/data/accessibility/html/select-expected-auralinux.txt
@@ -0,0 +1,25 @@
+[document web]
+++[section]
+++++[combo box] expandable haspopup:menu
+++++++[menu]
+++++++++[menu item] name='Placeholder option' selectable selected posinset:1 setsize:3
+++++++++[menu item] name='Option 1' posinset:2 setsize:3
+++++++++[menu item] name='Option 2' posinset:3 setsize:3
+++++[combo box] expandable haspopup:menu
+++++++[menu]
+++++++++[menu item] name='Option 1' posinset:1 setsize:3
+++++++++[menu item] name='Option 2' selectable selected posinset:2 setsize:3
+++++++++[menu item] name='Option 3' posinset:3 setsize:3
+++++[combo box] expandable required haspopup:menu
+++++++[menu]
+++++++++[menu item] name='Option 1' selectable selected posinset:1 setsize:3
+++++++++[menu item] name='Option 2' posinset:2 setsize:3
+++++++++[menu item] name='Option 3' posinset:3 setsize:3
+++++[list box] multiselectable
+++++++[list item] name='Option 1' posinset:1 setsize:3
+++++++[list item] name='Option 2' posinset:2 setsize:3
+++++++[list item] name='Option 3' posinset:3 setsize:3
+++++[list box]
+++++++[list item] name='Option 1' posinset:1 setsize:3
+++++++[list item] name='Option 2' posinset:2 setsize:3
+++++++[list item] name='Option 3' posinset:3 setsize:3
diff --git a/content/test/data/accessibility/html/select.html b/content/test/data/accessibility/html/select.html
index 17d690e..fc2ca669 100644
--- a/content/test/data/accessibility/html/select.html
+++ b/content/test/data/accessibility/html/select.html
@@ -11,6 +11,11 @@
 @BLINK-ALLOW:setSize*
 @BLINK-ALLOW:posInSet*
 @BLINK-ALLOW:haspopup*
+@AURALINUX-ALLOW:haspopup*
+@AURALINUX-ALLOW:posinset*
+@AURALINUX-ALLOW:setsize*
+@AURALINUX-ALLOW:expand*
+@AURALINUX-ALLOW:required
 -->
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/small-expected-auralinux.txt b/content/test/data/accessibility/html/small-expected-auralinux.txt
new file mode 100644
index 0000000..140e0d4
--- /dev/null
+++ b/content/test/data/accessibility/html/small-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web]
+++[paragraph]
+++++[text] name='Chromium'
+++++[text] name='open source project'
diff --git a/content/test/data/accessibility/html/source-expected-auralinux.txt b/content/test/data/accessibility/html/source-expected-auralinux.txt
new file mode 100644
index 0000000..da6f0cd
--- /dev/null
+++ b/content/test/data/accessibility/html/source-expected-auralinux.txt
@@ -0,0 +1,11 @@
+[document web] focusable focused
+++[section]
+++++[audio] focusable
+++++++[section]
+++++++++[tool bar] name='audio' description='audio' horizontal
+++++++++++[tool bar] name='audio' description='audio' horizontal
+++++++++++++[push button] name='play' description='begin playback' focusable
+++++++++++++[text] name='0:00'
+++++++++++++[text] name='/ 0:00'
+++++++++++++[slider] description='audio time scrubber' focusable horizontal
+++++++++++++[push button] name='mute' description='mute audio track'
diff --git a/content/test/data/accessibility/html/source.html b/content/test/data/accessibility/html/source.html
index c902fb7..238e8b7e7 100644
--- a/content/test/data/accessibility/html/source.html
+++ b/content/test/data/accessibility/html/source.html
@@ -1,3 +1,8 @@
+<!--
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:horizontal
+@AURALINUX-ALLOW:vertical
+-->
 <!DOCTYPE html>
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/span-expected-auralinux.txt b/content/test/data/accessibility/html/span-expected-auralinux.txt
new file mode 100644
index 0000000..e8bdc30
--- /dev/null
+++ b/content/test/data/accessibility/html/span-expected-auralinux.txt
@@ -0,0 +1,70 @@
+[document web]
+++[paragraph]
+++++[text] name='This'
+++++[text] name=' '
+++++[text] name='paragraph has '
+++++[text] name='text'
+++++[text] name=' '
+++++[text] name='in'
+++++[text] name=' '
+++++[text] name='spans'
+++++[text] name='.'
+++[paragraph]
+++++[text] name='E1. Eat'
+++++[text] name=' '
+++++[link] name='space'
+++++++[text] name='space'
+++[paragraph]
+++++[text] name='E2. Eat'
+++++[text] name=' '
+++++[link] name='space'
+++++++[text] name='space'
+++[paragraph]
+++++[text] name='E3. Eat'
+++++[text] name=' '
+++++[link] name='space'
+++++++[text] name='space'
+++[paragraph]
+++++[link] name='E4. Eat'
+++++++[text] name='E4. Eat'
+++++[text] name=' '
+++++[text] name='space'
+++[paragraph]
+++++[link] name='E5. Eat'
+++++++[text] name='E5. Eat'
+++++[text] name=' '
+++++[text] name='space'
+++[paragraph]
+++++[link] name='E6. Eat'
+++++++[text] name='E6. Eat'
+++++[text] name=' '
+++++[text] name='space'
+++[paragraph]
+++++[text] name='K1. Keep'
+++++[text] name=' '
+++++[text] name='space'
+++[paragraph]
+++++[text] name='K2. Keep'
+++++[text] name=' '
+++++[text] name='space'
+++[paragraph]
+++++[text] name='K3. Keep'
+++++[text] name=' '
+++++[text] name='space'
+++[paragraph]
+++++[text] name='K4. Keep '
+++++[text] name='space'
+++[paragraph]
+++++[text] name='K5. Keep'
+++++[text] name=' '
+++++[text] name='space'
+++[paragraph]
+++++[text] name='K6. Keep '
+++++[text] name='space'
+++[paragraph]
+++++[text] name='K7. Keep'
+++++[text] name=' space'
+++[paragraph]
+++++[text] name='K8. Keep'
+++++[text] name=' '
+++++[text] name='space'
diff --git a/content/test/data/accessibility/html/strong-expected-auralinux.txt b/content/test/data/accessibility/html/strong-expected-auralinux.txt
new file mode 100644
index 0000000..c056af1e
--- /dev/null
+++ b/content/test/data/accessibility/html/strong-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[text] name='Strong text'
diff --git a/content/test/data/accessibility/html/style-expected-auralinux.txt b/content/test/data/accessibility/html/style-expected-auralinux.txt
new file mode 100644
index 0000000..19b9c9f
--- /dev/null
+++ b/content/test/data/accessibility/html/style-expected-auralinux.txt
@@ -0,0 +1 @@
+[document web]
diff --git a/content/test/data/accessibility/html/sub-expected-auralinux.txt b/content/test/data/accessibility/html/sub-expected-auralinux.txt
new file mode 100644
index 0000000..5d75f9c
--- /dev/null
+++ b/content/test/data/accessibility/html/sub-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[paragraph]
+++++[text] name='This text contains '
+++++[subscript] name='subscript'
+++++[text] name=' text.'
diff --git a/content/test/data/accessibility/html/summary-expected-auralinux.txt b/content/test/data/accessibility/html/summary-expected-auralinux.txt
new file mode 100644
index 0000000..a4032a4
--- /dev/null
+++ b/content/test/data/accessibility/html/summary-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web]
+++[panel]
+++++[toggle button] name='details tag'
+++++++[text] name='details tag'
diff --git a/content/test/data/accessibility/html/sup-expected-auralinux.txt b/content/test/data/accessibility/html/sup-expected-auralinux.txt
new file mode 100644
index 0000000..50beb15
--- /dev/null
+++ b/content/test/data/accessibility/html/sup-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[paragraph]
+++++[text] name='This text contains'
+++++[superscript] name='superscript'
+++++[text] name='text.'
diff --git a/content/test/data/accessibility/html/svg-expected-auralinux.txt b/content/test/data/accessibility/html/svg-expected-auralinux.txt
new file mode 100644
index 0000000..d7ff00d3
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[section]
+++++[document frame] name='svg'
+++++++[section]
+++++++++[text] name='Test'
diff --git a/content/test/data/accessibility/html/table-layout-expected-auralinux.txt b/content/test/data/accessibility/html/table-layout-expected-auralinux.txt
new file mode 100644
index 0000000..7be2ccc
--- /dev/null
+++ b/content/test/data/accessibility/html/table-layout-expected-auralinux.txt
@@ -0,0 +1,23 @@
+[document web] name='Table example #2'
+++[section]
+++++[section]
+++++++[section] name='1'
+++++++++[text] name='1'
+++++++[section] name='2'
+++++++++[text] name='2'
+++++++[section] name='3'
+++++++++[text] name='3'
+++++[section]
+++++++[section] name='4'
+++++++++[text] name='4'
+++++++[section] name='5'
+++++++++[text] name='5'
+++++++[section] name='6'
+++++++++[text] name='6'
+++++[section]
+++++++[section] name='7'
+++++++++[text] name='7'
+++++++[section] name='8'
+++++++++[text] name='8'
+++++++[section] name='9'
+++++++++[text] name='9'
diff --git a/content/test/data/accessibility/html/table-presentation-expected-auralinux.txt b/content/test/data/accessibility/html/table-presentation-expected-auralinux.txt
new file mode 100644
index 0000000..a4d0af5
--- /dev/null
+++ b/content/test/data/accessibility/html/table-presentation-expected-auralinux.txt
@@ -0,0 +1,9 @@
+[document web] name='Table with role=presentation'
+++[section]
+++++[text] name='1'
+++[section]
+++++[text] name='2'
+++[section]
+++++[text] name='4'
+++[section]
+++++[text] name='5'
diff --git a/content/test/data/accessibility/html/table-simple-expected-auralinux.txt b/content/test/data/accessibility/html/table-simple-expected-auralinux.txt
new file mode 100644
index 0000000..1e1cb95
--- /dev/null
+++ b/content/test/data/accessibility/html/table-simple-expected-auralinux.txt
@@ -0,0 +1,17 @@
+[document web] name='Table example'
+++[table]
+++++[table row]
+++++++[column header] name='Pair'
+++++++++[text] name='Pair'
+++++++[column header] name='Single'
+++++++++[text] name='Single'
+++++[table row]
+++++++[table cell] name='AB'
+++++++++[text] name='AB'
+++++++[table cell] name='B'
+++++++++[text] name='B'
+++++[table row]
+++++++[table cell] name='CD'
+++++++++[text] name='CD'
+++++++[table cell] name='D'
+++++++++[text] name='D'
diff --git a/content/test/data/accessibility/html/table-spans-expected-auralinux.txt b/content/test/data/accessibility/html/table-spans-expected-auralinux.txt
new file mode 100644
index 0000000..06d542a4
--- /dev/null
+++ b/content/test/data/accessibility/html/table-spans-expected-auralinux.txt
@@ -0,0 +1,21 @@
+[document web] name='Table example with rowspan and colspan'
+++[table]
+++++[table row]
+++++++[table cell] name='AD'
+++++++++[text] name='AD'
+++++++[table cell] name='BC'
+++++++++[text] name='BC'
+++++[table row]
+++++++[table cell] name='EF'
+++++++++[text] name='EF'
+++[table]
+++++[table row]
+++++++[table cell] name='AD'
+++++++++[text] name='AD'
+++++++[table cell] name='BC'
+++++++++[text] name='BC'
+++++[table row]
+++++++[table cell] name='EF'
+++++++++[text] name='EF'
+++++++[table cell] name='GH'
+++++++++[text] name='GH'
diff --git a/content/test/data/accessibility/html/table-th-colheader-expected-auralinux.txt b/content/test/data/accessibility/html/table-th-colheader-expected-auralinux.txt
new file mode 100644
index 0000000..1de731f
--- /dev/null
+++ b/content/test/data/accessibility/html/table-th-colheader-expected-auralinux.txt
@@ -0,0 +1,12 @@
+[document web]
+++[table]
+++++[table row] posinset:1 setsize:2
+++++++[column header] name='Firstname' table-cell-index:0
+++++++++[text] name='Firstname'
+++++++[column header] name='Lastname' table-cell-index:1
+++++++++[text] name='Lastname'
+++++[table row] posinset:2 setsize:2
+++++++[table cell] name='Jill' table-cell-index:2
+++++++++[text] name='Jill'
+++++++[table cell] name='Smith' table-cell-index:3
+++++++++[text] name='Smith'
diff --git a/content/test/data/accessibility/html/table-th-colheader.html b/content/test/data/accessibility/html/table-th-colheader.html
index bbdaf7a..298a669 100644
--- a/content/test/data/accessibility/html/table-th-colheader.html
+++ b/content/test/data/accessibility/html/table-th-colheader.html
@@ -1,4 +1,7 @@
 <!--
+@AURALINUX-ALLOW:table-cell-index*
+@AURALINUX-ALLOW:posinset*
+@AURALINUX-ALLOW:setsize*
 @WIN-ALLOW:row_*
 @WIN-ALLOW:column_*
 @MAC-ALLOW:AXIndex=*
diff --git a/content/test/data/accessibility/html/table-th-rowheader-expected-auralinux.txt b/content/test/data/accessibility/html/table-th-rowheader-expected-auralinux.txt
new file mode 100644
index 0000000..39622ccd
--- /dev/null
+++ b/content/test/data/accessibility/html/table-th-rowheader-expected-auralinux.txt
@@ -0,0 +1,12 @@
+[document web] name='Table example - th rowheader'
+++[table]
+++++[table row]
+++++++[row header] name='Firstname'
+++++++++[text] name='Firstname'
+++++++[table cell] name='Jill'
+++++++++[text] name='Jill'
+++++[table row]
+++++++[row header] name='Lastname'
+++++++++[text] name='Lastname'
+++++++[table cell] name='Smith'
+++++++++[text] name='Smith'
diff --git a/content/test/data/accessibility/html/table-thead-tbody-tfoot-expected-auralinux.txt b/content/test/data/accessibility/html/table-thead-tbody-tfoot-expected-auralinux.txt
new file mode 100644
index 0000000..36aeba2
--- /dev/null
+++ b/content/test/data/accessibility/html/table-thead-tbody-tfoot-expected-auralinux.txt
@@ -0,0 +1,22 @@
+[document web] name='Table example - thead, tbody, tfoot'
+++[table]
+++++[table row]
+++++++[column header] name='Sum'
+++++++++[text] name='Sum'
+++++++[column header] name='Subtraction'
+++++++++[text] name='Subtraction'
+++++[table row]
+++++++[table cell] name='10'
+++++++++[text] name='10'
+++++++[table cell] name='7'
+++++++++[text] name='7'
+++++[table row]
+++++++[table cell] name='2'
+++++++++[text] name='2'
+++++++[table cell] name='4'
+++++++++[text] name='4'
+++++[table row]
+++++++[table cell] name='12'
+++++++++[text] name='12'
+++++++[table cell] name='3'
+++++++++[text] name='3'
diff --git a/content/test/data/accessibility/html/textarea-expected-auralinux.txt b/content/test/data/accessibility/html/textarea-expected-auralinux.txt
new file mode 100644
index 0000000..614277c
--- /dev/null
+++ b/content/test/data/accessibility/html/textarea-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable focused
+++[section]
+++++[entry] editable focusable multi-line selectable-text
diff --git a/content/test/data/accessibility/html/textarea-read-only-expected-auralinux.txt b/content/test/data/accessibility/html/textarea-read-only-expected-auralinux.txt
new file mode 100644
index 0000000..ee6fd8a
--- /dev/null
+++ b/content/test/data/accessibility/html/textarea-read-only-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable focused
+++[section]
+++++[entry] focusable multi-line selectable-text
diff --git a/content/test/data/accessibility/html/textarea-read-only.html b/content/test/data/accessibility/html/textarea-read-only.html
index ffa31da..675b5c5 100644
--- a/content/test/data/accessibility/html/textarea-read-only.html
+++ b/content/test/data/accessibility/html/textarea-read-only.html
@@ -7,6 +7,10 @@
 @WIN-ALLOW:selection_start*
 @WIN-ALLOW:selection_end*
 @BLINK-ALLOW:textSel*
+@AURALINUX-ALLOW:text-input-type*
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:*-line
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/textarea-with-selection-expected-auralinux.txt b/content/test/data/accessibility/html/textarea-with-selection-expected-auralinux.txt
new file mode 100644
index 0000000..c9caf30e9
--- /dev/null
+++ b/content/test/data/accessibility/html/textarea-with-selection-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web] focusable
+++[section]
+++++[entry] editable focusable focused multi-line selectable-text
diff --git a/content/test/data/accessibility/html/textarea-with-selection.html b/content/test/data/accessibility/html/textarea-with-selection.html
index 07b6e82e..b758be14 100644
--- a/content/test/data/accessibility/html/textarea-with-selection.html
+++ b/content/test/data/accessibility/html/textarea-with-selection.html
@@ -7,6 +7,10 @@
 @WIN-ALLOW:selection_start*
 @WIN-ALLOW:selection_end*
 @BLINK-ALLOW:textSel*
+@AURALINUX-ALLOW:text-input-type*
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:*-line
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/textarea.html b/content/test/data/accessibility/html/textarea.html
index 694bab7a..c540d6705 100644
--- a/content/test/data/accessibility/html/textarea.html
+++ b/content/test/data/accessibility/html/textarea.html
@@ -10,6 +10,10 @@
 @WIN-ALLOW:selection_start*
 @WIN-ALLOW:selection_end*
 @BLINK-ALLOW:textSel*
+@AURALINUX-ALLOW:text-input-type*
+@AURALINUX-ALLOW:editable
+@AURALINUX-ALLOW:focus*
+@AURALINUX-ALLOW:*-line
 -->
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/time-expected-auralinux.txt b/content/test/data/accessibility/html/time-expected-auralinux.txt
new file mode 100644
index 0000000..4d2f4b2
--- /dev/null
+++ b/content/test/data/accessibility/html/time-expected-auralinux.txt
@@ -0,0 +1,7 @@
+[document web]
+++[section]
+++++[static]
+++++++[text] name='10:00'
+++++[text] name=' '
+++++[static]
+++++++[text] name='Valentines day'
diff --git a/content/test/data/accessibility/html/title-expected-auralinux.txt b/content/test/data/accessibility/html/title-expected-auralinux.txt
new file mode 100644
index 0000000..17f87dd
--- /dev/null
+++ b/content/test/data/accessibility/html/title-expected-auralinux.txt
@@ -0,0 +1 @@
+[document web] name='Title of the document'
diff --git a/content/test/data/accessibility/html/transition-expected-auralinux.txt b/content/test/data/accessibility/html/transition-expected-auralinux.txt
new file mode 100644
index 0000000..dc212f2
--- /dev/null
+++ b/content/test/data/accessibility/html/transition-expected-auralinux.txt
@@ -0,0 +1,4 @@
+[document web]
+++[section]
+++++[push button] name='GrowButton'
+++++[text] name='Done'
diff --git a/content/test/data/accessibility/html/ul-expected-auralinux.txt b/content/test/data/accessibility/html/ul-expected-auralinux.txt
new file mode 100644
index 0000000..56f58a6e
--- /dev/null
+++ b/content/test/data/accessibility/html/ul-expected-auralinux.txt
@@ -0,0 +1,11 @@
+[document web]
+++[list]
+++++[list item]
+++++++[static] name='• '
+++++++[text] name='Item 1'
+++++[list item]
+++++++[static] name='• '
+++++++[text] name='Item 2'
+++++[list item]
+++++++[static] name='• '
+++++++[text] name='Item 3'
diff --git a/content/test/data/accessibility/html/var-expected-auralinux.txt b/content/test/data/accessibility/html/var-expected-auralinux.txt
new file mode 100644
index 0000000..0523b56
--- /dev/null
+++ b/content/test/data/accessibility/html/var-expected-auralinux.txt
@@ -0,0 +1,3 @@
+[document web]
+++[section]
+++++[text] name='Variable'
diff --git a/content/test/data/accessibility/html/wbr-expected-auralinux.txt b/content/test/data/accessibility/html/wbr-expected-auralinux.txt
new file mode 100644
index 0000000..2fef7a07
--- /dev/null
+++ b/content/test/data/accessibility/html/wbr-expected-auralinux.txt
@@ -0,0 +1,5 @@
+[document web]
+++[section]
+++++[text] name='Supercali'
+++++[text] name='fragilistic'
+++++[text] name='expialidocious'
diff --git a/content/test/test_blink_web_unit_test_support.cc b/content/test/test_blink_web_unit_test_support.cc
index b3d52deb..bee17f25 100644
--- a/content/test/test_blink_web_unit_test_support.cc
+++ b/content/test/test_blink_web_unit_test_support.cc
@@ -50,7 +50,6 @@
 #include "gin/v8_initializer.h"  // nogncheck
 #endif
 
-#include "content/renderer/media/webrtc/rtc_certificate.h"
 #include "third_party/webrtc/rtc_base/rtccertificate.h"  // nogncheck
 
 using blink::WebString;
@@ -145,7 +144,6 @@
   gin::V8Initializer::LoadV8Natives();
 #endif
 
-
   scoped_refptr<base::SingleThreadTaskRunner> dummy_task_runner;
   std::unique_ptr<base::ThreadTaskRunnerHandle> dummy_task_runner_handle;
   if (!base::ThreadTaskRunnerHandle::IsSet()) {
@@ -332,15 +330,13 @@
   bool IsSupportedKeyParams(const blink::WebRTCKeyParams& key_params) override {
     return false;
   }
-  std::unique_ptr<blink::WebRTCCertificate> FromPEM(
+  rtc::scoped_refptr<rtc::RTCCertificate> FromPEM(
       blink::WebString pem_private_key,
       blink::WebString pem_certificate) override {
     rtc::scoped_refptr<rtc::RTCCertificate> certificate =
         rtc::RTCCertificate::FromPEM(rtc::RTCCertificatePEM(
             pem_private_key.Utf8(), pem_certificate.Utf8()));
-    if (!certificate)
-      return nullptr;
-    return std::make_unique<RTCCertificate>(certificate);
+    return certificate;
   }
 };
 
diff --git a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc
index c8e0ba40..3984d81 100644
--- a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc
+++ b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc
@@ -7,6 +7,8 @@
 #include <stdint.h>
 #include <utility>
 
+#include "base/hash.h"
+#include "base/metrics/histogram_functions.h"
 #include "content/public/browser/browser_context.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
@@ -113,6 +115,19 @@
   return true;
 }
 
+// Generates a hash from a UUID suitable for
+// base::UmaHistogramSparse(positive int).
+//
+// Hash values can be produced manually using tool: bluetooth_metrics_hash.
+int HashUUID(const device::BluetoothUUID& uuid) {
+  // TODO(520284): Other than verifying that |uuid| contains a value, this logic
+  // should be migrated to a dedicated histogram macro for hashed strings.
+  uint32_t data = base::PersistentHash(uuid.canonical_value());
+
+  // Strip off the sign bit to make the hash look nicer.
+  return static_cast<int>(data & 0x7fffffff);
+}
+
 }  // namespace
 
 BluetoothSocketAsyncApiFunction::BluetoothSocketAsyncApiFunction() {}
@@ -342,6 +357,9 @@
       service_options.channel.reset(new int(*(options->channel)));
   }
 
+  base::UmaHistogramSparse("Extensions.BluetoothSocket.ListenRFCOMM.Service",
+                           HashUUID(uuid));
+
   adapter->CreateRfcommService(uuid, service_options, callback, error_callback);
 }
 
@@ -392,6 +410,9 @@
     }
   }
 
+  base::UmaHistogramSparse("Extensions.BluetoothSocket.ListenL2CAP.Service",
+                           HashUUID(uuid));
+
   adapter->CreateL2capService(uuid, service_options, callback, error_callback);
 }
 
@@ -492,6 +513,9 @@
 void BluetoothSocketConnectFunction::ConnectToService(
     device::BluetoothDevice* device,
     const device::BluetoothUUID& uuid) {
+  base::UmaHistogramSparse("Extensions.BluetoothSocket.Connect.Service",
+                           HashUUID(uuid));
+
   device->ConnectToService(
       uuid,
       base::Bind(&BluetoothSocketConnectFunction::OnConnect, this),
diff --git a/google_apis/gaia/gaia_auth_util.cc b/google_apis/gaia/gaia_auth_util.cc
index 7acc27650..e2bb6147 100644
--- a/google_apis/gaia/gaia_auth_util.cc
+++ b/google_apis/gaia/gaia_auth_util.cc
@@ -30,21 +30,19 @@
 
 std::string CanonicalizeEmailImpl(const std::string& email_address,
                                   bool change_googlemail_to_gmail) {
+  std::string lower_case_email = base::ToLowerASCII(email_address);
   std::vector<std::string> parts = base::SplitString(
-      email_address, "@", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  if (parts.size() != 2U) {
-    NOTREACHED() << "expecting exactly one @, but got "
-                 << (parts.empty() ? 0 : parts.size() - 1)
-                 << " : " << email_address;
-  } else {
-    if (change_googlemail_to_gmail && parts[1] == kGooglemailDomain)
-      parts[1] = kGmailDomain;
+      lower_case_email, "@", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  if (parts.size() != 2U)
+    return lower_case_email;
 
-    if (parts[1] == kGmailDomain)  // only strip '.' for gmail accounts.
-      base::RemoveChars(parts[0], ".", &parts[0]);
-  }
+  if (change_googlemail_to_gmail && parts[1] == kGooglemailDomain)
+    parts[1] = kGmailDomain;
 
-  std::string new_email = base::ToLowerASCII(base::JoinString(parts, "@"));
+  if (parts[1] == kGmailDomain)  // only strip '.' for gmail accounts.
+    base::RemoveChars(parts[0], ".", &parts[0]);
+
+  std::string new_email = base::JoinString(parts, "@");
   VLOG(1) << "Canonicalized " << email_address << " to " << new_email;
   return new_email;
 }
diff --git a/google_apis/gaia/gaia_auth_util.h b/google_apis/gaia/gaia_auth_util.h
index 4322a36..175a41d 100644
--- a/google_apis/gaia/gaia_auth_util.h
+++ b/google_apis/gaia/gaia_auth_util.h
@@ -36,6 +36,8 @@
 
 // Perform basic canonicalization of |email_address|, taking into account that
 // gmail does not consider '.' or caps inside a username to matter.
+// If |email_address| is not a valid, returns it in lower case without
+// additional canonicalization.
 std::string CanonicalizeEmail(const std::string& email_address);
 
 // Returns the canonical form of the given domain.
diff --git a/google_apis/gaia/gaia_auth_util_unittest.cc b/google_apis/gaia/gaia_auth_util_unittest.cc
index 51d1a7af..9eb1308 100644
--- a/google_apis/gaia/gaia_auth_util_unittest.cc
+++ b/google_apis/gaia/gaia_auth_util_unittest.cc
@@ -14,6 +14,14 @@
   EXPECT_EQ(lower_case, CanonicalizeEmail(lower_case));
 }
 
+TEST(GaiaAuthUtilTest, InvalidEmailAddress) {
+  const char invalid_email1[] = "user";
+  const char invalid_email2[] = "user@@what.com";
+  EXPECT_EQ(invalid_email1, CanonicalizeEmail(invalid_email1));
+  EXPECT_EQ(invalid_email2, CanonicalizeEmail(invalid_email2));
+  EXPECT_EQ("user", CanonicalizeEmail("USER"));
+}
+
 TEST(GaiaAuthUtilTest, EmailAddressIgnoreCaps) {
   EXPECT_EQ(CanonicalizeEmail("user@what.com"),
             CanonicalizeEmail("UsEr@what.com"));
@@ -39,6 +47,11 @@
             CanonicalizeEmail("UsEr@gmail.com"));
 }
 
+TEST(GaiaAuthUtilTest, EmailAddressIgnoreOneUsernameDotAndIgnoreCaps) {
+  EXPECT_EQ(CanonicalizeEmail("user@gmail.com"),
+            CanonicalizeEmail("US.ER@GMAIL.COM"));
+}
+
 TEST(GaiaAuthUtilTest, EmailAddressIgnoreManyUsernameDots) {
   EXPECT_EQ(CanonicalizeEmail("u.ser@gmail.com"),
             CanonicalizeEmail("Us.E.r@gmail.com"));
diff --git a/google_apis/gaia/gaia_oauth_client.cc b/google_apis/gaia/gaia_oauth_client.cc
index 40e866b..c6c0221 100644
--- a/google_apis/gaia/gaia_oauth_client.cc
+++ b/google_apis/gaia/gaia_oauth_client.cc
@@ -9,17 +9,20 @@
 
 #include "base/json/json_reader.h"
 #include "base/logging.h"
+#include "base/memory/weak_ptr.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/gaia_urls.h"
+#include "net/base/backoff_entry.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_throttler_entry.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 #include "url/gurl.h"
 
 namespace {
@@ -30,19 +33,30 @@
 
 namespace gaia {
 
-// Use a non-zero number, so unit tests can differentiate the URLFetcher used by
-// this class from other fetchers (most other code just hardcodes the ID to 0).
-const int GaiaOAuthClient::kUrlFetcherId = 17109006;
-
 class GaiaOAuthClient::Core
-    : public base::RefCountedThreadSafe<GaiaOAuthClient::Core>,
-      public net::URLFetcherDelegate {
+    : public base::RefCountedThreadSafe<GaiaOAuthClient::Core> {
  public:
-  Core(net::URLRequestContextGetter* request_context_getter)
-      : num_retries_(0),
-        request_context_getter_(request_context_getter),
+  Core(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+      : backoff_entry_(&backoff_policy_),
+        num_retries_(0),
+        max_retries_(0),
+        url_loader_factory_(url_loader_factory),
         delegate_(NULL),
-        request_type_(NO_PENDING_REQUEST) {
+        request_type_(NO_PENDING_REQUEST),
+        weak_ptr_factory_(this) {
+    backoff_policy_.num_errors_to_ignore =
+        net::URLRequestThrottlerEntry::kDefaultNumErrorsToIgnore;
+    backoff_policy_.initial_delay_ms =
+        net::URLRequestThrottlerEntry::kDefaultInitialDelayMs;
+    backoff_policy_.multiply_factor =
+        net::URLRequestThrottlerEntry::kDefaultMultiplyFactor;
+    backoff_policy_.jitter_factor =
+        net::URLRequestThrottlerEntry::kDefaultJitterFactor;
+    backoff_policy_.maximum_backoff_ms =
+        net::URLRequestThrottlerEntry::kDefaultMaximumBackoffMs;
+    backoff_policy_.entry_lifetime_ms =
+        net::URLRequestThrottlerEntry::kDefaultEntryLifetimeMs;
+    backoff_policy_.always_use_initial_delay = false;
   }
 
   void GetTokensFromAuthCode(const OAuthClientInfo& oauth_client_info,
@@ -68,8 +82,8 @@
                     int max_retries,
                     Delegate* delegate);
 
-  // net::URLFetcherDelegate implementation.
-  void OnURLFetchComplete(const net::URLFetcher* source) override;
+  // Called as a SimpleURLLoader callback
+  void OnURLLoadComplete(std::unique_ptr<std::string> body);
 
  private:
   friend class base::RefCountedThreadSafe<Core>;
@@ -84,26 +98,50 @@
     USER_INFO,
   };
 
-  ~Core() override {}
+  ~Core() {}
 
   void GetUserInfoImpl(RequestType type,
                        const std::string& oauth_access_token,
                        int max_retries,
                        Delegate* delegate);
-  void MakeGaiaRequest(
+
+  // Stores request settings into |this| and calls SendRequest().
+  void MakeRequest(
+      RequestType type,
       const GURL& url,
-      const std::string& post_body,
+      std::string post_body /* may be empty if not needed*/,
+      std::string authorization_header /* empty if not needed */,
       int max_retries,
       GaiaOAuthClient::Delegate* delegate,
-      const net::NetworkTrafficAnnotationTag& traffic_annotation);
-  void HandleResponse(const net::URLFetcher* source,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation);
+
+  // Sends out a request based on things MakeRequest() stored...
+  // once |backoff_entry_| says it's OK. Can be called many times to retry,
+  // assuming |request_| is destroyed first.
+  void SendRequest();
+
+  // Actually sends the request.
+  void SendRequestImpl();
+
+  void HandleResponse(std::unique_ptr<std::string> body,
                       bool* should_retry_request);
 
+  net::BackoffEntry::Policy backoff_policy_;
+  net::BackoffEntry backoff_entry_;
+
   int num_retries_;
-  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+  int max_retries_;
+  GURL url_;
+  net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
+  std::string post_body_;
+  std::string authorization_header_;
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   GaiaOAuthClient::Delegate* delegate_;
-  std::unique_ptr<net::URLFetcher> request_;
+  std::unique_ptr<network::SimpleURLLoader> request_;
   RequestType request_type_;
+
+  base::WeakPtrFactory<Core> weak_ptr_factory_;
 };
 
 void GaiaOAuthClient::Core::GetTokensFromAuthCode(
@@ -111,8 +149,6 @@
     const std::string& auth_code,
     int max_retries,
     GaiaOAuthClient::Delegate* delegate) {
-  DCHECK_EQ(request_type_, NO_PENDING_REQUEST);
-  request_type_ = TOKENS_FROM_AUTH_CODE;
   std::string post_body =
       "code=" + net::EscapeUrlEncodedData(auth_code, true) +
       "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id,
@@ -122,7 +158,7 @@
       "&redirect_uri=" +
       net::EscapeUrlEncodedData(oauth_client_info.redirect_uri, true) +
       "&grant_type=authorization_code";
-  net::NetworkTrafficAnnotationTag traffic_annotation =
+  net::MutableNetworkTrafficAnnotationTag traffic_annotation(
       net::DefineNetworkTrafficAnnotation("gaia_oauth_client_get_tokens", R"(
         semantics {
           sender: "OAuth 2.0 calls"
@@ -151,9 +187,11 @@
               SigninAllowed: false
             }
           }
-        })");
-  MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_url()), post_body,
-                  max_retries, delegate, traffic_annotation);
+        })"));
+  MakeRequest(TOKENS_FROM_AUTH_CODE,
+              GURL(GaiaUrls::GetInstance()->oauth2_token_url()), post_body,
+              /* authorization_header = */ std::string(), max_retries, delegate,
+              traffic_annotation);
 }
 
 void GaiaOAuthClient::Core::RefreshToken(
@@ -162,8 +200,6 @@
     const std::vector<std::string>& scopes,
     int max_retries,
     GaiaOAuthClient::Delegate* delegate) {
-  DCHECK_EQ(request_type_, NO_PENDING_REQUEST);
-  request_type_ = REFRESH_TOKEN;
   std::string post_body =
       "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) +
       "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id,
@@ -177,7 +213,7 @@
     post_body += "&scope=" + net::EscapeUrlEncodedData(scopes_string, true);
   }
 
-  net::NetworkTrafficAnnotationTag traffic_annotation =
+  net::MutableNetworkTrafficAnnotationTag traffic_annotation(
       net::DefineNetworkTrafficAnnotation("gaia_oauth_client_refresh_token", R"(
         semantics {
           sender: "OAuth 2.0 calls"
@@ -204,9 +240,11 @@
               SigninAllowed: false
             }
           }
-        })");
-  MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_url()), post_body,
-                  max_retries, delegate, traffic_annotation);
+        })"));
+  MakeRequest(REFRESH_TOKEN, GURL(GaiaUrls::GetInstance()->oauth2_token_url()),
+              post_body,
+              /* authorization_header = */ std::string(), max_retries, delegate,
+              traffic_annotation);
 }
 
 void GaiaOAuthClient::Core::GetUserEmail(const std::string& oauth_access_token,
@@ -232,12 +270,7 @@
     const std::string& oauth_access_token,
     int max_retries,
     Delegate* delegate) {
-  DCHECK_EQ(request_type_, NO_PENDING_REQUEST);
-  DCHECK(!request_.get());
-  request_type_ = type;
-  delegate_ = delegate;
-  num_retries_ = 0;
-  net::NetworkTrafficAnnotationTag traffic_annotation =
+  net::MutableNetworkTrafficAnnotationTag traffic_annotation(
       net::DefineNetworkTrafficAnnotation("gaia_oauth_client_get_user_info", R"(
         semantics {
           sender: "OAuth 2.0 calls"
@@ -263,35 +296,20 @@
               SigninAllowed: false
             }
           }
-        })");
-  request_ = net::URLFetcher::Create(
-      kUrlFetcherId, GURL(GaiaUrls::GetInstance()->oauth_user_info_url()),
-      net::URLFetcher::GET, this, traffic_annotation);
-  request_->SetRequestContext(request_context_getter_.get());
-  request_->AddExtraRequestHeader("Authorization: OAuth " + oauth_access_token);
-  request_->SetMaxRetriesOn5xx(max_retries);
-  request_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
-                         net::LOAD_DO_NOT_SAVE_COOKIES);
-  MarkURLFetcherAsGaia(request_.get());
-
-  // Fetchers are sometimes cancelled because a network change was detected,
-  // especially at startup and after sign-in on ChromeOS. Retrying once should
-  // be enough in those cases; let the fetcher retry up to 3 times just in case.
-  // http://crbug.com/163710
-  request_->SetAutomaticallyRetryOnNetworkChanges(3);
-  request_->Start();
+        })"));
+  std::string auth = "OAuth " + oauth_access_token;
+  MakeRequest(type, GaiaUrls::GetInstance()->oauth_user_info_url(),
+              /* post_body = */ std::string(), auth, max_retries, delegate,
+              traffic_annotation);
 }
 
 void GaiaOAuthClient::Core::GetTokenInfo(const std::string& qualifier,
                                          const std::string& query,
                                          int max_retries,
                                          Delegate* delegate) {
-  DCHECK_EQ(request_type_, NO_PENDING_REQUEST);
-  DCHECK(!request_.get());
-  request_type_ = TOKEN_INFO;
   std::string post_body =
       qualifier + "=" + net::EscapeUrlEncodedData(query, true);
-  net::NetworkTrafficAnnotationTag traffic_annotation =
+  net::MutableNetworkTrafficAnnotationTag traffic_annotation(
       net::DefineNetworkTrafficAnnotation("gaia_oauth_client_get_token_info",
                                           R"(
         semantics {
@@ -323,65 +341,111 @@
               SigninAllowed: false
             }
           }
-        })");
-  MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_info_url()),
-                  post_body, max_retries, delegate, traffic_annotation);
+        })"));
+  MakeRequest(TOKEN_INFO,
+              GURL(GaiaUrls::GetInstance()->oauth2_token_info_url()), post_body,
+              /* authorization_header = */ std::string(), max_retries, delegate,
+              traffic_annotation);
 }
 
-void GaiaOAuthClient::Core::MakeGaiaRequest(
+void GaiaOAuthClient::Core::MakeRequest(
+    RequestType type,
     const GURL& url,
-    const std::string& post_body,
+    std::string post_body,
+    std::string authorization_header,
     int max_retries,
     GaiaOAuthClient::Delegate* delegate,
-    const net::NetworkTrafficAnnotationTag& traffic_annotation) {
-  DCHECK(!request_.get()) << "Tried to fetch two things at once!";
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
+  DCHECK_EQ(request_type_, NO_PENDING_REQUEST);
+  request_type_ = type;
   delegate_ = delegate;
   num_retries_ = 0;
-  request_ = net::URLFetcher::Create(kUrlFetcherId, url, net::URLFetcher::POST,
-                                     this, traffic_annotation);
-  request_->SetRequestContext(request_context_getter_.get());
-  request_->SetUploadData("application/x-www-form-urlencoded", post_body);
-  request_->SetMaxRetriesOn5xx(max_retries);
-  request_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
-                         net::LOAD_DO_NOT_SAVE_COOKIES);
-  MarkURLFetcherAsGaia(request_.get());
-  // See comment on SetAutomaticallyRetryOnNetworkChanges() above.
-  request_->SetAutomaticallyRetryOnNetworkChanges(3);
-  request_->Start();
+  max_retries_ = max_retries;
+  url_ = url;
+  traffic_annotation_ = traffic_annotation;
+  post_body_ = std::move(post_body);
+  authorization_header_ = std::move(authorization_header);
+  SendRequest();
 }
 
-// URLFetcher::Delegate implementation.
-void GaiaOAuthClient::Core::OnURLFetchComplete(
-    const net::URLFetcher* source) {
-  bool should_retry = false;
-  HandleResponse(source, &should_retry);
-  if (should_retry) {
-    // Explicitly call ReceivedContentWasMalformed() to ensure the current
-    // request gets counted as a failure for calculation of the back-off
-    // period.  If it was already a failure by status code, this call will
-    // be ignored.
-    request_->ReceivedContentWasMalformed();
-    num_retries_++;
-    // We must set our request_context_getter_ again because
-    // URLFetcher::Core::RetryOrCompleteUrlFetch resets it to NULL...
-    request_->SetRequestContext(request_context_getter_.get());
-    request_->Start();
+void GaiaOAuthClient::Core::SendRequest() {
+  if (backoff_entry_.ShouldRejectRequest()) {
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&GaiaOAuthClient::Core::SendRequestImpl,
+                       weak_ptr_factory_.GetWeakPtr()),
+        backoff_entry_.GetTimeUntilRelease());
+  } else {
+    SendRequestImpl();
   }
 }
 
-void GaiaOAuthClient::Core::HandleResponse(
-    const net::URLFetcher* source,
-    bool* should_retry_request) {
+void GaiaOAuthClient::Core::SendRequestImpl() {
+  DCHECK(!request_.get()) << "Tried to fetch two things at once!";
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = url_;
+  resource_request->method = post_body_.empty() ? "GET" : "POST";
+  resource_request->load_flags =
+      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+  if (!authorization_header_.empty())
+    resource_request->headers.SetHeader("Authorization", authorization_header_);
+
+  request_ = network::SimpleURLLoader::Create(
+      std::move(resource_request),
+      static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation_));
+
+  if (!post_body_.empty()) {
+    request_->AttachStringForUpload(post_body_,
+                                    "application/x-www-form-urlencoded");
+  }
+
+  // Retry is implemented internally.
+  request_->SetRetryOptions(0, network::SimpleURLLoader::RETRY_NEVER);
+
+  // TODO(https://crbug.com/808498) re-add data use measurement once
+  // SimpleURLLoader supports it. Previous way of setting it was:
+  // MarkURLFetcherAsGaia(request_.get());
+
+  request_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      // Unretained(this) is safe since |this| owns |request_|, and its deletion
+      // will cancel the callback.
+      base::BindOnce(&GaiaOAuthClient::Core::OnURLLoadComplete,
+                     base::Unretained(this)));
+}
+
+void GaiaOAuthClient::Core::OnURLLoadComplete(
+    std::unique_ptr<std::string> body) {
+  bool should_retry = false;
+  base::WeakPtr<GaiaOAuthClient::Core> weak_this =
+      weak_ptr_factory_.GetWeakPtr();
+  // HandleResponse() may delete |this| if it assigns |should_retry| == false.
+  HandleResponse(std::move(body), &should_retry);
+  if (should_retry) {
+    num_retries_++;
+    backoff_entry_.InformOfRequest(false);
+    SendRequest();
+  } else {
+    if (weak_this)
+      backoff_entry_.InformOfRequest(true);
+  }
+}
+
+void GaiaOAuthClient::Core::HandleResponse(std::unique_ptr<std::string> body,
+                                           bool* should_retry_request) {
+  *should_retry_request = false;
   // Move ownership of the request fetcher into a local scoped_ptr which
-  // will be nuked when we're done handling the request, unless we need
-  // to retry, in which case ownership will be returned to request_.
-  std::unique_ptr<net::URLFetcher> old_request = std::move(request_);
-  DCHECK_EQ(source, old_request.get());
+  // will be nuked when we're done handling the request.
+  std::unique_ptr<network::SimpleURLLoader> source = std::move(request_);
+
+  int response_code = -1;
+  if (source->ResponseInfo() && source->ResponseInfo()->headers)
+    response_code = source->ResponseInfo()->headers->response_code();
 
   // HTTP_BAD_REQUEST means the arguments are invalid.  HTTP_UNAUTHORIZED means
   // the access or refresh token is invalid. No point retrying. We are
   // done here.
-  int response_code = source->GetResponseCode();
   if (response_code == net::HTTP_BAD_REQUEST ||
       response_code == net::HTTP_UNAUTHORIZED) {
     delegate_->OnOAuthError();
@@ -389,9 +453,8 @@
   }
 
   std::unique_ptr<base::DictionaryValue> response_dict;
-  if (source->GetResponseCode() == net::HTTP_OK) {
-    std::string data;
-    source->GetResponseAsString(&data);
+  if (response_code == net::HTTP_OK && body) {
+    std::string data = std::move(*body);
     std::unique_ptr<base::Value> message_value = base::JSONReader::Read(data);
     if (message_value.get() && message_value->is_dict()) {
       response_dict.reset(
@@ -402,13 +465,11 @@
   if (!response_dict.get()) {
     // If we don't have an access token yet and the the error was not
     // RC_BAD_REQUEST, we may need to retry.
-    if ((source->GetMaxRetriesOn5xx() != -1) &&
-        (num_retries_ >= source->GetMaxRetriesOn5xx())) {
+    if ((max_retries_ != -1) && (num_retries_ >= max_retries_)) {
       // Retry limit reached. Give up.
       request_type_ = NO_PENDING_REQUEST;
-      delegate_->OnNetworkError(source->GetResponseCode());
+      delegate_->OnNetworkError(response_code);
     } else {
-      request_ = std::move(old_request);
       *should_retry_request = true;
     }
     return;
@@ -471,8 +532,9 @@
   }
 }
 
-GaiaOAuthClient::GaiaOAuthClient(net::URLRequestContextGetter* context_getter) {
-  core_ = new Core(context_getter);
+GaiaOAuthClient::GaiaOAuthClient(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+  core_ = new Core(std::move(url_loader_factory));
 }
 
 GaiaOAuthClient::~GaiaOAuthClient() {
diff --git a/google_apis/gaia/gaia_oauth_client.h b/google_apis/gaia/gaia_oauth_client.h
index 1d5f525..46415c4 100644
--- a/google_apis/gaia/gaia_oauth_client.h
+++ b/google_apis/gaia/gaia_oauth_client.h
@@ -13,8 +13,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/values.h"
 
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
 }
 
 // A helper class to get and refresh OAuth2 refresh and access tokens.
@@ -32,8 +32,6 @@
 
 class GaiaOAuthClient {
  public:
-  const static int kUrlFetcherId;
-
   class Delegate {
    public:
     // Invoked on a successful response to the GetTokensFromAuthCode request.
@@ -64,7 +62,8 @@
     virtual ~Delegate() {}
   };
 
-  GaiaOAuthClient(net::URLRequestContextGetter* context_getter);
+  GaiaOAuthClient(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
   ~GaiaOAuthClient();
 
   // In the below methods, |max_retries| specifies the maximum number of times
diff --git a/google_apis/gaia/gaia_oauth_client_unittest.cc b/google_apis/gaia/gaia_oauth_client_unittest.cc
index 0de4893..4b53e0a 100644
--- a/google_apis/gaia/gaia_oauth_client_unittest.cc
+++ b/google_apis/gaia/gaia_oauth_client_unittest.cc
@@ -9,17 +9,17 @@
 
 #include "base/json/json_reader.h"
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/time/tick_clock.h"
 #include "base/values.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_status.h"
-#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -32,103 +32,87 @@
 
 namespace {
 
-// Responds as though OAuth returned from the server.
-class MockOAuthFetcher : public net::TestURLFetcher {
+// Simulates some number of failures, followed by an optional success.
+// Does not distinguish between different URLs.
+class ResponseInjector {
  public:
-  MockOAuthFetcher(int response_code,
-                   int max_failure_count,
-                   bool complete_immediately,
-                   const GURL& url,
-                   const std::string& results,
-                   net::URLFetcher::RequestType request_type,
-                   net::URLFetcherDelegate* d)
-      : net::TestURLFetcher(0, url, d),
-        max_failure_count_(max_failure_count),
+  explicit ResponseInjector(network::TestURLLoaderFactory* url_loader_factory)
+      : url_loader_factory_(url_loader_factory),
+        response_code_(net::HTTP_OK),
+        complete_immediately_(true),
         current_failure_count_(0),
-        complete_immediately_(complete_immediately) {
-    set_url(url);
-    set_response_code(response_code);
-    SetResponseString(results);
+        max_failure_count_(0) {
+    url_loader_factory->SetInterceptor(
+        base::BindRepeating(&ResponseInjector::AdjustResponseBasedOnSettings,
+                            base::Unretained(this)));
   }
 
-  ~MockOAuthFetcher() override {}
+  ~ResponseInjector() {
+    url_loader_factory_->SetInterceptor(
+        base::BindRepeating([](const network::ResourceRequest& request) {
+          ADD_FAILURE() << "Unexpected fetch of:" << request.url;
+        }));
+  }
 
-  void Start() override {
-    if ((GetResponseCode() != net::HTTP_OK) && (max_failure_count_ != -1) &&
-        (current_failure_count_ == max_failure_count_)) {
-      set_response_code(net::HTTP_OK);
+  void AdjustResponseBasedOnSettings(const network::ResourceRequest& request) {
+    url_loader_factory_->ClearResponses();
+    DCHECK(pending_url_.is_empty());
+    pending_url_ = request.url;
+    if (complete_immediately_) {
+      Finish();
     }
-
-    net::Error error = net::OK;
-    if (GetResponseCode() != net::HTTP_OK) {
-      error = net::ERR_FAILED;
-      current_failure_count_++;
-    }
-    set_status(net::URLRequestStatus::FromError(error));
-
-    if (complete_immediately_)
-      delegate()->OnURLFetchComplete(this);
   }
 
   void Finish() {
-    ASSERT_FALSE(complete_immediately_);
-    delegate()->OnURLFetchComplete(this);
+    net::HttpStatusCode response_code = response_code_;
+    if (response_code_ != net::HTTP_OK && (max_failure_count_ != -1) &&
+        (current_failure_count_ == max_failure_count_))
+      response_code = net::HTTP_OK;
+
+    if (response_code != net::HTTP_OK)
+      ++current_failure_count_;
+
+    url_loader_factory_->AddResponse(pending_url_.spec(), results_,
+                                     response_code);
+    pending_url_ = GURL();
   }
 
- private:
-  int max_failure_count_;
-  int current_failure_count_;
-  bool complete_immediately_;
-  DISALLOW_COPY_AND_ASSIGN(MockOAuthFetcher);
-};
+  std::string GetUploadData() {
+    const std::vector<network::TestURLLoaderFactory::PendingRequest>& pending =
+        *url_loader_factory_->pending_requests();
+    if (pending.size() == 1) {
+      return network::GetUploadData(pending[0].request);
+    } else {
+      ADD_FAILURE() << "Unexpected state in GetUploadData";
+      return "";
+    }
+  }
 
-class MockOAuthFetcherFactory : public net::URLFetcherFactory,
-                                public net::ScopedURLFetcherFactory {
- public:
-  MockOAuthFetcherFactory()
-      : net::ScopedURLFetcherFactory(this),
-        response_code_(net::HTTP_OK),
-        complete_immediately_(true) {
-  }
-  ~MockOAuthFetcherFactory() override {}
-  std::unique_ptr<net::URLFetcher> CreateURLFetcher(
-      int id,
-      const GURL& url,
-      net::URLFetcher::RequestType request_type,
-      net::URLFetcherDelegate* d,
-      net::NetworkTrafficAnnotationTag traffic_annotation) override {
-    url_fetcher_ = new MockOAuthFetcher(
-        response_code_,
-        max_failure_count_,
-        complete_immediately_,
-        url,
-        results_,
-        request_type,
-        d);
-    return std::unique_ptr<net::URLFetcher>(url_fetcher_);
-  }
   void set_response_code(int response_code) {
-    response_code_ = response_code;
+    response_code_ = static_cast<net::HttpStatusCode>(response_code);
   }
+
   void set_max_failure_count(int count) {
     max_failure_count_ = count;
   }
+
   void set_results(const std::string& results) {
     results_ = results;
   }
-  MockOAuthFetcher* get_url_fetcher() {
-    return url_fetcher_;
-  }
+
   void set_complete_immediately(bool complete_immediately) {
     complete_immediately_ = complete_immediately;
   }
  private:
-  MockOAuthFetcher* url_fetcher_;
-  int response_code_;
+  network::TestURLLoaderFactory* url_loader_factory_;
+  GURL pending_url_;
+
+  net::HttpStatusCode response_code_;
   bool complete_immediately_;
+  int current_failure_count_;
   int max_failure_count_;
   std::string results_;
-  DISALLOW_COPY_AND_ASSIGN(MockOAuthFetcherFactory);
+  DISALLOW_COPY_AND_ASSIGN(ResponseInjector);
 };
 
 const std::string kTestAccessToken = "1/fFAGRNJru1FTz70BzhT3Zg";
@@ -182,23 +166,33 @@
 
 class GaiaOAuthClientTest : public testing::Test {
  protected:
+  GaiaOAuthClientTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {}
+
   void SetUp() override {
     client_info_.client_id = "test_client_id";
     client_info_.client_secret = "test_client_secret";
     client_info_.redirect_uri = "test_redirect_uri";
   };
 
- protected:
-  net::TestURLRequestContextGetter* GetRequestContext() {
-    if (!request_context_getter_.get()) {
-      request_context_getter_ = new net::TestURLRequestContextGetter(
-          message_loop_.task_runner());
-    }
-    return request_context_getter_.get();
+  scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory() {
+    return base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+        &url_loader_factory_);
   }
 
-  base::MessageLoop message_loop_;
-  scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+  void FlushNetwork() {
+    // An event loop spin is required for things to be delivered from
+    // TestURLLoaderFactory to its clients via mojo pipes. In addition,
+    // some retries may have back off, so may need to advance (mock) time
+    // for them to finish, too.
+    scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  }
+
+ protected:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  network::TestURLLoaderFactory url_loader_factory_;
+
   OAuthClientInfo client_info_;
 };
 
@@ -249,12 +243,13 @@
   EXPECT_CALL(delegate, OnNetworkError(response_code))
       .Times(1);
 
-  MockOAuthFetcherFactory factory;
-  factory.set_response_code(response_code);
-  factory.set_max_failure_count(4);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_response_code(response_code);
+  injector.set_max_failure_count(4);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.GetTokensFromAuthCode(client_info_, "auth_code", 2, &delegate);
+  FlushNetwork();
 }
 
 TEST_F(GaiaOAuthClientTest, NetworkFailureRecover) {
@@ -264,13 +259,47 @@
   EXPECT_CALL(delegate, OnGetTokensResponse(kTestRefreshToken, kTestAccessToken,
       kTestExpiresIn)).Times(1);
 
-  MockOAuthFetcherFactory factory;
-  factory.set_response_code(response_code);
-  factory.set_max_failure_count(4);
-  factory.set_results(kDummyGetTokensResult);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_response_code(response_code);
+  injector.set_max_failure_count(4);
+  injector.set_results(kDummyGetTokensResult);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.GetTokensFromAuthCode(client_info_, "auth_code", -1, &delegate);
+  FlushNetwork();
+}
+
+TEST_F(GaiaOAuthClientTest, NetworkFailureRecoverBackoff) {
+  // Make sure long backoffs are expontential.
+  int response_code = net::HTTP_INTERNAL_SERVER_ERROR;
+
+  MockGaiaOAuthClientDelegate delegate;
+  EXPECT_CALL(delegate, OnGetTokensResponse(kTestRefreshToken, kTestAccessToken,
+                                            kTestExpiresIn))
+      .Times(1);
+
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_response_code(response_code);
+  injector.set_max_failure_count(21);
+  injector.set_results(kDummyGetTokensResult);
+
+  base::TimeTicks start =
+      scoped_task_environment_.GetMockTickClock()->NowTicks();
+
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
+  auth.GetTokensFromAuthCode(client_info_, "auth_code", -1, &delegate);
+  FlushNetwork();
+
+  // Default params are:
+  //    40% jitter, 700ms initial, 1.4 exponent, ignore first 2 failures.
+  // So after 19 retries, delay is at least:
+  //    0.6 * 700ms * 1.4^(19-2) ~ 128s
+  // After 20:
+  //    0.6 * 700ms * 1.4^(20-2) ~ 179s
+  //
+  // ... so the whole thing should take at least 307s
+  EXPECT_GE(scoped_task_environment_.GetMockTickClock()->NowTicks() - start,
+            base::TimeDelta::FromSeconds(307));
 }
 
 TEST_F(GaiaOAuthClientTest, OAuthFailure) {
@@ -279,13 +308,14 @@
   MockGaiaOAuthClientDelegate delegate;
   EXPECT_CALL(delegate, OnOAuthError()).Times(1);
 
-  MockOAuthFetcherFactory factory;
-  factory.set_response_code(response_code);
-  factory.set_max_failure_count(-1);
-  factory.set_results(kDummyGetTokensResult);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_response_code(response_code);
+  injector.set_max_failure_count(-1);
+  injector.set_results(kDummyGetTokensResult);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.GetTokensFromAuthCode(client_info_, "auth_code", -1, &delegate);
+  FlushNetwork();
 }
 
 
@@ -294,11 +324,12 @@
   EXPECT_CALL(delegate, OnGetTokensResponse(kTestRefreshToken, kTestAccessToken,
       kTestExpiresIn)).Times(1);
 
-  MockOAuthFetcherFactory factory;
-  factory.set_results(kDummyGetTokensResult);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_results(kDummyGetTokensResult);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.GetTokensFromAuthCode(client_info_, "auth_code", -1, &delegate);
+  FlushNetwork();
 }
 
 TEST_F(GaiaOAuthClientTest, GetTokensAfterNetworkFailure) {
@@ -311,14 +342,16 @@
   EXPECT_CALL(success_delegate, OnGetTokensResponse(kTestRefreshToken,
       kTestAccessToken, kTestExpiresIn)).Times(1);
 
-  MockOAuthFetcherFactory factory;
-  factory.set_response_code(response_code);
-  factory.set_max_failure_count(4);
-  factory.set_results(kDummyGetTokensResult);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_response_code(response_code);
+  injector.set_max_failure_count(4);
+  injector.set_results(kDummyGetTokensResult);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.GetTokensFromAuthCode(client_info_, "auth_code", 2, &failure_delegate);
+  FlushNetwork();
   auth.GetTokensFromAuthCode(client_info_, "auth_code", -1, &success_delegate);
+  FlushNetwork();
 }
 
 TEST_F(GaiaOAuthClientTest, RefreshTokenSuccess) {
@@ -326,16 +359,16 @@
   EXPECT_CALL(delegate, OnRefreshTokenResponse(kTestAccessToken,
       kTestExpiresIn)).Times(1);
 
-  MockOAuthFetcherFactory factory;
-  factory.set_results(kDummyRefreshTokenResult);
-  factory.set_complete_immediately(false);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_results(kDummyRefreshTokenResult);
+  injector.set_complete_immediately(false);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.RefreshToken(client_info_, "refresh_token", std::vector<std::string>(),
                     -1, &delegate);
-  EXPECT_THAT(factory.get_url_fetcher()->upload_data(),
-              Not(HasSubstr("scope")));
-  factory.get_url_fetcher()->Finish();
+  EXPECT_THAT(injector.GetUploadData(), Not(HasSubstr("scope")));
+  injector.Finish();
+  FlushNetwork();
 }
 
 TEST_F(GaiaOAuthClientTest, RefreshTokenDownscopingSuccess) {
@@ -343,39 +376,40 @@
   EXPECT_CALL(delegate, OnRefreshTokenResponse(kTestAccessToken,
       kTestExpiresIn)).Times(1);
 
-  MockOAuthFetcherFactory factory;
-  factory.set_results(kDummyRefreshTokenResult);
-  factory.set_complete_immediately(false);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_results(kDummyRefreshTokenResult);
+  injector.set_complete_immediately(false);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.RefreshToken(client_info_, "refresh_token",
                     std::vector<std::string>(1, "scope4test"), -1, &delegate);
-  EXPECT_THAT(factory.get_url_fetcher()->upload_data(),
-              HasSubstr("&scope=scope4test"));
-  factory.get_url_fetcher()->Finish();
+  EXPECT_THAT(injector.GetUploadData(), HasSubstr("&scope=scope4test"));
+  injector.Finish();
+  FlushNetwork();
 }
 
-
 TEST_F(GaiaOAuthClientTest, GetUserEmail) {
   MockGaiaOAuthClientDelegate delegate;
   EXPECT_CALL(delegate, OnGetUserEmailResponse(kTestUserEmail)).Times(1);
 
-  MockOAuthFetcherFactory factory;
-  factory.set_results(kDummyUserInfoResult);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_results(kDummyUserInfoResult);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.GetUserEmail("access_token", 1, &delegate);
+  FlushNetwork();
 }
 
 TEST_F(GaiaOAuthClientTest, GetUserId) {
   MockGaiaOAuthClientDelegate delegate;
   EXPECT_CALL(delegate, OnGetUserIdResponse(kTestUserId)).Times(1);
 
-  MockOAuthFetcherFactory factory;
-  factory.set_results(kDummyUserIdResult);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_results(kDummyUserIdResult);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.GetUserId("access_token", 1, &delegate);
+  FlushNetwork();
 }
 
 TEST_F(GaiaOAuthClientTest, GetUserInfo) {
@@ -385,11 +419,12 @@
   EXPECT_CALL(delegate, OnGetUserInfoResponsePtr(_))
       .WillOnce(SaveArg<0>(&captured_result));
 
-  MockOAuthFetcherFactory factory;
-  factory.set_results(kDummyFullUserInfoResult);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_results(kDummyFullUserInfoResult);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.GetUserInfo("access_token", 1, &delegate);
+  FlushNetwork();
 
   std::unique_ptr<base::Value> value =
       base::JSONReader::Read(kDummyFullUserInfoResult);
@@ -408,11 +443,12 @@
   EXPECT_CALL(delegate, OnGetTokenInfoResponsePtr(_))
       .WillOnce(SaveArg<0>(&captured_result));
 
-  MockOAuthFetcherFactory factory;
-  factory.set_results(kDummyTokenInfoResult);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_results(kDummyTokenInfoResult);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.GetTokenInfo("some_token", 1, &delegate);
+  FlushNetwork();
 
   std::string issued_to;
   ASSERT_TRUE(captured_result->GetString("issued_to", &issued_to));
@@ -426,11 +462,12 @@
   EXPECT_CALL(delegate, OnGetTokenInfoResponsePtr(_))
       .WillOnce(SaveArg<0>(&captured_result));
 
-  MockOAuthFetcherFactory factory;
-  factory.set_results(kDummyTokenHandleInfoResult);
+  ResponseInjector injector(&url_loader_factory_);
+  injector.set_results(kDummyTokenHandleInfoResult);
 
-  GaiaOAuthClient auth(GetRequestContext());
+  GaiaOAuthClient auth(GetSharedURLLoaderFactory());
   auth.GetTokenHandleInfo("some_handle", 1, &delegate);
+  FlushNetwork();
 
   std::string audience;
   ASSERT_TRUE(captured_result->GetString("audience", &audience));
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index b01f2546..dde7505 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -739,6 +739,66 @@
     }
 
     builders {
+      name: "android-cronet-arm-dbg"
+      mixins: "android-ci"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
+      name: "android-cronet-arm-rel"
+      mixins: "android-ci"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
+      name: "android-cronet-arm64-dbg"
+      mixins: "android-ci"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
+      name: "android-cronet-arm64-rel"
+      mixins: "android-ci"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
+      name: "android-cronet-asan-arm-rel"
+      mixins: "android-ci"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
+      name: "android-cronet-kitkat-arm-rel"
+      mixins: "android-ci"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
+      name: "android-cronet-lollipop-arm-rel"
+      mixins: "android-ci"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
+      name: "android-cronet-marshmallow-arm64-rel"
+      mixins: "android-ci"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
+      name: "android-cronet-x86-dbg"
+      mixins: "android-ci"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
+      name: "android-cronet-x86-rel"
+      mixins: "android-ci"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
       name: "android-kitkat-arm-rel"
       mixins: "android-ci"
       dimensions: "os:Ubuntu-14.04"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index df7281a9..397f4c0 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -1534,6 +1534,55 @@
     short_name: "dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/android-cronet-arm-dbg"
+    category: "cronet|luci|arm"
+    short_name: "dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/android-cronet-arm-rel"
+    category: "cronet|luci|arm"
+    short_name: "rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/android-cronet-arm64-dbg"
+    category: "cronet|luci|arm64"
+    short_name: "dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/android-cronet-arm64-rel"
+    category: "cronet|luci|arm64"
+    short_name: "rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/android-cronet-asan-arm-rel"
+    category: "cronet|luci|asan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/android-cronet-kitkat-arm-rel"
+    category: "cronet|luci|test"
+    short_name: "k"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/android-cronet-lollipop-arm-rel"
+    category: "cronet|luci|test"
+    short_name: "l"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/android-cronet-marshmallow-arm64-rel"
+    category: "cronet|luci|test"
+    short_name: "m"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/android-cronet-x86-dbg"
+    category: "cronet|luci|x86"
+    short_name: "dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/android-cronet-x86-rel"
+    category: "cronet|luci|x86"
+    short_name: "rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Android arm Builder (dbg)"
     category: "builder|arm"
     short_name: "32"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 6db45a2..7cb47dc 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -205,6 +205,16 @@
   triggers: "WinMSVC64 Goma Canary"
   triggers: "Windows Clang deterministic"
   triggers: "Windows deterministic"
+  triggers: "android-cronet-arm-dbg"
+  triggers: "android-cronet-arm-rel"
+  triggers: "android-cronet-arm64-dbg"
+  triggers: "android-cronet-arm64-rel"
+  triggers: "android-cronet-asan-arm-rel"
+  triggers: "android-cronet-kitkat-arm-rel"
+  triggers: "android-cronet-lollipop-arm-rel"
+  triggers: "android-cronet-marshmallow-arm64-rel"
+  triggers: "android-cronet-x86-dbg"
+  triggers: "android-cronet-x86-rel"
   triggers: "android-kitkat-arm-rel"
   triggers: "android-marshmallow-arm64-rel"
   triggers: "android-mojo-webview-rel"
@@ -539,6 +549,116 @@
 }
 
 job {
+  id: "android-cronet-arm-dbg"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-cronet-arm-dbg"
+  }
+}
+
+job {
+  id: "android-cronet-arm-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-cronet-arm-rel"
+  }
+}
+
+job {
+  id: "android-cronet-arm64-dbg"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-cronet-arm64-dbg"
+  }
+}
+
+job {
+  id: "android-cronet-arm64-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-cronet-arm64-rel"
+  }
+}
+
+job {
+  id: "android-cronet-asan-arm-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-cronet-asan-arm-rel"
+  }
+}
+
+job {
+  id: "android-cronet-kitkat-arm-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-cronet-kitkat-arm-rel"
+  }
+}
+
+job {
+  id: "android-cronet-lollipop-arm-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-cronet-lollipop-arm-rel"
+  }
+}
+
+job {
+  id: "android-cronet-marshmallow-arm64-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-cronet-marshmallow-arm64-rel"
+  }
+}
+
+job {
+  id: "android-cronet-x86-dbg"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-cronet-x86-dbg"
+  }
+}
+
+job {
+  id: "android-cronet-x86-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-cronet-x86-rel"
+  }
+}
+
+job {
+  id: "android-kitkat-arm-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-kitkat-arm-rel"
+  }
+}
+
+job {
   id: "android-kitkat-arm-rel"
   acl_sets: "default"
   buildbucket: {
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 9193f5a..dc0ee53 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -219,15 +219,6 @@
       <message name="IDS_IOS_APP_LAUNCHER_OPEN_APP_BUTTON_LABEL" desc="Label of a button to open an application. [Length: 10em] [iOS only]">
         Open
       </message>
-      <message name="IDS_IOS_AUTOFILL" desc="Title for the view in Settings for enabling/disabling Autofill. [Length: 15em] [iOS only]">
-        Autofill Forms
-      </message>
-      <message name="IDS_IOS_ENABLE_AUTOFILL_PROFILES" desc="Title for the the toggle in Autofill Settings for enabling/disabling Autofill Addresses. Title case. [Length: unlimited] [iOS only]">
-        Save and Fill Addresses
-      </message>
-      <message name="IDS_IOS_ENABLE_AUTOFILL_CREDIT_CARDS" desc="Title for the toggle in Autofill Settings for enabling/disabling Autofill Credit Cards. Title case. [Length: unlimited] [iOS only]">
-        Save and Fill Credit Cards
-      </message>
       <message name="IDS_IOS_AUTOFILL_ACCNAME_HIDE_KEYBOARD" desc="The accessible name for the hide keyboard button in the keyboard accessory shown when filling out forms. [Length: unlimited] [iOS only]">
         Hide keyboard
       </message>
@@ -246,9 +237,6 @@
       <message name="IDS_IOS_AUTOFILL_ADDRESS2" desc="Title of the field of a profile address representing the second line of the address. [Length: 15em] [iOS only]">
         Address 2
       </message>
-      <message name="IDS_IOS_AUTOFILL_ADDRESSES_GROUP_NAME" desc="The name of the Addresses group of the Autofill settings. Title case. [Length: 20em] [iOS only]">
-        Addresses
-      </message>
       <message name="IDS_IOS_AUTOFILL_CARDHOLDER" desc="Title of the field representing the full name of a credit card holder. [Length: 15em] [iOS only]">
         Name on Card
       </message>
@@ -264,9 +252,6 @@
       <message name="IDS_IOS_AUTOFILL_COUNTRY" desc="Title of the field of a profile address representing the country/nation of the address. [Length: 15em] [iOS only]">
         Country
       </message>
-      <message name="IDS_IOS_AUTOFILL_CREDITCARDS_GROUP_NAME" desc="The name of the Credit Cards group of the Autofill dialog. Title case. [Length: 20em] [iOS only]">
-        Credit Cards
-      </message>
       <message name="IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_MONTH" desc="The placeholder text for credit card expiration month in the payments card unmask dialog. [Length: 4em] [iOS only]">
         MM
       </message>
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
index dc6cd67..ca92c95 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
@@ -1380,17 +1380,17 @@
                            target:nil
                            action:nil];
 
-  // Set Select button.
-  titleString = l10n_util::GetNSString(IDS_IOS_BOOKMARK_CONTEXT_BAR_SELECT);
-  UIBarButtonItem* selectButton =
+  // Set Edit button.
+  titleString = l10n_util::GetNSString(IDS_IOS_BOOKMARK_CONTEXT_BAR_EDIT);
+  UIBarButtonItem* editButton =
       [[UIBarButtonItem alloc] initWithTitle:titleString
                                        style:UIBarButtonItemStylePlain
                                       target:self
                                       action:@selector(trailingButtonClicked)];
-  selectButton.accessibilityIdentifier = kBookmarkHomeTrailingButtonIdentifier;
-  selectButton.enabled = [self hasBookmarksOrFolders];
+  editButton.accessibilityIdentifier = kBookmarkHomeTrailingButtonIdentifier;
+  editButton.enabled = [self hasBookmarksOrFolders];
 
-  [self setToolbarItems:@[ newFolderButton, spaceButton, selectButton ]
+  [self setToolbarItems:@[ newFolderButton, spaceButton, editButton ]
                animated:NO];
 }
 
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
index 3e8c698c2..335c675 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
@@ -1717,7 +1717,7 @@
 }
 
 + (NSString*)contextBarSelectString {
-  return l10n_util::GetNSString(IDS_IOS_BOOKMARK_CONTEXT_BAR_SELECT);
+  return l10n_util::GetNSString(IDS_IOS_BOOKMARK_CONTEXT_BAR_EDIT);
 }
 
 + (NSString*)contextBarMoreString {
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 3939e7b8..11dd18f5 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -9,13 +9,15 @@
     "about_chrome_collection_view_controller.mm",
     "accounts_collection_view_controller.h",
     "accounts_collection_view_controller.mm",
-    "autofill_collection_view_controller.h",
-    "autofill_collection_view_controller.mm",
+    "autofill_credit_card_collection_view_controller.h",
+    "autofill_credit_card_collection_view_controller.mm",
     "autofill_credit_card_edit_collection_view_controller.h",
     "autofill_credit_card_edit_collection_view_controller.mm",
     "autofill_edit_collection_view_controller+protected.h",
     "autofill_edit_collection_view_controller.h",
     "autofill_edit_collection_view_controller.mm",
+    "autofill_profile_collection_view_controller.h",
+    "autofill_profile_collection_view_controller.mm",
     "autofill_profile_edit_collection_view_controller.h",
     "autofill_profile_edit_collection_view_controller.mm",
     "bandwidth_management_collection_view_controller.h",
@@ -104,13 +106,14 @@
     "resources:encryption_error",
     "resources:settings_about_chrome",
     "resources:settings_accounts_add_account",
+    "resources:settings_addresses",
     "resources:settings_article_suggestions",
-    "resources:settings_autofill_forms",
     "resources:settings_bandwidth",
     "resources:settings_content_settings",
     "resources:settings_debug",
     "resources:settings_error",
     "resources:settings_passwords",
+    "resources:settings_payment_methods",
     "resources:settings_privacy",
     "resources:settings_search_engine",
     "resources:settings_sync",
@@ -263,7 +266,8 @@
   testonly = true
   sources = [
     "about_chrome_collection_view_controller_unittest.mm",
-    "autofill_collection_view_controller_unittest.mm",
+    "autofill_credit_card_collection_view_controller_unittest.mm",
+    "autofill_profile_collection_view_controller_unittest.mm",
     "autofill_profile_edit_collection_view_controller_unittest.mm",
     "bandwidth_management_collection_view_controller_unittest.mm",
     "block_popups_collection_view_controller_unittest.mm",
@@ -365,7 +369,8 @@
   testonly = true
   sources = [
     "accounts_collection_egtest.mm",
-    "autofill_settings_egtest.mm",
+    "autofill_credit_card_settings_egtest.mm",
+    "autofill_profile_settings_egtest.mm",
     "block_popups_egtest.mm",
     "clear_browsing_data_egtest.mm",
     "passwords_settings_egtest.mm",
diff --git a/ios/chrome/browser/ui/settings/autofill_collection_view_controller.h b/ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller.h
similarity index 69%
copy from ios/chrome/browser/ui/settings/autofill_collection_view_controller.h
copy to ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller.h
index 1d192fd..edf12822 100644
--- a/ios/chrome/browser/ui/settings/autofill_collection_view_controller.h
+++ b/ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_COLLECTION_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_COLLECTION_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_CREDIT_CARD_COLLECTION_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_CREDIT_CARD_COLLECTION_VIEW_CONTROLLER_H_
 
 #import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
 
@@ -12,7 +12,7 @@
 }  // namespace ios
 
 // The collection view for the Autofill settings.
-@interface AutofillCollectionViewController
+@interface AutofillCreditCardCollectionViewController
     : SettingsRootCollectionViewController
 
 // The designated initializer. |browserState| must not be nil.
@@ -24,4 +24,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_COLLECTION_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_CREDIT_CARD_COLLECTION_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/autofill_collection_view_controller.mm b/ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller.mm
similarity index 61%
copy from ios/chrome/browser/ui/settings/autofill_collection_view_controller.mm
copy to ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller.mm
index bbd765f..a8a6c3a8 100644
--- a/ios/chrome/browser/ui/settings/autofill_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller.mm
@@ -1,8 +1,8 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// 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 "ios/chrome/browser/ui/settings/autofill_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller.h"
 
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
@@ -12,13 +12,13 @@
 #import "components/autofill/ios/browser/credit_card_util.h"
 #import "components/autofill/ios/browser/personal_data_manager_observer_bridge.h"
 #include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
 #import "ios/chrome/browser/ui/settings/autofill_credit_card_edit_collection_view_controller.h"
-#import "ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/cells/autofill_data_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_text_item.h"
@@ -34,47 +34,40 @@
 
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
   SectionIdentifierSwitches = kSectionIdentifierEnumZero,
-  SectionIdentifierProfiles,
   SectionIdentifierCards,
 };
 
 typedef NS_ENUM(NSInteger, ItemType) {
-  ItemTypeAutofillSwitch = kItemTypeEnumZero,
-  ItemTypeAutofillAddressSwitch,
-  ItemTypeAutofillCardSwitch,
-  ItemTypeAddress,
+  ItemTypeAutofillCardSwitch = kItemTypeEnumZero,
   ItemTypeCard,
   ItemTypeHeader,
 };
 
 }  // namespace
 
-#pragma mark - AutofillCollectionViewController
+#pragma mark - AutofillCreditCardCollectionViewController
 
-@interface AutofillCollectionViewController ()<PersonalDataManagerObserver> {
-  std::string _locale;  // User locale.
+@interface AutofillCreditCardCollectionViewController ()<
+    PersonalDataManagerObserver> {
   autofill::PersonalDataManager* _personalDataManager;
 
   ios::ChromeBrowserState* _browserState;
   std::unique_ptr<autofill::PersonalDataManagerObserverBridge> _observer;
 
-  // Deleting profiles and credit cards updates PersonalDataManager resulting in
-  // an observer callback, which handles general data updates with a reloadData.
+  // Deleting credit cards updates PersonalDataManager resulting in an observer
+  // callback, which handles general data updates with a reloadData.
   // It is better to handle user-initiated changes with more specific actions
   // such as inserting or removing items/sections. This boolean is used to
   // stop the observer callback from acting on user-initiated changes.
   BOOL _deletionInProgress;
 }
 
-@property(nonatomic, getter=isAutofillEnabled) BOOL autofillEnabled;
-@property(nonatomic, getter=isAutofillProfileEnabled)
-    BOOL autofillProfileEnabled;
 @property(nonatomic, getter=isAutofillCreditCardEnabled)
     BOOL autofillCreditCardEnabled;
 
 @end
 
-@implementation AutofillCollectionViewController
+@implementation AutofillCreditCardCollectionViewController
 
 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
   DCHECK(browserState);
@@ -83,10 +76,9 @@
       [super initWithLayout:layout style:CollectionViewControllerStyleAppBar];
   if (self) {
     self.collectionViewAccessibilityIdentifier = @"kAutofillCollectionViewId";
-    self.title = l10n_util::GetNSString(IDS_IOS_AUTOFILL);
+    self.title = l10n_util::GetNSString(IDS_AUTOFILL_PAYMENT_METHODS);
     self.shouldHideDoneButton = YES;
     _browserState = browserState;
-    _locale = GetApplicationContext()->GetApplicationLocale();
     _personalDataManager =
         autofill::PersonalDataManagerFactory::GetForBrowserState(_browserState);
     _observer.reset(new autofill::PersonalDataManagerObserverBridge(self));
@@ -111,36 +103,14 @@
   CollectionViewModel* model = self.collectionViewModel;
 
   [model addSectionWithIdentifier:SectionIdentifierSwitches];
-  [model addItem:[self autofillSwitchItem]
-      toSectionWithIdentifier:SectionIdentifierSwitches];
-  [model addItem:[self addressSwitchItem]
-      toSectionWithIdentifier:SectionIdentifierSwitches];
   [model addItem:[self cardSwitchItem]
       toSectionWithIdentifier:SectionIdentifierSwitches];
 
-  [self populateProfileSection];
   [self populateCardSection];
 }
 
 #pragma mark - LoadModel Helpers
 
-// Populates profile section using personalDataManager.
-- (void)populateProfileSection {
-  CollectionViewModel* model = self.collectionViewModel;
-  const std::vector<autofill::AutofillProfile*> autofillProfiles =
-      _personalDataManager->GetProfiles();
-  if (!autofillProfiles.empty()) {
-    [model addSectionWithIdentifier:SectionIdentifierProfiles];
-    [model setHeader:[self profileSectionHeader]
-        forSectionWithIdentifier:SectionIdentifierProfiles];
-    for (autofill::AutofillProfile* autofillProfile : autofillProfiles) {
-      DCHECK(autofillProfile);
-      [model addItem:[self itemForProfile:*autofillProfile]
-          toSectionWithIdentifier:SectionIdentifierProfiles];
-    }
-  }
-}
-
 // Populates card section using personalDataManager.
 - (void)populateCardSection {
   CollectionViewModel* model = self.collectionViewModel;
@@ -158,43 +128,19 @@
   }
 }
 
-- (CollectionViewItem*)autofillSwitchItem {
-  SettingsSwitchItem* switchItem =
-      [[SettingsSwitchItem alloc] initWithType:ItemTypeAutofillSwitch];
-  switchItem.text = l10n_util::GetNSString(IDS_IOS_AUTOFILL);
-  switchItem.on = [self isAutofillEnabled];
-  switchItem.accessibilityIdentifier = @"autofillItem_switch";
-  return switchItem;
-}
-
-- (CollectionViewItem*)addressSwitchItem {
-  SettingsSwitchItem* switchItem =
-      [[SettingsSwitchItem alloc] initWithType:ItemTypeAutofillAddressSwitch];
-  switchItem.text = l10n_util::GetNSString(IDS_IOS_ENABLE_AUTOFILL_PROFILES);
-  switchItem.on = [self isAutofillProfileEnabled];
-  switchItem.accessibilityIdentifier = @"addressItem_switch";
-  return switchItem;
-}
-
 - (CollectionViewItem*)cardSwitchItem {
   SettingsSwitchItem* switchItem =
       [[SettingsSwitchItem alloc] initWithType:ItemTypeAutofillCardSwitch];
   switchItem.text =
-      l10n_util::GetNSString(IDS_IOS_ENABLE_AUTOFILL_CREDIT_CARDS);
+      l10n_util::GetNSString(IDS_AUTOFILL_ENABLE_CREDIT_CARDS_TOGGLE_LABEL);
   switchItem.on = [self isAutofillCreditCardEnabled];
   switchItem.accessibilityIdentifier = @"cardItem_switch";
   return switchItem;
 }
 
-- (CollectionViewItem*)profileSectionHeader {
-  SettingsTextItem* header = [self genericHeader];
-  header.text = l10n_util::GetNSString(IDS_IOS_AUTOFILL_ADDRESSES_GROUP_NAME);
-  return header;
-}
-
 - (CollectionViewItem*)cardSectionHeader {
   SettingsTextItem* header = [self genericHeader];
-  header.text = l10n_util::GetNSString(IDS_IOS_AUTOFILL_CREDITCARDS_GROUP_NAME);
+  header.text = l10n_util::GetNSString(IDS_AUTOFILL_PAYMENT_METHODS);
   return header;
 }
 
@@ -205,35 +151,11 @@
   return header;
 }
 
-- (CollectionViewItem*)itemForProfile:
-    (const autofill::AutofillProfile&)autofillProfile {
-  std::string guid(autofillProfile.guid());
-  NSString* title = base::SysUTF16ToNSString(autofillProfile.GetInfo(
-      autofill::AutofillType(autofill::NAME_FULL), _locale));
-  NSString* subTitle = base::SysUTF16ToNSString(autofillProfile.GetInfo(
-      autofill::AutofillType(autofill::ADDRESS_HOME_LINE1), _locale));
-  bool isServerProfile = autofillProfile.record_type() ==
-                         autofill::AutofillProfile::SERVER_PROFILE;
-
-  AutofillDataItem* item =
-      [[AutofillDataItem alloc] initWithType:ItemTypeAddress];
-  item.text = title;
-  item.leadingDetailText = subTitle;
-  item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
-  item.accessibilityIdentifier = title;
-  item.GUID = guid;
-  item.deletable = !isServerProfile;
-  if (isServerProfile) {
-    item.trailingDetailText =
-        l10n_util::GetNSString(IDS_IOS_AUTOFILL_WALLET_SERVER_NAME);
-  }
-  return item;
-}
-
 - (CollectionViewItem*)itemForCreditCard:
     (const autofill::CreditCard&)creditCard {
   std::string guid(creditCard.guid());
-  NSString* creditCardName = autofill::GetCreditCardName(creditCard, _locale);
+  NSString* creditCardName = autofill::GetCreditCardName(
+      creditCard, GetApplicationContext()->GetApplicationLocale());
 
   AutofillDataItem* item = [[AutofillDataItem alloc] initWithType:ItemTypeCard];
   item.text = creditCardName;
@@ -249,9 +171,8 @@
   return item;
 }
 
-- (BOOL)localProfilesOrCreditCardsExist {
-  return !_personalDataManager->GetProfiles().empty() ||
-         !_personalDataManager->GetLocalCreditCards().empty();
+- (BOOL)localCreditCardsExist {
+  return !_personalDataManager->GetLocalCreditCards().empty();
 }
 
 #pragma mark - SettingsRootCollectionViewController
@@ -261,7 +182,7 @@
 }
 
 - (BOOL)editButtonEnabled {
-  return [self localProfilesOrCreditCardsExist];
+  return [self localCreditCardsExist];
 }
 
 #pragma mark - UICollectionViewDataSource
@@ -273,54 +194,19 @@
 
   ItemType itemType = static_cast<ItemType>(
       [self.collectionViewModel itemTypeForIndexPath:indexPath]);
-
-  if (![cell isKindOfClass:[SettingsSwitchCell class]])
-    return cell;
-
-  SEL action = nil;
-  switch (itemType) {
-    case ItemTypeAutofillSwitch:
-      action = @selector(autofillSwitchChanged:);
-      break;
-    case ItemTypeAutofillAddressSwitch:
-      action = @selector(autofillAddressSwitchChanged:);
-      break;
-    case ItemTypeAutofillCardSwitch:
-      action = @selector(autofillCardSwitchChanged:);
-      break;
-    default:
-      NOTREACHED() << "Unknown Switch cell item type.";
-      break;
+  if (itemType == ItemTypeAutofillCardSwitch) {
+    SettingsSwitchCell* switchCell =
+        base::mac::ObjCCastStrict<SettingsSwitchCell>(cell);
+    [switchCell.switchView addTarget:self
+                              action:@selector(autofillCardSwitchChanged:)
+                    forControlEvents:UIControlEventValueChanged];
   }
-  SettingsSwitchCell* switchCell =
-      base::mac::ObjCCastStrict<SettingsSwitchCell>(cell);
-  [switchCell.switchView addTarget:self
-                            action:action
-                  forControlEvents:UIControlEventValueChanged];
 
   return cell;
 }
 
 #pragma mark - Switch Callbacks
 
-- (void)autofillSwitchChanged:(UISwitch*)switchView {
-  BOOL switchIsOn = [switchView isOn];
-  [self setSwitchItemOn:switchIsOn itemType:ItemTypeAutofillSwitch];
-  [self setAutofillEnabled:switchIsOn];
-  [self setSwitchItemEnabled:switchIsOn itemType:ItemTypeAutofillAddressSwitch];
-  [self setSwitchItemEnabled:switchIsOn itemType:ItemTypeAutofillCardSwitch];
-  if (switchIsOn) {
-    [self autofillAddressSwitchChanged:switchView];
-    [self autofillCardSwitchChanged:switchView];
-  }
-}
-
-- (void)autofillAddressSwitchChanged:(UISwitch*)switchView {
-  [self setSwitchItemOn:[switchView isOn]
-               itemType:ItemTypeAutofillAddressSwitch];
-  [self setAutofillProfileEnabled:[switchView isOn]];
-}
-
 - (void)autofillCardSwitchChanged:(UISwitch*)switchView {
   [self setSwitchItemOn:[switchView isOn] itemType:ItemTypeAutofillCardSwitch];
   [self setAutofillCreditCardEnabled:[switchView isOn]];
@@ -374,14 +260,7 @@
 - (BOOL)collectionView:(UICollectionView*)collectionView
     hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
   NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
-  switch (type) {
-    case ItemTypeAutofillSwitch:
-    case ItemTypeAutofillAddressSwitch:
-    case ItemTypeAutofillCardSwitch:
-      return YES;
-    default:
-      return NO;
-  }
+  return type == ItemTypeAutofillCardSwitch;
 }
 
 #pragma mark - UICollectionViewDelegate
@@ -398,32 +277,18 @@
   }
 
   CollectionViewModel* model = self.collectionViewModel;
-  SettingsRootCollectionViewController* controller;
-  switch ([model itemTypeForIndexPath:indexPath]) {
-    case ItemTypeAddress: {
-      const std::vector<autofill::AutofillProfile*> autofillProfiles =
-          _personalDataManager->GetProfiles();
-      controller = [AutofillProfileEditCollectionViewController
-          controllerWithProfile:*autofillProfiles[indexPath.item]
-            personalDataManager:_personalDataManager];
-      break;
-    }
-    case ItemTypeCard: {
-      const std::vector<autofill::CreditCard*>& creditCards =
-          _personalDataManager->GetCreditCards();
-      controller = [[AutofillCreditCardEditCollectionViewController alloc]
+  NSInteger type = [model itemTypeForIndexPath:indexPath];
+  if (type != ItemTypeCard)
+    return;
+
+  const std::vector<autofill::CreditCard*>& creditCards =
+      _personalDataManager->GetCreditCards();
+  AutofillCreditCardEditCollectionViewController* controller =
+      [[AutofillCreditCardEditCollectionViewController alloc]
            initWithCreditCard:*creditCards[indexPath.item]
           personalDataManager:_personalDataManager];
-      break;
-    }
-    default:
-      break;
-  }
-
-  if (controller) {
-    controller.dispatcher = self.dispatcher;
-    [self.navigationController pushViewController:controller animated:YES];
-  }
+  controller.dispatcher = self.dispatcher;
+  [self.navigationController pushViewController:controller animated:YES];
 }
 
 #pragma mark - MDCCollectionViewEditingDelegate
@@ -435,16 +300,12 @@
 - (void)collectionViewWillBeginEditing:(UICollectionView*)collectionView {
   [super collectionViewWillBeginEditing:collectionView];
 
-  [self setSwitchItemEnabled:NO itemType:ItemTypeAutofillSwitch];
-  [self setSwitchItemEnabled:NO itemType:ItemTypeAutofillAddressSwitch];
   [self setSwitchItemEnabled:NO itemType:ItemTypeAutofillCardSwitch];
 }
 
 - (void)collectionViewWillEndEditing:(UICollectionView*)collectionView {
   [super collectionViewWillEndEditing:collectionView];
 
-  [self setSwitchItemEnabled:YES itemType:ItemTypeAutofillSwitch];
-  [self setSwitchItemEnabled:YES itemType:ItemTypeAutofillAddressSwitch];
   [self setSwitchItemEnabled:YES itemType:ItemTypeAutofillCardSwitch];
 }
 
@@ -481,7 +342,6 @@
     return;
 
   // TODO(crbug.com/650390) Generalize removing empty sections
-  [self removeSectionIfEmptyForSectionWithIdentifier:SectionIdentifierProfiles];
   [self removeSectionIfEmptyForSectionWithIdentifier:SectionIdentifierCards];
 }
 
@@ -497,10 +357,10 @@
       [self.collectionViewModel sectionForSectionIdentifier:sectionIdentifier];
   if ([self.collectionView numberOfItemsInSection:section] == 0) {
     // Avoid reference cycle in block.
-    __weak AutofillCollectionViewController* weakSelf = self;
+    __weak AutofillCreditCardCollectionViewController* weakSelf = self;
     [self.collectionView performBatchUpdates:^{
       // Obtain strong reference again.
-      AutofillCollectionViewController* strongSelf = weakSelf;
+      AutofillCreditCardCollectionViewController* strongSelf = weakSelf;
       if (!strongSelf) {
         return;
       }
@@ -513,13 +373,13 @@
     }
         completion:^(BOOL finished) {
           // Obtain strong reference again.
-          AutofillCollectionViewController* strongSelf = weakSelf;
+          AutofillCreditCardCollectionViewController* strongSelf = weakSelf;
           if (!strongSelf) {
             return;
           }
 
           // Turn off edit mode if there is nothing to edit.
-          if (![strongSelf localProfilesOrCreditCardsExist] &&
+          if (![strongSelf localCreditCardsExist] &&
               [strongSelf.editor isEditing]) {
             [[strongSelf editor] setEditing:NO];
           }
@@ -535,7 +395,7 @@
   if (_deletionInProgress)
     return;
 
-  if (![self localProfilesOrCreditCardsExist] && [self.editor isEditing]) {
+  if (![self localCreditCardsExist] && [self.editor isEditing]) {
     // Turn off edit mode if there exists nothing to edit.
     [self.editor setEditing:NO];
   }
@@ -546,24 +406,6 @@
 
 #pragma mark - Getters and Setter
 
-- (BOOL)isAutofillEnabled {
-  return autofill::prefs::IsAutofillEnabled(_browserState->GetPrefs());
-}
-
-- (void)setAutofillEnabled:(BOOL)isEnabled {
-  return autofill::prefs::SetAutofillEnabled(_browserState->GetPrefs(),
-                                             isEnabled);
-}
-
-- (BOOL)isAutofillProfileEnabled {
-  return autofill::prefs::IsProfileAutofillEnabled(_browserState->GetPrefs());
-}
-
-- (void)setAutofillProfileEnabled:(BOOL)isEnabled {
-  return autofill::prefs::SetProfileAutofillEnabled(_browserState->GetPrefs(),
-                                                    isEnabled);
-}
-
 - (BOOL)isAutofillCreditCardEnabled {
   return autofill::prefs::IsCreditCardAutofillEnabled(
       _browserState->GetPrefs());
diff --git a/ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller_unittest.mm
new file mode 100644
index 0000000..3072f85
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller_unittest.mm
@@ -0,0 +1,139 @@
+// 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 "ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller.h"
+
+#include "base/guid.h"
+#include "base/mac/foundation_util.h"
+#include "base/strings/utf_string_conversions.h"
+#import "base/test/ios/wait_util.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/ui/collection_view/collection_view_controller_test.h"
+#include "ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h"
+#include "ios/web/public/test/test_web_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface SettingsRootCollectionViewController (ExposedForTesting)
+- (void)editButtonPressed;
+@end
+
+namespace {
+
+class AutofillCreditCardCollectionViewControllerTest
+    : public CollectionViewControllerTest {
+ protected:
+  AutofillCreditCardCollectionViewControllerTest() {
+    TestChromeBrowserState::Builder test_cbs_builder;
+    chrome_browser_state_ = test_cbs_builder.Build();
+    // Credit card import requires a PersonalDataManager which itself needs the
+    // WebDataService; this is not initialized on a TestChromeBrowserState by
+    // default.
+    chrome_browser_state_->CreateWebDataService();
+  }
+
+  CollectionViewController* InstantiateController() override {
+    return [[AutofillCreditCardCollectionViewController alloc]
+        initWithBrowserState:chrome_browser_state_.get()];
+  }
+
+  void AddCreditCard(const std::string& origin,
+                     const std::string& card_holder_name,
+                     const std::string& card_number) {
+    autofill::PersonalDataManager* personal_data_manager =
+        autofill::PersonalDataManagerFactory::GetForBrowserState(
+            chrome_browser_state_.get());
+    PersonalDataManagerDataChangedObserver observer(personal_data_manager);
+
+    autofill::CreditCard credit_card(base::GenerateGUID(), origin);
+    credit_card.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL,
+                           base::ASCIIToUTF16(card_holder_name));
+    credit_card.SetRawInfo(autofill::CREDIT_CARD_NUMBER,
+                           base::ASCIIToUTF16(card_number));
+    personal_data_manager->OnAcceptedLocalCreditCardSave(credit_card);
+    observer.Wait();  // Wait for completion of the asynchronous operation.
+  }
+
+  web::TestWebThreadBundle thread_bundle_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+};
+
+// Default test case of no addresses or credit cards.
+TEST_F(AutofillCreditCardCollectionViewControllerTest, TestInitialization) {
+  CreateController();
+  CheckController();
+
+  // Expect one header section.
+  EXPECT_EQ(1, NumberOfSections());
+  // Expect header section to contain one row (the credit card Autofill toggle).
+  EXPECT_EQ(1, NumberOfItemsInSection(0));
+}
+
+// Adding a single credit card results in a credit card section.
+TEST_F(AutofillCreditCardCollectionViewControllerTest, TestOneCreditCard) {
+  AddCreditCard("https://www.example.com/", "John Doe", "378282246310005");
+  CreateController();
+  CheckController();
+
+  // Expect two sections (header and credit cards section).
+  EXPECT_EQ(2, NumberOfSections());
+  // Expect address section to contain one row (the credit card itself).
+  EXPECT_EQ(1, NumberOfItemsInSection(1));
+}
+
+// Deleting the only credit card results in item deletion and section deletion.
+TEST_F(AutofillCreditCardCollectionViewControllerTest,
+       TestOneCreditCardItemDeleted) {
+  AddCreditCard("https://www.example.com/", "John Doe", "378282246310005");
+  CreateController();
+  CheckController();
+
+  // Expect two sections (header and credit cards section).
+  EXPECT_EQ(2, NumberOfSections());
+  // Expect address section to contain one row (the credit card itself).
+  EXPECT_EQ(1, NumberOfItemsInSection(1));
+
+  AutofillCreditCardCollectionViewController* view_controller =
+      base::mac::ObjCCastStrict<AutofillCreditCardCollectionViewController>(
+          controller());
+  // Put the collectionView in 'edit' mode.
+  [view_controller editButtonPressed];
+
+  // This is a bit of a shortcut, since actually clicking on the 'delete'
+  // button would be tough.
+  void (^delete_item_with_wait)(int, int) = ^(int i, int j) {
+    __block BOOL completion_called = NO;
+    this->DeleteItem(i, j, ^{
+      completion_called = YES;
+    });
+    EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
+        base::test::ios::kWaitForUIElementTimeout, ^bool() {
+          return completion_called;
+        }));
+  };
+
+  autofill::PersonalDataManager* personal_data_manager =
+      autofill::PersonalDataManagerFactory::GetForBrowserState(
+          chrome_browser_state_.get());
+  PersonalDataManagerDataChangedObserver observer(personal_data_manager);
+
+  // This call cause a modification of the PersonalDataManager, so wait until
+  // the asynchronous task complete in addition to waiting for the UI update.
+  delete_item_with_wait(1, 0);
+  observer.Wait();  // Wait for completion of the asynchronous operation.
+
+  // Exit 'edit' mode.
+  [view_controller editButtonPressed];
+
+  // Expect one header section only.
+  EXPECT_EQ(1, NumberOfSections());
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/ui/settings/autofill_credit_card_settings_egtest.mm b/ios/chrome/browser/ui/settings/autofill_credit_card_settings_egtest.mm
new file mode 100644
index 0000000..026fc43
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/autofill_credit_card_settings_egtest.mm
@@ -0,0 +1,224 @@
+// 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 <XCTest/XCTest.h>
+
+#import "base/test/ios/wait_util.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
+#include "ios/chrome/browser/ui/tools_menu/public/tools_menu_constants.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/chrome/test/app/chrome_test_util.h"
+#import "ios/chrome/test/app/web_view_interaction_test_util.h"
+#include "ios/chrome/test/earl_grey/accessibility_util.h"
+#import "ios/chrome/test/earl_grey/chrome_actions.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
+#import "ios/chrome/test/earl_grey/chrome_matchers.h"
+#import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using chrome_test_util::ButtonWithAccessibilityLabel;
+using chrome_test_util::ButtonWithAccessibilityLabelId;
+using chrome_test_util::NavigationBarDoneButton;
+using chrome_test_util::SettingsDoneButton;
+using chrome_test_util::SettingsMenuBackButton;
+
+namespace {
+
+// Expectation of how the saved Autofill credit card looks like, a map from cell
+// name IDs to expected contents.
+struct DisplayStringIDToExpectedResult {
+  int display_string_id;
+  NSString* expected_result;
+};
+
+const DisplayStringIDToExpectedResult kExpectedFields[] = {
+    {IDS_IOS_AUTOFILL_CARDHOLDER, @"Test User"},
+    {IDS_IOS_AUTOFILL_CARD_NUMBER, @"4111111111111111"},
+    {IDS_IOS_AUTOFILL_EXP_MONTH, @"11"},
+    {IDS_IOS_AUTOFILL_EXP_YEAR, @"2022"}};
+
+NSString* const kCreditCardLabel =
+    @"Test User, Visa  ‪• • • • 1111‬";
+
+}  // namespace
+
+// Various tests for the Autofill credit cards section of the settings.
+@interface AutofillCreditCardSettingsTestCase : ChromeTestCase
+@end
+
+@implementation AutofillCreditCardSettingsTestCase {
+  // The PersonalDataManager instance for the current browser state.
+  autofill::PersonalDataManager* _personalDataManager;
+}
+
+- (void)setUp {
+  [super setUp];
+
+  _personalDataManager =
+      autofill::PersonalDataManagerFactory::GetForBrowserState(
+          chrome_test_util::GetOriginalBrowserState());
+  _personalDataManager->SetSyncingForTest(true);
+}
+
+- (void)tearDown {
+  // Clear existing credit cards.
+  for (const auto* creditCard : _personalDataManager->GetCreditCards()) {
+    _personalDataManager->RemoveByGUID(creditCard->guid());
+  }
+
+  [super tearDown];
+}
+
+- (autofill::CreditCard)addCreditCard {
+  autofill::CreditCard creditCard = autofill::test::GetCreditCard();
+  size_t creditCardCount = _personalDataManager->GetCreditCards().size();
+  _personalDataManager->AddCreditCard(creditCard);
+  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
+                 base::test::ios::kWaitForActionTimeout,
+                 ^bool() {
+                   return creditCardCount <
+                          _personalDataManager->GetCreditCards().size();
+                 }),
+             @"Failed to add credit card.");
+  return creditCard;
+}
+
+// Helper to open the settings page for Autofill credit cards.
+- (void)openCreditCardsSettings {
+  [ChromeEarlGreyUI openSettingsMenu];
+  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(
+                                          l10n_util::GetNSString(
+                                              IDS_AUTOFILL_PAYMENT_METHODS))]
+      performAction:grey_tap()];
+}
+
+// Helper to open the settings page for the Autofill credit card with |label|.
+- (void)openEditCreditCard:(NSString*)label {
+  [self openCreditCardsSettings];
+
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(label)]
+      performAction:grey_tap()];
+}
+
+// Close the settings.
+- (void)exitSettingsMenu {
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
+      performAction:grey_tap()];
+  // Wait for UI components to finish loading.
+  [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
+}
+
+// Test that the page for viewing Autofill credit card details is as expected.
+- (void)testCreditCardViewPage {
+  autofill::CreditCard creditCard = [self addCreditCard];
+  [self openEditCreditCard:kCreditCardLabel];
+
+  // Check that all fields and values match the expectations.
+  for (const DisplayStringIDToExpectedResult& expectation : kExpectedFields) {
+    [[EarlGrey selectElementWithMatcher:
+                   grey_accessibilityLabel([NSString
+                       stringWithFormat:@"%@, %@",
+                                        l10n_util::GetNSString(
+                                            expectation.display_string_id),
+                                        expectation.expected_result])]
+        assertWithMatcher:grey_notNil()];
+  }
+
+  // Go back to the list view page.
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+
+  [self exitSettingsMenu];
+}
+
+// Test that the page for viewing Autofill credit card details is accessible.
+- (void)testAccessibilityOnCreditCardViewPage {
+  autofill::CreditCard creditCard = [self addCreditCard];
+  [self openEditCreditCard:kCreditCardLabel];
+
+  chrome_test_util::VerifyAccessibilityForCurrentScreen();
+
+  // Go back to the list view page.
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+
+  [self exitSettingsMenu];
+}
+
+// Test that the page for editing Autofill credit card details is accessible.
+- (void)testAccessibilityOnCreditCardEditPage {
+  autofill::CreditCard creditCard = [self addCreditCard];
+  [self openEditCreditCard:kCreditCardLabel];
+
+  // Switch on edit mode.
+  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                          IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON)]
+      performAction:grey_tap()];
+  chrome_test_util::VerifyAccessibilityForCurrentScreen();
+
+  // Go back to the list view page.
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+
+  [self exitSettingsMenu];
+}
+
+// Checks that the Autofill credit cards list view is in edit mode and the
+// Autofill credit cards switch is disabled.
+- (void)testListViewEditMode {
+  autofill::CreditCard creditCard = [self addCreditCard];
+  [self openCreditCardsSettings];
+
+  // Switch on edit mode.
+  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                          IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON)]
+      performAction:grey_tap()];
+
+  // Check the Autofill credit card switch is disabled.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
+                                          @"cardItem_switch", YES, NO)]
+      assertWithMatcher:grey_notNil()];
+
+  [self exitSettingsMenu];
+}
+
+// Checks that the Autofill credit card switch can be toggled on/off and the
+// list of Autofill credit cards is not affected by it.
+- (void)testToggleCreditCardSwitch {
+  autofill::CreditCard creditCard = [self addCreditCard];
+  [self openCreditCardsSettings];
+
+  // Toggle the Autofill credit cards switch off.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
+                                          @"cardItem_switch", YES, YES)]
+      performAction:chrome_test_util::TurnSettingsSwitchOn(NO)];
+
+  // Expect Autofill credit cards to remain visible.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(kCreditCardLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Toggle the Autofill credit cards switch back on.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
+                                          @"cardItem_switch", NO, YES)]
+      performAction:chrome_test_util::TurnSettingsSwitchOn(YES)];
+
+  // Expect Autofill credit cards to remain visible.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(kCreditCardLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  [self exitSettingsMenu];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/settings/autofill_collection_view_controller.h b/ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller.h
similarity index 70%
rename from ios/chrome/browser/ui/settings/autofill_collection_view_controller.h
rename to ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller.h
index 1d192fd..16794cb 100644
--- a/ios/chrome/browser/ui/settings/autofill_collection_view_controller.h
+++ b/ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_COLLECTION_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_COLLECTION_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_PROFILE_COLLECTION_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_PROFILE_COLLECTION_VIEW_CONTROLLER_H_
 
 #import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
 
@@ -12,7 +12,7 @@
 }  // namespace ios
 
 // The collection view for the Autofill settings.
-@interface AutofillCollectionViewController
+@interface AutofillProfileCollectionViewController
     : SettingsRootCollectionViewController
 
 // The designated initializer. |browserState| must not be nil.
@@ -24,4 +24,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_COLLECTION_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_AUTOFILL_PROFILE_COLLECTION_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/autofill_collection_view_controller.mm b/ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller.mm
similarity index 62%
rename from ios/chrome/browser/ui/settings/autofill_collection_view_controller.mm
rename to ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller.mm
index bbd765f..2ad7f63e 100644
--- a/ios/chrome/browser/ui/settings/autofill_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller.mm
@@ -2,22 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/autofill_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller.h"
 
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/common/autofill_prefs.h"
-#import "components/autofill/ios/browser/credit_card_util.h"
 #import "components/autofill/ios/browser/personal_data_manager_observer_bridge.h"
 #include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
-#import "ios/chrome/browser/ui/settings/autofill_credit_card_edit_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/cells/autofill_data_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h"
@@ -35,46 +34,39 @@
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
   SectionIdentifierSwitches = kSectionIdentifierEnumZero,
   SectionIdentifierProfiles,
-  SectionIdentifierCards,
 };
 
 typedef NS_ENUM(NSInteger, ItemType) {
-  ItemTypeAutofillSwitch = kItemTypeEnumZero,
-  ItemTypeAutofillAddressSwitch,
-  ItemTypeAutofillCardSwitch,
+  ItemTypeAutofillAddressSwitch = kItemTypeEnumZero,
   ItemTypeAddress,
-  ItemTypeCard,
   ItemTypeHeader,
 };
 
 }  // namespace
 
-#pragma mark - AutofillCollectionViewController
+#pragma mark - AutofillProfileCollectionViewController
 
-@interface AutofillCollectionViewController ()<PersonalDataManagerObserver> {
-  std::string _locale;  // User locale.
+@interface AutofillProfileCollectionViewController ()<
+    PersonalDataManagerObserver> {
   autofill::PersonalDataManager* _personalDataManager;
 
   ios::ChromeBrowserState* _browserState;
   std::unique_ptr<autofill::PersonalDataManagerObserverBridge> _observer;
 
-  // Deleting profiles and credit cards updates PersonalDataManager resulting in
-  // an observer callback, which handles general data updates with a reloadData.
+  // Deleting profiles updates PersonalDataManager resulting in an observer
+  // callback, which handles general data updates with a reloadData.
   // It is better to handle user-initiated changes with more specific actions
   // such as inserting or removing items/sections. This boolean is used to
   // stop the observer callback from acting on user-initiated changes.
   BOOL _deletionInProgress;
 }
 
-@property(nonatomic, getter=isAutofillEnabled) BOOL autofillEnabled;
 @property(nonatomic, getter=isAutofillProfileEnabled)
     BOOL autofillProfileEnabled;
-@property(nonatomic, getter=isAutofillCreditCardEnabled)
-    BOOL autofillCreditCardEnabled;
 
 @end
 
-@implementation AutofillCollectionViewController
+@implementation AutofillProfileCollectionViewController
 
 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
   DCHECK(browserState);
@@ -83,10 +75,9 @@
       [super initWithLayout:layout style:CollectionViewControllerStyleAppBar];
   if (self) {
     self.collectionViewAccessibilityIdentifier = @"kAutofillCollectionViewId";
-    self.title = l10n_util::GetNSString(IDS_IOS_AUTOFILL);
+    self.title = l10n_util::GetNSString(IDS_AUTOFILL_ADDRESSES);
     self.shouldHideDoneButton = YES;
     _browserState = browserState;
-    _locale = GetApplicationContext()->GetApplicationLocale();
     _personalDataManager =
         autofill::PersonalDataManagerFactory::GetForBrowserState(_browserState);
     _observer.reset(new autofill::PersonalDataManagerObserverBridge(self));
@@ -111,15 +102,10 @@
   CollectionViewModel* model = self.collectionViewModel;
 
   [model addSectionWithIdentifier:SectionIdentifierSwitches];
-  [model addItem:[self autofillSwitchItem]
-      toSectionWithIdentifier:SectionIdentifierSwitches];
   [model addItem:[self addressSwitchItem]
       toSectionWithIdentifier:SectionIdentifierSwitches];
-  [model addItem:[self cardSwitchItem]
-      toSectionWithIdentifier:SectionIdentifierSwitches];
 
   [self populateProfileSection];
-  [self populateCardSection];
 }
 
 #pragma mark - LoadModel Helpers
@@ -141,60 +127,19 @@
   }
 }
 
-// Populates card section using personalDataManager.
-- (void)populateCardSection {
-  CollectionViewModel* model = self.collectionViewModel;
-  const std::vector<autofill::CreditCard*>& creditCards =
-      _personalDataManager->GetCreditCards();
-  if (!creditCards.empty()) {
-    [model addSectionWithIdentifier:SectionIdentifierCards];
-    [model setHeader:[self cardSectionHeader]
-        forSectionWithIdentifier:SectionIdentifierCards];
-    for (autofill::CreditCard* creditCard : creditCards) {
-      DCHECK(creditCard);
-      [model addItem:[self itemForCreditCard:*creditCard]
-          toSectionWithIdentifier:SectionIdentifierCards];
-    }
-  }
-}
-
-- (CollectionViewItem*)autofillSwitchItem {
-  SettingsSwitchItem* switchItem =
-      [[SettingsSwitchItem alloc] initWithType:ItemTypeAutofillSwitch];
-  switchItem.text = l10n_util::GetNSString(IDS_IOS_AUTOFILL);
-  switchItem.on = [self isAutofillEnabled];
-  switchItem.accessibilityIdentifier = @"autofillItem_switch";
-  return switchItem;
-}
-
 - (CollectionViewItem*)addressSwitchItem {
   SettingsSwitchItem* switchItem =
       [[SettingsSwitchItem alloc] initWithType:ItemTypeAutofillAddressSwitch];
-  switchItem.text = l10n_util::GetNSString(IDS_IOS_ENABLE_AUTOFILL_PROFILES);
+  switchItem.text =
+      l10n_util::GetNSString(IDS_AUTOFILL_ENABLE_PROFILES_TOGGLE_LABEL);
   switchItem.on = [self isAutofillProfileEnabled];
   switchItem.accessibilityIdentifier = @"addressItem_switch";
   return switchItem;
 }
 
-- (CollectionViewItem*)cardSwitchItem {
-  SettingsSwitchItem* switchItem =
-      [[SettingsSwitchItem alloc] initWithType:ItemTypeAutofillCardSwitch];
-  switchItem.text =
-      l10n_util::GetNSString(IDS_IOS_ENABLE_AUTOFILL_CREDIT_CARDS);
-  switchItem.on = [self isAutofillCreditCardEnabled];
-  switchItem.accessibilityIdentifier = @"cardItem_switch";
-  return switchItem;
-}
-
 - (CollectionViewItem*)profileSectionHeader {
   SettingsTextItem* header = [self genericHeader];
-  header.text = l10n_util::GetNSString(IDS_IOS_AUTOFILL_ADDRESSES_GROUP_NAME);
-  return header;
-}
-
-- (CollectionViewItem*)cardSectionHeader {
-  SettingsTextItem* header = [self genericHeader];
-  header.text = l10n_util::GetNSString(IDS_IOS_AUTOFILL_CREDITCARDS_GROUP_NAME);
+  header.text = l10n_util::GetNSString(IDS_AUTOFILL_ADDRESSES);
   return header;
 }
 
@@ -208,10 +153,12 @@
 - (CollectionViewItem*)itemForProfile:
     (const autofill::AutofillProfile&)autofillProfile {
   std::string guid(autofillProfile.guid());
-  NSString* title = base::SysUTF16ToNSString(autofillProfile.GetInfo(
-      autofill::AutofillType(autofill::NAME_FULL), _locale));
+  NSString* title = base::SysUTF16ToNSString(
+      autofillProfile.GetInfo(autofill::AutofillType(autofill::NAME_FULL),
+                              GetApplicationContext()->GetApplicationLocale()));
   NSString* subTitle = base::SysUTF16ToNSString(autofillProfile.GetInfo(
-      autofill::AutofillType(autofill::ADDRESS_HOME_LINE1), _locale));
+      autofill::AutofillType(autofill::ADDRESS_HOME_LINE1),
+      GetApplicationContext()->GetApplicationLocale()));
   bool isServerProfile = autofillProfile.record_type() ==
                          autofill::AutofillProfile::SERVER_PROFILE;
 
@@ -230,28 +177,8 @@
   return item;
 }
 
-- (CollectionViewItem*)itemForCreditCard:
-    (const autofill::CreditCard&)creditCard {
-  std::string guid(creditCard.guid());
-  NSString* creditCardName = autofill::GetCreditCardName(creditCard, _locale);
-
-  AutofillDataItem* item = [[AutofillDataItem alloc] initWithType:ItemTypeCard];
-  item.text = creditCardName;
-  item.leadingDetailText = autofill::GetCreditCardObfuscatedNumber(creditCard);
-  item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
-  item.accessibilityIdentifier = creditCardName;
-  item.deletable = autofill::IsCreditCardLocal(creditCard);
-  item.GUID = guid;
-  if (![item isDeletable]) {
-    item.trailingDetailText =
-        l10n_util::GetNSString(IDS_IOS_AUTOFILL_WALLET_SERVER_NAME);
-  }
-  return item;
-}
-
-- (BOOL)localProfilesOrCreditCardsExist {
-  return !_personalDataManager->GetProfiles().empty() ||
-         !_personalDataManager->GetLocalCreditCards().empty();
+- (BOOL)localProfilesExist {
+  return !_personalDataManager->GetProfiles().empty();
 }
 
 #pragma mark - SettingsRootCollectionViewController
@@ -261,7 +188,7 @@
 }
 
 - (BOOL)editButtonEnabled {
-  return [self localProfilesOrCreditCardsExist];
+  return [self localProfilesExist];
 }
 
 #pragma mark - UICollectionViewDataSource
@@ -273,59 +200,25 @@
 
   ItemType itemType = static_cast<ItemType>(
       [self.collectionViewModel itemTypeForIndexPath:indexPath]);
-
-  if (![cell isKindOfClass:[SettingsSwitchCell class]])
-    return cell;
-
-  SEL action = nil;
-  switch (itemType) {
-    case ItemTypeAutofillSwitch:
-      action = @selector(autofillSwitchChanged:);
-      break;
-    case ItemTypeAutofillAddressSwitch:
-      action = @selector(autofillAddressSwitchChanged:);
-      break;
-    case ItemTypeAutofillCardSwitch:
-      action = @selector(autofillCardSwitchChanged:);
-      break;
-    default:
-      NOTREACHED() << "Unknown Switch cell item type.";
-      break;
+  if (itemType == ItemTypeAutofillAddressSwitch) {
+    SettingsSwitchCell* switchCell =
+        base::mac::ObjCCastStrict<SettingsSwitchCell>(cell);
+    [switchCell.switchView addTarget:self
+                              action:@selector(autofillAddressSwitchChanged:)
+                    forControlEvents:UIControlEventValueChanged];
   }
-  SettingsSwitchCell* switchCell =
-      base::mac::ObjCCastStrict<SettingsSwitchCell>(cell);
-  [switchCell.switchView addTarget:self
-                            action:action
-                  forControlEvents:UIControlEventValueChanged];
 
   return cell;
 }
 
 #pragma mark - Switch Callbacks
 
-- (void)autofillSwitchChanged:(UISwitch*)switchView {
-  BOOL switchIsOn = [switchView isOn];
-  [self setSwitchItemOn:switchIsOn itemType:ItemTypeAutofillSwitch];
-  [self setAutofillEnabled:switchIsOn];
-  [self setSwitchItemEnabled:switchIsOn itemType:ItemTypeAutofillAddressSwitch];
-  [self setSwitchItemEnabled:switchIsOn itemType:ItemTypeAutofillCardSwitch];
-  if (switchIsOn) {
-    [self autofillAddressSwitchChanged:switchView];
-    [self autofillCardSwitchChanged:switchView];
-  }
-}
-
 - (void)autofillAddressSwitchChanged:(UISwitch*)switchView {
   [self setSwitchItemOn:[switchView isOn]
                itemType:ItemTypeAutofillAddressSwitch];
   [self setAutofillProfileEnabled:[switchView isOn]];
 }
 
-- (void)autofillCardSwitchChanged:(UISwitch*)switchView {
-  [self setSwitchItemOn:[switchView isOn] itemType:ItemTypeAutofillCardSwitch];
-  [self setAutofillCreditCardEnabled:[switchView isOn]];
-}
-
 #pragma mark - Switch Helpers
 
 // Sets switchItem's state to |on|. It is important that there is only one item
@@ -374,14 +267,7 @@
 - (BOOL)collectionView:(UICollectionView*)collectionView
     hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
   NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
-  switch (type) {
-    case ItemTypeAutofillSwitch:
-    case ItemTypeAutofillAddressSwitch:
-    case ItemTypeAutofillCardSwitch:
-      return YES;
-    default:
-      return NO;
-  }
+  return type == ItemTypeAutofillAddressSwitch;
 }
 
 #pragma mark - UICollectionViewDelegate
@@ -398,32 +284,17 @@
   }
 
   CollectionViewModel* model = self.collectionViewModel;
-  SettingsRootCollectionViewController* controller;
-  switch ([model itemTypeForIndexPath:indexPath]) {
-    case ItemTypeAddress: {
-      const std::vector<autofill::AutofillProfile*> autofillProfiles =
-          _personalDataManager->GetProfiles();
-      controller = [AutofillProfileEditCollectionViewController
+  if ([model itemTypeForIndexPath:indexPath] != ItemTypeAddress)
+    return;
+
+  const std::vector<autofill::AutofillProfile*> autofillProfiles =
+      _personalDataManager->GetProfiles();
+  AutofillProfileEditCollectionViewController* controller =
+      [AutofillProfileEditCollectionViewController
           controllerWithProfile:*autofillProfiles[indexPath.item]
             personalDataManager:_personalDataManager];
-      break;
-    }
-    case ItemTypeCard: {
-      const std::vector<autofill::CreditCard*>& creditCards =
-          _personalDataManager->GetCreditCards();
-      controller = [[AutofillCreditCardEditCollectionViewController alloc]
-           initWithCreditCard:*creditCards[indexPath.item]
-          personalDataManager:_personalDataManager];
-      break;
-    }
-    default:
-      break;
-  }
-
-  if (controller) {
-    controller.dispatcher = self.dispatcher;
-    [self.navigationController pushViewController:controller animated:YES];
-  }
+  controller.dispatcher = self.dispatcher;
+  [self.navigationController pushViewController:controller animated:YES];
 }
 
 #pragma mark - MDCCollectionViewEditingDelegate
@@ -435,17 +306,13 @@
 - (void)collectionViewWillBeginEditing:(UICollectionView*)collectionView {
   [super collectionViewWillBeginEditing:collectionView];
 
-  [self setSwitchItemEnabled:NO itemType:ItemTypeAutofillSwitch];
   [self setSwitchItemEnabled:NO itemType:ItemTypeAutofillAddressSwitch];
-  [self setSwitchItemEnabled:NO itemType:ItemTypeAutofillCardSwitch];
 }
 
 - (void)collectionViewWillEndEditing:(UICollectionView*)collectionView {
   [super collectionViewWillEndEditing:collectionView];
 
-  [self setSwitchItemEnabled:YES itemType:ItemTypeAutofillSwitch];
   [self setSwitchItemEnabled:YES itemType:ItemTypeAutofillAddressSwitch];
-  [self setSwitchItemEnabled:YES itemType:ItemTypeAutofillCardSwitch];
 }
 
 - (BOOL)collectionView:(UICollectionView*)collectionView
@@ -482,7 +349,6 @@
 
   // TODO(crbug.com/650390) Generalize removing empty sections
   [self removeSectionIfEmptyForSectionWithIdentifier:SectionIdentifierProfiles];
-  [self removeSectionIfEmptyForSectionWithIdentifier:SectionIdentifierCards];
 }
 
 // Remove the section from the model and collectionView if there are no more
@@ -497,10 +363,10 @@
       [self.collectionViewModel sectionForSectionIdentifier:sectionIdentifier];
   if ([self.collectionView numberOfItemsInSection:section] == 0) {
     // Avoid reference cycle in block.
-    __weak AutofillCollectionViewController* weakSelf = self;
+    __weak AutofillProfileCollectionViewController* weakSelf = self;
     [self.collectionView performBatchUpdates:^{
       // Obtain strong reference again.
-      AutofillCollectionViewController* strongSelf = weakSelf;
+      AutofillProfileCollectionViewController* strongSelf = weakSelf;
       if (!strongSelf) {
         return;
       }
@@ -513,13 +379,13 @@
     }
         completion:^(BOOL finished) {
           // Obtain strong reference again.
-          AutofillCollectionViewController* strongSelf = weakSelf;
+          AutofillProfileCollectionViewController* strongSelf = weakSelf;
           if (!strongSelf) {
             return;
           }
 
           // Turn off edit mode if there is nothing to edit.
-          if (![strongSelf localProfilesOrCreditCardsExist] &&
+          if (![strongSelf localProfilesExist] &&
               [strongSelf.editor isEditing]) {
             [[strongSelf editor] setEditing:NO];
           }
@@ -535,7 +401,7 @@
   if (_deletionInProgress)
     return;
 
-  if (![self localProfilesOrCreditCardsExist] && [self.editor isEditing]) {
+  if (![self localProfilesExist] && [self.editor isEditing]) {
     // Turn off edit mode if there exists nothing to edit.
     [self.editor setEditing:NO];
   }
@@ -546,15 +412,6 @@
 
 #pragma mark - Getters and Setter
 
-- (BOOL)isAutofillEnabled {
-  return autofill::prefs::IsAutofillEnabled(_browserState->GetPrefs());
-}
-
-- (void)setAutofillEnabled:(BOOL)isEnabled {
-  return autofill::prefs::SetAutofillEnabled(_browserState->GetPrefs(),
-                                             isEnabled);
-}
-
 - (BOOL)isAutofillProfileEnabled {
   return autofill::prefs::IsProfileAutofillEnabled(_browserState->GetPrefs());
 }
@@ -564,14 +421,4 @@
                                                     isEnabled);
 }
 
-- (BOOL)isAutofillCreditCardEnabled {
-  return autofill::prefs::IsCreditCardAutofillEnabled(
-      _browserState->GetPrefs());
-}
-
-- (void)setAutofillCreditCardEnabled:(BOOL)isEnabled {
-  return autofill::prefs::SetCreditCardAutofillEnabled(
-      _browserState->GetPrefs(), isEnabled);
-}
-
 @end
diff --git a/ios/chrome/browser/ui/settings/autofill_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller_unittest.mm
similarity index 64%
rename from ios/chrome/browser/ui/settings/autofill_collection_view_controller_unittest.mm
rename to ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller_unittest.mm
index fefccb4..59166596 100644
--- a/ios/chrome/browser/ui/settings/autofill_collection_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller_unittest.mm
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/autofill_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller.h"
 
 #include "base/guid.h"
 #include "base/mac/foundation_util.h"
 #include "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #include "components/autofill/core/browser/autofill_profile.h"
-#include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
@@ -28,10 +27,10 @@
 
 namespace {
 
-class AutofillCollectionViewControllerTest
+class AutofillProfileCollectionViewControllerTest
     : public CollectionViewControllerTest {
  protected:
-  AutofillCollectionViewControllerTest() {
+  AutofillProfileCollectionViewControllerTest() {
     TestChromeBrowserState::Builder test_cbs_builder;
     chrome_browser_state_ = test_cbs_builder.Build();
     // Profile import requires a PersonalDataManager which itself needs the
@@ -41,7 +40,7 @@
   }
 
   CollectionViewController* InstantiateController() override {
-    return [[AutofillCollectionViewController alloc]
+    return [[AutofillProfileCollectionViewController alloc]
         initWithBrowserState:chrome_browser_state_.get()];
   }
 
@@ -65,67 +64,43 @@
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
 };
 
-// Default test case of no addresses or credit cards.
-TEST_F(AutofillCollectionViewControllerTest, TestInitialization) {
+// Default test case of no addresses.
+TEST_F(AutofillProfileCollectionViewControllerTest, TestInitialization) {
   CreateController();
   CheckController();
 
   // Expect one header section.
   EXPECT_EQ(1, NumberOfSections());
-  // Expect header section to contain three rows.
-  EXPECT_EQ(3, NumberOfItemsInSection(0));
+  // Expect header section to contain one row (the address Autofill toggle).
+  EXPECT_EQ(1, NumberOfItemsInSection(0));
 }
 
 // Adding a single address results in an address section.
-TEST_F(AutofillCollectionViewControllerTest, TestOneProfile) {
+TEST_F(AutofillProfileCollectionViewControllerTest, TestOneProfile) {
   AddProfile("https://www.example.com/", "John Doe", "1 Main Street");
   CreateController();
+  CheckController();
+
   // Expect two sections (header and addresses section).
   EXPECT_EQ(2, NumberOfSections());
-  // Expect header section to contain three rows.
-  EXPECT_EQ(3, NumberOfItemsInSection(0));
-  // Expect address section to contain 1 row (the address itself).
-  EXPECT_EQ(1, NumberOfItemsInSection(1));
-}
-
-// Adding a single credit card results in a credit card section.
-TEST_F(AutofillCollectionViewControllerTest, TestOneCreditCard) {
-  autofill::PersonalDataManager* personal_data_manager =
-      autofill::PersonalDataManagerFactory::GetForBrowserState(
-          chrome_browser_state_.get());
-  PersonalDataManagerDataChangedObserver observer(personal_data_manager);
-
-  autofill::CreditCard credit_card(base::GenerateGUID(),
-                                   "https://www.example.com/");
-  credit_card.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL,
-                         base::ASCIIToUTF16("Alan Smithee"));
-  credit_card.SetRawInfo(autofill::CREDIT_CARD_NUMBER,
-                         base::ASCIIToUTF16("378282246310005"));
-  personal_data_manager->OnAcceptedLocalCreditCardSave(credit_card);
-  observer.Wait();  // Wait for completion of the asynchronous operation.
-
-  CreateController();
-  // Expect two sections (header and credit card section).
-  EXPECT_EQ(2, NumberOfSections());
-  // Expect header section to contain three rows.
-  EXPECT_EQ(3, NumberOfItemsInSection(0));
-  // Expect credit card section to contain 1 row (the credit card itself).
+  // Expect address section to contain one row (the address itself).
   EXPECT_EQ(1, NumberOfItemsInSection(1));
 }
 
 // Deleting the only profile results in item deletion and section deletion.
-TEST_F(AutofillCollectionViewControllerTest, TestOneProfileItemDeleted) {
+TEST_F(AutofillProfileCollectionViewControllerTest, TestOneProfileItemDeleted) {
   AddProfile("https://www.example.com/", "John Doe", "1 Main Street");
   CreateController();
+  CheckController();
+
   // Expect two sections (header and addresses section).
   EXPECT_EQ(2, NumberOfSections());
-  // Expect header section to contain three rows.
-  EXPECT_EQ(3, NumberOfItemsInSection(0));
-  // Expect address section to contain 1 row (the address itself).
+  // Expect address section to contain one row (the address itself).
   EXPECT_EQ(1, NumberOfItemsInSection(1));
 
-  AutofillCollectionViewController* view_controller =
-      base::mac::ObjCCastStrict<AutofillCollectionViewController>(controller());
+  AutofillProfileCollectionViewController* view_controller =
+      base::mac::ObjCCastStrict<AutofillProfileCollectionViewController>(
+          controller());
   // Put the collectionView in 'edit' mode.
   [view_controller editButtonPressed];
 
@@ -155,11 +130,8 @@
   // Exit 'edit' mode.
   [view_controller editButtonPressed];
 
-  // Verify the resulting UI.
-  // Expect one header section.
+  // Expect one header section only.
   EXPECT_EQ(1, NumberOfSections());
-  // Expect header section to contain three rows.
-  EXPECT_EQ(3, NumberOfItemsInSection(0));
 }
 
 }  // namespace
diff --git a/ios/chrome/browser/ui/settings/autofill_profile_settings_egtest.mm b/ios/chrome/browser/ui/settings/autofill_profile_settings_egtest.mm
new file mode 100644
index 0000000..71121f91
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/autofill_profile_settings_egtest.mm
@@ -0,0 +1,298 @@
+// 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 <XCTest/XCTest.h>
+
+#import "base/test/ios/wait_util.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
+#include "ios/chrome/browser/ui/tools_menu/public/tools_menu_constants.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/chrome/test/app/chrome_test_util.h"
+#import "ios/chrome/test/app/web_view_interaction_test_util.h"
+#include "ios/chrome/test/earl_grey/accessibility_util.h"
+#import "ios/chrome/test/earl_grey/chrome_actions.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
+#import "ios/chrome/test/earl_grey/chrome_matchers.h"
+#import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using chrome_test_util::ButtonWithAccessibilityLabel;
+using chrome_test_util::ButtonWithAccessibilityLabelId;
+using chrome_test_util::NavigationBarDoneButton;
+using chrome_test_util::SettingsDoneButton;
+using chrome_test_util::SettingsMenuBackButton;
+
+namespace {
+
+// Expectation of how the saved Autofill profile looks like, a map from cell
+// name IDs to expected contents.
+struct DisplayStringIDToExpectedResult {
+  int display_string_id;
+  NSString* expected_result;
+};
+
+const DisplayStringIDToExpectedResult kExpectedFields[] = {
+    {IDS_IOS_AUTOFILL_FULLNAME, @"John H. Doe"},
+    {IDS_IOS_AUTOFILL_COMPANY_NAME, @"Underworld"},
+    {IDS_IOS_AUTOFILL_ADDRESS1, @"666 Erebus St."},
+    {IDS_IOS_AUTOFILL_ADDRESS2, @"Apt 8"},
+    {IDS_IOS_AUTOFILL_CITY, @"Elysium"},
+    {IDS_IOS_AUTOFILL_STATE, @"CA"},
+    {IDS_IOS_AUTOFILL_ZIP, @"91111"},
+    {IDS_IOS_AUTOFILL_PHONE, @"16502111111"},
+    {IDS_IOS_AUTOFILL_EMAIL, @"johndoe@hades.com"}};
+
+NSString* const kProfileLabel = @"John H. Doe, 666 Erebus St.";
+
+// Expectation of how user-typed country names should be canonicalized.
+struct UserTypedCountryExpectedResultPair {
+  NSString* user_typed_country;
+  NSString* expected_result;
+};
+
+const UserTypedCountryExpectedResultPair kCountryTests[] = {
+    {@"Brasil", @"Brazil"},
+    {@"China", @"China"},
+    {@"DEUTSCHLAND", @"Germany"},
+    {@"GREAT BRITAIN", @"United Kingdom"},
+    {@"IN", @"India"},
+    {@"JaPaN", @"Japan"},
+    {@"JP", @"Japan"},
+    {@"Nigeria", @"Nigeria"},
+    {@"TW", @"Taiwan"},
+    {@"U.S.A.", @"United States"},
+    {@"UK", @"United Kingdom"},
+    {@"USA", @"United States"},
+    {@"Nonexistia", @""},
+};
+
+// Given a resource ID of a category of an Autofill profile, it returns a
+// NSString consisting of the resource string concatenated with "_textField".
+// This is the a11y ID of the text field corresponding to the category in the
+// edit dialog of the Autofill profile.
+NSString* GetTextFieldForID(int categoryId) {
+  return [NSString
+      stringWithFormat:@"%@_textField", l10n_util::GetNSString(categoryId)];
+}
+
+}  // namespace
+
+// Various tests for the Autofill profiles section of the settings.
+@interface AutofillProfileSettingsTestCase : ChromeTestCase
+@end
+
+@implementation AutofillProfileSettingsTestCase {
+  // The PersonalDataManager instance for the current browser state.
+  autofill::PersonalDataManager* _personalDataManager;
+}
+
+- (void)setUp {
+  [super setUp];
+
+  _personalDataManager =
+      autofill::PersonalDataManagerFactory::GetForBrowserState(
+          chrome_test_util::GetOriginalBrowserState());
+  _personalDataManager->SetSyncingForTest(true);
+}
+
+- (void)tearDown {
+  // Clear existing profiles.
+  for (const auto* profile : _personalDataManager->GetProfiles()) {
+    _personalDataManager->RemoveByGUID(profile->guid());
+  }
+
+  [super tearDown];
+}
+
+- (autofill::AutofillProfile)addAutofillProfile {
+  autofill::AutofillProfile profile = autofill::test::GetFullProfile();
+  size_t profileCount = _personalDataManager->GetProfiles().size();
+  _personalDataManager->AddProfile(profile);
+  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
+                 base::test::ios::kWaitForActionTimeout,
+                 ^bool() {
+                   return profileCount <
+                          _personalDataManager->GetProfiles().size();
+                 }),
+             @"Failed to add profile.");
+  return profile;
+}
+
+// Helper to open the settings page for Autofill profiles.
+- (void)openAutofillProfilesSettings {
+  [ChromeEarlGreyUI openSettingsMenu];
+  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(
+                                          l10n_util::GetNSString(
+                                              IDS_AUTOFILL_ADDRESSES))]
+      performAction:grey_tap()];
+}
+
+// Helper to open the settings page for the Autofill profile with |label|.
+- (void)openEditProfile:(NSString*)label {
+  [self openAutofillProfilesSettings];
+
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(label)]
+      performAction:grey_tap()];
+}
+
+// Close the settings.
+- (void)exitSettingsMenu {
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
+      performAction:grey_tap()];
+  // Wait for UI components to finish loading.
+  [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
+}
+
+// Test that the page for viewing Autofill profile details is as expected.
+- (void)testAutofillProfileViewPage {
+  autofill::AutofillProfile profile = [self addAutofillProfile];
+  [self openEditProfile:kProfileLabel];
+
+  // Check that all fields and values match the expectations.
+  for (const DisplayStringIDToExpectedResult& expectation : kExpectedFields) {
+    [[EarlGrey selectElementWithMatcher:
+                   grey_accessibilityLabel([NSString
+                       stringWithFormat:@"%@, %@",
+                                        l10n_util::GetNSString(
+                                            expectation.display_string_id),
+                                        expectation.expected_result])]
+        assertWithMatcher:grey_notNil()];
+  }
+
+  // Go back to the list view page.
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+
+  [self exitSettingsMenu];
+}
+
+// Test that editing country names is followed by validating the value and
+// replacing it with a canonical one.
+- (void)testAutofillProfileEditing {
+  autofill::AutofillProfile profile = [self addAutofillProfile];
+  [self openEditProfile:kProfileLabel];
+
+  // Keep editing the Country field and verify that validation works.
+  for (const UserTypedCountryExpectedResultPair& expectation : kCountryTests) {
+    // Switch on edit mode.
+    [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                            IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON)]
+        performAction:grey_tap()];
+
+    // Replace the text field with the user-version of the country.
+    [[EarlGrey selectElementWithMatcher:grey_accessibilityID(GetTextFieldForID(
+                                            IDS_IOS_AUTOFILL_COUNTRY))]
+        performAction:grey_replaceText(expectation.user_typed_country)];
+
+    // Switch off edit mode.
+    [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
+        performAction:grey_tap()];
+
+    // Verify that the country value was changed to canonical.
+    [[EarlGrey selectElementWithMatcher:
+                   grey_accessibilityLabel(
+                       [NSString stringWithFormat:@"%@, %@",
+                                                  l10n_util::GetNSString(
+                                                      IDS_IOS_AUTOFILL_COUNTRY),
+                                                  expectation.expected_result])]
+        assertWithMatcher:grey_notNil()];
+  }
+
+  // Go back to the list view page.
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+
+  [self exitSettingsMenu];
+}
+
+// Test that the page for viewing Autofill profile details is accessible.
+- (void)testAccessibilityOnAutofillProfileViewPage {
+  autofill::AutofillProfile profile = [self addAutofillProfile];
+  [self openEditProfile:kProfileLabel];
+
+  chrome_test_util::VerifyAccessibilityForCurrentScreen();
+
+  // Go back to the list view page.
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+
+  [self exitSettingsMenu];
+}
+
+// Test that the page for editing Autofill profile details is accessible.
+- (void)testAccessibilityOnAutofillProfileEditPage {
+  autofill::AutofillProfile profile = [self addAutofillProfile];
+  [self openEditProfile:kProfileLabel];
+
+  // Switch on edit mode.
+  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                          IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON)]
+      performAction:grey_tap()];
+  chrome_test_util::VerifyAccessibilityForCurrentScreen();
+
+  // Go back to the list view page.
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+
+  [self exitSettingsMenu];
+}
+
+// Checks that the Autofill profiles list view is in edit mode and the Autofill
+// profiles switch is disabled.
+- (void)testListViewEditMode {
+  autofill::AutofillProfile profile = [self addAutofillProfile];
+  [self openAutofillProfilesSettings];
+
+  // Switch on edit mode.
+  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                          IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON)]
+      performAction:grey_tap()];
+
+  // Check the Autofill profile switch is disabled.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
+                                          @"addressItem_switch", YES, NO)]
+      assertWithMatcher:grey_notNil()];
+
+  [self exitSettingsMenu];
+}
+
+// Checks that the Autofill profile switch can be toggled on/off and the list of
+// Autofill profiles is not affected by it.
+- (void)testToggleAutofillProfileSwitch {
+  autofill::AutofillProfile profile = [self addAutofillProfile];
+  [self openAutofillProfilesSettings];
+
+  // Toggle the Autofill profiles switch off.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
+                                          @"addressItem_switch", YES, YES)]
+      performAction:chrome_test_util::TurnSettingsSwitchOn(NO)];
+
+  // Expect Autofill profiles to remain visible.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(kProfileLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Toggle the Autofill profiles switch back on.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
+                                          @"addressItem_switch", NO, YES)]
+      performAction:chrome_test_util::TurnSettingsSwitchOn(YES)];
+
+  // Expect Autofill profiles to remain visible.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(kProfileLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  [self exitSettingsMenu];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/settings/autofill_settings_egtest.mm b/ios/chrome/browser/ui/settings/autofill_settings_egtest.mm
deleted file mode 100644
index f8dc8fe..0000000
--- a/ios/chrome/browser/ui/settings/autofill_settings_egtest.mm
+++ /dev/null
@@ -1,395 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import <XCTest/XCTest.h>
-
-#import "base/test/ios/wait_util.h"
-#include "components/autofill/core/browser/autofill_profile.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/credit_card.h"
-#include "components/autofill/core/browser/personal_data_manager.h"
-#include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
-#include "ios/chrome/browser/ui/tools_menu/public/tools_menu_constants.h"
-#include "ios/chrome/grit/ios_strings.h"
-#import "ios/chrome/test/app/chrome_test_util.h"
-#import "ios/chrome/test/app/web_view_interaction_test_util.h"
-#include "ios/chrome/test/earl_grey/accessibility_util.h"
-#import "ios/chrome/test/earl_grey/chrome_actions.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
-#import "ios/chrome/test/earl_grey/chrome_matchers.h"
-#import "ios/chrome/test/earl_grey/chrome_test_case.h"
-#import "ios/web/public/test/http_server/http_server.h"
-#include "ios/web/public/test/http_server/http_server_util.h"
-#include "ui/base/l10n/l10n_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using chrome_test_util::ButtonWithAccessibilityLabel;
-using chrome_test_util::ButtonWithAccessibilityLabelId;
-using chrome_test_util::NavigationBarDoneButton;
-using chrome_test_util::SettingsDoneButton;
-using chrome_test_util::SettingsMenuBackButton;
-
-namespace {
-
-// Expectation of how the saved autofill profile looks like, a map from cell
-// name IDs to expected contents.
-struct DisplayStringIDToExpectedResult {
-  int display_string_id;
-  NSString* expected_result;
-};
-
-const DisplayStringIDToExpectedResult kExpectedFields[] = {
-    {IDS_IOS_AUTOFILL_FULLNAME, @"John H. Doe"},
-    {IDS_IOS_AUTOFILL_COMPANY_NAME, @"Underworld"},
-    {IDS_IOS_AUTOFILL_ADDRESS1, @"666 Erebus St."},
-    {IDS_IOS_AUTOFILL_ADDRESS2, @"Apt 8"},
-    {IDS_IOS_AUTOFILL_CITY, @"Elysium"},
-    {IDS_IOS_AUTOFILL_STATE, @"CA"},
-    {IDS_IOS_AUTOFILL_ZIP, @"91111"},
-    {IDS_IOS_AUTOFILL_PHONE, @"16502111111"},
-    {IDS_IOS_AUTOFILL_EMAIL, @"johndoe@hades.com"}};
-
-NSString* const kAddressLabel = @"John H. Doe, 666 Erebus St.";
-
-// Expectation of how user-typed country names should be canonicalized.
-struct UserTypedCountryExpectedResultPair {
-  NSString* user_typed_country;
-  NSString* expected_result;
-};
-
-const UserTypedCountryExpectedResultPair kCountryTests[] = {
-    {@"Brasil", @"Brazil"},
-    {@"China", @"China"},
-    {@"DEUTSCHLAND", @"Germany"},
-    {@"GREAT BRITAIN", @"United Kingdom"},
-    {@"IN", @"India"},
-    {@"JaPaN", @"Japan"},
-    {@"JP", @"Japan"},
-    {@"Nigeria", @"Nigeria"},
-    {@"TW", @"Taiwan"},
-    {@"U.S.A.", @"United States"},
-    {@"UK", @"United Kingdom"},
-    {@"USA", @"United States"},
-    {@"Nonexistia", @""},
-};
-
-// Given a resource ID of a category of an address profile, it returns a
-// NSString consisting of the resource string concatenated with "_textField".
-// This is the a11y ID of the text field corresponding to the category in the
-// edit dialog of the address profile.
-NSString* GetTextFieldForID(int categoryId) {
-  return [NSString
-      stringWithFormat:@"%@_textField", l10n_util::GetNSString(categoryId)];
-}
-
-}  // namespace
-
-// Various tests for the Autofill section of the settings.
-@interface AutofillSettingsTestCase : ChromeTestCase
-@end
-
-@implementation AutofillSettingsTestCase {
-  // The PersonalDataManager instance for the current browser state.
-  autofill::PersonalDataManager* _personalDataManager;
-}
-
-- (void)setUp {
-  [super setUp];
-
-  _personalDataManager =
-      autofill::PersonalDataManagerFactory::GetForBrowserState(
-          chrome_test_util::GetOriginalBrowserState());
-  _personalDataManager->SetSyncingForTest(true);
-}
-
-- (void)tearDown {
-  // Clear existing profiles and credit cards.
-  for (const auto* profile : _personalDataManager->GetProfiles()) {
-    _personalDataManager->RemoveByGUID(profile->guid());
-  }
-  for (const auto* creditCard : _personalDataManager->GetCreditCards()) {
-    _personalDataManager->RemoveByGUID(creditCard->guid());
-  }
-
-  [super tearDown];
-}
-
-- (autofill::AutofillProfile)addAutofillProfile {
-  autofill::AutofillProfile profile = autofill::test::GetFullProfile();
-  size_t profileCount = _personalDataManager->GetProfiles().size();
-  _personalDataManager->AddProfile(profile);
-  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
-                 base::test::ios::kWaitForActionTimeout,
-                 ^bool() {
-                   return profileCount <
-                          _personalDataManager->GetProfiles().size();
-                 }),
-             @"Failed to add profile.");
-  return profile;
-}
-
-- (autofill::CreditCard)addCreditCard {
-  autofill::CreditCard creditCard = autofill::test::GetCreditCard();  // Visa.
-  size_t cardCount = _personalDataManager->GetCreditCards().size();
-  _personalDataManager->AddCreditCard(creditCard);
-  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
-                 base::test::ios::kWaitForActionTimeout,
-                 ^bool() {
-                   return cardCount <
-                          _personalDataManager->GetCreditCards().size();
-                 }),
-             @"Failed to add credit card.");
-  return creditCard;
-}
-
-// Helper to open the settings page for the record with |address|.
-- (void)openEditAddress:(NSString*)address {
-  // Go to Autofill Settings.
-  [ChromeEarlGreyUI openSettingsMenu];
-  NSString* label = l10n_util::GetNSString(IDS_IOS_AUTOFILL);
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(label)]
-      performAction:grey_tap()];
-
-  NSString* cellLabel = address;
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(cellLabel)]
-      performAction:grey_tap()];
-}
-
-// Close the settings.
-- (void)exitSettingsMenu {
-  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
-      performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
-      performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
-      performAction:grey_tap()];
-  // Wait for UI components to finish loading.
-  [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
-}
-
-// Test that the page for viewing autofill profile details is as expected.
-- (void)testAutofillProfileViewPage {
-  autofill::AutofillProfile profile = [self addAutofillProfile];
-  [self openEditAddress:kAddressLabel];
-
-  // Check that all fields and values match the expectations.
-  for (const DisplayStringIDToExpectedResult& expectation : kExpectedFields) {
-    [[EarlGrey
-        selectElementWithMatcher:
-            grey_accessibilityLabel([NSString
-                stringWithFormat:@"%@, %@", l10n_util::GetNSString(
-                                                expectation.display_string_id),
-                                 expectation.expected_result])]
-        assertWithMatcher:grey_notNil()];
-  }
-
-  [self exitSettingsMenu];
-}
-
-// Test that editing country names is followed by validating the value and
-// replacing it with a canonical one.
-- (void)testAutofillProfileEditing {
-  autofill::AutofillProfile profile = [self addAutofillProfile];
-  [self openEditAddress:kAddressLabel];
-
-  // Keep editing the Country field and verify that validation works.
-  for (const UserTypedCountryExpectedResultPair& expectation : kCountryTests) {
-    // Switch on edit mode.
-    [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
-                                            IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON)]
-        performAction:grey_tap()];
-
-    // Replace the text field with the user-version of the country.
-    [[EarlGrey selectElementWithMatcher:grey_accessibilityID(GetTextFieldForID(
-                                            IDS_IOS_AUTOFILL_COUNTRY))]
-        performAction:grey_replaceText(expectation.user_typed_country)];
-
-    // Switch off edit mode.
-    [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
-        performAction:grey_tap()];
-
-    // Verify that the country value was changed to canonical.
-    [[EarlGrey
-        selectElementWithMatcher:
-            grey_accessibilityLabel([NSString
-                stringWithFormat:@"%@, %@", l10n_util::GetNSString(
-                                                IDS_IOS_AUTOFILL_COUNTRY),
-                                 expectation.expected_result])]
-        assertWithMatcher:grey_notNil()];
-  }
-
-  [self exitSettingsMenu];
-}
-
-// Test that the page for viewing autofill profile details is accessible.
-- (void)testAccessibilityOnAutofillProfileViewPage {
-  autofill::AutofillProfile profile = [self addAutofillProfile];
-  [self openEditAddress:kAddressLabel];
-
-  chrome_test_util::VerifyAccessibilityForCurrentScreen();
-
-  [self exitSettingsMenu];
-}
-
-// Test that the page for editing autofill profile details is accessible.
-- (void)testAccessibilityOnAutofillProfileEditPage {
-  autofill::AutofillProfile profile = [self addAutofillProfile];
-  [self openEditAddress:kAddressLabel];
-
-  // Switch on edit mode.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
-                                          IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON)]
-      performAction:grey_tap()];
-  chrome_test_util::VerifyAccessibilityForCurrentScreen();
-
-  [self exitSettingsMenu];
-}
-
-// Checks that if the autofill profiles and credit cards list view is in edit
-// mode, the Autofill, address, and credit card switches are disabled.
-- (void)testListViewEditMode {
-  autofill::AutofillProfile profile = [self addAutofillProfile];
-  autofill::CreditCard creditCard = [self addCreditCard];
-
-  // Go to Autofill Settings.
-  [ChromeEarlGreyUI openSettingsMenu];
-  [[EarlGrey
-      selectElementWithMatcher:ButtonWithAccessibilityLabel(
-                                   l10n_util::GetNSString(IDS_IOS_AUTOFILL))]
-      performAction:grey_tap()];
-
-  // Switch on edit mode.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
-                                          IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON)]
-      performAction:grey_tap()];
-
-  // Check the Autofill, address, and credit card switches are disabled.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
-                                          @"autofillItem_switch", YES, NO)]
-      assertWithMatcher:grey_notNil()];
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
-                                          @"addressItem_switch", YES, NO)]
-      assertWithMatcher:grey_notNil()];
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
-                                          @"cardItem_switch", YES, NO)]
-      assertWithMatcher:grey_notNil()];
-}
-
-// Checks that the autofill address switch can be toggled on/off independently
-// and the list of autofill profiles is not affected by it.
-- (void)testToggleAutofillAddressSwitch {
-  autofill::AutofillProfile profile = [self addAutofillProfile];
-
-  // Go to Autofill Settings.
-  [ChromeEarlGreyUI openSettingsMenu];
-  [[EarlGrey
-      selectElementWithMatcher:ButtonWithAccessibilityLabel(
-                                   l10n_util::GetNSString(IDS_IOS_AUTOFILL))]
-      performAction:grey_tap()];
-
-  // Toggle the Autofill address switch off.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
-                                          @"addressItem_switch", YES, YES)]
-      performAction:chrome_test_util::TurnSettingsSwitchOn(NO)];
-
-  // Expect Autofill profiles to remain visible.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(
-                                          @"John H. Doe, 666 Erebus St.")]
-      assertWithMatcher:grey_notNil()];
-
-  // Toggle the Autofill address switch back on.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
-                                          @"addressItem_switch", NO, YES)]
-      performAction:chrome_test_util::TurnSettingsSwitchOn(YES)];
-
-  // Expect Autofill profiles to remain visible.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(
-                                          @"John H. Doe, 666 Erebus St.")]
-      assertWithMatcher:grey_notNil()];
-}
-
-// Checks that the autofill credit card switch can be toggled on/off
-// independently and the list of autofill credit cards is not affected by it.
-- (void)testToggleAutofillCreditCardSwitch {
-  autofill::CreditCard creditCard = [self addCreditCard];
-
-  // Go to Autofill Settings.
-  [ChromeEarlGreyUI openSettingsMenu];
-  [[EarlGrey
-      selectElementWithMatcher:ButtonWithAccessibilityLabel(
-                                   l10n_util::GetNSString(IDS_IOS_AUTOFILL))]
-      performAction:grey_tap()];
-
-  // Toggle the Autofill credit card switch off.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
-                                          @"cardItem_switch", YES, YES)]
-      performAction:chrome_test_util::TurnSettingsSwitchOn(NO)];
-
-  // Expect Autofill credit cards to remain visible.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityLabel(
-                     @"Test User, Visa  ‪• • • • 1111‬")]
-      assertWithMatcher:grey_notNil()];
-
-  // Toggle the Autofill credit card switch back on.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
-                                          @"cardItem_switch", NO, YES)]
-      performAction:chrome_test_util::TurnSettingsSwitchOn(YES)];
-
-  // Expect Autofill credit cards to remain visible.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityLabel(
-                     @"Test User, Visa  ‪• • • • 1111‬")]
-      assertWithMatcher:grey_notNil()];
-}
-
-// Tests that toggling the Autofill switch on and off disables and enables the
-// Autofill address and credit card switches respectively and that the list of
-// autofill addresses and credit cards is not affected by it.
-- (void)testToggleAutofillSwitches {
-  autofill::AutofillProfile profile = [self addAutofillProfile];
-  autofill::CreditCard creditCard = [self addCreditCard];
-
-  // Go to Autofill Settings.
-  [ChromeEarlGreyUI openSettingsMenu];
-  [[EarlGrey
-      selectElementWithMatcher:ButtonWithAccessibilityLabel(
-                                   l10n_util::GetNSString(IDS_IOS_AUTOFILL))]
-      performAction:grey_tap()];
-
-  // Toggle the Autofill switch off and back on.
-  for (BOOL expectedState : {YES, NO}) {
-    // Toggle the Autofill switch.
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
-                                            @"autofillItem_switch",
-                                            expectedState, YES)]
-        performAction:chrome_test_util::TurnSettingsSwitchOn(!expectedState)];
-
-    // Expect address and credit card switches to be off when Autofill toggle is
-    // off and on when Autofill toggle is on.
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
-                                            @"addressItem_switch",
-                                            !expectedState, YES)]
-        assertWithMatcher:grey_notNil()];
-    [[EarlGrey
-        selectElementWithMatcher:chrome_test_util::SettingsSwitchCell(
-                                     @"cardItem_switch", !expectedState, YES)]
-        assertWithMatcher:grey_notNil()];
-
-    // Expect Autofill addresses and credit cards to remain visible.
-    [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(
-                                            @"John H. Doe, 666 Erebus St.")]
-        assertWithMatcher:grey_notNil()];
-    [[EarlGrey selectElementWithMatcher:
-                   grey_accessibilityLabel(
-                       @"Test User, Visa  ‪• • • • 1111‬")]
-        assertWithMatcher:grey_notNil()];
-  }
-}
-
-@end
diff --git a/ios/chrome/browser/ui/settings/resources/BUILD.gn b/ios/chrome/browser/ui/settings/resources/BUILD.gn
index da19098..2a00f0e6 100644
--- a/ios/chrome/browser/ui/settings/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/resources/BUILD.gn
@@ -67,15 +67,6 @@
   ]
 }
 
-imageset("settings_autofill_forms") {
-  sources = [
-    "settings_autofill_forms.imageset/Contents.json",
-    "settings_autofill_forms.imageset/settings_autofill_forms.png",
-    "settings_autofill_forms.imageset/settings_autofill_forms@2x.png",
-    "settings_autofill_forms.imageset/settings_autofill_forms@3x.png",
-  ]
-}
-
 imageset("settings_bandwidth") {
   sources = [
     "settings_bandwidth.imageset/Contents.json",
@@ -112,6 +103,24 @@
   ]
 }
 
+imageset("settings_payment_methods") {
+  sources = [
+    "settings_payment_methods.imageset/Contents.json",
+    "settings_payment_methods.imageset/payment_methods.png",
+    "settings_payment_methods.imageset/payment_methods@2x.png",
+    "settings_payment_methods.imageset/payment_methods@3x.png",
+  ]
+}
+
+imageset("settings_addresses") {
+  sources = [
+    "settings_addresses.imageset/Contents.json",
+    "settings_addresses.imageset/addresses.png",
+    "settings_addresses.imageset/addresses@2x.png",
+    "settings_addresses.imageset/addresses@3x.png",
+  ]
+}
+
 imageset("settings_privacy") {
   sources = [
     "settings_privacy.imageset/Contents.json",
diff --git a/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/Contents.json b/ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/Contents.json
similarity index 66%
rename from ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/Contents.json
rename to ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/Contents.json
index 248316be..5738db13 100644
--- a/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/Contents.json
+++ b/ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/Contents.json
@@ -3,21 +3,21 @@
         {
             "idiom": "universal",
             "scale": "1x",
-            "filename": "settings_autofill_forms.png"
+            "filename": "addresses.png"
         },
         {
             "idiom": "universal",
             "scale": "2x",
-            "filename": "settings_autofill_forms@2x.png"
+            "filename": "addresses@2x.png"
         },
         {
             "idiom": "universal",
             "scale": "3x",
-            "filename": "settings_autofill_forms@3x.png"
+            "filename": "addresses@3x.png"
         }
     ],
     "info": {
         "version": 1,
         "author": "xcode"
     }
-}
+}
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/addresses.png b/ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/addresses.png
new file mode 100644
index 0000000..fc1fb7a
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/addresses.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/addresses@2x.png b/ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/addresses@2x.png
new file mode 100644
index 0000000..6924e8f8
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/addresses@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/addresses@3x.png b/ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/addresses@3x.png
new file mode 100644
index 0000000..eda1d38
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/settings_addresses.imageset/addresses@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/settings_autofill_forms.png b/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/settings_autofill_forms.png
deleted file mode 100644
index 5b5fb84..0000000
--- a/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/settings_autofill_forms.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/settings_autofill_forms@2x.png b/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/settings_autofill_forms@2x.png
deleted file mode 100644
index 3f62534..0000000
--- a/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/settings_autofill_forms@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/settings_autofill_forms@3x.png b/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/settings_autofill_forms@3x.png
deleted file mode 100644
index d65d49d..0000000
--- a/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/settings_autofill_forms@3x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/Contents.json b/ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/Contents.json
similarity index 66%
copy from ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/Contents.json
copy to ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/Contents.json
index 248316be..31e25f4 100644
--- a/ios/chrome/browser/ui/settings/resources/settings_autofill_forms.imageset/Contents.json
+++ b/ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/Contents.json
@@ -3,21 +3,21 @@
         {
             "idiom": "universal",
             "scale": "1x",
-            "filename": "settings_autofill_forms.png"
+            "filename": "payment_methods.png"
         },
         {
             "idiom": "universal",
             "scale": "2x",
-            "filename": "settings_autofill_forms@2x.png"
+            "filename": "payment_methods@2x.png"
         },
         {
             "idiom": "universal",
             "scale": "3x",
-            "filename": "settings_autofill_forms@3x.png"
+            "filename": "payment_methods@3x.png"
         }
     ],
     "info": {
         "version": 1,
         "author": "xcode"
     }
-}
+}
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/payment_methods.png b/ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/payment_methods.png
new file mode 100644
index 0000000..05cf013
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/payment_methods.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/payment_methods@2x.png b/ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/payment_methods@2x.png
new file mode 100644
index 0000000..fde276e
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/payment_methods@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/payment_methods@3x.png b/ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/payment_methods@3x.png
new file mode 100644
index 0000000..f2a085a8
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/settings_payment_methods.imageset/payment_methods@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm b/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
index 25ca899..405df2d 100644
--- a/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
@@ -44,7 +44,8 @@
 #import "ios/chrome/browser/ui/commands/settings_main_page_commands.h"
 #import "ios/chrome/browser/ui/settings/about_chrome_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/accounts_collection_view_controller.h"
-#import "ios/chrome/browser/ui/settings/autofill_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill_credit_card_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/bandwidth_management_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/cells/account_signin_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_detail_item.h"
@@ -96,7 +97,9 @@
 NSString* const kSyncAndGoogleServicesImageName = @"sync_and_google_services";
 NSString* const kSettingsSearchEngineImageName = @"settings_search_engine";
 NSString* const kSettingsPasswordsImageName = @"settings_passwords";
-NSString* const kSettingsAutofillFormsImageName = @"settings_autofill_forms";
+NSString* const kSettingsAutofillCreditCardImageName =
+    @"settings_payment_methods";
+NSString* const kSettingsAutofillProfileImageName = @"settings_addresses";
 NSString* const kSettingsVoiceSearchImageName = @"settings_voice_search";
 NSString* const kSettingsPrivacyImageName = @"settings_privacy";
 NSString* const kSettingsContentSettingsImageName =
@@ -124,7 +127,8 @@
   ItemTypeHeader,
   ItemTypeSearchEngine,
   ItemTypeSavedPasswords,
-  ItemTypeAutofill,
+  ItemTypeAutofillCreditCard,
+  ItemTypeAutofillProfile,
   ItemTypeVoiceSearch,
   ItemTypePrivacy,
   ItemTypeContentSettings,
@@ -240,7 +244,8 @@
   SettingsDetailItem* _voiceSearchDetailItem;
   SettingsDetailItem* _defaultSearchEngineItem;
   SettingsDetailItem* _savePasswordsDetailItem;
-  SettingsDetailItem* _autoFillDetailItem;
+  SettingsDetailItem* _autoFillProfileDetailItem;
+  SettingsDetailItem* _autoFillCreditCardDetailItem;
 
   // YES if the user used at least once the sign-in promo view buttons.
   BOOL _signinStarted;
@@ -424,7 +429,9 @@
       toSectionWithIdentifier:SectionIdentifierBasics];
   [model addItem:[self savePasswordsDetailItem]
       toSectionWithIdentifier:SectionIdentifierBasics];
-  [model addItem:[self autoFillDetailItem]
+  [model addItem:[self AutoFillCreditCardDetailItem]
+      toSectionWithIdentifier:SectionIdentifierBasics];
+  [model addItem:[self autoFillProfileDetailItem]
       toSectionWithIdentifier:SectionIdentifierBasics];
 
   // Advanced Section
@@ -562,19 +569,34 @@
   return _savePasswordsDetailItem;
 }
 
-- (CollectionViewItem*)autoFillDetailItem {
-  BOOL autofillEnabled =
-      autofill::prefs::IsAutofillEnabled(_browserState->GetPrefs());
-  NSString* autofillDetail = autofillEnabled
-                                 ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
-                                 : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
-  _autoFillDetailItem =
-      [self detailItemWithType:ItemTypeAutofill
-                          text:l10n_util::GetNSString(IDS_IOS_AUTOFILL)
-                    detailText:autofillDetail
-                 iconImageName:kSettingsAutofillFormsImageName];
+- (CollectionViewItem*)AutoFillCreditCardDetailItem {
+  BOOL autofillCreditCardEnabled =
+      autofill::prefs::IsCreditCardAutofillEnabled(_browserState->GetPrefs());
+  NSString* detailText = autofillCreditCardEnabled
+                             ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
+                             : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+  _autoFillCreditCardDetailItem = [self
+      detailItemWithType:ItemTypeAutofillCreditCard
+                    text:l10n_util::GetNSString(IDS_AUTOFILL_PAYMENT_METHODS)
+              detailText:detailText
+           iconImageName:kSettingsAutofillCreditCardImageName];
 
-  return _autoFillDetailItem;
+  return _autoFillCreditCardDetailItem;
+}
+
+- (CollectionViewItem*)autoFillProfileDetailItem {
+  BOOL autofillProfileEnabled =
+      autofill::prefs::IsProfileAutofillEnabled(_browserState->GetPrefs());
+  NSString* detailText = autofillProfileEnabled
+                             ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
+                             : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+  _autoFillProfileDetailItem =
+      [self detailItemWithType:ItemTypeAutofillProfile
+                          text:l10n_util::GetNSString(IDS_AUTOFILL_ADDRESSES)
+                    detailText:detailText
+                 iconImageName:kSettingsAutofillProfileImageName];
+
+  return _autoFillProfileDetailItem;
 }
 
 - (CollectionViewItem*)voiceSearchDetailItem {
@@ -856,8 +878,12 @@
       controller = [[SavePasswordsCollectionViewController alloc]
           initWithBrowserState:_browserState];
       break;
-    case ItemTypeAutofill:
-      controller = [[AutofillCollectionViewController alloc]
+    case ItemTypeAutofillCreditCard:
+      controller = [[AutofillCreditCardCollectionViewController alloc]
+          initWithBrowserState:_browserState];
+      break;
+    case ItemTypeAutofillProfile:
+      controller = [[AutofillProfileCollectionViewController alloc]
           initWithBrowserState:_browserState];
       break;
     case ItemTypeVoiceSearch:
@@ -1260,15 +1286,24 @@
     [self reconfigureCellsForItems:@[ _savePasswordsDetailItem ]];
   }
 
-  if (preferenceName == autofill::prefs::kAutofillCreditCardEnabled ||
-      preferenceName == autofill::prefs::kAutofillProfileEnabled) {
-    BOOL autofillEnabled =
-        autofill::prefs::IsAutofillEnabled(_browserState->GetPrefs());
-    NSString* autofillDetail =
-        autofillEnabled ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
-                        : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
-    _autoFillDetailItem.detailText = autofillDetail;
-    [self reconfigureCellsForItems:@[ _autoFillDetailItem ]];
+  if (preferenceName == autofill::prefs::kAutofillProfileEnabled) {
+    BOOL autofillProfileEnabled =
+        autofill::prefs::IsProfileAutofillEnabled(_browserState->GetPrefs());
+    NSString* detailText = autofillProfileEnabled
+                               ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
+                               : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+    _autoFillProfileDetailItem.detailText = detailText;
+    [self reconfigureCellsForItems:@[ _autoFillProfileDetailItem ]];
+  }
+
+  if (preferenceName == autofill::prefs::kAutofillCreditCardEnabled) {
+    BOOL autofillCreditCardEnabled =
+        autofill::prefs::IsCreditCardAutofillEnabled(_browserState->GetPrefs());
+    NSString* detailText = autofillCreditCardEnabled
+                               ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
+                               : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+    _autoFillCreditCardDetailItem.detailText = detailText;
+    [self reconfigureCellsForItems:@[ _autoFillCreditCardDetailItem ]];
   }
 }
 
diff --git a/ios/chrome/browser/ui/settings/settings_egtest.mm b/ios/chrome/browser/ui/settings/settings_egtest.mm
index ff402e29..2199806 100644
--- a/ios/chrome/browser/ui/settings/settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/settings_egtest.mm
@@ -97,9 +97,13 @@
 id<GREYMatcher> SearchEngineButton() {
   return ButtonWithAccessibilityLabelId(IDS_IOS_SEARCH_ENGINE_SETTING_TITLE);
 }
-// Matcher for the Autofill Forms cell on the main Settings screen.
-id<GREYMatcher> AutofillButton() {
-  return ButtonWithAccessibilityLabelId(IDS_IOS_AUTOFILL);
+// Matcher for the payment methods cell on the main Settings screen.
+id<GREYMatcher> PaymentMethodsButton() {
+  return ButtonWithAccessibilityLabelId(IDS_AUTOFILL_PAYMENT_METHODS);
+}
+// Matcher for the addresses cell on the main Settings screen.
+id<GREYMatcher> AddressesButton() {
+  return ButtonWithAccessibilityLabelId(IDS_AUTOFILL_ADDRESSES);
 }
 // Matcher for the Google Chrome cell on the main Settings screen.
 id<GREYMatcher> GoogleChromeButton() {
@@ -709,10 +713,18 @@
   [self closeSubSettingsMenu];
 }
 
-// Verifies the UI elements are accessible on the Autofill Forms page.
-- (void)testAccessibilityOnAutofillForms {
+// Verifies the UI elements are accessible on the payment methods page.
+- (void)testAccessibilityOnPaymentMethods {
   [ChromeEarlGreyUI openSettingsMenu];
-  [ChromeEarlGreyUI tapSettingsMenuButton:AutofillButton()];
+  [ChromeEarlGreyUI tapSettingsMenuButton:PaymentMethodsButton()];
+  chrome_test_util::VerifyAccessibilityForCurrentScreen();
+  [self closeSubSettingsMenu];
+}
+
+// Verifies the UI elements are accessible on the addresses page.
+- (void)testAccessibilityOnAddresses {
+  [ChromeEarlGreyUI openSettingsMenu];
+  [ChromeEarlGreyUI tapSettingsMenuButton:AddressesButton()];
   chrome_test_util::VerifyAccessibilityForCurrentScreen();
   [self closeSubSettingsMenu];
 }
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
index 3d0ebca9..46241fda0c 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -15,7 +15,7 @@
 #import "ios/chrome/browser/ui/material_components/app_bar_presenting.h"
 #import "ios/chrome/browser/ui/material_components/utils.h"
 #import "ios/chrome/browser/ui/settings/accounts_collection_view_controller.h"
-#import "ios/chrome/browser/ui/settings/autofill_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/autofill_profile_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/import_data_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.h"
@@ -248,8 +248,8 @@
 + (SettingsNavigationController*)
 newAutofillController:(ios::ChromeBrowserState*)browserState
              delegate:(id<SettingsNavigationControllerDelegate>)delegate {
-  AutofillCollectionViewController* controller =
-      [[AutofillCollectionViewController alloc]
+  AutofillProfileCollectionViewController* controller =
+      [[AutofillProfileCollectionViewController alloc]
           initWithBrowserState:browserState];
   controller.dispatcher = [delegate dispatcherForSettings];
 
diff --git a/ios/web_view/internal/translate/cwv_translation_controller.mm b/ios/web_view/internal/translate/cwv_translation_controller.mm
index 64cf3111..13bee0b 100644
--- a/ios/web_view/internal/translate/cwv_translation_controller.mm
+++ b/ios/web_view/internal/translate/cwv_translation_controller.mm
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/sys_string_conversions.h"
@@ -25,26 +26,39 @@
 NSErrorDomain const CWVTranslationErrorDomain =
     @"org.chromium.chromewebview.TranslationErrorDomain";
 
-const NSInteger CWVTranslationErrorNetwork =
-    translate::TranslateErrors::NETWORK;
-const NSInteger CWVTranslationErrorInitializationError =
-    translate::TranslateErrors::INITIALIZATION_ERROR;
-const NSInteger CWVTranslationErrorUnknownLanguage =
-    translate::TranslateErrors::UNKNOWN_LANGUAGE;
-const NSInteger CWVTranslationErrorUnsupportedLanguage =
-    translate::TranslateErrors::UNSUPPORTED_LANGUAGE;
-const NSInteger CWVTranslationErrorIdenticalLanguages =
-    translate::TranslateErrors::IDENTICAL_LANGUAGES;
-const NSInteger CWVTranslationErrorTranslationError =
-    translate::TranslateErrors::TRANSLATION_ERROR;
-const NSInteger CWVTranslationErrorTranslationTimeout =
-    translate::TranslateErrors::TRANSLATION_TIMEOUT;
-const NSInteger CWVTranslationErrorUnexpectedScriptError =
-    translate::TranslateErrors::UNEXPECTED_SCRIPT_ERROR;
-const NSInteger CWVTranslationErrorBadOrigin =
-    translate::TranslateErrors::BAD_ORIGIN;
-const NSInteger CWVTranslationErrorScriptLoadError =
-    translate::TranslateErrors::SCRIPT_LOAD_ERROR;
+namespace {
+// Converts a |translate::TranslateErrors::Type| to a |CWVTranslationError|.
+CWVTranslationError CWVConvertTranslateError(
+    translate::TranslateErrors::Type type) {
+  switch (type) {
+    case translate::TranslateErrors::NONE:
+      return CWVTranslationErrorNone;
+    case translate::TranslateErrors::NETWORK:
+      return CWVTranslationErrorNetwork;
+    case translate::TranslateErrors::INITIALIZATION_ERROR:
+      return CWVTranslationErrorInitializationError;
+    case translate::TranslateErrors::UNKNOWN_LANGUAGE:
+      return CWVTranslationErrorUnknownLanguage;
+    case translate::TranslateErrors::UNSUPPORTED_LANGUAGE:
+      return CWVTranslationErrorUnsupportedLanguage;
+    case translate::TranslateErrors::IDENTICAL_LANGUAGES:
+      return CWVTranslationErrorIdenticalLanguages;
+    case translate::TranslateErrors::TRANSLATION_ERROR:
+      return CWVTranslationErrorTranslationError;
+    case translate::TranslateErrors::TRANSLATION_TIMEOUT:
+      return CWVTranslationErrorTranslationTimeout;
+    case translate::TranslateErrors::UNEXPECTED_SCRIPT_ERROR:
+      return CWVTranslationErrorUnexpectedScriptError;
+    case translate::TranslateErrors::BAD_ORIGIN:
+      return CWVTranslationErrorBadOrigin;
+    case translate::TranslateErrors::SCRIPT_LOAD_ERROR:
+      return CWVTranslationErrorScriptLoadError;
+    case translate::TranslateErrors::TRANSLATE_ERROR_MAX:
+      NOTREACHED();
+      return CWVTranslationErrorNone;
+  }
+}
+}  // namespace
 
 @interface CWVTranslationController ()
 
@@ -90,7 +104,7 @@
   NSError* error;
   if (errorType != translate::TranslateErrors::NONE) {
     error = [NSError errorWithDomain:CWVTranslationErrorDomain
-                                code:errorType
+                                code:CWVConvertTranslateError(errorType)
                             userInfo:nil];
   }
 
diff --git a/ios/web_view/public/cwv_translation_controller.h b/ios/web_view/public/cwv_translation_controller.h
index 4fe77e7..3672772 100644
--- a/ios/web_view/public/cwv_translation_controller.h
+++ b/ios/web_view/public/cwv_translation_controller.h
@@ -16,29 +16,33 @@
 @protocol CWVTranslationControllerDelegate;
 
 // The error domain for translation errors.
-extern NSErrorDomain const CWVTranslationErrorDomain;
+FOUNDATION_EXPORT CWV_EXPORT NSErrorDomain const CWVTranslationErrorDomain;
 
 // Possible error codes during translation.
-// No connectivity.
-extern const NSInteger CWVTranslationErrorNetwork;
-// The translation script failed to initialize.
-extern const NSInteger CWVTranslationErrorInitializationError;
-// The page's language could not be detected.
-extern const NSInteger CWVTranslationErrorUnknownLanguage;
-// The server detected a language that the browser does not know.
-extern const NSInteger CWVTranslationErrorUnsupportedLanguage;
-// The original and target languages are the same.
-extern const NSInteger CWVTranslationErrorIdenticalLanguages;
-// An error was reported by the translation script during translation.
-extern const NSInteger CWVTranslationErrorTranslationError;
-// The library doesn't finish the translation.
-extern const NSInteger CWVTranslationErrorTranslationTimeout;
-// The library raises an unexpected exception.
-extern const NSInteger CWVTranslationErrorUnexpectedScriptError;
-// The library is blocked because of bad origin.
-extern const NSInteger CWVTranslationErrorBadOrigin;
-// Loader fails to load a dependent JavaScript.
-extern const NSInteger CWVTranslationErrorScriptLoadError;
+typedef NS_ENUM(NSInteger, CWVTranslationError) {
+  // No error.
+  CWVTranslationErrorNone = 0,
+  // No connectivity.
+  CWVTranslationErrorNetwork,
+  // The translation script failed to initialize.
+  CWVTranslationErrorInitializationError,
+  // The page's language could not be detected.
+  CWVTranslationErrorUnknownLanguage,
+  // The server detected a language that the browser does not know.
+  CWVTranslationErrorUnsupportedLanguage,
+  // The original and target languages are the same.
+  CWVTranslationErrorIdenticalLanguages,
+  // An error was reported by the translation script during translation.
+  CWVTranslationErrorTranslationError,
+  // The library doesn't finish the translation.
+  CWVTranslationErrorTranslationTimeout,
+  // The library raises an unexpected exception.
+  CWVTranslationErrorUnexpectedScriptError,
+  // The library is blocked because of bad origin.
+  CWVTranslationErrorBadOrigin,
+  // Loader fails to load a dependent JavaScript.
+  CWVTranslationErrorScriptLoadError,
+};
 
 // Allows page translation from one language to another.
 CWV_EXPORT
diff --git a/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.cc b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.cc
index 7c09af1..a8f7023 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.cc
@@ -80,22 +80,6 @@
 
 }  // namespace
 
-// An input buffer and the corresponding output video frame awaiting
-// consumption, provided by the client.
-struct VaapiJpegDecodeAccelerator::DecodeRequest {
-  DecodeRequest(int32_t bitstream_buffer_id,
-                std::unique_ptr<UnalignedSharedMemory> shm,
-                const scoped_refptr<VideoFrame>& video_frame)
-      : bitstream_buffer_id(bitstream_buffer_id),
-        shm(std::move(shm)),
-        video_frame(video_frame) {}
-  ~DecodeRequest() = default;
-
-  int32_t bitstream_buffer_id;
-  std::unique_ptr<UnalignedSharedMemory> shm;
-  scoped_refptr<VideoFrame> video_frame;
-};
-
 void VaapiJpegDecodeAccelerator::NotifyError(int32_t bitstream_buffer_id,
                                              Error error) {
   DCHECK(task_runner_->BelongsToCurrentThread());
@@ -239,18 +223,18 @@
 }
 
 void VaapiJpegDecodeAccelerator::DecodeTask(
-    std::unique_ptr<DecodeRequest> request) {
+    int32_t bitstream_buffer_id,
+    std::unique_ptr<UnalignedSharedMemory> shm,
+    scoped_refptr<VideoFrame> video_frame) {
   DVLOGF(4);
   DCHECK(decoder_task_runner_->BelongsToCurrentThread());
   TRACE_EVENT0("jpeg", "DecodeTask");
 
   JpegParseResult parse_result;
-  if (!ParseJpegPicture(
-          reinterpret_cast<const uint8_t*>(request->shm->memory()),
-          request->shm->size(), &parse_result)) {
+  if (!ParseJpegPicture(reinterpret_cast<const uint8_t*>(shm->memory()),
+                        shm->size(), &parse_result)) {
     VLOGF(1) << "ParseJpegPicture failed";
-    NotifyErrorFromDecoderThread(request->bitstream_buffer_id,
-                                 PARSE_JPEG_FAILED);
+    NotifyErrorFromDecoderThread(bitstream_buffer_id, PARSE_JPEG_FAILED);
     return;
   }
 
@@ -258,8 +242,7 @@
       VaSurfaceFormatForJpeg(parse_result.frame_header);
   if (!new_va_rt_format) {
     VLOGF(1) << "Unsupported subsampling";
-    NotifyErrorFromDecoderThread(request->bitstream_buffer_id,
-                                 UNSUPPORTED_JPEG);
+    NotifyErrorFromDecoderThread(bitstream_buffer_id, UNSUPPORTED_JPEG);
     return;
   }
 
@@ -276,8 +259,7 @@
     if (!vaapi_wrapper_->CreateSurfaces(va_rt_format_, new_coded_size, 1,
                                         &va_surfaces)) {
       VLOGF(1) << "Create VA surface failed";
-      NotifyErrorFromDecoderThread(request->bitstream_buffer_id,
-                                   PLATFORM_FAILURE);
+      NotifyErrorFromDecoderThread(bitstream_buffer_id, PLATFORM_FAILURE);
       return;
     }
     va_surface_id_ = va_surfaces[0];
@@ -287,16 +269,13 @@
   if (!VaapiJpegDecoder::Decode(vaapi_wrapper_.get(), parse_result,
                                 va_surface_id_)) {
     VLOGF(1) << "Decode JPEG failed";
-    NotifyErrorFromDecoderThread(request->bitstream_buffer_id,
-                                 PLATFORM_FAILURE);
+    NotifyErrorFromDecoderThread(bitstream_buffer_id, PLATFORM_FAILURE);
     return;
   }
 
-  if (!OutputPicture(va_surface_id_, request->bitstream_buffer_id,
-                     request->video_frame)) {
+  if (!OutputPicture(va_surface_id_, bitstream_buffer_id, video_frame)) {
     VLOGF(1) << "Output picture failed";
-    NotifyErrorFromDecoderThread(request->bitstream_buffer_id,
-                                 PLATFORM_FAILURE);
+    NotifyErrorFromDecoderThread(bitstream_buffer_id, PLATFORM_FAILURE);
     return;
   }
 }
@@ -326,13 +305,10 @@
     return;
   }
 
-  std::unique_ptr<DecodeRequest> request(
-      new DecodeRequest(bitstream_buffer.id(), std::move(shm), video_frame));
-
   decoder_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VaapiJpegDecodeAccelerator::DecodeTask,
-                     base::Unretained(this), base::Passed(std::move(request))));
+      FROM_HERE, base::BindOnce(&VaapiJpegDecodeAccelerator::DecodeTask,
+                                base::Unretained(this), bitstream_buffer.id(),
+                                std::move(shm), std::move(video_frame)));
 }
 
 bool VaapiJpegDecodeAccelerator::IsSupported() {
diff --git a/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.h b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.h
index f3f6e74..472f92f 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.h
@@ -46,16 +46,16 @@
   bool IsSupported() override;
 
  private:
-  struct DecodeRequest;
-
   // Notifies the client that an error has occurred and decoding cannot
   // continue.
   void NotifyError(int32_t bitstream_buffer_id, Error error);
   void NotifyErrorFromDecoderThread(int32_t bitstream_buffer_id, Error error);
   void VideoFrameReady(int32_t bitstream_buffer_id);
 
-  // Processes one decode |request|.
-  void DecodeTask(std::unique_ptr<DecodeRequest> request);
+  // Processes one decode request.
+  void DecodeTask(int32_t bitstream_buffer_id,
+                  std::unique_ptr<UnalignedSharedMemory> shm,
+                  scoped_refptr<VideoFrame> video_frame);
 
   // Puts contents of |va_surface| into given |video_frame|, releases the
   // surface and passes the |input_buffer_id| of the resulting picture to
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index 7df3268..b3ae27543 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -3135,6 +3135,14 @@
 //  }
 EVENT_TYPE(COOKIE_STORE_SESSION_PERSISTENCE)
 
+// Event emitted when a particular origin is removed from the persistent
+// store on shutdown.
+//  {
+//    "origin": <Origin being filtered>
+//    "is_https": <Secure status of origin>
+//  }
+EVENT_TYPE(COOKIE_PERSISTENT_STORE_ORIGIN_FILTERED)
+
 // Event emitted when the persistent database load is started and completed.
 //  {
 //  }
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 03702a71..4a58166e 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -6706,14 +6706,7 @@
   }
 }
 
-#if defined(OS_CHROMEOS)
-// https://crbug.com/873851.
-#define MAYBE_TestPostChunkedDataAfterStart \
-  DISABLED_TestPostChunkedDataAfterStart
-#else
-#define MAYBE_TestPostChunkedDataAfterStart TestPostChunkedDataAfterStart
-#endif
-TEST_F(URLRequestTestHTTP, MAYBE_TestPostChunkedDataAfterStart) {
+TEST_F(URLRequestTestHTTP, TestPostChunkedDataAfterStart) {
   ASSERT_TRUE(http_test_server()->Start());
 
   TestDelegate d;
@@ -6730,9 +6723,14 @@
     r->Start();
     EXPECT_TRUE(r->is_pending());
 
+    // Pump messages until we start sending headers..
     base::RunLoop().RunUntilIdle();
+
+    // And now wait for completion.
+    base::RunLoop run_loop;
+    d.set_on_complete(run_loop.QuitClosure());
     AddDataToUpload(writer.get());
-    d.RunUntilComplete();
+    run_loop.Run();
 
     VerifyReceivedDataMatchesChunks(r.get(), &d);
   }
diff --git a/remoting/base/DEPS b/remoting/base/DEPS
index 7530772..0d4159d 100644
--- a/remoting/base/DEPS
+++ b/remoting/base/DEPS
@@ -3,6 +3,7 @@
   "+google_apis",
   "+mojo/core/embedder",
   "+net",
+  "+services/network/public/cpp",
   "+third_party/breakpad",
   "+third_party/zlib",
   "+ui/base",
diff --git a/remoting/base/gaia_oauth_client.cc b/remoting/base/gaia_oauth_client.cc
index b492b7b..1274347 100644
--- a/remoting/base/gaia_oauth_client.cc
+++ b/remoting/base/gaia_oauth_client.cc
@@ -14,8 +14,8 @@
 namespace remoting {
 
 GaiaOAuthClient::GaiaOAuthClient(
-    scoped_refptr<net::URLRequestContextGetter> url_request_context_getter)
-    : gaia_oauth_client_(url_request_context_getter.get()) {}
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : gaia_oauth_client_(std::move(url_loader_factory)) {}
 
 GaiaOAuthClient::~GaiaOAuthClient() = default;
 
diff --git a/remoting/base/gaia_oauth_client.h b/remoting/base/gaia_oauth_client.h
index 439aa3b1..79737fa 100644
--- a/remoting/base/gaia_oauth_client.h
+++ b/remoting/base/gaia_oauth_client.h
@@ -9,7 +9,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #include "remoting/base/oauth_client.h"
 
@@ -22,7 +22,7 @@
                         public gaia::GaiaOAuthClient::Delegate {
  public:
   GaiaOAuthClient(
-      scoped_refptr<net::URLRequestContextGetter> url_request_context_getter);
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 
   ~GaiaOAuthClient() override;
 
diff --git a/remoting/base/oauth_token_getter_impl.cc b/remoting/base/oauth_token_getter_impl.cc
index 360b4bc..9c8e3ed 100644
--- a/remoting/base/oauth_token_getter_impl.cc
+++ b/remoting/base/oauth_token_getter_impl.cc
@@ -11,9 +11,9 @@
 #include "base/containers/queue.h"
 #include "base/strings/string_util.h"
 #include "google_apis/google_api_keys.h"
-#include "net/url_request/url_request_context_getter.h"
 #include "remoting/base/logging.h"
 #include "remoting/base/oauth_helper.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace remoting {
 
@@ -30,14 +30,12 @@
 OAuthTokenGetterImpl::OAuthTokenGetterImpl(
     std::unique_ptr<OAuthIntermediateCredentials> intermediate_credentials,
     const OAuthTokenGetter::CredentialsUpdatedCallback& on_credentials_update,
-    const scoped_refptr<net::URLRequestContextGetter>&
-        url_request_context_getter,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     bool auto_refresh)
     : intermediate_credentials_(std::move(intermediate_credentials)),
       gaia_oauth_client_(
-          new gaia::GaiaOAuthClient(url_request_context_getter.get())),
-      credentials_updated_callback_(on_credentials_update),
-      url_request_context_getter_(url_request_context_getter) {
+          new gaia::GaiaOAuthClient(std::move(url_loader_factory))),
+      credentials_updated_callback_(on_credentials_update) {
   if (auto_refresh) {
     refresh_timer_.reset(new base::OneShotTimer());
   }
@@ -45,13 +43,11 @@
 
 OAuthTokenGetterImpl::OAuthTokenGetterImpl(
     std::unique_ptr<OAuthAuthorizationCredentials> authorization_credentials,
-    const scoped_refptr<net::URLRequestContextGetter>&
-        url_request_context_getter,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     bool auto_refresh)
     : authorization_credentials_(std::move(authorization_credentials)),
       gaia_oauth_client_(
-          new gaia::GaiaOAuthClient(url_request_context_getter.get())),
-      url_request_context_getter_(url_request_context_getter) {
+          new gaia::GaiaOAuthClient(std::move(url_loader_factory))) {
   if (auto_refresh) {
     refresh_timer_.reset(new base::OneShotTimer());
   }
diff --git a/remoting/base/oauth_token_getter_impl.h b/remoting/base/oauth_token_getter_impl.h
index a016e9a..bdb20080 100644
--- a/remoting/base/oauth_token_getter_impl.h
+++ b/remoting/base/oauth_token_getter_impl.h
@@ -13,9 +13,9 @@
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "remoting/base/oauth_token_getter.h"
 
-namespace net {
-class URLRequestContextGetter;
-}  // namespace net
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
 
 namespace remoting {
 
@@ -33,13 +33,11 @@
   OAuthTokenGetterImpl(
       std::unique_ptr<OAuthIntermediateCredentials> intermediate_credentials,
       const OAuthTokenGetter::CredentialsUpdatedCallback& on_credentials_update,
-      const scoped_refptr<net::URLRequestContextGetter>&
-          url_request_context_getter,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       bool auto_refresh);
   OAuthTokenGetterImpl(
       std::unique_ptr<OAuthAuthorizationCredentials> authorization_credentials,
-      const scoped_refptr<net::URLRequestContextGetter>&
-          url_request_context_getter,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       bool auto_refresh);
   ~OAuthTokenGetterImpl() override;
 
@@ -72,7 +70,6 @@
   std::unique_ptr<OAuthAuthorizationCredentials> authorization_credentials_;
   std::unique_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_;
   OAuthTokenGetter::CredentialsUpdatedCallback credentials_updated_callback_;
-  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
 
   bool response_pending_ = false;
   bool email_verified_ = false;
diff --git a/remoting/client/DEPS b/remoting/client/DEPS
index 81ac47553..4f4a49f3 100644
--- a/remoting/client/DEPS
+++ b/remoting/client/DEPS
@@ -7,4 +7,5 @@
   "+remoting/codec",
   "+remoting/protocol",
   "+remoting/signaling",
+  "+services/network",
 ]
diff --git a/remoting/client/chromoting_client_runtime.cc b/remoting/client/chromoting_client_runtime.cc
index e0d6a59..3bb00f2 100644
--- a/remoting/client/chromoting_client_runtime.cc
+++ b/remoting/client/chromoting_client_runtime.cc
@@ -16,6 +16,8 @@
 #include "remoting/base/telemetry_log_writer.h"
 #include "remoting/base/url_request_context_getter.h"
 #include "remoting/client/oauth_token_getter_proxy.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/transitional_url_loader_factory_owner.h"
 
 namespace {
 
@@ -57,6 +59,9 @@
   network_task_runner_ = AutoThread::CreateWithType(
       "native_net", ui_task_runner_, base::MessageLoop::TYPE_IO);
   url_requester_ = new URLRequestContextGetter(network_task_runner_);
+  url_loader_factory_owner_ =
+      std::make_unique<network::TransitionalURLLoaderFactoryOwner>(
+          url_requester_);
 }
 
 ChromotingClientRuntime::~ChromotingClientRuntime() {
@@ -91,4 +96,9 @@
       delegate_->oauth_token_getter(), ui_task_runner());
 }
 
+scoped_refptr<network::SharedURLLoaderFactory>
+ChromotingClientRuntime::url_loader_factory() {
+  return url_loader_factory_owner_->GetURLLoaderFactory();
+}
+
 }  // namespace remoting
diff --git a/remoting/client/chromoting_client_runtime.h b/remoting/client/chromoting_client_runtime.h
index 071609b..714e502 100644
--- a/remoting/client/chromoting_client_runtime.h
+++ b/remoting/client/chromoting_client_runtime.h
@@ -22,6 +22,11 @@
 struct DefaultSingletonTraits;
 }  // namespace base
 
+namespace network {
+class SharedURLLoaderFactory;
+class TransitionalURLLoaderFactoryOwner;
+}  // namespace network
+
 // Houses the global resources on which the Chromoting components run
 // (e.g. message loops and task runners).
 namespace remoting {
@@ -72,6 +77,8 @@
     return url_requester_;
   }
 
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory();
+
   ChromotingEventLogWriter* log_writer() { return log_writer_.get(); }
 
  private:
@@ -96,6 +103,8 @@
   scoped_refptr<AutoThreadTaskRunner> network_task_runner_;
 
   scoped_refptr<net::URLRequestContextGetter> url_requester_;
+  std::unique_ptr<network::TransitionalURLLoaderFactoryOwner>
+      url_loader_factory_owner_;
 
   // For logging session stage changes and stats.
   std::unique_ptr<TelemetryLogWriter> log_writer_;
diff --git a/remoting/host/DEPS b/remoting/host/DEPS
index f53dd06..b498013 100644
--- a/remoting/host/DEPS
+++ b/remoting/host/DEPS
@@ -19,6 +19,7 @@
   # //remoting uses device::PowerSaveBlocker directly. See crbug.com/689423
   "+services/device/public",
   "+services/device/wake_lock/power_save_blocker",
+  "+services/network",
   "+third_party/jsoncpp",
   "+third_party/skia",
   "+third_party/webrtc",
diff --git a/remoting/host/chromoting_host_context.cc b/remoting/host/chromoting_host_context.cc
index c95bc23..5c46220 100644
--- a/remoting/host/chromoting_host_context.cc
+++ b/remoting/host/chromoting_host_context.cc
@@ -13,6 +13,8 @@
 #include "build/build_config.h"
 #include "remoting/base/auto_thread.h"
 #include "remoting/base/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/transitional_url_loader_factory_owner.h"
 
 namespace remoting {
 
@@ -46,7 +48,11 @@
       url_request_context_getter_(url_request_context_getter),
       system_input_injector_factory_(system_input_injector_factory) {}
 
-ChromotingHostContext::~ChromotingHostContext() = default;
+ChromotingHostContext::~ChromotingHostContext() {
+  if (url_loader_factory_owner_)
+    network_task_runner_->DeleteSoon(FROM_HERE,
+                                     url_loader_factory_owner_.release());
+}
 
 std::unique_ptr<ChromotingHostContext> ChromotingHostContext::Copy() {
   return base::WrapUnique(new ChromotingHostContext(
@@ -96,6 +102,17 @@
   return url_request_context_getter_;
 }
 
+scoped_refptr<network::SharedURLLoaderFactory>
+ChromotingHostContext::url_loader_factory() {
+  DCHECK(network_task_runner_->RunsTasksInCurrentSequence());
+  if (!url_loader_factory_owner_) {
+    url_loader_factory_owner_ =
+        std::make_unique<network::TransitionalURLLoaderFactoryOwner>(
+            url_request_context_getter_);
+  }
+  return url_loader_factory_owner_->GetURLLoaderFactory();
+}
+
 ui::SystemInputInjectorFactory*
 ChromotingHostContext::system_input_injector_factory() const {
   return system_input_injector_factory_;
diff --git a/remoting/host/chromoting_host_context.h b/remoting/host/chromoting_host_context.h
index 47a42dc3..f857b98 100644
--- a/remoting/host/chromoting_host_context.h
+++ b/remoting/host/chromoting_host_context.h
@@ -19,6 +19,11 @@
 class URLRequestContextGetter;
 }  // namespace net
 
+namespace network {
+class SharedURLLoaderFactory;
+class TransitionalURLLoaderFactoryOwner;
+}  // namespace network
+
 namespace ui {
 class SystemInputInjectorFactory;
 }  // namespace ui
@@ -92,6 +97,8 @@
   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter()
       const;
 
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory();
+
   // Gives the factory which builds the SystemInputInjector, which takes events
   // and passes them to the system for dispatch.
   //
@@ -135,6 +142,10 @@
   // Serves URLRequestContexts that use the network and UI task runners.
   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
 
+  // Makes a SharedURLLoaderFactory out of |url_request_context_getter_|
+  std::unique_ptr<network::TransitionalURLLoaderFactoryOwner>
+      url_loader_factory_owner_;
+
   // A factory which makes a SystemInputInjector. Currently only non-null on
   // chromeos, though it's intended to be set everywhere mus is used.
   ui::SystemInputInjectorFactory* system_input_injector_factory_;
diff --git a/remoting/host/linux/BUILD.gn b/remoting/host/linux/BUILD.gn
index 32ccdcc..fa24d0b9 100644
--- a/remoting/host/linux/BUILD.gn
+++ b/remoting/host/linux/BUILD.gn
@@ -140,6 +140,7 @@
     "//remoting/host:remoting_infoplist_strings",
     "//remoting/host/native_messaging",
     "//remoting/host/setup",
+    "//services/network/public/mojom",
   ]
 
   # The |major|, |build| and |patch| versions are inherited from Chrome.
diff --git a/remoting/host/mac/BUILD.gn b/remoting/host/mac/BUILD.gn
index f9d46f4..a3fc05b8 100644
--- a/remoting/host/mac/BUILD.gn
+++ b/remoting/host/mac/BUILD.gn
@@ -165,6 +165,7 @@
     "//remoting/host:remoting_infoplist_strings",
     "//remoting/host/native_messaging",
     "//remoting/host/setup",
+    "//services/network/public/mojom",
   ]
   foreach(locale, remoting_locales_with_underscores) {
     deps += [
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index 287f3a1..d1063f4f 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -99,6 +99,7 @@
 #include "remoting/protocol/transport_context.h"
 #include "remoting/signaling/push_notification_subscriber.h"
 #include "remoting/signaling/xmpp_signal_strategy.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/webrtc/rtc_base/scoped_ref_ptr.h"
 
 #if defined(OS_POSIX)
@@ -1430,9 +1431,8 @@
       oauth_credentials(new OAuthTokenGetter::OAuthAuthorizationCredentials(
           xmpp_server_config_.username, oauth_refresh_token_,
           use_service_account_));
-  oauth_token_getter_.reset(
-      new OAuthTokenGetterImpl(std::move(oauth_credentials),
-                               context_->url_request_context_getter(), false));
+  oauth_token_getter_.reset(new OAuthTokenGetterImpl(
+      std::move(oauth_credentials), context_->url_loader_factory(), false));
   signaling_connector_.reset(new SignalingConnector(
       xmpp_signal_strategy, std::move(dns_blackhole_checker),
       oauth_token_getter_.get(),
diff --git a/remoting/host/setup/BUILD.gn b/remoting/host/setup/BUILD.gn
index 2f5da09..9e642c4 100644
--- a/remoting/host/setup/BUILD.gn
+++ b/remoting/host/setup/BUILD.gn
@@ -37,6 +37,8 @@
     "//remoting/base:authorization",
     "//remoting/host",
     "//remoting/host/native_messaging",
+    "//services/network/public/cpp",
+    "//services/network/public/mojom",
   ]
 
   if (is_mac || is_ios) {
diff --git a/remoting/host/setup/host_starter.cc b/remoting/host/setup/host_starter.cc
index 2b33db7..7790aead 100644
--- a/remoting/host/setup/host_starter.cc
+++ b/remoting/host/setup/host_starter.cc
@@ -17,6 +17,7 @@
 #include "google_apis/google_api_keys.h"
 #include "remoting/base/oauth_helper.h"
 #include "remoting/host/pin_hash.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace {
 const int kMaxGetTokensRetries = 3;
@@ -42,9 +43,10 @@
 
 std::unique_ptr<HostStarter> HostStarter::Create(
     const std::string& chromoting_hosts_url,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     net::URLRequestContextGetter* url_request_context_getter) {
   return base::WrapUnique(new HostStarter(
-      std::make_unique<gaia::GaiaOAuthClient>(url_request_context_getter),
+      std::make_unique<gaia::GaiaOAuthClient>(url_loader_factory),
       std::make_unique<remoting::ServiceClient>(chromoting_hosts_url,
                                                 url_request_context_getter),
       remoting::DaemonController::Create()));
diff --git a/remoting/host/setup/host_starter.h b/remoting/host/setup/host_starter.h
index 52eacfd..32eab3b 100644
--- a/remoting/host/setup/host_starter.h
+++ b/remoting/host/setup/host_starter.h
@@ -20,6 +20,10 @@
 class URLRequestContextGetter;
 }
 
+namespace network {
+class SharedURLLoaderFactory;
+}
+
 namespace remoting {
 
 // A helper class that registers and starts a host.
@@ -40,6 +44,7 @@
   // Creates a HostStarter.
   static std::unique_ptr<HostStarter> Create(
       const std::string& chromoting_hosts_url,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       net::URLRequestContextGetter* url_request_context_getter);
 
   // Registers a new host with the Chromoting service, and starts it.
diff --git a/remoting/host/setup/me2me_native_messaging_host_main.cc b/remoting/host/setup/me2me_native_messaging_host_main.cc
index fef7902..9194571 100644
--- a/remoting/host/setup/me2me_native_messaging_host_main.cc
+++ b/remoting/host/setup/me2me_native_messaging_host_main.cc
@@ -33,6 +33,8 @@
 #include "remoting/host/setup/me2me_native_messaging_host.h"
 #include "remoting/host/switches.h"
 #include "remoting/host/usage_stats_consent.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/transitional_url_loader_factory_owner.h"
 
 #if defined(OS_MACOSX)
 #include "base/mac/scoped_nsautorelease_pool.h"
@@ -181,8 +183,10 @@
   // OAuth client (for credential requests). IO thread is used for blocking
   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter(
       new URLRequestContextGetter(io_thread.task_runner()));
+  network::TransitionalURLLoaderFactoryOwner url_loader_factory_owner(
+      url_request_context_getter);
   std::unique_ptr<OAuthClient> oauth_client(
-      new GaiaOAuthClient(url_request_context_getter));
+      new GaiaOAuthClient(url_loader_factory_owner.GetURLLoaderFactory()));
 
   net::URLFetcher::SetIgnoreCertificateRequests(true);
 
diff --git a/remoting/host/setup/start_host_main.cc b/remoting/host/setup/start_host_main.cc
index 5190b4d..42e4111c 100644
--- a/remoting/host/setup/start_host_main.cc
+++ b/remoting/host/setup/start_host_main.cc
@@ -24,6 +24,8 @@
 #include "remoting/base/url_request_context_getter.h"
 #include "remoting/host/setup/host_starter.h"
 #include "remoting/host/setup/pin_validator.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/transitional_url_loader_factory_owner.h"
 
 #if defined(OS_POSIX)
 #include <termios.h>
@@ -221,12 +223,15 @@
 
   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter(
       new remoting::URLRequestContextGetter(io_thread.task_runner()));
+  network::TransitionalURLLoaderFactoryOwner url_loader_factory_owner(
+      url_request_context_getter);
 
   net::URLFetcher::SetIgnoreCertificateRequests(true);
 
   // Start the host.
   std::unique_ptr<HostStarter> host_starter(HostStarter::Create(
       remoting::ServiceUrls::GetInstance()->directory_hosts_url(),
+      url_loader_factory_owner.GetURLLoaderFactory(),
       url_request_context_getter.get()));
   if (redirect_url.empty()) {
     redirect_url = remoting::GetDefaultOauthRedirectUrl();
diff --git a/remoting/host/win/BUILD.gn b/remoting/host/win/BUILD.gn
index 72f12fc6..3e7054e4 100644
--- a/remoting/host/win/BUILD.gn
+++ b/remoting/host/win/BUILD.gn
@@ -407,6 +407,7 @@
     "//remoting/host/setup",
     "//remoting/protocol",
     "//sandbox/win:sandbox",  # Should always use Windows version
+    "//services/network/public/mojom",
     "//third_party/webrtc/modules/desktop_capture",
   ]
 
diff --git a/remoting/ios/DEPS b/remoting/ios/DEPS
index a513cf89..3b6691ff 100644
--- a/remoting/ios/DEPS
+++ b/remoting/ios/DEPS
@@ -14,4 +14,5 @@
   "+third_party/webrtc",
   "+third_party/ocmock",
   "+third_party/protobuf/src",
+  "+services/network/public/cpp",
 ]
diff --git a/remoting/ios/facade/remoting_oauth_authentication.mm b/remoting/ios/facade/remoting_oauth_authentication.mm
index 9549999..7f25698 100644
--- a/remoting/ios/facade/remoting_oauth_authentication.mm
+++ b/remoting/ios/facade/remoting_oauth_authentication.mm
@@ -25,6 +25,7 @@
 #include "net/url_request/url_request_context_getter.h"
 #include "remoting/base/oauth_token_getter.h"
 #include "remoting/base/oauth_token_getter_impl.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 static const char kOauthRedirectUrl[] =
     "https://chromoting-oauth.talkgadget."
@@ -52,7 +53,7 @@
   std::unique_ptr<remoting::OAuthTokenGetter> oauth_tokenGetter(
       new remoting::OAuthTokenGetterImpl(
           std::move(oauth_credentials), on_credentials_update,
-          RemotingService.instance.runtime->url_requester(),
+          RemotingService.instance.runtime->url_loader_factory(),
           /*auto_refresh=*/true));
   return oauth_tokenGetter;
 }
@@ -68,7 +69,7 @@
   std::unique_ptr<remoting::OAuthTokenGetter> oauth_tokenGetter(
       new remoting::OAuthTokenGetterImpl(
           std::move(oauth_credentials),
-          RemotingService.instance.runtime->url_requester(),
+          RemotingService.instance.runtime->url_loader_factory(),
           /*auto_refresh=*/true));
   return oauth_tokenGetter;
 }
diff --git a/remoting/test/BUILD.gn b/remoting/test/BUILD.gn
index fa1b4c3..66c7f18 100644
--- a/remoting/test/BUILD.gn
+++ b/remoting/test/BUILD.gn
@@ -243,6 +243,7 @@
     ":test_support",
     "//base",
     "//net:test_support",
+    "//services/network:test_support",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/libyuv",
diff --git a/remoting/test/DEPS b/remoting/test/DEPS
index 7a7eb3a..d6d217b 100644
--- a/remoting/test/DEPS
+++ b/remoting/test/DEPS
@@ -9,5 +9,6 @@
   "+remoting/signaling",
   "+ui/gfx",
   "+ui/events/keycodes/dom",
+  "+services/network",
   "+third_party/skia",
 ]
diff --git a/remoting/test/access_token_fetcher.cc b/remoting/test/access_token_fetcher.cc
index 34e10af..c8c214bc 100644
--- a/remoting/test/access_token_fetcher.cc
+++ b/remoting/test/access_token_fetcher.cc
@@ -15,6 +15,8 @@
 #include "google_apis/google_api_keys.h"
 #include "net/url_request/url_fetcher.h"
 #include "remoting/base/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/transitional_url_loader_factory_owner.h"
 
 namespace {
 const int kMaxGetTokensRetries = 3;
@@ -76,12 +78,27 @@
                              /*delegate=*/this);
 }
 
-void AccessTokenFetcher::CreateNewGaiaOAuthClientInstance() {
-  scoped_refptr<remoting::URLRequestContextGetter> request_context_getter;
-  request_context_getter = new remoting::URLRequestContextGetter(
-      base::ThreadTaskRunnerHandle::Get());
+void AccessTokenFetcher::SetURLLoaderFactoryForTesting(
+    scoped_refptr<network::SharedURLLoaderFactory>
+        url_loader_factory_for_testing) {
+  url_loader_factory_for_testing_ = url_loader_factory_for_testing;
+}
 
-  auth_client_.reset(new gaia::GaiaOAuthClient(request_context_getter.get()));
+void AccessTokenFetcher::CreateNewGaiaOAuthClientInstance() {
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
+  if (url_loader_factory_for_testing_) {
+    url_loader_factory = url_loader_factory_for_testing_;
+  } else {
+    scoped_refptr<remoting::URLRequestContextGetter> request_context_getter;
+    request_context_getter = new remoting::URLRequestContextGetter(
+        base::ThreadTaskRunnerHandle::Get());
+
+    url_loader_factory_owner_.reset(
+        new network::TransitionalURLLoaderFactoryOwner(request_context_getter));
+    url_loader_factory = url_loader_factory_owner_->GetURLLoaderFactory();
+  }
+
+  auth_client_.reset(new gaia::GaiaOAuthClient(url_loader_factory));
 }
 
 void AccessTokenFetcher::OnGetTokensResponse(const std::string& refresh_token,
diff --git a/remoting/test/access_token_fetcher.h b/remoting/test/access_token_fetcher.h
index 46d499c..da0c9e7 100644
--- a/remoting/test/access_token_fetcher.h
+++ b/remoting/test/access_token_fetcher.h
@@ -13,6 +13,11 @@
 #include "base/memory/ref_counted.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
 
+namespace network {
+class SharedURLLoaderFactory;
+class TransitionalURLLoaderFactoryOwner;
+};  // namespace network
+
 namespace remoting {
 namespace test {
 
@@ -41,6 +46,10 @@
       const std::string& refresh_token,
       const AccessTokenCallback& callback);
 
+  void SetURLLoaderFactoryForTesting(
+      scoped_refptr<network::SharedURLLoaderFactory>
+          url_loader_factory_for_testing);
+
  private:
   // gaia::GaiaOAuthClient::Delegate Interface.
   void OnGetTokensResponse(const std::string& refresh_token,
@@ -77,6 +86,13 @@
   // the Gaia service request.
   gaia::OAuthClientInfo oauth_client_info_;
 
+  // Used to feed network into |auth_client_|.
+  std::unique_ptr<network::TransitionalURLLoaderFactoryOwner>
+      url_loader_factory_owner_;
+
+  scoped_refptr<network::SharedURLLoaderFactory>
+      url_loader_factory_for_testing_;
+
   // Used to make token requests to GAIA.
   std::unique_ptr<gaia::GaiaOAuthClient> auth_client_;
 
diff --git a/remoting/test/access_token_fetcher_unittest.cc b/remoting/test/access_token_fetcher_unittest.cc
index 9a383f2..12d40288 100644
--- a/remoting/test/access_token_fetcher_unittest.cc
+++ b/remoting/test/access_token_fetcher_unittest.cc
@@ -8,11 +8,12 @@
 
 #include "base/bind.h"
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_loop_current.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "google_apis/gaia/gaia_urls.h"
-#include "net/url_request/test_url_fetcher_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -66,28 +67,33 @@
                               const std::string& refresh_token);
 
  protected:
-  // Test interface.
-  void SetUp() override;
-
   void SetFakeResponse(const GURL& url,
                        const std::string& data,
                        net::HttpStatusCode code,
-                       net::URLRequestStatus::Status status);
+                       int net_error);
+
+  scoped_refptr<network::SharedURLLoaderFactory> shared_factory() {
+    return shared_factory_;
+  }
 
   // Used for result verification
   std::string access_token_retrieved_;
   std::string refresh_token_retrieved_;
 
  private:
-  net::FakeURLFetcherFactory url_fetcher_factory_;
-  std::unique_ptr<base::MessageLoopForIO> message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> shared_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AccessTokenFetcherTest);
 };
 
 AccessTokenFetcherTest::AccessTokenFetcherTest()
-    : url_fetcher_factory_(nullptr) {
-}
+    : scoped_task_environment_(
+          base::test::ScopedTaskEnvironment::MainThreadType::IO),
+      shared_factory_(
+          base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+              &test_url_loader_factory_)) {}
 
 AccessTokenFetcherTest::~AccessTokenFetcherTest() = default;
 
@@ -101,30 +107,21 @@
   done_closure.Run();
 }
 
-void AccessTokenFetcherTest::SetUp() {
-  if (!base::MessageLoopCurrent::Get()) {
-    // Create a temporary message loop if the current thread does not already
-    // have one so we can use its task runner to create a request object.
-    message_loop_.reset(new base::MessageLoopForIO);
-  }
-}
-
-void AccessTokenFetcherTest::SetFakeResponse(
-    const GURL& url,
-    const std::string& data,
-    net::HttpStatusCode code,
-    net::URLRequestStatus::Status status) {
-  url_fetcher_factory_.SetFakeResponse(url, data, code, status);
+void AccessTokenFetcherTest::SetFakeResponse(const GURL& url,
+                                             const std::string& data,
+                                             net::HttpStatusCode code,
+                                             int net_error) {
+  test_url_loader_factory_.AddResponse(
+      url, network::CreateResourceResponseHead(code), data,
+      network::URLLoaderCompletionStatus(net_error));
 }
 
 TEST_F(AccessTokenFetcherTest, ExchangeAuthCodeForAccessToken) {
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
-                  kAuthCodeExchangeValidResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kAuthCodeExchangeValidResponse, net::HTTP_OK, net::OK);
 
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_info_url(),
-                  kValidTokenInfoResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kValidTokenInfoResponse, net::HTTP_OK, net::OK);
 
   base::RunLoop run_loop;
   AccessTokenCallback access_token_callback =
@@ -132,6 +129,7 @@
                  base::Unretained(this), run_loop.QuitClosure());
 
   AccessTokenFetcher access_token_fetcher;
+  access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory());
   access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue,
                                                   access_token_callback);
 
@@ -143,12 +141,10 @@
 
 TEST_F(AccessTokenFetcherTest, ExchangeRefreshTokenForAccessToken) {
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
-                  kRefreshTokenExchangeValidResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kRefreshTokenExchangeValidResponse, net::HTTP_OK, net::OK);
 
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_info_url(),
-                  kValidTokenInfoResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kValidTokenInfoResponse, net::HTTP_OK, net::OK);
 
   base::RunLoop run_loop;
   AccessTokenCallback access_token_callback =
@@ -156,6 +152,7 @@
                  base::Unretained(this), run_loop.QuitClosure());
 
   AccessTokenFetcher access_token_fetcher;
+  access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory());
   access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue,
                                                       access_token_callback);
 
@@ -167,12 +164,10 @@
 
 TEST_F(AccessTokenFetcherTest, MultipleAccessTokenCalls) {
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
-                  kAuthCodeExchangeValidResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kAuthCodeExchangeValidResponse, net::HTTP_OK, net::OK);
 
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_info_url(),
-                  kValidTokenInfoResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kValidTokenInfoResponse, net::HTTP_OK, net::OK);
 
   std::unique_ptr<base::RunLoop> run_loop;
   run_loop.reset(new base::RunLoop());
@@ -181,6 +176,7 @@
                  base::Unretained(this), run_loop->QuitClosure());
 
   AccessTokenFetcher access_token_fetcher;
+  access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory());
   access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue,
                                                   access_token_callback);
 
@@ -195,8 +191,7 @@
 
   // Update the response since we will call the refresh token method next.
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
-                  kRefreshTokenExchangeValidResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kRefreshTokenExchangeValidResponse, net::HTTP_OK, net::OK);
 
   run_loop.reset(new base::RunLoop());
   access_token_callback =
@@ -232,7 +227,7 @@
 TEST_F(AccessTokenFetcherTest, ExchangeAuthCode_Unauthorized_Error) {
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
                   kAuthCodeExchangeValidResponse, net::HTTP_UNAUTHORIZED,
-                  net::URLRequestStatus::FAILED);
+                  net::ERR_FAILED);
 
   base::RunLoop run_loop;
   AccessTokenCallback access_token_callback =
@@ -240,6 +235,7 @@
                  base::Unretained(this), run_loop.QuitClosure());
 
   AccessTokenFetcher access_token_fetcher;
+  access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory());
   access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue,
                                                   access_token_callback);
 
@@ -253,7 +249,7 @@
 TEST_F(AccessTokenFetcherTest, ExchangeRefreshToken_Unauthorized_Error) {
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
                   kRefreshTokenExchangeValidResponse, net::HTTP_UNAUTHORIZED,
-                  net::URLRequestStatus::FAILED);
+                  net::ERR_FAILED);
 
   base::RunLoop run_loop;
   AccessTokenCallback access_token_callback =
@@ -261,6 +257,7 @@
                  base::Unretained(this), run_loop.QuitClosure());
 
   AccessTokenFetcher access_token_fetcher;
+  access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory());
   access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue,
                                                       access_token_callback);
 
@@ -274,7 +271,7 @@
 TEST_F(AccessTokenFetcherTest, ExchangeAuthCode_NetworkError) {
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
                   kAuthCodeExchangeValidResponse, net::HTTP_NOT_FOUND,
-                  net::URLRequestStatus::FAILED);
+                  net::ERR_FAILED);
 
   base::RunLoop run_loop;
   AccessTokenCallback access_token_callback =
@@ -282,6 +279,7 @@
                  base::Unretained(this), run_loop.QuitClosure());
 
   AccessTokenFetcher access_token_fetcher;
+  access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory());
   access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue,
                                                   access_token_callback);
 
@@ -295,7 +293,7 @@
 TEST_F(AccessTokenFetcherTest, ExchangeRefreshToken_NetworkError) {
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
                   kRefreshTokenExchangeValidResponse, net::HTTP_NOT_FOUND,
-                  net::URLRequestStatus::FAILED);
+                  net::ERR_FAILED);
 
   base::RunLoop run_loop;
   AccessTokenCallback access_token_callback =
@@ -303,6 +301,7 @@
                  base::Unretained(this), run_loop.QuitClosure());
 
   AccessTokenFetcher access_token_fetcher;
+  access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory());
   access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue,
                                                       access_token_callback);
 
@@ -315,12 +314,10 @@
 
 TEST_F(AccessTokenFetcherTest, AuthCode_GetTokenInfoResponse_InvalidToken) {
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
-                  kAuthCodeExchangeValidResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kAuthCodeExchangeValidResponse, net::HTTP_OK, net::OK);
 
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_info_url(),
-                  kInvalidTokenInfoResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kInvalidTokenInfoResponse, net::HTTP_OK, net::OK);
 
   base::RunLoop run_loop;
   AccessTokenCallback access_token_callback =
@@ -328,6 +325,7 @@
                  base::Unretained(this), run_loop.QuitClosure());
 
   AccessTokenFetcher access_token_fetcher;
+  access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory());
   access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue,
                                                   access_token_callback);
 
@@ -340,8 +338,7 @@
 
 TEST_F(AccessTokenFetcherTest, ExchangeAuthCodeForAccessToken_EmptyToken) {
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
-                  kAuthCodeExchangeEmptyResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kAuthCodeExchangeEmptyResponse, net::HTTP_OK, net::OK);
 
   base::RunLoop run_loop;
   AccessTokenCallback access_token_callback =
@@ -349,6 +346,7 @@
                  base::Unretained(this), run_loop.QuitClosure());
 
   AccessTokenFetcher access_token_fetcher;
+  access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory());
   access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue,
                                                   access_token_callback);
 
@@ -361,12 +359,10 @@
 
 TEST_F(AccessTokenFetcherTest, RefreshToken_GetTokenInfoResponse_InvalidToken) {
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
-                  kRefreshTokenExchangeValidResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kRefreshTokenExchangeValidResponse, net::HTTP_OK, net::OK);
 
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_info_url(),
-                  kInvalidTokenInfoResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kInvalidTokenInfoResponse, net::HTTP_OK, net::OK);
 
   base::RunLoop run_loop;
   AccessTokenCallback access_token_callback =
@@ -374,6 +370,7 @@
                  base::Unretained(this), run_loop.QuitClosure());
 
   AccessTokenFetcher access_token_fetcher;
+  access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory());
   access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue,
                                                       access_token_callback);
 
@@ -386,8 +383,7 @@
 
 TEST_F(AccessTokenFetcherTest, ExchangeRefreshTokenForAccessToken_EmptyToken) {
   SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
-                  kRefreshTokenExchangeEmptyResponse, net::HTTP_OK,
-                  net::URLRequestStatus::SUCCESS);
+                  kRefreshTokenExchangeEmptyResponse, net::HTTP_OK, net::OK);
 
   base::RunLoop run_loop;
   AccessTokenCallback access_token_callback =
@@ -395,6 +391,7 @@
                  base::Unretained(this), run_loop.QuitClosure());
 
   AccessTokenFetcher access_token_fetcher;
+  access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory());
   access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue,
                                                       access_token_callback);
 
diff --git a/services/network/session_cleanup_cookie_store.cc b/services/network/session_cleanup_cookie_store.cc
index 7396b34..74939b56 100644
--- a/services/network/session_cleanup_cookie_store.cc
+++ b/services/network/session_cleanup_cookie_store.cc
@@ -14,19 +14,41 @@
 #include "base/memory/ref_counted.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
+#include "base/values.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_util.h"
 #include "net/extras/sqlite/cookie_crypto_delegate.h"
+#include "net/log/net_log.h"
 #include "url/gurl.h"
 
 namespace network {
 
+namespace {
+
+std::unique_ptr<base::Value> CookieStoreOriginFiltered(
+    const std::string& origin,
+    bool is_https,
+    net::NetLogCaptureMode capture_mode) {
+  if (!capture_mode.include_cookies_and_credentials())
+    return nullptr;
+  auto dict = std::make_unique<base::DictionaryValue>();
+  dict->SetString("origin", origin);
+  dict->SetBoolean("is_https", is_https);
+  return dict;
+}
+
+}  // namespace
+
 SessionCleanupCookieStore::SessionCleanupCookieStore(
     const scoped_refptr<net::SQLitePersistentCookieStore>& cookie_store)
     : persistent_store_(cookie_store) {}
 
-SessionCleanupCookieStore::~SessionCleanupCookieStore() {}
+SessionCleanupCookieStore::~SessionCleanupCookieStore() {
+  net_log_.AddEvent(
+      net::NetLogEventType::COOKIE_PERSISTENT_STORE_CLOSED,
+      net::NetLog::StringCallback("type", "SessionCleanupCookieStore"));
+}
 
 void SessionCleanupCookieStore::DeleteSessionCookies(
     DeleteCookiePredicate delete_cookie_predicate) {
@@ -46,6 +68,10 @@
         !delete_cookie_predicate.Run(cookie.first, cookie.second)) {
       continue;
     }
+    net_log_.AddEvent(
+        net::NetLogEventType::COOKIE_PERSISTENT_STORE_ORIGIN_FILTERED,
+        base::BindRepeating(&CookieStoreOriginFiltered, cookie.first,
+                            cookie.second));
     session_only_cookies.push_back(cookie);
   }
 
@@ -54,6 +80,7 @@
 
 void SessionCleanupCookieStore::Load(const LoadedCallback& loaded_callback,
                                      const net::NetLogWithSource& net_log) {
+  net_log_ = net_log;
   persistent_store_->Load(
       base::BindRepeating(&SessionCleanupCookieStore::OnLoad, this,
                           loaded_callback),
diff --git a/services/network/session_cleanup_cookie_store.h b/services/network/session_cleanup_cookie_store.h
index 6c8b9456..2443984 100644
--- a/services/network/session_cleanup_cookie_store.h
+++ b/services/network/session_cleanup_cookie_store.h
@@ -78,6 +78,8 @@
   // will be kept.
   bool force_keep_session_state_ = false;
 
+  net::NetLogWithSource net_log_;
+
   DISALLOW_COPY_AND_ASSIGN(SessionCleanupCookieStore);
 };
 
diff --git a/services/network/session_cleanup_cookie_store_unittest.cc b/services/network/session_cleanup_cookie_store_unittest.cc
index 2d2791e..1a9fe536 100644
--- a/services/network/session_cleanup_cookie_store_unittest.cc
+++ b/services/network/session_cleanup_cookie_store_unittest.cc
@@ -13,6 +13,9 @@
 #include "base/task/task_scheduler/task_scheduler.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/time/time.h"
+#include "net/log/net_log_capture_mode.h"
+#include "net/log/test_net_log.h"
+#include "net/log/test_net_log_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -42,7 +45,7 @@
     store_->Load(
         base::BindRepeating(&SessionCleanupCookieStoreTest::OnLoaded,
                             base::Unretained(this), &run_loop, &cookies),
-        net::NetLogWithSource());
+        net_log_.bound());
     run_loop.Run();
     return cookies;
   }
@@ -85,6 +88,7 @@
       base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()});
   base::ScopedTempDir temp_dir_;
   scoped_refptr<SessionCleanupCookieStore> store_;
+  net::BoundTestNetLog net_log_;
 };
 
 TEST_F(SessionCleanupCookieStoreTest, TestPersistence) {
@@ -127,6 +131,67 @@
   cookies.clear();
 }
 
+TEST_F(SessionCleanupCookieStoreTest, TestNetLogIncludeCookies) {
+  CanonicalCookieVector cookies = CreateAndLoad();
+  base::Time t = base::Time::Now();
+  AddCookie("A", "B", "nonpersistent.com", "/", t);
+
+  // Cookies from "nonpersistent.com" should be deleted.
+  store_->DeleteSessionCookies(
+      base::BindRepeating([](const std::string& domain, bool is_https) {
+        return domain == "nonpersistent.com";
+      }));
+  DestroyStore();
+
+  net::TestNetLogEntry::List entries;
+  net_log_.GetEntries(&entries);
+  size_t pos = net::ExpectLogContainsSomewhere(
+      entries, 0, net::NetLogEventType::COOKIE_PERSISTENT_STORE_ORIGIN_FILTERED,
+      net::NetLogEventPhase::NONE);
+  std::string cookie_origin;
+  bool cookie_is_https = true;
+  EXPECT_TRUE(entries[pos].GetStringValue("origin", &cookie_origin));
+  EXPECT_TRUE(entries[pos].GetBooleanValue("is_https", &cookie_is_https));
+  EXPECT_EQ("nonpersistent.com", cookie_origin);
+  EXPECT_EQ(false, cookie_is_https);
+  pos = net::ExpectLogContainsSomewhere(
+      entries, pos, net::NetLogEventType::COOKIE_PERSISTENT_STORE_CLOSED,
+      net::NetLogEventPhase::NONE);
+  std::string event_type;
+  EXPECT_TRUE(entries[pos].GetStringValue("type", &event_type));
+  EXPECT_EQ("SessionCleanupCookieStore", event_type);
+}
+
+TEST_F(SessionCleanupCookieStoreTest, TestNetLogDoNotIncludeCookies) {
+  CanonicalCookieVector cookies = CreateAndLoad();
+  base::Time t = base::Time::Now();
+  AddCookie("A", "B", "nonpersistent.com", "/", t);
+
+  net_log_.SetCaptureMode(net::NetLogCaptureMode::Default());
+  // Cookies from "nonpersistent.com" should be deleted.
+  store_->DeleteSessionCookies(
+      base::BindRepeating([](const std::string& domain, bool is_https) {
+        return domain == "nonpersistent.com";
+      }));
+  DestroyStore();
+
+  net::TestNetLogEntry::List entries;
+  net_log_.GetEntries(&entries);
+  size_t pos = net::ExpectLogContainsSomewhere(
+      entries, 0, net::NetLogEventType::COOKIE_PERSISTENT_STORE_ORIGIN_FILTERED,
+      net::NetLogEventPhase::NONE);
+  std::string cookie_origin;
+  bool cookie_is_https = true;
+  EXPECT_FALSE(entries[pos].GetStringValue("origin", &cookie_origin));
+  EXPECT_FALSE(entries[pos].GetBooleanValue("is_https", &cookie_is_https));
+  pos = net::ExpectLogContainsSomewhere(
+      entries, pos, net::NetLogEventType::COOKIE_PERSISTENT_STORE_CLOSED,
+      net::NetLogEventPhase::NONE);
+  std::string event_type;
+  EXPECT_TRUE(entries[pos].GetStringValue("type", &event_type));
+  EXPECT_EQ("SessionCleanupCookieStore", event_type);
+}
+
 TEST_F(SessionCleanupCookieStoreTest, TestDeleteSessionCookies) {
   CanonicalCookieVector cookies = CreateAndLoad();
   ASSERT_EQ(0u, cookies.size());
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index e9486db..c5142a8 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -11604,7 +11604,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -11648,7 +11647,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -11692,7 +11690,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -11736,7 +11733,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -11780,7 +11776,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -11824,7 +11819,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -11868,7 +11862,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -11912,7 +11905,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -11956,7 +11948,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12000,7 +11991,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12044,7 +12034,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12088,7 +12077,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12132,7 +12120,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12177,7 +12164,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12224,7 +12210,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12269,7 +12254,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12313,7 +12297,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12357,7 +12340,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12402,7 +12384,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12450,7 +12431,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12496,7 +12476,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12543,7 +12522,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12588,7 +12566,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12633,7 +12610,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12678,7 +12654,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12722,7 +12697,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12766,7 +12740,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12810,7 +12783,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12854,7 +12826,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12898,7 +12869,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12942,7 +12912,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -12986,7 +12955,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13030,7 +12998,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13074,7 +13041,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13118,7 +13084,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13162,7 +13127,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13206,7 +13170,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13250,7 +13213,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13294,7 +13256,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13338,7 +13299,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13382,7 +13342,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13426,7 +13385,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13470,7 +13428,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13514,7 +13471,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13558,7 +13514,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13602,7 +13557,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13647,7 +13601,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13691,7 +13644,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13735,7 +13687,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13779,7 +13730,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13823,7 +13773,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13867,7 +13816,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13911,7 +13859,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13955,7 +13902,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -13999,7 +13945,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -14043,7 +13988,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -14088,7 +14032,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -14132,7 +14075,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -14176,7 +14118,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -14220,7 +14161,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -14309,7 +14249,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -14354,7 +14293,6 @@
               "os": "Android"
             }
           ],
-          "hard_timeout": 960,
           "output_links": [
             {
               "link": [
@@ -17519,6 +17457,857 @@
       }
     ]
   },
+  "android-cronet-arm-dbg": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_sample_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_sample_test_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_smoketests_missing_native_library_instrumentation_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_smoketests_missing_native_library_instrumentation_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_smoketests_platform_only_instrumentation_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_smoketests_platform_only_instrumentation_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_test_instrumentation_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_test_instrumentation_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_unittests_android"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_unittests_android"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "net_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "net_unittests"
+      }
+    ]
+  },
+  "android-cronet-arm-rel": {},
+  "android-cronet-arm64-dbg": {
+    "additional_compile_targets": [
+      "cronet_package",
+      "cronet_perf_test_apk",
+      "cronet_sample_test_apk",
+      "cronet_smoketests_missing_native_library_instrumentation_apk",
+      "cronet_smoketests_platform_only_instrumentation_apk",
+      "cronet_test_instrumentation_apk",
+      "cronet_unittests_android",
+      "net_unittests"
+    ]
+  },
+  "android-cronet-arm64-rel": {
+    "additional_compile_targets": [
+      "cronet_package",
+      "cronet_perf_test_apk",
+      "cronet_sample_test_apk",
+      "cronet_smoketests_missing_native_library_instrumentation_apk",
+      "cronet_smoketests_platform_only_instrumentation_apk",
+      "cronet_test_instrumentation_apk",
+      "cronet_unittests_android",
+      "net_unittests"
+    ]
+  },
+  "android-cronet-asan-arm-rel": {
+    "additional_compile_targets": [
+      "cronet_package",
+      "cronet_perf_test_apk",
+      "cronet_sample_test_apk",
+      "cronet_smoketests_missing_native_library_instrumentation_apk",
+      "cronet_smoketests_platform_only_instrumentation_apk",
+      "cronet_test_instrumentation_apk",
+      "cronet_unittests_android",
+      "net_unittests"
+    ]
+  },
+  "android-cronet-kitkat-arm-rel": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_sample_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_sample_test_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_smoketests_missing_native_library_instrumentation_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_smoketests_missing_native_library_instrumentation_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_smoketests_platform_only_instrumentation_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_smoketests_platform_only_instrumentation_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_test_instrumentation_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_test_instrumentation_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_unittests_android"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_unittests_android"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "net_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "net_unittests"
+      }
+    ]
+  },
+  "android-cronet-lollipop-arm-rel": {},
+  "android-cronet-marshmallow-arm64-rel": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_sample_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_sample_test_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_smoketests_missing_native_library_instrumentation_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_smoketests_missing_native_library_instrumentation_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_smoketests_platform_only_instrumentation_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_smoketests_platform_only_instrumentation_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_test_instrumentation_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_test_instrumentation_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cronet_unittests_android"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cronet_unittests_android"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "net_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "net_unittests"
+      }
+    ]
+  },
+  "android-cronet-x86-dbg": {
+    "additional_compile_targets": [
+      "cronet_package",
+      "cronet_perf_test_apk",
+      "cronet_sample_test_apk",
+      "cronet_smoketests_missing_native_library_instrumentation_apk",
+      "cronet_smoketests_platform_only_instrumentation_apk",
+      "cronet_test_instrumentation_apk",
+      "cronet_unittests_android",
+      "net_unittests"
+    ]
+  },
+  "android-cronet-x86-rel": {
+    "additional_compile_targets": [
+      "cronet_package",
+      "cronet_perf_test_apk",
+      "cronet_sample_test_apk",
+      "cronet_smoketests_missing_native_library_instrumentation_apk",
+      "cronet_smoketests_platform_only_instrumentation_apk",
+      "cronet_test_instrumentation_apk",
+      "cronet_unittests_android",
+      "net_unittests"
+    ]
+  },
   "android-kitkat-arm-rel": {
     "additional_compile_targets": [
       "cronet_test_instrumentation_apk",
diff --git a/testing/buildbot/filters/mash.browser_tests.filter b/testing/buildbot/filters/mash.browser_tests.filter
index 5199c5b2..d31a67dc 100644
--- a/testing/buildbot/filters/mash.browser_tests.filter
+++ b/testing/buildbot/filters/mash.browser_tests.filter
@@ -41,10 +41,10 @@
 -BrowserNonClientFrameViewAshTest.TopViewInset/*
 
 # Direct access to ash window frames, tablet mode, overview mode, etc.
--HostedAppNonClientFrameViewAshTest.*
 -NonHomeLauncherBrowserNonClientFrameViewAshTest.*
 
 # Fix immersive fullscreen mode in mash. https://crbug.com/844748.
+# Needs EventGenerator to work across window tree hosts. crbug.com/814675
 -ImmersiveModeBrowserViewTest.TestCaptionButtonsReceiveEventsInAppImmersiveMode*
 -ImmersiveModeBrowserViewTest.TestCaptionButtonsReceiveEventsInBrowserImmersiveMode*
 
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index 1dc1018..d5022c3e 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -83,6 +83,7 @@
 
 # https://crbug.com/816684 Track Page Load Metrics.
 -PageLoadMetricsBrowserTest.LoadingMetricsFailed
+-PageLoadMetricsBrowserTest.ReceivedCompleteResources
 
 # https://crbug.com/810329 DnsProbe browsertests that rely on delaying requests:
 -DnsProbeBrowserTest.NxdomainProbeResultWithWorkingSlowCorrections
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index e7fb72d..fcedc44 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -448,6 +448,10 @@
     "label": "//components/cronet/android:cronet_package",
     "type": "additional_compile_target",
   },
+  "cronet_perf_test_apk": {
+    "label": "//components/cronet/android:cronet_perf_test_apk",
+    "type": "additional_compile_target",
+  },
   "cronet_sample_test_apk": {
     "label": "//components/cronet/android:cronet_sample_test_apk",
     "type": "console_test_launcher",
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 8d57650..1693da1 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -173,6 +173,115 @@
         'use_swarming': False,
         'os_type': 'android',
       },
+      'android-cronet-arm-dbg': {
+        'test_suites': {
+          'gtest_tests': 'cronet_gtests',
+        },
+        'swarming': {
+          'dimension_sets': [
+            {
+              'device_os': 'KTU84P',
+              'device_type': 'hammerhead',
+              'os': 'Android',
+            },
+          ],
+        },
+        'os_type': 'android',
+      },
+      'android-cronet-arm-rel': {},
+      'android-cronet-arm64-dbg': {
+        'additional_compile_targets': [
+          'cronet_package',
+          'cronet_perf_test_apk',
+          'cronet_sample_test_apk',
+          'cronet_smoketests_missing_native_library_instrumentation_apk',
+          'cronet_smoketests_platform_only_instrumentation_apk',
+          'cronet_test_instrumentation_apk',
+          'cronet_unittests_android',
+          'net_unittests',
+        ],
+      },
+      'android-cronet-arm64-rel': {
+        'additional_compile_targets': [
+          'cronet_package',
+          'cronet_perf_test_apk',
+          'cronet_sample_test_apk',
+          'cronet_smoketests_missing_native_library_instrumentation_apk',
+          'cronet_smoketests_platform_only_instrumentation_apk',
+          'cronet_test_instrumentation_apk',
+          'cronet_unittests_android',
+          'net_unittests',
+        ],
+      },
+      'android-cronet-asan-arm-rel': {
+        # TODO(jbudorick): Run tests on this on swarming after
+        # implementing a reasonable mechanism for doing so w/ asan.
+        'additional_compile_targets': [
+          'cronet_package',
+          'cronet_perf_test_apk',
+          'cronet_sample_test_apk',
+          'cronet_smoketests_missing_native_library_instrumentation_apk',
+          'cronet_smoketests_platform_only_instrumentation_apk',
+          'cronet_test_instrumentation_apk',
+          'cronet_unittests_android',
+          'net_unittests',
+        ],
+      },
+      'android-cronet-kitkat-arm-rel': {
+        'test_suites': {
+          'gtest_tests': 'cronet_gtests',
+        },
+        'swarming': {
+          'dimension_sets': [
+            {
+              'device_os': 'KTU84P',
+              'device_type': 'hammerhead',
+              'os': 'Android',
+            },
+          ],
+        },
+        'os_type': 'android',
+      },
+      'android-cronet-lollipop-arm-rel': {},
+      'android-cronet-marshmallow-arm64-rel': {
+        'test_suites': {
+          'gtest_tests': 'cronet_gtests',
+        },
+        'swarming': {
+          'dimension_sets': [
+            {
+              'device_os': 'MMB29Q',
+              'device_type': 'bullhead',
+              'os': 'Android',
+            },
+          ],
+        },
+        'os_type': 'android',
+      },
+      'android-cronet-x86-dbg': {
+        'additional_compile_targets': [
+          'cronet_package',
+          'cronet_perf_test_apk',
+          'cronet_sample_test_apk',
+          'cronet_smoketests_missing_native_library_instrumentation_apk',
+          'cronet_smoketests_platform_only_instrumentation_apk',
+          'cronet_test_instrumentation_apk',
+          'cronet_unittests_android',
+          'net_unittests',
+        ],
+      },
+      'android-cronet-x86-rel': {
+        'additional_compile_targets': [
+          'cronet_package',
+          'cronet_perf_test_apk',
+          'cronet_sample_test_apk',
+          'cronet_smoketests_missing_native_library_instrumentation_apk',
+          'cronet_smoketests_platform_only_instrumentation_apk',
+          'cronet_test_instrumentation_apk',
+          'cronet_unittests_android',
+          'net_unittests',
+        ],
+      },
       'android-kitkat-arm-rel': {
         'additional_compile_targets': [
           'cronet_test_instrumentation_apk',
@@ -293,7 +402,6 @@
               'os': 'Android',
             },
           ],
-          'hard_timeout': 960,
         },
         'os_type': 'android',
       },
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index c3d9e7d..f71972b 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -56,9 +56,137 @@
 crbug.com/591099 external/wpt/css/css-writing-modes/two-levels-of-orthogonal-flows-percentage.html [ Pass ]
 crbug.com/855279 fast/css/text-overflow-ellipsis-vertical-hittest.html [ Pass ]
 crbug.com/591099 fast/dom/inner-text-first-letter.html [ Pass ]
-crbug.com/591099 fast/dom/nodesFromRect/nodesFromRect-basic.html [ Pass ]
+crbug.com/591099 fast/dom/nodesFromRect/nodesFromRect-basic.html [ Failure Pass ]
 
 # New failures are appended below by the script.
+crbug.com/874588 accessibility/adjacent-continuations-cause-assertion-failure.html [ Crash ]
+crbug.com/874588 accessibility/anonymous-render-block-in-continuation-causes-crash.html [ Crash ]
+crbug.com/874588 accessibility/aom-click-action.html [ Crash ]
+crbug.com/874588 accessibility/aom-computed-accessible-node.html [ Crash ]
+crbug.com/874588 accessibility/aom-computed-boolean-properties.html [ Crash ]
+crbug.com/874588 accessibility/aom-computed-int-properties.html [ Crash ]
+crbug.com/874588 accessibility/aom-computed-relation-accessors.html [ Crash ]
+crbug.com/874588 accessibility/aom-computed-string-properties.html [ Crash ]
+crbug.com/874588 accessibility/aom-relation-list-properties.html [ Crash ]
+crbug.com/874588 accessibility/aom-relation-properties.html [ Crash ]
+crbug.com/874588 accessibility/aom-string-properties.html [ Crash ]
+crbug.com/874588 accessibility/aom.html [ Crash ]
+crbug.com/874588 accessibility/appearance-affects-role.html [ Crash ]
+crbug.com/874588 accessibility/aria-checkbox-checked-mixed.html [ Crash ]
+crbug.com/874588 accessibility/aria-combo-box-with-delay-add.html [ Crash ]
+crbug.com/874588 accessibility/aria-combo-box-with-delay.html [ Crash ]
+crbug.com/874588 accessibility/aria-combo-box.html [ Crash ]
+crbug.com/874588 accessibility/aria-controls.html [ Crash ]
+crbug.com/874588 accessibility/aria-disabled.html [ Crash ]
+crbug.com/874588 accessibility/aria-flowto.html [ Crash ]
+crbug.com/874588 accessibility/aria-grid-readonly-propagation.html [ Crash ]
+crbug.com/874588 accessibility/aria-hidden-children-not-in-text-from-content.html [ Crash ]
+crbug.com/874588 accessibility/aria-hidden-hides-all-elements.html [ Crash ]
+crbug.com/874588 accessibility/aria-hidden-update.html [ Crash ]
+crbug.com/874588 accessibility/aria-hidden-updates-alldescendants.html [ Crash ]
+crbug.com/874588 accessibility/aria-hidden-with-elements.html [ Crash ]
+crbug.com/874588 accessibility/aria-hidden.html [ Crash ]
+crbug.com/874588 accessibility/aria-label.html [ Crash ]
+crbug.com/874588 accessibility/aria-labelledby-overrides-label.html [ Crash ]
+crbug.com/874588 accessibility/aria-modal.html [ Crash ]
+crbug.com/874588 accessibility/aria-option-role.html [ Crash ]
+crbug.com/874588 accessibility/aria-orientation.html [ Crash ]
+crbug.com/874588 accessibility/aria-owns-dynamic-changes.html [ Crash ]
+crbug.com/874588 accessibility/aria-owns-ignores-leafs.html [ Crash ]
+crbug.com/874588 accessibility/aria-owns-sends-notification.html [ Crash ]
+crbug.com/874588 accessibility/aria-owns.html [ Crash ]
+crbug.com/874588 accessibility/aria-relations-should-ignore-hidden-targets.html [ Crash ]
+crbug.com/874588 accessibility/aria-roles.html [ Crash ]
+crbug.com/874588 accessibility/aria-row-name.html [ Crash ]
+crbug.com/874588 accessibility/aria-tab-roles.html [ Crash ]
+crbug.com/874588 accessibility/aria-tables.html [ Crash ]
+crbug.com/874588 accessibility/aria-tree.html [ Crash ]
+crbug.com/874588 accessibility/aria-treeitem-checkable.html [ Crash ]
+crbug.com/874588 accessibility/aria1.1-combo-box-with-delay.html [ Crash ]
+crbug.com/874588 accessibility/aria1.1-combo-box.html [ Crash ]
+crbug.com/874588 accessibility/bounds-calc.html [ Crash ]
+crbug.com/874588 accessibility/br-element-has-correct-title.html [ Crash ]
+crbug.com/874588 accessibility/canvas-fallback-content-labels.html [ Crash ]
+crbug.com/874588 accessibility/container-node-delete-causes-crash.html [ Crash ]
+crbug.com/874588 accessibility/css-first-letter-children.html [ Crash ]
+crbug.com/874588 accessibility/css-generated-content.html [ Crash ]
+crbug.com/874588 accessibility/css-styles.html [ Crash ]
+crbug.com/874588 accessibility/default-language.html [ Crash ]
+crbug.com/874588 accessibility/description-calc-aria-describedby.html [ Crash ]
+crbug.com/874588 accessibility/description-calc-inputs.html [ Crash ]
+crbug.com/874588 accessibility/description-calc-summary.html [ Crash ]
+crbug.com/874588 accessibility/description-calc-table-caption.html [ Crash ]
+crbug.com/874588 accessibility/disabled-controls.html [ Crash ]
+crbug.com/874588 accessibility/disabled-not-selectable.html [ Crash ]
+crbug.com/874588 accessibility/dl-role.html [ Crash ]
+crbug.com/874588 accessibility/element-role-mapping-focusable.html [ Crash ]
+crbug.com/874588 accessibility/first-letter-text-transform-causes-crash.html [ Crash ]
+crbug.com/874588 accessibility/focus-action-clicks-element-in-active-descendant.html [ Crash ]
+crbug.com/874588 accessibility/idref-newlines.html [ Crash ]
+crbug.com/874588 accessibility/image-map-title-causes-crash.html [ Crash ]
+crbug.com/874588 accessibility/image-map-update-parent-crash.html [ Crash ]
+crbug.com/874588 accessibility/img-fallsback-to-title.html [ Crash ]
+crbug.com/874588 accessibility/in-page-link-target.html [ Crash ]
+crbug.com/874588 accessibility/inline-text-bidi-bounds-for-range.html [ Crash ]
+crbug.com/874588 accessibility/inline-text-bounds-for-range-br.html [ Crash ]
+crbug.com/874588 accessibility/inline-text-change-style.html [ Crash ]
+crbug.com/874588 accessibility/inline-text-changes.html [ Crash ]
+crbug.com/874588 accessibility/inline-text-word-boundaries.html [ Crash ]
+crbug.com/874588 accessibility/inline-text-word-boundary-causes-crash.html [ Crash ]
+crbug.com/874588 accessibility/input-aria-required.html [ Crash ]
+crbug.com/874588 accessibility/input-mixed.html [ Crash ]
+crbug.com/874588 accessibility/input-type-range-aria-value.html [ Crash ]
+crbug.com/874588 accessibility/input-type-range-value-change.html [ Crash ]
+crbug.com/874588 accessibility/input-type-text-caret-position.html [ Crash ]
+crbug.com/874588 accessibility/insert-adjacent-html-causes-crash.xhtml [ Crash ]
+crbug.com/874588 accessibility/insert-selected-option-into-select-causes-crash.html [ Crash ]
+crbug.com/874588 accessibility/label-for-control-hittest.html [ Crash ]
+crbug.com/874588 accessibility/language-meta-tag-dynamically-changing.html [ Crash ]
+crbug.com/874588 accessibility/link-inside-button-accessible-text.html [ Crash ]
+crbug.com/874588 accessibility/menu-item-crash.html [ Crash ]
+crbug.com/874588 accessibility/menu-list-popup-reuses-objects.html [ Crash ]
+crbug.com/874588 accessibility/menu-list-selection-changed.html [ Crash ]
+crbug.com/874588 accessibility/meter-value.html [ Crash ]
+crbug.com/874588 accessibility/name-calc-aria-hidden.html [ Crash ]
+crbug.com/874588 accessibility/name-calc-aria-label.html [ Crash ]
+crbug.com/874588 accessibility/name-calc-aria-labelledby.html [ Crash ]
+crbug.com/874588 accessibility/name-calc-figure.html [ Crash ]
+crbug.com/874588 accessibility/name-calc-group-inside-treeitem.html [ Crash ]
+crbug.com/874588 accessibility/name-calc-inputs.html [ Crash ]
+crbug.com/874588 accessibility/name-calc-native-markup-buttons.html [ Crash ]
+crbug.com/874588 accessibility/name-calc-native-markup-input-buttons.html [ Crash ]
+crbug.com/874588 accessibility/name-calc-summary.html [ Crash ]
+crbug.com/874588 accessibility/name-calc-visibility.html [ Crash ]
+crbug.com/874588 accessibility/nochildren-elements.html [ Crash ]
+crbug.com/874588 accessibility/other-aria-attribute-change-sends-notification.html [ Crash ]
+crbug.com/874588 accessibility/presentation-owned-elements.html [ Crash ]
+crbug.com/874588 accessibility/presentational-leaf.html [ Crash ]
+crbug.com/874588 accessibility/press-works-on-text-fields.html [ Crash ]
+crbug.com/874588 accessibility/removed-anonymous-block-child-causes-crash.html [ Crash ]
+crbug.com/874588 accessibility/render-counter-text.html [ Crash ]
+crbug.com/874588 accessibility/role-attribute.html [ Crash ]
+crbug.com/874588 accessibility/role-change.html [ Crash ]
+crbug.com/874588 accessibility/selection-change-notification-aria-textbox.html [ Crash ]
+crbug.com/874588 accessibility/selection-change-notification-input.html [ Crash ]
+crbug.com/874588 accessibility/selection-change-notification-statictext.html [ Crash ]
+crbug.com/874588 accessibility/selection-change-notification-textarea.html [ Crash ]
+crbug.com/874588 accessibility/selection-follows-focus.html [ Crash ]
+crbug.com/874588 accessibility/set-selection-child-offset.html [ Crash ]
+crbug.com/874588 accessibility/table-caption.html [ Crash ]
+crbug.com/874588 accessibility/table-cells-with-colspan.html [ Crash ]
+crbug.com/874588 accessibility/table-destroyed-crash.html [ Crash ]
+crbug.com/874588 accessibility/table-header-column-row.html [ Crash ]
+crbug.com/874588 accessibility/table-headers.html [ Crash ]
+crbug.com/874588 accessibility/table-row-with-aria-role.html [ Crash ]
+crbug.com/874588 accessibility/table-with-empty-thead-causes-crash.html [ Crash ]
+crbug.com/874588 accessibility/table-with-grid-roles.html [ Crash ]
+crbug.com/874588 accessibility/table-with-hidden-head-section.html [ Crash ]
+crbug.com/874588 accessibility/text-change-notification.html [ Crash ]
+crbug.com/874588 accessibility/text-changes-with-relations.html [ Crash ]
+crbug.com/874588 accessibility/textarea-caret-position.html [ Crash ]
+crbug.com/874588 accessibility/textarea-selection.html [ Crash ]
+crbug.com/874588 accessibility/title-ui-element-correctness.html [ Crash ]
+crbug.com/874588 accessibility/whitespace-in-name-calc.html [ Crash ]
 crbug.com/591099 animations/rotate-transform-equivalent.html [ Failure ]
 crbug.com/728378 compositing/culling/tile-occlusion-boundaries.html [ Failure ]
 crbug.com/591099 compositing/iframes/floating-self-painting-frame.html [ Failure ]
@@ -264,7 +392,7 @@
 crbug.com/591099 external/wpt/html/browsers/the-window-object/window-open-noopener.html?_parent [ Timeout ]
 crbug.com/591099 external/wpt/html/browsers/the-window-object/window-open-noopener.html?_self [ Timeout ]
 crbug.com/591099 external/wpt/html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-positive.html [ Timeout ]
-crbug.com/591099 external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-success.sub.html [ Failure ]
+crbug.com/591099 external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-success.sub.html [ Failure Pass ]
 crbug.com/591099 external/wpt/html/rendering/non-replaced-elements/the-hr-element-0/color.html [ Failure ]
 crbug.com/591099 external/wpt/html/rendering/non-replaced-elements/the-page/body-margin-2j.html [ Failure ]
 crbug.com/591099 external/wpt/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-auto.html [ Failure ]
@@ -276,7 +404,7 @@
 crbug.com/591099 external/wpt/html/semantics/scripting-1/the-script-element/async_010.htm [ Pass ]
 crbug.com/591099 external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html [ Failure ]
 crbug.com/591099 external/wpt/html/user-activation/activation-api-iframe.tenative.html [ Failure ]
-crbug.com/591099 external/wpt/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/canblock-window.html [ Failure ]
+crbug.com/591099 external/wpt/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/canblock-window.html [ Failure Pass ]
 crbug.com/591099 external/wpt/infrastructure/reftest/reftest_ref_timeout.html [ Timeout ]
 crbug.com/591099 external/wpt/media-source/mediasource-avtracks.html [ Failure ]
 crbug.com/591099 external/wpt/media-source/mediasource-config-change-mp4-av-audio-bitrate.html [ Failure ]
@@ -292,7 +420,6 @@
 crbug.com/591099 external/wpt/requestidlecallback/callback-timeout.html [ Timeout ]
 crbug.com/591099 external/wpt/service-workers/service-worker/navigation-preload/broken-chunked-encoding.https.html [ Failure ]
 crbug.com/591099 external/wpt/speech-api/SpeechSynthesis-speak-twice.html [ Timeout ]
-crbug.com/591099 external/wpt/svg/painting/reftests/markers-orient-001.svg [ Pass ]
 crbug.com/591099 external/wpt/svg/painting/reftests/paint-context-001.svg [ Failure ]
 crbug.com/591099 external/wpt/svg/painting/reftests/paint-context-002.svg [ Failure ]
 crbug.com/591099 external/wpt/svg/path/bearing/zero.svg [ Failure ]
@@ -407,7 +534,7 @@
 crbug.com/591099 fast/table/dynamic-descendant-percentage-height.html [ Failure ]
 crbug.com/591099 fast/table/empty-table-percent-height.html [ Failure ]
 crbug.com/591099 fast/table/height-percent-test-vertical.html [ Failure ]
-crbug.com/591099 fast/table/percent-height-overflow-auto-content-in-cell.html [ Failure Pass ]
+crbug.com/591099 fast/table/percent-height-overflow-auto-content-in-cell.html [ Failure ]
 crbug.com/591099 fast/table/percent-height-overflow-scroll-content-in-cell.html [ Failure ]
 crbug.com/858998 fast/table/table-continuation-outline-paint-crash.html [ Failure ]
 crbug.com/591099 fast/table/table-display-types-vertical.html [ Failure ]
@@ -447,6 +574,10 @@
 crbug.com/591099 http/tests/images/restyle-decode-error.html [ Failure ]
 crbug.com/591099 http/tests/local/fileapi/select-dragged-file-input.html [ Skip ]
 crbug.com/591099 http/tests/misc/object-embedding-svg-delayed-size-negotiation.xhtml [ Failure ]
+crbug.com/591099 http/tests/navigation/form-targets-cross-site-frame-get.html [ Failure ]
+crbug.com/591099 http/tests/navigation/form-targets-cross-site-frame-no-referrer.html [ Failure ]
+crbug.com/591099 http/tests/navigation/form-targets-cross-site-frame-post.html [ Failure ]
+crbug.com/591099 http/tests/navigation/form-with-enctype-targets-cross-site-frame.html [ Failure ]
 crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-appcache.https.html [ Crash Failure ]
 crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.https.html [ Crash Failure Pass ]
 crbug.com/591099 http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ]
@@ -455,7 +586,7 @@
 crbug.com/591099 images/color-profile-image-filter-all.html [ Failure ]
 crbug.com/714962 inspector-protocol/css/css-get-platform-fonts.js [ Failure ]
 crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-pseudo-element.js [ Failure ]
-crbug.com/714962 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-viewport.js [ Failure ]
+crbug.com/714962 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-viewport.js [ Failure Pass ]
 crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot.js [ Failure ]
 crbug.com/714962 inspector-protocol/layout-fonts/languages-emoji-rare-glyphs.js [ Failure ]
 crbug.com/591099 inspector-protocol/timeline/page-frames.js [ Failure Pass ]
@@ -521,7 +652,7 @@
 crbug.com/591099 printing/iframe-svg-in-object-print.html [ Failure ]
 crbug.com/591099 scrollbars/auto-scrollbar-fit-content.html [ Failure ]
 crbug.com/591099 scrollbars/overflow-scrollbar-combinations.html [ Failure ]
-crbug.com/591099 shadow-dom/imperative-api.html [ Pass ]
+crbug.com/591099 shadow-dom/imperative-api.html [ Failure Pass ]
 crbug.com/591099 storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html [ Pass ]
 crbug.com/591099 storage/indexeddb/objectstore-cursor.html [ Pass ]
 crbug.com/591099 svg/custom/object-sizing-no-width-height.xhtml [ Failure ]
@@ -550,7 +681,7 @@
 crbug.com/591099 virtual/android/ [ Skip ]
 crbug.com/591099 virtual/exotic-color-space/ [ Skip ]
 crbug.com/591099 virtual/feature-policy-vibrate/ [ Skip ]
-crbug.com/591099 virtual/gpu-rasterization/images/color-profile-image-filter-all.html [ Failure ]
+crbug.com/591099 virtual/gpu-rasterization/images/color-profile-image-filter-all.html [ Failure Timeout ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blending-color-over-image.html [ Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blending-gradient-over-pattern.html [ Pass Timeout ]
 crbug.com/591099 virtual/intersection-observer-v2/http/tests/intersection-observer/v2/cross-origin-effects.html [ Failure ]
@@ -570,7 +701,7 @@
 crbug.com/591099 virtual/paint-touchaction-rects/fast/events/touch/touch-rect-assert-first-layer-special.html [ Failure ]
 crbug.com/591099 virtual/paint-touchaction-rects/fast/events/touch/touch-rect-crash-on-unpromote-layer.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/ [ Skip ]
-crbug.com/591099 virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-doesnt-send-reports-without-violation.https.sub.html [ Pass Timeout ]
+crbug.com/591099 virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-doesnt-send-reports-without-violation.https.sub.html [ Pass ]
 crbug.com/591099 virtual/scroll_customization/ [ Skip ]
 crbug.com/591099 virtual/scroll_customization/fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
 crbug.com/591099 virtual/sharedarraybuffer/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-simple-success.html [ Pass ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
index 967c5da0..1925422 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
@@ -357,7 +357,6 @@
 crbug.com/854192 paint/pagination/composited-paginated-outlined-box.html [ Failure ]
 
 # Paint invalidation test failures. Needs investigation.
-crbug.com/857322 paint/invalidation/background/obscured-background-no-repaint.html [ Failure Crash ]
 crbug.com/854196 paint/invalidation/compositing/clipping-should-not-repaint-composited-descendants.html [ Failure ]
 crbug.com/854196 paint/invalidation/compositing/overlap-test-with-filter.html [ Failure ]
 crbug.com/854196 paint/invalidation/compositing/repaint-overflow-scrolled-squashed-content.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index 748065b..c31e6771 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -1396,3 +1396,5 @@
 
 Bug(none) fast/events/touch/compositor-touch-hit-rects-svg.html [ Failure ]
 Bug(none) virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-svg.html [ Failure ]
+Bug(none) fast/events/touch/compositor-touch-hit-rects-continuation.html [ Failure ]
+Bug(none) virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-continuation.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 6255b1b..315f82a 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -3925,6 +3925,9 @@
 crbug.com/874567 [ Mac ] svg/custom/getscreenctm-in-scrollable-div-area-nested.xhtml [ Pass Failure ]
 crbug.com/874837 [ Win ] ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling.htm [ Pass Failure ]
 crbug.com/874866 [ Mac ] virtual/new-remote-playback-pipeline/media/controls/modern/doubletap-to-jump-backwards-at-start.html [ Pass Timeout ]
+crbug.com/874931 virtual/threaded/fast/scroll-behavior/smooth-scroll/mousewheel-scroll.html [ Pass Failure ]
+crbug.com/875003 [ Win ] editing/caret/caret-is-hidden-when-no-focus.html [ Pass Failure ]
+crbug.com/875009 virtual/enable_wasm_streaming/http/tests/wasm_streaming/wasm_response_apis.html [ Crash Pass ]
 
 crbug.com/715718 external/wpt/media-source/mediasource-activesourcebuffers.html [ Failure Pass ]
 crbug.com/715718 external/wpt/media-source/mediasource-remove.html [ Failure Pass ]
@@ -4895,11 +4898,6 @@
 crbug.com/871578 [ Mac ] virtual/outofblink-cors/external/wpt/xhr/timeout-multiple-fetches.html [ Failure Pass ]
 crbug.com/871578 [ Mac ] virtual/outofblink-cors-ns/external/wpt/xhr/timeout-multiple-fetches.html [ Failure Pass ]
 
-# Flaky on various bots
-crbug.com/869470 external/wpt/background-fetch/fetch.https.window.html [ Pass Failure ]
-crbug.com/869470 external/wpt/background-fetch/get.https.window.html [ Pass Failure ]
-crbug.com/869470 external/wpt/background-fetch/get-ids.https.window.html [ Pass Failure Crash ]
-
 # Flaky middleClinkAutoscroll increased flakiness on Mac
 crbug.com/851090 [ Mac ] fast/events/middleClickAutoscroll-click-hyperlink.html [ Failure Pass ]
 crbug.com/851090 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-nested-divs.html [ Failure Pass Timeout ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
index f146212..84350694 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
@@ -8,8 +8,8 @@
 promise_test(async test => {
   // 6.3.1.9.2: If |registration|’s active worker is null, then reject promise
   //            with a TypeError and abort these steps.
-  const script = 'resources/sw.js';
-  const scope = 'resources/scope' + location.pathname;
+  const script = 'service_workers/sw.js';
+  const scope = 'service_workers/' + location.pathname;
 
   const serviceWorkerRegistration =
       await service_worker_unregister_and_register(test, script, scope);
@@ -21,7 +21,7 @@
   await promise_rejects(
       test, new TypeError(),
       serviceWorkerRegistration.backgroundFetch.fetch(
-          uniqueId(), ['resources/sw.js']),
+          uniqueId(), ['resources/feature-name.txt']),
       'fetch() must reject on pending and installing workers');
 
 }, 'Background Fetch requires an activated Service Worker');
@@ -44,7 +44,9 @@
   // 6.3.1.7.2: If |internalRequest|’s mode is "no-cors", then return a
   //            promise rejected with a TypeError.
   {
-    const request = new Request('resources/sw.js', {mode: 'no-cors'});
+    const request =
+        new Request('resources/feature-name.txt', {mode: 'no-cors'});
+
     await promise_rejects(
         test, new TypeError(), backgroundFetch.fetch(uniqueId(), request),
         'Requests must not be in no-cors mode');
@@ -56,8 +58,8 @@
   // 6.3.1.9.2: If |bgFetchMap[id]| exists, reject |promise| with a TypeError
   //            and abort these steps.
   return promise_rejects(test, new TypeError(), Promise.all([
-    backgroundFetch.fetch('my-id', 'resources/sw.js'),
-    backgroundFetch.fetch('my-id', 'resources/feature-name.txt')
+    backgroundFetch.fetch('my-id', 'resources/feature-name.txt?1'),
+    backgroundFetch.fetch('my-id', 'resources/feature-name.txt?2')
   ]));
 
 }, 'IDs must be unique among active Background Fetch registrations');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/get-ids.https.window.js b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/get-ids.https.window.js
index 28f5bc3..4c8bf26 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/get-ids.https.window.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/get-ids.https.window.js
@@ -8,8 +8,8 @@
 // https://wicg.github.io/background-fetch/#background-fetch-manager-getIds
 
 promise_test(async test => {
-  const script = 'resources/sw.js';
-  const scope = 'resources/scope' + location.pathname;
+  const script = 'service_workers/sw.js';
+  const scope = 'service_workers/' + location.pathname;
 
   const serviceWorkerRegistration =
       await service_worker_unregister_and_register(test, script, scope);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/get.https.window.js b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/get.https.window.js
index 0b272db..a0b2acd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/get.https.window.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/get.https.window.js
@@ -8,8 +8,8 @@
 // https://wicg.github.io/background-fetch/#background-fetch-manager-get
 
 promise_test(async test => {
-  const script = 'resources/sw.js';
-  const scope = 'resources/scope' + location.pathname;
+  const script = 'service_workers/sw.js';
+  const scope = 'service_workers/' + location.pathname;
 
   const serviceWorkerRegistration =
       await service_worker_unregister_and_register(test, script, scope);
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-continuation-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-continuation-expected.txt
new file mode 100644
index 0000000..f054ef4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-continuation-expected.txt
@@ -0,0 +1,7 @@
+This tests verifies the hit test regions given to the compositor specifically for continuation case. It can only be run in DumpRenderTree.
+
+continuation: #document scrolling (13, 79, 101, 11)
+continuation: #document scrolling (13, 90, 290, 12)
+continuation: #document scrolling (13, 102, 121, 11)
+
+
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-continuation.html b/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-continuation.html
new file mode 100644
index 0000000..c28f97168
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-continuation.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/compositor-touch-hit-rects.css">
+</head>
+<body>
+<p id="description">
+This tests verifies the hit test regions given to the compositor specifically for
+continuation case. It can only be run in DumpRenderTree.</p>
+
+<div id="tests">
+  <div>
+    <b class="testcase" id="continuation">
+      This b tag
+      <div>causes a</div>
+      continuation
+    </b>
+  </div>
+</div>
+
+<div id="console"></div>
+<script src="resources/compositor-touch-hit-rects.js"></script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/containing-block-position-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/containing-block-position-change-expected.txt
index 47bdeea0..d581392f 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/containing-block-position-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/containing-block-position-change-expected.txt
@@ -20,12 +20,12 @@
         {
           "object": "LayoutNGBlockFlow (positioned) DIV",
           "rect": [158, 74, 50, 50],
-          "reason": "style change"
+          "reason": "subtree"
         },
         {
           "object": "LayoutNGBlockFlow (positioned) DIV",
           "rect": [100, 74, 50, 50],
-          "reason": "style change"
+          "reason": "subtree"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-pattern-update-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-pattern-update-2-expected.txt
index 3332943..e25edd7 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-pattern-update-2-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-pattern-update-2-expected.txt
@@ -9,17 +9,17 @@
         {
           "object": "InlineTextBox 'X'",
           "rect": [8, 8, 300, 100],
-          "reason": "SVG resource change"
+          "reason": "subtree"
         },
         {
           "object": "InlineTextBox 'Y'",
           "rect": [8, 8, 300, 100],
-          "reason": "SVG resource change"
+          "reason": "subtree"
         },
         {
           "object": "InlineTextBox 'Z'",
           "rect": [8, 8, 300, 100],
-          "reason": "SVG resource change"
+          "reason": "subtree"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-pattern-update-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-pattern-update-expected.txt
index edbb1e6..b9b5dc3 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-pattern-update-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-pattern-update-expected.txt
@@ -9,7 +9,7 @@
         {
           "object": "InlineTextBox 'X'",
           "rect": [8, 8, 100, 100],
-          "reason": "SVG resource change"
+          "reason": "subtree"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/tspan-pattern-update-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/tspan-pattern-update-expected.txt
index 42cd41e..bfa78fa 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/tspan-pattern-update-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/tspan-pattern-update-expected.txt
@@ -9,7 +9,7 @@
         {
           "object": "InlineTextBox 'Y'",
           "rect": [8, 8, 300, 100],
-          "reason": "SVG resource change"
+          "reason": "subtree"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/animated-row-background-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/animated-row-background-expected.txt
index 80e1961..49aa880e 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/animated-row-background-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/animated-row-background-expected.txt
@@ -9,12 +9,12 @@
         {
           "object": "LayoutTableRow TR id='row'",
           "rect": [8, 10, 210, 102],
-          "reason": "full"
+          "reason": "background"
         },
         {
           "object": "LayoutImage IMG id='image'",
           "rect": [8, 114, 50, 50],
-          "reason": "full"
+          "reason": "image"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_AdobeRGB_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_AdobeRGB_opaque.png
new file mode 100644
index 0000000..c4496db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_AdobeRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_AdobeRGB_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_AdobeRGB_transparent.png
new file mode 100644
index 0000000..3b4cfda5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_AdobeRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_DisplayP3_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_DisplayP3_opaque.png
new file mode 100644
index 0000000..e7a142c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_DisplayP3_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_DisplayP3_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_DisplayP3_transparent.png
new file mode 100644
index 0000000..0b03531
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_DisplayP3_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_ProPhoto_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_ProPhoto_opaque.png
new file mode 100644
index 0000000..a1dc7dd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_ProPhoto_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_ProPhoto_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_ProPhoto_transparent.png
new file mode 100644
index 0000000..be2eb12
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_ProPhoto_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_Rec2020_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_Rec2020_opaque.png
new file mode 100644
index 0000000..e2a2d14
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_Rec2020_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_Rec2020_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_Rec2020_transparent.png
new file mode 100644
index 0000000..960d7d8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_Rec2020_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_opaque.png
new file mode 100644
index 0000000..557e947
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_transparent.png
new file mode 100644
index 0000000..b9a7806
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_opaque.png
new file mode 100644
index 0000000..80cf978
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_transparent.png
new file mode 100644
index 0000000..3ec565f8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_opaque.png
new file mode 100644
index 0000000..5f3134b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_transparent.png
new file mode 100644
index 0000000..500a70e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_opaque.png
new file mode 100644
index 0000000..b5d0e07
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_transparent.png
new file mode 100644
index 0000000..e4ec3e4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_Rec2020_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_Rec2020_opaque.png
new file mode 100644
index 0000000..c487d584
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_Rec2020_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_Rec2020_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_Rec2020_transparent.png
new file mode 100644
index 0000000..78fe202
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_Rec2020_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png
new file mode 100644
index 0000000..c9d9e77
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png
new file mode 100644
index 0000000..3016404
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_sRGB_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_sRGB_opaque.png
new file mode 100644
index 0000000..babf232a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_sRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_sRGB_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_sRGB_transparent.png
new file mode 100644
index 0000000..3016404
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_sRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_sRGB_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_sRGB_opaque.png
new file mode 100644
index 0000000..8a66534
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_sRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_sRGB_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_sRGB_transparent.png
new file mode 100644
index 0000000..e51cda7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_sRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_AdobeRGB_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_AdobeRGB_opaque.png
new file mode 100644
index 0000000..8b787b5c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_AdobeRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_AdobeRGB_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_AdobeRGB_transparent.png
new file mode 100644
index 0000000..727028e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_AdobeRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_DisplayP3_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_DisplayP3_opaque.png
new file mode 100644
index 0000000..fe8bdd49
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_DisplayP3_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_DisplayP3_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_DisplayP3_transparent.png
new file mode 100644
index 0000000..b836afeb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_DisplayP3_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_ProPhoto_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_ProPhoto_opaque.png
new file mode 100644
index 0000000..5ecd868
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_ProPhoto_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_ProPhoto_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_ProPhoto_transparent.png
new file mode 100644
index 0000000..85a349dc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_ProPhoto_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_Rec2020_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_Rec2020_opaque.png
new file mode 100644
index 0000000..599cd34
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_Rec2020_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_Rec2020_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_Rec2020_transparent.png
new file mode 100644
index 0000000..ecf65c3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_Rec2020_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_e-sRGB_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_e-sRGB_opaque.png
new file mode 100644
index 0000000..4b20278e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_e-sRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_e-sRGB_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_e-sRGB_transparent.png
new file mode 100644
index 0000000..e38b29d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_e-sRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_sRGB_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_sRGB_opaque.png
new file mode 100644
index 0000000..9cab6d1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_sRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_sRGB_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_sRGB_transparent.png
new file mode 100644
index 0000000..5fa01e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_8bit_sRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/background/obscured-background-no-repaint.html b/third_party/WebKit/LayoutTests/paint/invalidation/background/obscured-background-no-repaint.html
index 5552f12..12dd6c9 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/background/obscured-background-no-repaint.html
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/background/obscured-background-no-repaint.html
@@ -49,29 +49,18 @@
         testRunner.dumpAsText();
     }
 
-    if (window.internals)
-        internals.runtimeFlags.paintUnderInvalidationCheckingEnabled = true;
-
     function finish() {
         var layerTree = internals.layerTreeAsText(document, internals.LAYER_TREE_INCLUDES_PAINT_INVALIDATIONS);
-        var invalidations = JSON.parse(layerTree)["objectPaintInvalidations"];
-        // Passes if there is no invalidations other than imgForAdvanceImageAnimation,
-        // or only invalidations because of background obscuration change.
-        // This is because before the delayed image decoder finishes decoding the image,
-        // we first assume the image is not opaque. If the image is found actually opaque
-        // after decoding, the background obscuration status of covered elements will
-        // change and cause paint invalidation.
         var invalidatedObjects = {};
-        if (invalidations) {
-            for (var i = 0; i < invalidations.length; ++i) {
-                var object = invalidations[i].object;
-                if (object.indexOf('imgForAdvanceImageAnimation') != -1)
-                    continue;
-                invalidatedObjects[object] = true;
+        for (var layer of JSON.parse(layerTree).layers) {
+            if (layer.paintInvalidations) {
+                for (var invalidation of layer.paintInvalidations)
+                    invalidatedObjects[invalidation.object] = true;
             }
         }
-
-        if (Object.keys(invalidatedObjects).length)
+        // Passes if there is no invalidations other than imgForAdvanceImageAnimation.
+        if (Object.keys(invalidatedObjects).length != 1 ||
+            !Object.keys(invalidatedObjects)[0].includes('imgForAdvanceImageAnimation'))
             output.textContent = 'FAIL: Unexpected paint invalidations: ' + JSON.stringify(invalidatedObjects) + '\n' + layerTree;
         else
             output.textContent = 'PASS';
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-pattern-update-2-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-pattern-update-2-expected.txt
index 1647bf07..076c09be 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-pattern-update-2-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-pattern-update-2-expected.txt
@@ -20,17 +20,17 @@
         {
           "object": "InlineTextBox 'X'",
           "rect": [8, 8, 300, 100],
-          "reason": "SVG resource change"
+          "reason": "subtree"
         },
         {
           "object": "InlineTextBox 'Y'",
           "rect": [8, 8, 300, 100],
-          "reason": "SVG resource change"
+          "reason": "subtree"
         },
         {
           "object": "InlineTextBox 'Z'",
           "rect": [8, 8, 300, 100],
-          "reason": "SVG resource change"
+          "reason": "subtree"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-pattern-update-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-pattern-update-expected.txt
index 55354ee..7a6bf3c 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-pattern-update-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-pattern-update-expected.txt
@@ -20,7 +20,7 @@
         {
           "object": "InlineTextBox 'X'",
           "rect": [8, 8, 100, 100],
-          "reason": "SVG resource change"
+          "reason": "subtree"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/tspan-pattern-update-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/svg/tspan-pattern-update-expected.txt
index c84442f..4d76df4d 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/tspan-pattern-update-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/tspan-pattern-update-expected.txt
@@ -20,7 +20,7 @@
         {
           "object": "InlineTextBox 'Y'",
           "rect": [8, 8, 300, 100],
-          "reason": "SVG resource change"
+          "reason": "subtree"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/animated-row-background-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/animated-row-background-expected.txt
index 99018a1..0344b504 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/animated-row-background-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/animated-row-background-expected.txt
@@ -20,12 +20,12 @@
         {
           "object": "LayoutTableRow TR id='row'",
           "rect": [8, 10, 210, 102],
-          "reason": "full"
+          "reason": "background"
         },
         {
           "object": "LayoutImage IMG id='image'",
           "rect": [8, 114, 50, 50],
-          "reason": "full"
+          "reason": "image"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/portals/portals-api.html b/third_party/WebKit/LayoutTests/portals/portals-api.html
new file mode 100644
index 0000000..3aa64eb2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/portals/portals-api.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Portals API test</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+  <script>
+    test(function() {
+      assert_true(document.createElement('portal') instanceof HTMLPortalElement);
+    }, "portal element exists")
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
index e01e9a0c..ed1e5e2a 100644
--- a/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
@@ -863,6 +863,7 @@
     property valueType
 html element picture
 html element plaintext
+html element portal
 html element pre
     property width
 html element progress
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 9f79e066..1735d82 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -3416,6 +3416,9 @@
 interface HTMLPictureElement : HTMLElement
     attribute @@toStringTag
     method constructor
+interface HTMLPortalElement : HTMLElement
+    attribute @@toStringTag
+    method constructor
 interface HTMLPreElement : HTMLElement
     attribute @@toStringTag
     getter width
diff --git a/third_party/arcore-android-sdk/BUILD.gn b/third_party/arcore-android-sdk/BUILD.gn
index 417f8cd2f..c6b23c1 100644
--- a/third_party/arcore-android-sdk/BUILD.gn
+++ b/third_party/arcore-android-sdk/BUILD.gn
@@ -10,30 +10,6 @@
   jar_path = "libarcore_client_c.jar"
 }
 
-if (!android_64bit_target_cpu || !build_apk_secondary_abi ||
-    current_toolchain == android_secondary_abi_toolchain) {
-  copy("libarcore_library") {
-    if (current_cpu == "arm") {
-      sources = [
-        "libraries/android_arm/libarcore_sdk_c_minimal.so",
-      ]
-    } else if (current_cpu == "arm64") {
-      sources = [
-        "libraries/android_arm64/libarcore_sdk_c_minimal.so",
-      ]
-    }
-    outputs = [
-      "${root_out_dir}/libarcore_sdk_c_minimal.so",
-    ]
-  }
-} else {
-  group("libarcore_library_secondary_abi") {
-    public_deps = [
-      ":libarcore_library($android_secondary_abi_toolchain)",
-    ]
-  }
-}
-
 config("libarcore_config") {
   include_dirs = [ "src/libraries/include/" ]
 }
diff --git a/third_party/arcore-android-sdk/README.chromium b/third_party/arcore-android-sdk/README.chromium
index 3221dc8c..dc56206 100644
--- a/third_party/arcore-android-sdk/README.chromium
+++ b/third_party/arcore-android-sdk/README.chromium
@@ -29,6 +29,9 @@
  * https://github.com/google-ar/arcore-unity-sdk/blob/master/LICENSE
 
 Changes:
+2018-08-10 - First, removed arcore shim copy target from BUILD.gn since it is no
+             longer needed. Second, added zero-byte dummy arcore library for AR
+             module support.
 2018-07-26 - Added test-apks/ subdirectory for storing production versions of AR
              APKs for testing.
 2018-07-19 - Updated BUILD.gn to support secondary abi (the previous change for
diff --git a/third_party/arcore-android-sdk/libarcore_dummy.so b/third_party/arcore-android-sdk/libarcore_dummy.so
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/arcore-android-sdk/libarcore_dummy.so
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 793b27f..bafa46b 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -331,7 +331,6 @@
     "platform/web_resource_timing_info.h",
     "platform/web_rtc_answer_options.h",
     "platform/web_rtc_api_name.h",
-    "platform/web_rtc_certificate.h",
     "platform/web_rtc_certificate_generator.h",
     "platform/web_rtc_configuration.h",
     "platform/web_rtc_data_channel_handler.h",
diff --git a/third_party/blink/public/platform/web_rtc_certificate.h b/third_party/blink/public/platform/web_rtc_certificate.h
deleted file mode 100644
index 7d73419..0000000
--- a/third_party/blink/public/platform/web_rtc_certificate.h
+++ /dev/null
@@ -1,79 +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 THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_RTC_CERTIFICATE_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_RTC_CERTIFICATE_H_
-
-#include "third_party/blink/public/platform/web_vector.h"
-
-#include "third_party/blink/public/platform/web_rtc_key_params.h"
-#include "third_party/blink/public/platform/web_string.h"
-
-#include <memory>
-
-namespace blink {
-
-// https://w3c.github.io/webrtc-pc/#rtcdtlsfingerprint*
-class WebRTCDtlsFingerprint {
- public:
-  WebRTCDtlsFingerprint(WebString algorithm, WebString value)
-      : algorithm_(algorithm), value_(value) {}
-
-  WebString Algorithm() const { return algorithm_; }
-  WebString Value() const { return value_; }
-
- private:
-  WebString algorithm_;
-  WebString value_;
-};
-
-// Corresponds to |rtc::RTCCertificatePEM| in WebRTC.
-// See |WebRTCCertificate::ToPEM| and |WebRTCCertificateGenerator::FromPEM|.
-class WebRTCCertificatePEM {
- public:
-  WebRTCCertificatePEM(WebString private_key, WebString certificate)
-      : private_key_(private_key), certificate_(certificate) {}
-
-  WebString PrivateKey() const { return private_key_; }
-  WebString Certificate() const { return certificate_; }
-
- private:
-  WebString private_key_;
-  WebString certificate_;
-};
-
-// WebRTCCertificate is an interface defining what Blink needs to know about
-// certificates, hiding Chromium and WebRTC layer implementation details. It is
-// possible to create shallow copies of the WebRTCCertificate. When all copies
-// are destroyed, the implementation specific data must be freed.
-// WebRTCCertificate objects thus act as references to the reference counted
-// internal data.
-class WebRTCCertificate {
- public:
-  WebRTCCertificate() = default;
-  virtual ~WebRTCCertificate() = default;
-
-  // Copies the WebRTCCertificate object without copying the underlying
-  // implementation specific (WebRTC layer) certificate. When all copies are
-  // destroyed the underlying data is freed.
-  virtual std::unique_ptr<WebRTCCertificate> ShallowCopy() const = 0;
-
-  // Returns the expiration time in ms relative to epoch, 1970-01-01T00:00:00Z.
-  virtual uint64_t Expires() const = 0;
-  virtual WebVector<WebRTCDtlsFingerprint> GetFingerprints() const = 0;
-  // Creates a PEM strings representation of the certificate. See also
-  // |WebRTCCertificateGenerator::FromPEM|.
-  virtual WebRTCCertificatePEM ToPEM() const = 0;
-  // Checks if the two certificate objects represent the same certificate value,
-  // as should be the case for a clone and the original.
-  virtual bool Equals(const WebRTCCertificate& other) const = 0;
-
- private:
-  WebRTCCertificate(const WebRTCCertificate&) = delete;
-  WebRTCCertificate& operator=(const WebRTCCertificate&) = delete;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_RTC_CERTIFICATE_H_
diff --git a/third_party/blink/public/platform/web_rtc_certificate_generator.h b/third_party/blink/public/platform/web_rtc_certificate_generator.h
index 4cde8d8..f5940a3d 100644
--- a/third_party/blink/public/platform/web_rtc_certificate_generator.h
+++ b/third_party/blink/public/platform/web_rtc_certificate_generator.h
@@ -32,9 +32,9 @@
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_RTC_CERTIFICATE_GENERATOR_H_
 
 #include "third_party/blink/public/platform/web_callbacks.h"
-#include "third_party/blink/public/platform/web_rtc_certificate.h"
 #include "third_party/blink/public/platform/web_rtc_key_params.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/webrtc/api/peerconnectioninterface.h"
 
 #include <memory>
 
@@ -45,7 +45,7 @@
 namespace blink {
 
 using WebRTCCertificateCallback =
-    WebCallbacks<std::unique_ptr<WebRTCCertificate>, void>;
+    WebCallbacks<rtc::scoped_refptr<rtc::RTCCertificate>, void>;
 
 // Interface defining a class that can generate WebRTCCertificates
 // asynchronously.
@@ -72,8 +72,8 @@
   virtual bool IsSupportedKeyParams(const WebRTCKeyParams&) = 0;
 
   // Creates a certificate from the PEM strings. See also
-  // |WebRTCCertificate::ToPEM|.
-  virtual std::unique_ptr<WebRTCCertificate> FromPEM(
+  // |rtc::RTCCertificate::ToPEM|.
+  virtual rtc::scoped_refptr<rtc::RTCCertificate> FromPEM(
       blink::WebString pem_private_key,
       blink::WebString pem_certificate) = 0;
 };
diff --git a/third_party/blink/public/platform/web_rtc_configuration.h b/third_party/blink/public/platform/web_rtc_configuration.h
index 5d4b8da..2e7b75ed 100644
--- a/third_party/blink/public/platform/web_rtc_configuration.h
+++ b/third_party/blink/public/platform/web_rtc_configuration.h
@@ -32,7 +32,6 @@
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_RTC_CONFIGURATION_H_
 
 #include "third_party/blink/public/platform/web_common.h"
-#include "third_party/blink/public/platform/web_rtc_certificate.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/webrtc/api/peerconnectioninterface.h"
 
@@ -51,7 +50,7 @@
       webrtc::PeerConnectionInterface::kBundlePolicyBalanced;
   webrtc::PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy =
       webrtc::PeerConnectionInterface::kRtcpMuxPolicyRequire;
-  WebVector<std::unique_ptr<WebRTCCertificate>> certificates;
+  WebVector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates;
   int ice_candidate_pool_size = 0;
   WebRTCSdpSemantics sdp_semantics = WebRTCSdpSemantics::kDefault;
 };
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index a11781d..55a5d0f 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -117,7 +117,6 @@
   BLINK_PLATFORM_EXPORT static void EnableNotifications(bool);
   BLINK_PLATFORM_EXPORT static void EnableOnDeviceChange(bool);
   BLINK_PLATFORM_EXPORT static void EnableOrientationEvent(bool);
-  BLINK_PLATFORM_EXPORT static void EnableOriginPolicy(bool);
   BLINK_PLATFORM_EXPORT static void EnableOverflowIconsForMediaControls(bool);
   BLINK_PLATFORM_EXPORT static void EnableOverlayScrollbars(bool);
   BLINK_PLATFORM_EXPORT static void EnableOutOfBlinkCORS(bool);
@@ -131,6 +130,7 @@
   BLINK_PLATFORM_EXPORT static void EnablePermissionsAPI(bool);
   BLINK_PLATFORM_EXPORT static void EnablePictureInPicture(bool);
   BLINK_PLATFORM_EXPORT static void EnablePictureInPictureAPI(bool);
+  BLINK_PLATFORM_EXPORT static void EnablePortals(bool);
   BLINK_PLATFORM_EXPORT static void EnablePreciseMemoryInfo(bool);
   BLINK_PLATFORM_EXPORT static void EnablePreloadDefaultIsMetadata(bool);
   BLINK_PLATFORM_EXPORT static void EnablePreloadImageSrcSetEnabled(bool);
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
index eb804767..10e3e7af 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
@@ -51,7 +51,7 @@
           Platform::Current()->CreateRTCCertificateGenerator());
       if (!certificate_generator)
         return nullptr;
-      std::unique_ptr<WebRTCCertificate> certificate =
+      rtc::scoped_refptr<rtc::RTCCertificate> certificate =
           certificate_generator->FromPEM(pem_private_key, pem_certificate);
       if (!certificate)
         return nullptr;
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
index 346d8ccf..963f9e0b 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
@@ -48,10 +48,10 @@
   }
   if (wrapper_type_info == &V8RTCCertificate::wrapperTypeInfo) {
     RTCCertificate* certificate = wrappable->ToImpl<RTCCertificate>();
-    WebRTCCertificatePEM pem = certificate->Certificate().ToPEM();
+    rtc::RTCCertificatePEM pem = certificate->Certificate()->ToPEM();
     WriteTag(kRTCCertificateTag);
-    WriteUTF8String(pem.PrivateKey());
-    WriteUTF8String(pem.Certificate());
+    WriteUTF8String(pem.private_key().c_str());
+    WriteUTF8String(pem.certificate().c_str());
     return true;
   }
   return false;
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
index 4d56531..6fd7f32 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -157,7 +157,7 @@
   V8TestingScope scope;
 
   // Make a certificate with the existing key above.
-  std::unique_ptr<WebRTCCertificate> web_certificate =
+  rtc::scoped_refptr<rtc::RTCCertificate> web_certificate =
       certificate_generator->FromPEM(
           WebString::FromUTF8(kEcdsaPrivateKey, sizeof(kEcdsaPrivateKey)),
           WebString::FromUTF8(kEcdsaCertificate, sizeof(kEcdsaCertificate)));
@@ -171,9 +171,9 @@
   ASSERT_TRUE(V8RTCCertificate::hasInstance(result, scope.GetIsolate()));
   RTCCertificate* new_certificate =
       V8RTCCertificate::ToImpl(result.As<v8::Object>());
-  WebRTCCertificatePEM pem = new_certificate->Certificate().ToPEM();
-  EXPECT_EQ(kEcdsaPrivateKey, pem.PrivateKey());
-  EXPECT_EQ(kEcdsaCertificate, pem.Certificate());
+  rtc::RTCCertificatePEM pem = new_certificate->Certificate()->ToPEM();
+  EXPECT_EQ(kEcdsaPrivateKey, pem.private_key());
+  EXPECT_EQ(kEcdsaCertificate, pem.certificate());
 }
 
 TEST(V8ScriptValueSerializerForModulesTest, DecodeRTCCertificate) {
@@ -198,9 +198,9 @@
   ASSERT_TRUE(V8RTCCertificate::hasInstance(result, scope.GetIsolate()));
   RTCCertificate* new_certificate =
       V8RTCCertificate::ToImpl(result.As<v8::Object>());
-  WebRTCCertificatePEM pem = new_certificate->Certificate().ToPEM();
-  EXPECT_EQ(kEcdsaPrivateKey, pem.PrivateKey());
-  EXPECT_EQ(kEcdsaCertificate, pem.Certificate());
+  rtc::RTCCertificatePEM pem = new_certificate->Certificate()->ToPEM();
+  EXPECT_EQ(kEcdsaPrivateKey, pem.private_key());
+  EXPECT_EQ(kEcdsaCertificate, pem.certificate());
 }
 
 TEST(V8ScriptValueSerializerForModulesTest, DecodeInvalidRTCCertificate) {
diff --git a/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc
index 51c7777..c1e352f 100644
--- a/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc
@@ -152,7 +152,8 @@
   return ListInterpolationFunctions::MaybeMergeSingles(
       std::move(start), std::move(end),
       ListInterpolationFunctions::LengthMatchingStrategy::kLowestCommonMultiple,
-      CSSImageInterpolationType::StaticMergeSingleConversions);
+      WTF::BindRepeating(
+          CSSImageInterpolationType::StaticMergeSingleConversions));
 }
 
 InterpolationValue
diff --git a/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
index 6afb7f99..dbfcf4af 100644
--- a/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
@@ -128,7 +128,7 @@
   return ListInterpolationFunctions::MaybeMergeSingles(
       std::move(start), std::move(end),
       ListInterpolationFunctions::LengthMatchingStrategy::kLowestCommonMultiple,
-      LengthInterpolationFunctions::MergeSingles);
+      WTF::BindRepeating(LengthInterpolationFunctions::MergeSingles));
 }
 
 InterpolationValue
@@ -149,8 +149,9 @@
   ListInterpolationFunctions::Composite(
       underlying_value_owner, underlying_fraction, *this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kLowestCommonMultiple,
-      LengthInterpolationFunctions::NonInterpolableValuesAreCompatible,
-      LengthInterpolationFunctions::Composite);
+      WTF::BindRepeating(
+          LengthInterpolationFunctions::NonInterpolableValuesAreCompatible),
+      WTF::BindRepeating(LengthInterpolationFunctions::Composite));
 }
 
 void CSSLengthListInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc
index 056643a..1a060ef 100644
--- a/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc
@@ -133,7 +133,7 @@
   return ListInterpolationFunctions::MaybeMergeSingles(
       std::move(start), std::move(end),
       ListInterpolationFunctions::LengthMatchingStrategy::kPadToLargest,
-      ShadowInterpolationFunctions::MaybeMergeSingles);
+      WTF::BindRepeating(ShadowInterpolationFunctions::MaybeMergeSingles));
 }
 
 InterpolationValue
@@ -151,8 +151,9 @@
   ListInterpolationFunctions::Composite(
       underlying_value_owner, underlying_fraction, *this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kPadToLargest,
-      ShadowInterpolationFunctions::NonInterpolableValuesAreCompatible,
-      ShadowInterpolationFunctions::Composite);
+      WTF::BindRepeating(
+          ShadowInterpolationFunctions::NonInterpolableValuesAreCompatible),
+      WTF::BindRepeating(ShadowInterpolationFunctions::Composite));
 }
 
 static scoped_refptr<ShadowList> CreateShadowList(
diff --git a/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
index 167a785..04dfbbb 100644
--- a/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
@@ -157,7 +157,7 @@
   return ListInterpolationFunctions::MaybeMergeSingles(
       std::move(start), std::move(end),
       ListInterpolationFunctions::LengthMatchingStrategy::kLowestCommonMultiple,
-      SizeInterpolationFunctions::MaybeMergeSingles);
+      WTF::BindRepeating(SizeInterpolationFunctions::MaybeMergeSingles));
 }
 
 InterpolationValue
@@ -176,9 +176,9 @@
   ListInterpolationFunctions::Composite(
       underlying_value_owner, underlying_fraction, *this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kLowestCommonMultiple,
-
-      SizeInterpolationFunctions::NonInterpolableValuesAreCompatible,
-      SizeInterpolationFunctions::Composite);
+      WTF::BindRepeating(
+          SizeInterpolationFunctions::NonInterpolableValuesAreCompatible),
+      WTF::BindRepeating(SizeInterpolationFunctions::Composite));
 }
 
 void CSSSizeListInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions.cc b/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
index 443cb687..5985398 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/animation/list_interpolation_functions.h"
 
 #include <memory>
+#include "base/callback.h"
 #include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
@@ -132,7 +133,7 @@
       InterpolationValue end(end_interpolable_list.Get(i % end_length)->Clone(),
                              end_non_interpolable_list.Get(i % end_length));
       PairwiseInterpolationValue result =
-          merge_single_item_conversions(std::move(start), std::move(end));
+          merge_single_item_conversions.Run(std::move(start), std::move(end));
       if (!result)
         return nullptr;
       result_start_interpolable_list->Set(
@@ -242,8 +243,8 @@
             ListInterpolationFunctions::LengthMatchingStrategy::
                 kLowestCommonMultiple ||
         (i < a.length() && i < b.length())) {
-      if (!non_interpolable_values_are_compatible(a.Get(i % a.length()),
-                                                  b.Get(i % b.length()))) {
+      if (!non_interpolable_values_are_compatible.Run(a.Get(i % a.length()),
+                                                      b.Get(i % b.length()))) {
         return false;
       }
     }
@@ -304,11 +305,11 @@
         ToNonInterpolableList(*underlying_value.non_interpolable_value);
 
     for (size_t i = 0; i < final_length; i++) {
-      composite_item(underlying_interpolable_list.GetMutable(i),
-                     underlying_non_interpolable_list.GetMutable(i),
-                     underlying_fraction,
-                     *interpolable_list.Get(i % value_length),
-                     non_interpolable_list.Get(i % value_length));
+      composite_item.Run(underlying_interpolable_list.GetMutable(i),
+                         underlying_non_interpolable_list.GetMutable(i),
+                         underlying_fraction,
+                         *interpolable_list.Get(i % value_length),
+                         non_interpolable_list.Get(i % value_length));
     }
   } else {
     DCHECK_EQ(length_matching_strategy, LengthMatchingStrategy::kPadToLargest);
@@ -322,10 +323,10 @@
         ToNonInterpolableList(*underlying_value.non_interpolable_value);
 
     for (size_t i = 0; i < value_length; i++) {
-      composite_item(underlying_interpolable_list.GetMutable(i),
-                     underlying_non_interpolable_list.GetMutable(i),
-                     underlying_fraction, *interpolable_list.Get(i),
-                     non_interpolable_list.Get(i));
+      composite_item.Run(underlying_interpolable_list.GetMutable(i),
+                         underlying_non_interpolable_list.GetMutable(i),
+                         underlying_fraction, *interpolable_list.Get(i),
+                         non_interpolable_list.Get(i));
     }
     for (size_t i = value_length; i < final_length; i++) {
       underlying_interpolable_list.GetMutable(i)->Scale(underlying_fraction);
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions.h b/third_party/blink/renderer/core/animation/list_interpolation_functions.h
index 252b2a69..72161040 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions.h
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions.h
@@ -26,8 +26,9 @@
   enum class LengthMatchingStrategy { kLowestCommonMultiple, kPadToLargest };
 
   using MergeSingleItemConversionsCallback =
-      PairwiseInterpolationValue (*)(InterpolationValue&& start,
-                                     InterpolationValue&& end);
+      base::RepeatingCallback<PairwiseInterpolationValue(InterpolationValue&&,
+                                                         InterpolationValue&&)>;
+
   static PairwiseInterpolationValue MaybeMergeSingles(
       InterpolationValue&& start,
       InterpolationValue&& end,
@@ -41,12 +42,14 @@
                           EqualNonInterpolableValuesCallback);
 
   using NonInterpolableValuesAreCompatibleCallback =
-      bool (*)(const NonInterpolableValue*, const NonInterpolableValue*);
-  using CompositeItemCallback = void (*)(std::unique_ptr<InterpolableValue>&,
-                                         scoped_refptr<NonInterpolableValue>&,
-                                         double underlying_fraction,
-                                         const InterpolableValue&,
-                                         const NonInterpolableValue*);
+      base::RepeatingCallback<bool(const NonInterpolableValue*,
+                                   const NonInterpolableValue*)>;
+  using CompositeItemCallback =
+      base::RepeatingCallback<void(std::unique_ptr<InterpolableValue>&,
+                                   scoped_refptr<NonInterpolableValue>&,
+                                   double underlying_fraction,
+                                   const InterpolableValue&,
+                                   const NonInterpolableValue*)>;
   static void Composite(UnderlyingValueOwner&,
                         double underlying_fraction,
                         const InterpolationType&,
diff --git a/third_party/blink/renderer/core/core_idl_files.gni b/third_party/blink/renderer/core/core_idl_files.gni
index 3dc993e3..062d701 100644
--- a/third_party/blink/renderer/core/core_idl_files.gni
+++ b/third_party/blink/renderer/core/core_idl_files.gni
@@ -278,6 +278,7 @@
                     "html/forms/validity_state.idl",
                     "html/media/html_audio_element.idl",
                     "html/media/media_error.idl",
+                    "html/portal/html_portal_element.idl",
                     "html/track/audio_track_list.idl",
                     "html/track/html_track_element.idl",
                     "html/track/text_track.idl",
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client.cc b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
index 02b2bb4..96a9490 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client.cc
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
@@ -143,7 +143,7 @@
   LayoutBlock* new_layout_block = CaretLayoutBlock(caret_position.AnchorNode());
   if (new_layout_block != layout_block_) {
     if (layout_block_)
-      layout_block_->SetMayNeedPaintInvalidation();
+      layout_block_->SetShouldCheckForPaintInvalidation();
     layout_block_ = new_layout_block;
     visual_rect_ = LayoutRect();
     if (new_layout_block) {
@@ -181,7 +181,7 @@
   }
 
   if (needs_paint_invalidation_)
-    new_layout_block->SetMayNeedPaintInvalidation();
+    new_layout_block->SetShouldCheckForPaintInvalidation();
 }
 
 void CaretDisplayItemClient::InvalidatePaint(
@@ -241,8 +241,7 @@
     // The caret may change paint offset without changing visual rect, and we
     // need to invalidate the display item client if the block is doing full
     // paint invalidation.
-    if (IsImmediateFullPaintInvalidationReason(
-            layout_block_->FullPaintInvalidationReason())) {
+    if (layout_block_->ShouldDoFullPaintInvalidation()) {
       object_invalidator.InvalidateDisplayItemClient(
           *this, PaintInvalidationReason::kCaret);
     }
diff --git a/third_party/blink/renderer/core/frame/event_handler_registry.cc b/third_party/blink/renderer/core/frame/event_handler_registry.cc
index fb49073..21094cf 100644
--- a/third_party/blink/renderer/core/frame/event_handler_registry.cc
+++ b/third_party/blink/renderer/core/frame/event_handler_registry.cc
@@ -292,8 +292,14 @@
     if (handler_class == kTouchStartOrMoveEventBlocking ||
         handler_class == kTouchStartOrMoveEventBlockingLowLatency) {
       if (auto* node = target->ToNode()) {
-        if (auto* layout_object = node->GetLayoutObject())
+        if (auto* layout_object = node->GetLayoutObject()) {
           layout_object->MarkEffectiveWhitelistedTouchActionChanged();
+          auto* continuation = layout_object->VirtualContinuation();
+          while (continuation) {
+            continuation->MarkEffectiveWhitelistedTouchActionChanged();
+            continuation = continuation->VirtualContinuation();
+          }
+        }
       } else if (auto* dom_window = target->ToLocalDOMWindow()) {
         // This event handler is on a window. Ensure the layout view is
         // invalidated because the layout view tracks the window's blocking
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index d09a43b..38215ec 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -480,7 +480,7 @@
   FrameRectsChanged();
 
   if (auto* layout_view = GetLayoutView())
-    layout_view->SetMayNeedPaintInvalidation();
+    layout_view->SetShouldCheckForPaintInvalidation();
 
   if (width_changed || height_changed) {
     ViewportSizeChanged(width_changed, height_changed);
@@ -758,7 +758,7 @@
         // LayoutView for paint invalidation. This simplifies our code as we
         // just always do a full tree walk.
         if (LayoutObject* container = root->Container())
-          container->SetMayNeedPaintInvalidation();
+          container->SetShouldCheckForPaintInvalidation();
       }
       layout_subtree_root_list_.Clear();
     } else {
@@ -1391,7 +1391,7 @@
     // If the layer has no visible content, then we shouldn't invalidate; but
     // if we're not compositing-inputs-clean, then we can't query
     // layer->SubtreeIsInvisible() here.
-    layout_object->SetMayNeedPaintInvalidationSubtree();
+    layout_object->SetSubtreeShouldCheckForPaintInvalidation();
     if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled() &&
         !layer->NeedsRepaint()) {
       // Paint properties of the layer relative to its containing graphics
@@ -2567,7 +2567,7 @@
       // PrePaintTreeWalk can reach this frame.
       frame_view.SetNeedsPaintPropertyUpdate();
       if (auto* owner = frame_view.GetFrame().OwnerLayoutObject())
-        owner->SetMayNeedPaintInvalidation();
+        owner->SetShouldCheckForPaintInvalidation();
     }
   });
 
@@ -4201,7 +4201,7 @@
     return;
   lifecycle_updates_throttled_ = false;
   if (auto* owner = GetFrame().OwnerLayoutObject())
-    owner->SetMayNeedPaintInvalidation();
+    owner->SetShouldCheckForPaintInvalidation();
 
   LayoutView* layout_view = GetLayoutView();
   bool layout_view_is_empty = layout_view && !layout_view->FirstChild();
diff --git a/third_party/blink/renderer/core/html/BUILD.gn b/third_party/blink/renderer/core/html/BUILD.gn
index bcb101f..422cf608 100644
--- a/third_party/blink/renderer/core/html/BUILD.gn
+++ b/third_party/blink/renderer/core/html/BUILD.gn
@@ -498,6 +498,8 @@
     "media/picture_in_picture_interstitial.h",
     "plugin_document.cc",
     "plugin_document.h",
+    "portal/html_portal_element.cc",
+    "portal/html_portal_element.h",
     "rel_list.cc",
     "rel_list.h",
     "shadow/details_marker_control.cc",
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 63c7907..3e8eb51 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -361,7 +361,7 @@
     return;
   canvas_is_clear_ = false;
   if (GetLayoutObject() && !LowLatencyEnabled())
-    GetLayoutObject()->SetMayNeedPaintInvalidation();
+    GetLayoutObject()->SetShouldCheckForPaintInvalidation();
   if (Is2d() && context_->ShouldAntialias() && GetPage() &&
       GetPage()->DeviceScaleFactorDeprecated() > 1.0f) {
     FloatRect inflated_rect = rect;
diff --git a/third_party/blink/renderer/core/html/forms/range_input_type.cc b/third_party/blink/renderer/core/html/forms/range_input_type.cc
index c709be1..9a69f6a 100644
--- a/third_party/blink/renderer/core/html/forms/range_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/range_input_type.cc
@@ -340,10 +340,8 @@
 
 void RangeInputType::ListAttributeTargetChanged() {
   tick_mark_values_dirty_ = true;
-  if (GetElement().GetLayoutObject())
-    GetElement()
-        .GetLayoutObject()
-        ->SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
+  if (auto* object = GetElement().GetLayoutObject())
+    object->SetSubtreeShouldDoFullPaintInvalidation();
   Element* slider_track_element = SliderTrackElement();
   if (slider_track_element->GetLayoutObject())
     slider_track_element->GetLayoutObject()->SetNeedsLayout(
diff --git a/third_party/blink/renderer/core/html/html_tag_names.json5 b/third_party/blink/renderer/core/html/html_tag_names.json5
index 8dadbe2..c94a0b9d 100644
--- a/third_party/blink/renderer/core/html/html_tag_names.json5
+++ b/third_party/blink/renderer/core/html/html_tag_names.json5
@@ -358,6 +358,11 @@
       name: "plaintext",
       interfaceName: "HTMLElement",
     },
+    {
+      name: "portal",
+      interfaceName: "HTMLPortalElement",
+      interfaceHeaderDir: "third_party/blink/renderer/core/html/portal",
+    },
     "pre",
     {
       name: "progress",
diff --git a/third_party/blink/renderer/core/html/portal/html_portal_element.cc b/third_party/blink/renderer/core/html/portal/html_portal_element.cc
new file mode 100644
index 0000000..b93a9d3
--- /dev/null
+++ b/third_party/blink/renderer/core/html/portal/html_portal_element.cc
@@ -0,0 +1,51 @@
+// 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 "third_party/blink/renderer/core/html/portal/html_portal_element.h"
+
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/html/html_unknown_element.h"
+#include "third_party/blink/renderer/core/html_names.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+
+HTMLPortalElement::HTMLPortalElement(Document& document)
+    : HTMLFrameOwnerElement(HTMLNames::portalTag, document) {}
+
+HTMLPortalElement::~HTMLPortalElement() {}
+
+HTMLElement* HTMLPortalElement::Create(Document& document) {
+  if (RuntimeEnabledFeatures::PortalsEnabled())
+    return new HTMLPortalElement(document);
+  return HTMLUnknownElement::Create(HTMLNames::portalTag, document);
+}
+
+HTMLPortalElement::InsertionNotificationRequest HTMLPortalElement::InsertedInto(
+    ContainerNode* node) {
+  auto result = HTMLFrameOwnerElement::InsertedInto(node);
+
+  Document& document = GetDocument();
+
+  if (node->IsInDocumentTree() && document.IsHTMLDocument()) {
+    document.GetFrame()->GetInterfaceProvider().GetInterface(
+        mojo::MakeRequest(&portal_ptr_));
+  }
+
+  return result;
+}
+
+void HTMLPortalElement::RemovedFrom(ContainerNode* node) {
+  HTMLFrameOwnerElement::RemovedFrom(node);
+
+  Document& document = GetDocument();
+
+  if (node->IsInDocumentTree() && document.IsHTMLDocument()) {
+    portal_ptr_.reset();
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/portal/html_portal_element.h b/third_party/blink/renderer/core/html/portal/html_portal_element.h
new file mode 100644
index 0000000..e459eca
--- /dev/null
+++ b/third_party/blink/renderer/core/html/portal/html_portal_element.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PORTAL_HTML_PORTAL_ELEMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PORTAL_HTML_PORTAL_ELEMENT_H_
+
+#include "third_party/blink/public/mojom/portal/portal.mojom-blink.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/dom/node.h"
+#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
+
+namespace blink {
+
+class Document;
+
+// The HTMLPortalElement implements the <portal> HTML element. The portal
+// element can be used to embed another top-level browsing context, which can be
+// activated using script. The portal element is still under development and not
+// part of the HTML standard. It can be enabled by passing
+// --enable-features=Portals. See
+// https://github.com/KenjiBaheux/portals/blob/master/explainer.md for more
+// details.
+class CORE_EXPORT HTMLPortalElement : public HTMLFrameOwnerElement {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static HTMLElement* Create(Document&);
+
+  ~HTMLPortalElement() override;
+
+ private:
+  explicit HTMLPortalElement(Document&);
+
+  // Node overrides
+  InsertionNotificationRequest InsertedInto(ContainerNode*) override;
+  void RemovedFrom(ContainerNode*) override;
+
+  // HTMLFrameOwnerElement overrides
+  ParsedFeaturePolicy ConstructContainerPolicy(Vector<String>*) const override {
+    return ParsedFeaturePolicy();
+  }
+
+  mojom::blink::PortalPtr portal_ptr_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PORTAL_HTML_PORTAL_ELEMENT_H_
diff --git a/third_party/blink/renderer/core/html/portal/html_portal_element.idl b/third_party/blink/renderer/core/html/portal/html_portal_element.idl
new file mode 100644
index 0000000..26a4055
--- /dev/null
+++ b/third_party/blink/renderer/core/html/portal/html_portal_element.idl
@@ -0,0 +1,9 @@
+// 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.
+
+// https://github.com/KenjiBaheux/portals/blob/master/explainer.md
+
+[HTMLConstructor, RuntimeEnabled=Portals]
+interface HTMLPortalElement : HTMLElement {
+};
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index eec4031..252aef8 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -287,7 +287,7 @@
   if (should_clip_overflow != HasOverflowClip()) {
     if (!should_clip_overflow)
       GetScrollableArea()->InvalidateAllStickyConstraints();
-    SetMayNeedPaintInvalidationSubtree();
+    SetSubtreeShouldCheckForPaintInvalidation();
     // The overflow clip paint property depends on whether overflow clip is
     // present so we need to update paint properties if this changes.
     SetNeedsPaintPropertyUpdate();
@@ -790,7 +790,7 @@
 void LayoutBlock::LayoutPositionedObject(LayoutBox* positioned_object,
                                          bool relayout_children,
                                          PositionedLayoutBehavior info) {
-  positioned_object->SetMayNeedPaintInvalidation();
+  positioned_object->SetShouldCheckForPaintInvalidation();
 
   SubtreeLayoutScope layout_scope(*positioned_object);
   // If positionedObject is fixed-positioned and moves with an absolute-
@@ -950,9 +950,9 @@
     parent->MarkContainerNeedsCollectInlines();
 }
 
-PaintInvalidationReason LayoutBlock::InvalidatePaint(
+void LayoutBlock::InvalidatePaint(
     const PaintInvalidatorContext& context) const {
-  return BlockPaintInvalidator(*this).InvalidatePaint(context);
+  BlockPaintInvalidator(*this).InvalidatePaint(context);
 }
 
 void LayoutBlock::ClearPreviousVisualRects() {
diff --git a/third_party/blink/renderer/core/layout/layout_block.h b/third_party/blink/renderer/core/layout/layout_block.h
index d3b523f3..e7cd22d 100644
--- a/third_party/blink/renderer/core/layout/layout_block.h
+++ b/third_party/blink/renderer/core/layout/layout_block.h
@@ -481,8 +481,7 @@
   }
 
  protected:
-  PaintInvalidationReason InvalidatePaint(
-      const PaintInvalidatorContext&) const override;
+  void InvalidatePaint(const PaintInvalidatorContext&) const override;
 
   void ClearPreviousVisualRects() override;
 
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index 313f270..0813f20 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -1522,7 +1522,7 @@
     CHECK(!next_sibling || next_sibling->IsBox());
     next = ToLayoutBox(next_sibling);
 
-    child->SetMayNeedPaintInvalidation();
+    child->SetShouldCheckForPaintInvalidation();
 
     if (child_to_exclude == child)
       continue;  // Skip this child, since it will be positioned by the
@@ -3868,7 +3868,7 @@
   LayoutBox& child = *floating_object.GetLayoutObject();
 
   // FIXME Investigate if this can be removed. crbug.com/370006
-  child.SetMayNeedPaintInvalidation();
+  child.SetShouldCheckForPaintInvalidation();
 
   logical_top_margin_edge =
       std::max(logical_top_margin_edge,
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
index 5e5e60f..527f2e5 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
@@ -1955,7 +1955,7 @@
       if (o->IsAtomicInlineLevel() || o->IsFloating() ||
           o->IsOutOfFlowPositioned()) {
         LayoutBox* box = ToLayoutBox(o);
-        box->SetMayNeedPaintInvalidation();
+        box->SetShouldCheckForPaintInvalidation();
 
         UpdateBlockChildDirtyBitsBeforeLayout(relayout_children, *box);
 
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 8b13b620..06476d1 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -1781,8 +1781,8 @@
     SetNeedsPaintPropertyUpdate();
   }
 
-  // TODO(chrishtr): support PaintInvalidationReason::kDelayedFull for animated
-  // border images.
+  // TODO(chrishtr): support delayed paint invalidation for animated border
+  // images.
   if ((StyleRef().BorderImage().GetImage() &&
        StyleRef().BorderImage().GetImage()->Data() == image) ||
       (StyleRef().MaskBoxImage().GetImage() &&
@@ -1864,14 +1864,14 @@
   // The location may change because of layout of other objects. Should check
   // this object for paint invalidation.
   if (!NeedsLayout())
-    SetMayNeedPaintInvalidation();
+    SetShouldCheckForPaintInvalidation();
 }
 
 void LayoutBox::SizeChanged() {
   // The size may change because of layout of other objects. Should check this
   // object for paint invalidation.
   if (!NeedsLayout())
-    SetMayNeedPaintInvalidation();
+    SetShouldCheckForPaintInvalidation();
 
   if (GetNode() && GetNode()->IsElementNode()) {
     Element& element = ToElement(*GetNode());
@@ -1895,25 +1895,23 @@
   if (MayNeedPaintInvalidationAnimatedBackgroundImage() &&
       !BackgroundIsKnownToBeObscured()) {
     SetShouldDoFullPaintInvalidationWithoutGeometryChange(
-        PaintInvalidationReason::kDelayedFull);
+        PaintInvalidationReason::kBackground);
+    SetShouldDelayFullPaintInvalidation();
   }
 
-  if (FullPaintInvalidationReason() != PaintInvalidationReason::kDelayedFull ||
-      !IntersectsVisibleViewport())
+  if (!ShouldDelayFullPaintInvalidation() || !IntersectsVisibleViewport())
     return;
 
-  // Do regular full paint invalidation if the object with
-  // PaintInvalidationReason::kDelayedFull is onscreen.
-  // Conservatively assume the delayed paint invalidation was caused by
-  // background image change.
+  // Do regular full paint invalidation if the object with delayed paint
+  // invalidation is onscreen. Conservatively assume the delayed paint
+  // invalidation was caused by background image change.
   SetBackgroundChangedSinceLastPaintInvalidation();
   SetShouldDoFullPaintInvalidationWithoutGeometryChange(
-      PaintInvalidationReason::kFull);
+      FullPaintInvalidationReason());
 }
 
-PaintInvalidationReason LayoutBox::InvalidatePaint(
-    const PaintInvalidatorContext& context) const {
-  return BoxPaintInvalidator(*this, context).InvalidatePaint();
+void LayoutBox::InvalidatePaint(const PaintInvalidatorContext& context) const {
+  BoxPaintInvalidator(*this, context).InvalidatePaint();
 }
 
 LayoutRect LayoutBox::OverflowClipRect(
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index f38d253..eaebbfe 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -1511,8 +1511,7 @@
   void ComputeSelfHitTestRects(Vector<LayoutRect>&,
                                const LayoutPoint& layer_offset) const override;
 
-  PaintInvalidationReason InvalidatePaint(
-      const PaintInvalidatorContext&) const override;
+  void InvalidatePaint(const PaintInvalidatorContext&) const override;
 
   bool ColumnFlexItemHasStretchAlignment() const;
   bool IsStretchingColumnFlexItem() const;
diff --git a/third_party/blink/renderer/core/layout/layout_box_test.cc b/third_party/blink/renderer/core/layout/layout_box_test.cc
index 30156c6..22b3c913 100644
--- a/third_party/blink/renderer/core/layout/layout_box_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_test.cc
@@ -453,7 +453,7 @@
   bool MaybeAnimated() override { return true; }
 };
 
-TEST_F(LayoutBoxTest, DeferredInvalidation) {
+TEST_F(LayoutBoxTest, DelayedInvalidation) {
   SetBodyInnerHTML("<img id='image' style='width: 100px; height: 100px;'/>");
   auto* obj = ToLayoutBox(GetLayoutObjectByElementId("image"));
   ASSERT_TRUE(obj);
@@ -465,19 +465,24 @@
   ToLayoutImage(obj)->ImageResource()->SetImageResource(image);
   ASSERT_TRUE(ToLayoutImage(obj)->CachedImage()->GetImage()->MaybeAnimated());
 
-  // CanDeferInvalidation::kYes results in a deferred invalidation.
   obj->ClearPaintInvalidationFlags();
+  EXPECT_FALSE(obj->ShouldDoFullPaintInvalidation());
   EXPECT_EQ(obj->FullPaintInvalidationReason(), PaintInvalidationReason::kNone);
-  obj->ImageChanged(image, ImageResourceObserver::CanDeferInvalidation::kYes);
-  EXPECT_EQ(obj->FullPaintInvalidationReason(),
-            PaintInvalidationReason::kDelayedFull);
+  EXPECT_FALSE(obj->ShouldDelayFullPaintInvalidation());
 
-  // CanDeferInvalidation::kNo results in a immediate invalidation.
-  obj->ClearPaintInvalidationFlags();
-  EXPECT_EQ(obj->FullPaintInvalidationReason(), PaintInvalidationReason::kNone);
-  obj->ImageChanged(image, ImageResourceObserver::CanDeferInvalidation::kNo);
+  // CanDeferInvalidation::kYes results in a deferred invalidation.
+  obj->ImageChanged(image, ImageResourceObserver::CanDeferInvalidation::kYes);
+  EXPECT_FALSE(obj->ShouldDoFullPaintInvalidation());
   EXPECT_EQ(obj->FullPaintInvalidationReason(),
             PaintInvalidationReason::kImage);
+  EXPECT_TRUE(obj->ShouldDelayFullPaintInvalidation());
+
+  // CanDeferInvalidation::kNo results in a immediate invalidation.
+  obj->ImageChanged(image, ImageResourceObserver::CanDeferInvalidation::kNo);
+  EXPECT_TRUE(obj->ShouldDoFullPaintInvalidation());
+  EXPECT_EQ(obj->FullPaintInvalidationReason(),
+            PaintInvalidationReason::kImage);
+  EXPECT_FALSE(obj->ShouldDelayFullPaintInvalidation());
 }
 
 TEST_F(LayoutBoxTest, MarkerContainerLayoutOverflowRect) {
diff --git a/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc
index 1d8a9b6e..f7ff1a8a 100644
--- a/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc
@@ -1260,7 +1260,7 @@
                                              const LayoutPoint& location) {
   // FIXME Investigate if this can be removed based on other flags.
   // crbug.com/370010
-  child->SetMayNeedPaintInvalidation();
+  child->SetShouldCheckForPaintInvalidation();
 
   // Place the child.
   child->SetLocation(location);
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_content.cc b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
index d9de52d..0023ca9 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_content.cc
+++ b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
@@ -278,6 +278,13 @@
   EmbeddedContentPainter(*this).PaintReplaced(paint_info, paint_offset);
 }
 
+void LayoutEmbeddedContent::InvalidatePaint(
+    const PaintInvalidatorContext& context) const {
+  LayoutReplaced::InvalidatePaint(context);
+  if (auto* plugin = Plugin())
+    plugin->InvalidatePaint();
+}
+
 CursorDirective LayoutEmbeddedContent::GetCursor(const LayoutPoint& point,
                                                  Cursor& cursor) const {
   if (Plugin()) {
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_content.h b/third_party/blink/renderer/core/layout/layout_embedded_content.h
index dd85f9b..8f59865 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_content.h
+++ b/third_party/blink/renderer/core/layout/layout_embedded_content.h
@@ -73,6 +73,7 @@
   void UpdateLayout() override;
   void PaintReplaced(const PaintInfo&,
                      const LayoutPoint& paint_offset) const override;
+  void InvalidatePaint(const PaintInvalidatorContext&) const final;
   CursorDirective GetCursor(const LayoutPoint&, Cursor&) const final;
 
   bool CanBeSelectionLeafInternal() const final { return true; }
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_object.cc b/third_party/blink/renderer/core/layout/layout_embedded_object.cc
index 7baf19b..a136627 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_embedded_object.cc
@@ -34,7 +34,6 @@
 #include "third_party/blink/renderer/core/layout/layout_analyzer.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/page/page.h"
-#include "third_party/blink/renderer/core/paint/embedded_object_paint_invalidator.h"
 #include "third_party/blink/renderer/core/paint/embedded_object_painter.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 
@@ -89,11 +88,6 @@
   EmbeddedObjectPainter(*this).PaintReplaced(paint_info, paint_offset);
 }
 
-PaintInvalidationReason LayoutEmbeddedObject::InvalidatePaint(
-    const PaintInvalidatorContext& context) const {
-  return EmbeddedObjectPaintInvalidator(*this, context).InvalidatePaint();
-}
-
 void LayoutEmbeddedObject::UpdateLayout() {
   DCHECK(NeedsLayout());
   LayoutAnalyzer::Scope analyzer(*this);
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_object.h b/third_party/blink/renderer/core/layout/layout_embedded_object.h
index 8ee32cd..42cd2a20 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_object.h
+++ b/third_party/blink/renderer/core/layout/layout_embedded_object.h
@@ -52,8 +52,6 @@
  private:
   void PaintReplaced(const PaintInfo&,
                      const LayoutPoint& paint_offset) const final;
-  PaintInvalidationReason InvalidatePaint(
-      const PaintInvalidatorContext&) const final;
 
   void UpdateLayout() final;
 
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index 054c5a5a..9131c71 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -1472,7 +1472,7 @@
 
     DCHECK(!flex_item.box->IsOutOfFlowPositioned());
 
-    child->SetMayNeedPaintInvalidation();
+    child->SetShouldCheckForPaintInvalidation();
 
     SetOverrideMainAxisContentSizeForChild(*child,
                                            flex_item.flexed_content_size);
diff --git a/third_party/blink/renderer/core/layout/layout_html_canvas.cc b/third_party/blink/renderer/core/layout/layout_html_canvas.cc
index 6de2e94..ad04b69 100644
--- a/third_party/blink/renderer/core/layout/layout_html_canvas.cc
+++ b/third_party/blink/renderer/core/layout/layout_html_canvas.cc
@@ -30,7 +30,6 @@
 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/page/page.h"
-#include "third_party/blink/renderer/core/paint/html_canvas_paint_invalidator.h"
 #include "third_party/blink/renderer/core/paint/html_canvas_painter.h"
 
 namespace blink {
@@ -83,9 +82,13 @@
     SetNeedsLayout(LayoutInvalidationReason::kSizeChanged);
 }
 
-PaintInvalidationReason LayoutHTMLCanvas::InvalidatePaint(
+void LayoutHTMLCanvas::InvalidatePaint(
     const PaintInvalidatorContext& context) const {
-  return HTMLCanvasPaintInvalidator(*this, context).InvalidatePaint();
+  auto* element = ToHTMLCanvasElement(GetNode());
+  if (element->IsDirty())
+    element->DoDeferredPaintInvalidation();
+
+  LayoutReplaced::InvalidatePaint(context);
 }
 
 CompositingReasons LayoutHTMLCanvas::AdditionalCompositingReasons() const {
diff --git a/third_party/blink/renderer/core/layout/layout_html_canvas.h b/third_party/blink/renderer/core/layout/layout_html_canvas.h
index 5058233..c3e989ed 100644
--- a/third_party/blink/renderer/core/layout/layout_html_canvas.h
+++ b/third_party/blink/renderer/core/layout/layout_html_canvas.h
@@ -41,8 +41,7 @@
   }
   PaintLayerType LayerTypeRequired() const override;
 
-  PaintInvalidationReason InvalidatePaint(
-      const PaintInvalidatorContext&) const final;
+  void InvalidatePaint(const PaintInvalidatorContext&) const final;
 
   void CanvasSizeChanged();
 
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index 3ae89ee..1c907c9 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -239,10 +239,11 @@
   }
 
   SetShouldDoFullPaintInvalidationWithoutGeometryChange(
-      defer == CanDeferInvalidation::kYes && ImageResource() &&
-              ImageResource()->MaybeAnimated()
-          ? PaintInvalidationReason::kDelayedFull
-          : PaintInvalidationReason::kImage);
+      PaintInvalidationReason::kImage);
+
+  if (defer == CanDeferInvalidation::kYes && ImageResource() &&
+      ImageResource()->MaybeAnimated())
+    SetShouldDelayFullPaintInvalidation();
 
   // Tell any potential compositing layers that the image needs updating.
   ContentChanged(kImageChanged);
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 073dc719..c740feb 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -170,7 +170,9 @@
 #endif
 
 struct SameSizeAsLayoutObject : DisplayItemClient {
-  ~SameSizeAsLayoutObject() override = default;  // Allocate vtable pointer.
+  // Normally this field uses the gap between DisplayItemClient and
+  // LayoutObject's other fields.
+  uint8_t paint_invalidation_reason_;
   void* pointers[5];
   Member<void*> members[1];
 #if DCHECK_IS_ON()
@@ -178,7 +180,6 @@
 #endif
   unsigned bitfields_;
   unsigned bitfields2_;
-  unsigned bitfields3_;
   // The following fields are in FragmentData.
   LayoutRect visual_rect_;
   LayoutPoint paint_offset_;
@@ -277,7 +278,8 @@
 }
 
 LayoutObject::LayoutObject(Node* node)
-    : style_(nullptr),
+    : full_paint_invalidation_reason_(PaintInvalidationReason::kNone),
+      style_(nullptr),
       node_(node),
       parent_(nullptr),
       previous_(nullptr),
@@ -1535,7 +1537,7 @@
   // Not using the WithoutGeometryChange version because we need to map the
   // partial invalidated rect to visual rect in backing or the containing
   // transform node.
-  SetMayNeedPaintInvalidation();
+  SetShouldCheckForPaintInvalidation();
 }
 
 LayoutRect LayoutObject::AbsoluteSelectionRect() const {
@@ -1550,9 +1552,9 @@
 }
 
 DISABLE_CFI_PERF
-PaintInvalidationReason LayoutObject::InvalidatePaint(
+void LayoutObject::InvalidatePaint(
     const PaintInvalidatorContext& context) const {
-  return ObjectPaintInvalidatorWithContext(*this, context).InvalidatePaint();
+  ObjectPaintInvalidatorWithContext(*this, context).InvalidatePaint();
 }
 
 void LayoutObject::AdjustVisualRectForCompositedScrolling(
@@ -1952,7 +1954,7 @@
 void LayoutObject::SetNeedsOverflowRecalcAfterStyleChange() {
   bool needed_recalc = NeedsOverflowRecalcAfterStyleChange();
   SetSelfNeedsOverflowRecalcAfterStyleChange();
-  SetMayNeedPaintInvalidation();
+  SetShouldCheckForPaintInvalidation();
   if (!needed_recalc)
     MarkContainerChainForOverflowRecalcIfNeeded();
 }
@@ -2057,7 +2059,7 @@
 
   if (diff.NeedsPaintInvalidationSubtree() ||
       updated_diff.NeedsPaintInvalidationSubtree()) {
-    SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
+    SetSubtreeShouldDoFullPaintInvalidation();
   } else if (diff.NeedsPaintInvalidationObject() ||
              updated_diff.NeedsPaintInvalidationObject()) {
     // TODO(wangxianzhu): For now LayoutSVGRoot::localVisualRect() depends on
@@ -2074,7 +2076,7 @@
     InvalidateClipPathCache();
 
   if (diff.NeedsVisualRectUpdate())
-    SetMayNeedPaintInvalidation();
+    SetShouldCheckForPaintInvalidation();
 
   // Text nodes share style with their parents but the paint properties don't
   // apply to them, hence the !isText() check. If property nodes are added or
@@ -3783,6 +3785,35 @@
   return ObjectIsRelayoutBoundary(this);
 }
 
+inline void LayoutObject::MarkAncestorsForPaintInvalidation() {
+  for (LayoutObject* parent = ParentCrossingFrames();
+       parent && !parent->ShouldCheckForPaintInvalidation();
+       parent = parent->ParentCrossingFrames())
+    parent->bitfields_.SetShouldCheckForPaintInvalidation(true);
+}
+
+inline void LayoutObject::SetNeedsPaintOffsetAndVisualRectUpdate() {
+  DCHECK(ShouldCheckForPaintInvalidation());
+  for (auto* object = this;
+       object && !object->NeedsPaintOffsetAndVisualRectUpdate();
+       object = object->ParentCrossingFrames()) {
+    object->bitfields_.SetNeedsPaintOffsetAndVisualRectUpdate(true);
+  }
+}
+
+void LayoutObject::SetShouldInvalidateSelection() {
+  if (!CanUpdateSelectionOnRootLineBoxes())
+    return;
+  bitfields_.SetShouldInvalidateSelection(true);
+  SetShouldCheckForPaintInvalidation();
+}
+
+void LayoutObject::SetShouldDoFullPaintInvalidation(
+    PaintInvalidationReason reason) {
+  SetShouldDoFullPaintInvalidationWithoutGeometryChange(reason);
+  SetNeedsPaintOffsetAndVisualRectUpdate();
+}
+
 static PaintInvalidationReason DocumentLifecycleBasedPaintInvalidationReason(
     const DocumentLifecycle& document_lifecycle) {
   switch (document_lifecycle.GetState()) {
@@ -3799,85 +3830,70 @@
   }
 }
 
-inline void LayoutObject::MarkAncestorsForPaintInvalidation() {
-  for (LayoutObject* parent = ParentCrossingFrames();
-       parent && !parent->ShouldCheckForPaintInvalidation();
-       parent = parent->ParentCrossingFrames())
-    parent->bitfields_.SetMayNeedPaintInvalidation(true);
-}
-
-inline void LayoutObject::SetNeedsPaintOffsetAndVisualRectUpdate() {
-  for (auto* object = this;
-       object && !object->NeedsPaintOffsetAndVisualRectUpdate();
-       object = object->ParentCrossingFrames()) {
-    object->bitfields_.SetNeedsPaintOffsetAndVisualRectUpdate(true);
-  }
-}
-
-void LayoutObject::SetShouldInvalidateSelection() {
-  if (!CanUpdateSelectionOnRootLineBoxes())
-    return;
-  bitfields_.SetShouldInvalidateSelection(true);
-  SetMayNeedPaintInvalidation();
-  GetFrameView()->ScheduleVisualUpdateForPaintInvalidationIfNeeded();
-}
-
-void LayoutObject::SetShouldDoFullPaintInvalidation(
-    PaintInvalidationReason reason) {
-  SetNeedsPaintOffsetAndVisualRectUpdate();
-  SetShouldDoFullPaintInvalidationWithoutGeometryChange(reason);
-}
-
 void LayoutObject::SetShouldDoFullPaintInvalidationWithoutGeometryChange(
     PaintInvalidationReason reason) {
   // Only full invalidation reasons are allowed.
   DCHECK(IsFullPaintInvalidationReason(reason));
+  // This is before the early return to ensure visual update is always scheduled
+  // in case that this is called not during a document lifecycle update.
+  SetShouldCheckForPaintInvalidationWithoutGeometryChange();
 
-  bool is_upgrading_delayed_full_to_full =
-      bitfields_.FullPaintInvalidationReason() ==
-          PaintInvalidationReason::kDelayedFull &&
-      reason != PaintInvalidationReason::kDelayedFull;
-
-  if (bitfields_.FullPaintInvalidationReason() ==
-          PaintInvalidationReason::kNone ||
-      is_upgrading_delayed_full_to_full) {
-    if (reason == PaintInvalidationReason::kFull) {
-      reason = DocumentLifecycleBasedPaintInvalidationReason(
-          GetDocument().Lifecycle());
-    }
-    bitfields_.SetFullPaintInvalidationReason(reason);
-    if (!is_upgrading_delayed_full_to_full)
-      MarkAncestorsForPaintInvalidation();
+  if (ShouldDoFullPaintInvalidation())
+    return;
+  if (reason == PaintInvalidationReason::kFull) {
+    reason = DocumentLifecycleBasedPaintInvalidationReason(
+        GetDocument().Lifecycle());
   }
-
-  GetFrameView()->ScheduleVisualUpdateForPaintInvalidationIfNeeded();
+  full_paint_invalidation_reason_ = reason;
+  bitfields_.SetShouldDelayFullPaintInvalidation(false);
 }
 
-void LayoutObject::SetMayNeedPaintInvalidation() {
+void LayoutObject::SetShouldCheckForPaintInvalidation() {
+  SetShouldCheckForPaintInvalidationWithoutGeometryChange();
   SetNeedsPaintOffsetAndVisualRectUpdate();
-  SetMayNeedPaintInvalidationWithoutGeometryChange();
 }
 
-void LayoutObject::SetMayNeedPaintInvalidationWithoutGeometryChange() {
-  if (MayNeedPaintInvalidation())
-    return;
-  bitfields_.SetMayNeedPaintInvalidation(true);
-  MarkAncestorsForPaintInvalidation();
+void LayoutObject::SetShouldCheckForPaintInvalidationWithoutGeometryChange() {
+  // This is before the early return to ensure visual update is always scheduled
+  // in case that this is called not during a document lifecycle update.
   GetFrameView()->ScheduleVisualUpdateForPaintInvalidationIfNeeded();
+
+  if (ShouldCheckForPaintInvalidation())
+    return;
+  bitfields_.SetShouldCheckForPaintInvalidation(true);
+  MarkAncestorsForPaintInvalidation();
 }
 
-void LayoutObject::SetMayNeedPaintInvalidationSubtree() {
-  if (MayNeedPaintInvalidationSubtree())
+void LayoutObject::SetSubtreeShouldCheckForPaintInvalidation() {
+  if (SubtreeShouldCheckForPaintInvalidation()) {
+    DCHECK(ShouldCheckForPaintInvalidation());
     return;
-  bitfields_.SetMayNeedPaintInvalidationSubtree(true);
-  SetMayNeedPaintInvalidation();
+  }
+  SetShouldCheckForPaintInvalidation();
+  bitfields_.SetSubtreeShouldCheckForPaintInvalidation(true);
 }
 
 void LayoutObject::SetMayNeedPaintInvalidationAnimatedBackgroundImage() {
   if (MayNeedPaintInvalidationAnimatedBackgroundImage())
     return;
   bitfields_.SetMayNeedPaintInvalidationAnimatedBackgroundImage(true);
-  SetMayNeedPaintInvalidationWithoutGeometryChange();
+  SetShouldCheckForPaintInvalidationWithoutGeometryChange();
+}
+
+void LayoutObject::SetShouldDelayFullPaintInvalidation() {
+  // Should have already set a full paint invalidation reason.
+  DCHECK(IsFullPaintInvalidationReason(full_paint_invalidation_reason_));
+
+  bitfields_.SetShouldDelayFullPaintInvalidation(true);
+  if (!ShouldCheckForPaintInvalidation()) {
+    // This will also schedule a visual update.
+    SetShouldCheckForPaintInvalidationWithoutGeometryChange();
+  } else {
+    // Schedule visual update for the next document cycle in which we will
+    // check if the delayed invalidation should be promoted to a real
+    // invalidation.
+    GetFrameView()->ScheduleVisualUpdateForPaintInvalidationIfNeeded();
+  }
 }
 
 void LayoutObject::ClearPaintInvalidationFlags() {
@@ -3887,25 +3903,37 @@
   DCHECK(!ShouldCheckForPaintInvalidation() || PaintInvalidationStateIsDirty());
 #endif
   fragment_.SetPartialInvalidationLocalRect(LayoutRect());
-  ClearShouldDoFullPaintInvalidation();
-  bitfields_.SetMayNeedPaintInvalidation(false);
-  bitfields_.SetMayNeedPaintInvalidationSubtree(false);
+  if (!ShouldDelayFullPaintInvalidation())
+    full_paint_invalidation_reason_ = PaintInvalidationReason::kNone;
+  bitfields_.SetShouldCheckForPaintInvalidation(false);
+  bitfields_.SetSubtreeShouldCheckForPaintInvalidation(false);
+  bitfields_.SetSubtreeShouldDoFullPaintInvalidation(false);
   bitfields_.SetMayNeedPaintInvalidationAnimatedBackgroundImage(false);
   bitfields_.SetNeedsPaintOffsetAndVisualRectUpdate(false);
   bitfields_.SetShouldInvalidateSelection(false);
   bitfields_.SetBackgroundChangedSinceLastPaintInvalidation(false);
 }
 
+#if DCHECK_IS_ON()
+bool LayoutObject::PaintInvalidationStateIsDirty() const {
+  return BackgroundChangedSinceLastPaintInvalidation() ||
+         ShouldCheckForPaintInvalidation() || ShouldInvalidateSelection() ||
+         NeedsPaintOffsetAndVisualRectUpdate() ||
+         ShouldDoFullPaintInvalidation() ||
+         SubtreeShouldDoFullPaintInvalidation() ||
+         MayNeedPaintInvalidationAnimatedBackgroundImage() ||
+         !fragment_.PartialInvalidationLocalRect().IsEmpty();
+}
+#endif
+
 bool LayoutObject::IsAllowedToModifyLayoutTreeStructure(Document& document) {
   return document.Lifecycle().StateAllowsLayoutTreeMutations();
 }
 
-void LayoutObject::
-    SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants() {
-  // Clear first because PaintInvalidationSubtree overrides other full paint
-  // invalidation reasons.
-  ClearShouldDoFullPaintInvalidation();
-  SetShouldDoFullPaintInvalidation(PaintInvalidationReason::kSubtree);
+void LayoutObject::SetSubtreeShouldDoFullPaintInvalidation(
+    PaintInvalidationReason reason) {
+  SetShouldDoFullPaintInvalidation(reason);
+  bitfields_.SetSubtreeShouldDoFullPaintInvalidation(true);
 }
 
 void LayoutObject::SetIsBackgroundAttachmentFixedObject(
@@ -3974,7 +4002,7 @@
 void LayoutObject::InvalidateIfControlStateChanged(ControlState control_state) {
   if (LayoutTheme::GetTheme().ControlStateChanged(GetNode(), StyleRef(),
                                                   control_state)) {
-    SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
+    SetSubtreeShouldDoFullPaintInvalidation();
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index c41cd25d..6bee3a61 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -237,10 +237,11 @@
   // Do not call VisualRect directly outside of the DisplayItemClient
   // interface, use a per-fragment one on FragmentData instead.
  private:
-  // Hide DisplayItemClient methods whose names are too generic for
-  // LayoutObject. Use LayoutObject methods instead, or explicitly cast.
+  // Hide DisplayItemClient's methods whose names are too generic for
+  // LayoutObjects. Should use LayoutObject's methods instead.
   using DisplayItemClient::Invalidate;
   using DisplayItemClient::IsValid;
+  using DisplayItemClient::GetPaintInvalidationReason;
 
   LayoutRect VisualRect() const final;
 
@@ -1478,17 +1479,15 @@
   // a small region of a canvas changes.
   void InvalidatePaintRectangle(const LayoutRect&);
 
-  void SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
-
   // Returns the rect that should have paint invalidated whenever this object
   // changes. The rect is in the view's coordinate space. This method deals with
   // outlines and overflow.
   virtual LayoutRect AbsoluteVisualRect() const;
 
-  // Returns the rect that should have paint invalidated whenever this object
+  // Returns the rect that should have raster invalidated whenever this object
   // changes. The rect is in the object's local coordinate space. This is for
   // non-SVG objects and LayoutSVGRoot only. SVG objects (except LayoutSVGRoot)
-  // should use visualRectInLocalSVGCoordinates() and map with SVG transforms
+  // should use VisualRectInLocalSVGCoordinates() and map with SVG transforms
   // instead.
   LayoutRect LocalVisualRect() const {
     if (StyleRef().Visibility() != EVisibility::kVisible &&
@@ -1746,32 +1745,34 @@
   virtual void ClearPreviousVisualRects();
 
   PaintInvalidationReason FullPaintInvalidationReason() const {
-    return bitfields_.FullPaintInvalidationReason();
+    return full_paint_invalidation_reason_;
   }
   bool ShouldDoFullPaintInvalidation() const {
-    return bitfields_.FullPaintInvalidationReason() !=
-           PaintInvalidationReason::kNone;
+    if (!ShouldDelayFullPaintInvalidation() &&
+        full_paint_invalidation_reason_ != PaintInvalidationReason::kNone) {
+      DCHECK(IsFullPaintInvalidationReason(full_paint_invalidation_reason_));
+      DCHECK(ShouldCheckForPaintInvalidation());
+      return true;
+    }
+    return false;
   }
   void SetShouldDoFullPaintInvalidation(
       PaintInvalidationReason = PaintInvalidationReason::kFull);
   void SetShouldDoFullPaintInvalidationWithoutGeometryChange(
       PaintInvalidationReason = PaintInvalidationReason::kFull);
-  void ClearShouldDoFullPaintInvalidation() {
-    bitfields_.SetFullPaintInvalidationReason(PaintInvalidationReason::kNone);
-  }
 
   void ClearPaintInvalidationFlags();
 
-  bool MayNeedPaintInvalidation() const {
-    return bitfields_.MayNeedPaintInvalidation();
+  bool ShouldCheckForPaintInvalidation() const {
+    return bitfields_.ShouldCheckForPaintInvalidation();
   }
-  void SetMayNeedPaintInvalidation();
-  void SetMayNeedPaintInvalidationWithoutGeometryChange();
+  void SetShouldCheckForPaintInvalidation();
+  void SetShouldCheckForPaintInvalidationWithoutGeometryChange();
 
-  bool MayNeedPaintInvalidationSubtree() const {
-    return bitfields_.MayNeedPaintInvalidationSubtree();
+  bool SubtreeShouldCheckForPaintInvalidation() const {
+    return bitfields_.SubtreeShouldCheckForPaintInvalidation();
   }
-  void SetMayNeedPaintInvalidationSubtree();
+  void SetSubtreeShouldCheckForPaintInvalidation();
 
   bool NeedsPaintOffsetAndVisualRectUpdate() const {
     return bitfields_.NeedsPaintOffsetAndVisualRectUpdate();
@@ -1782,20 +1783,33 @@
   }
   void SetMayNeedPaintInvalidationAnimatedBackgroundImage();
 
+  void SetSubtreeShouldDoFullPaintInvalidation(
+      PaintInvalidationReason reason = PaintInvalidationReason::kSubtree);
+  bool SubtreeShouldDoFullPaintInvalidation() const {
+    DCHECK(!bitfields_.SubtreeShouldDoFullPaintInvalidation() ||
+           ShouldDoFullPaintInvalidation());
+    return bitfields_.SubtreeShouldDoFullPaintInvalidation();
+  }
+
+  // If true, it means that invalidation and repainting of the object can be
+  // delayed until a future frame. This can be the case for an object whose
+  // content is not visible to the user.
+  bool ShouldDelayFullPaintInvalidation() const {
+    return bitfields_.ShouldDelayFullPaintInvalidation();
+  }
+  void SetShouldDelayFullPaintInvalidation();
+
   bool ShouldInvalidateSelection() const {
     return bitfields_.ShouldInvalidateSelection();
   }
   void SetShouldInvalidateSelection();
 
-  bool ShouldCheckForPaintInvalidation() const {
-    return MayNeedPaintInvalidation() || ShouldDoFullPaintInvalidation();
-  }
-
   virtual LayoutRect ViewRect() const;
 
-  // New version to replace the above old version.
-  virtual PaintInvalidationReason InvalidatePaint(
-      const PaintInvalidatorContext&) const;
+  // Called by PaintInvalidator during PrePaint. Checks paint invalidation flags
+  // and other changes that will cause different painting, and invalidate
+  // display item clients for painting if needed.
+  virtual void InvalidatePaint(const PaintInvalidatorContext&) const;
 
   // When this object is invalidated for paint, this method is called to
   // invalidate any DisplayItemClients owned by this object, including the
@@ -1878,8 +1892,8 @@
       layout_object_.bitfields_
           .SetDescendantEffectiveWhitelistedTouchActionChanged(false);
     }
-    void SetMayNeedPaintInvalidation() {
-      layout_object_.SetMayNeedPaintInvalidation();
+    void SetShouldCheckForPaintInvalidation() {
+      layout_object_.SetShouldCheckForPaintInvalidation();
     }
     void SetShouldDoFullPaintInvalidation(PaintInvalidationReason reason) {
       layout_object_.SetShouldDoFullPaintInvalidation(reason);
@@ -1892,6 +1906,9 @@
     void SetBackgroundChangedSinceLastPaintInvalidation() {
       layout_object_.SetBackgroundChangedSinceLastPaintInvalidation();
     }
+    void SetShouldDelayFullPaintInvalidation() {
+      layout_object_.SetShouldDelayFullPaintInvalidation();
+    }
     void EnsureIsReadyForPaintInvalidation() {
       layout_object_.EnsureIsReadyForPaintInvalidation();
     }
@@ -2209,12 +2226,7 @@
                                        const LayoutPoint& layer_offset) const {}
 
 #if DCHECK_IS_ON()
-  virtual bool PaintInvalidationStateIsDirty() const {
-    return BackgroundChangedSinceLastPaintInvalidation() ||
-           ShouldCheckForPaintInvalidation() || ShouldInvalidateSelection() ||
-           NeedsPaintOffsetAndVisualRectUpdate() ||
-           !fragment_.PartialInvalidationLocalRect().IsEmpty();
-  }
+  virtual bool PaintInvalidationStateIsDirty() const;
 #endif
 
   // Called before paint invalidation.
@@ -2329,6 +2341,14 @@
   void ApplyPseudoStyleChanges(const ComputedStyle& old_style);
   void ApplyFirstLineChanges(const ComputedStyle& old_style);
 
+  // This is set by Set[Subtree]ShouldDoFullPaintInvalidation, and cleared
+  // during PrePaint in this object's InvalidatePaint(). It's different from
+  // DisplayItemClient::GetPaintInvalidationReason() which is set during
+  // PrePaint and cleared in PaintController::FinishCycle().
+  // It's defined as the first field so that it can use the memory gap between
+  // DisplayItemClient and LayoutObject's other fields.
+  PaintInvalidationReason full_paint_invalidation_reason_;
+
   scoped_refptr<ComputedStyle> style_;
 
   // Oilpan: This untraced pointer to the owning Node is considered safe.
@@ -2388,10 +2408,12 @@
           child_needs_overflow_recalc_after_style_change_(false),
           preferred_logical_widths_dirty_(false),
           needs_collect_inlines_(false),
-          may_need_paint_invalidation_(false),
-          may_need_paint_invalidation_subtree_(false),
+          should_check_for_paint_invalidation_(true),
+          subtree_should_check_for_paint_invalidation_(false),
+          should_delay_full_paint_invalidation_(false),
+          subtree_should_do_full_paint_invalidation_(false),
           may_need_paint_invalidation_animated_background_image_(false),
-          needs_paint_offset_and_visual_rect_update_(false),
+          needs_paint_offset_and_visual_rect_update_(true),
           should_invalidate_selection_(false),
           floating_(false),
           is_anonymous_(!node),
@@ -2434,9 +2456,7 @@
           is_effective_root_scroller_(false),
           positioned_state_(kIsStaticallyPositioned),
           selection_state_(static_cast<unsigned>(SelectionState::kNone)),
-          background_obscuration_state_(kBackgroundObscurationStatusInvalid),
-          full_paint_invalidation_reason_(
-              static_cast<unsigned>(PaintInvalidationReason::kNone)) {}
+          background_obscuration_state_(kBackgroundObscurationStatusInvalid) {}
 
     // Self needs layout means that this layout object is marked for a full
     // layout. This is the default layout but it is expensive as it recomputes
@@ -2500,10 +2520,14 @@
     // Also maybe set to inline boxes to optimize the propagation.
     ADD_BOOLEAN_BITFIELD(needs_collect_inlines_, NeedsCollectInlines);
 
-    ADD_BOOLEAN_BITFIELD(may_need_paint_invalidation_,
-                         MayNeedPaintInvalidation);
-    ADD_BOOLEAN_BITFIELD(may_need_paint_invalidation_subtree_,
-                         MayNeedPaintInvalidationSubtree);
+    ADD_BOOLEAN_BITFIELD(should_check_for_paint_invalidation_,
+                         ShouldCheckForPaintInvalidation);
+    ADD_BOOLEAN_BITFIELD(subtree_should_check_for_paint_invalidation_,
+                         SubtreeShouldCheckForPaintInvalidation);
+    ADD_BOOLEAN_BITFIELD(should_delay_full_paint_invalidation_,
+                         ShouldDelayFullPaintInvalidation);
+    ADD_BOOLEAN_BITFIELD(subtree_should_do_full_paint_invalidation_,
+                         SubtreeShouldDoFullPaintInvalidation);
     ADD_BOOLEAN_BITFIELD(may_need_paint_invalidation_animated_background_image_,
                          MayNeedPaintInvalidationAnimatedBackgroundImage);
     ADD_BOOLEAN_BITFIELD(needs_paint_offset_and_visual_rect_update_,
@@ -2665,12 +2689,7 @@
     unsigned selection_state_ : 3;   // SelectionState
     // Mutable for getter which lazily update this field.
     mutable unsigned
-        background_obscuration_state_ : 2;         // BackgroundObscurationState
-
-    unsigned full_paint_invalidation_reason_ : 5;  // PaintInvalidationReason
-    static_assert(static_cast<unsigned>(PaintInvalidationReason::kMax) <
-                      (1u << 5),
-                  "PaintInvalidationReason should fit in the bit field");
+        background_obscuration_state_ : 2;  // BackgroundObscurationState
 
    public:
     bool IsOutOfFlowPositioned() const {
@@ -2732,14 +2751,6 @@
         BackgroundObscurationState s) const {
       background_obscuration_state_ = s;
     }
-
-    PaintInvalidationReason FullPaintInvalidationReason() const {
-      return static_cast<PaintInvalidationReason>(
-          full_paint_invalidation_reason_);
-    }
-    void SetFullPaintInvalidationReason(PaintInvalidationReason reason) {
-      full_paint_invalidation_reason_ = static_cast<unsigned>(reason);
-    }
   };
 
 #undef ADD_BOOLEAN_BITFIELD
@@ -2841,7 +2852,7 @@
 inline void LayoutObject::ClearNeedsLayout() {
   // Set flags for later stages/cycles.
   SetEverHadLayout();
-  SetMayNeedPaintInvalidation();
+  SetShouldCheckForPaintInvalidation();
 
   // Clear needsLayout flags.
   SetSelfNeedsLayout(false);
diff --git a/third_party/blink/renderer/core/layout/layout_object_test.cc b/third_party/blink/renderer/core/layout/layout_object_test.cc
index 9f6b920..a40375c 100644
--- a/third_party/blink/renderer/core/layout/layout_object_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_test.cc
@@ -319,10 +319,10 @@
   object->SetShouldDoFullPaintInvalidation();
   EXPECT_TRUE(object->ShouldDoFullPaintInvalidation());
   EXPECT_TRUE(object->NeedsPaintOffsetAndVisualRectUpdate());
-  object->SetMayNeedPaintInvalidation();
-  EXPECT_TRUE(object->MayNeedPaintInvalidation());
-  object->SetMayNeedPaintInvalidationSubtree();
-  EXPECT_TRUE(object->MayNeedPaintInvalidationSubtree());
+  object->SetShouldCheckForPaintInvalidation();
+  EXPECT_TRUE(object->ShouldCheckForPaintInvalidation());
+  object->SetSubtreeShouldCheckForPaintInvalidation();
+  EXPECT_TRUE(object->SubtreeShouldCheckForPaintInvalidation());
   object->SetMayNeedPaintInvalidationAnimatedBackgroundImage();
   EXPECT_TRUE(object->MayNeedPaintInvalidationAnimatedBackgroundImage());
   object->SetShouldInvalidateSelection();
@@ -339,8 +339,8 @@
   object->GetMutableForPainting().ClearPaintFlags();
 
   EXPECT_FALSE(object->ShouldDoFullPaintInvalidation());
-  EXPECT_FALSE(object->MayNeedPaintInvalidation());
-  EXPECT_FALSE(object->MayNeedPaintInvalidationSubtree());
+  EXPECT_FALSE(object->ShouldCheckForPaintInvalidation());
+  EXPECT_FALSE(object->SubtreeShouldCheckForPaintInvalidation());
   EXPECT_FALSE(object->MayNeedPaintInvalidationAnimatedBackgroundImage());
   EXPECT_FALSE(object->ShouldInvalidateSelection());
   EXPECT_FALSE(object->BackgroundChangedSinceLastPaintInvalidation());
@@ -369,55 +369,55 @@
   object->SetShouldDoFullPaintInvalidation();
   EXPECT_TRUE(object->ShouldDoFullPaintInvalidation());
   EXPECT_TRUE(object->NeedsPaintOffsetAndVisualRectUpdate());
-  EXPECT_TRUE(parent->MayNeedPaintInvalidation());
+  EXPECT_TRUE(parent->ShouldCheckForPaintInvalidation());
   EXPECT_TRUE(parent->NeedsPaintOffsetAndVisualRectUpdate());
   object->ClearPaintInvalidationFlags();
   EXPECT_FALSE(object->ShouldDoFullPaintInvalidation());
   EXPECT_FALSE(object->NeedsPaintOffsetAndVisualRectUpdate());
   parent->ClearPaintInvalidationFlags();
-  EXPECT_FALSE(parent->MayNeedPaintInvalidation());
+  EXPECT_FALSE(parent->ShouldCheckForPaintInvalidation());
   EXPECT_FALSE(parent->NeedsPaintOffsetAndVisualRectUpdate());
 
-  object->SetMayNeedPaintInvalidation();
-  EXPECT_TRUE(object->MayNeedPaintInvalidation());
+  object->SetShouldCheckForPaintInvalidation();
+  EXPECT_TRUE(object->ShouldCheckForPaintInvalidation());
   EXPECT_TRUE(object->NeedsPaintOffsetAndVisualRectUpdate());
-  EXPECT_TRUE(parent->MayNeedPaintInvalidation());
+  EXPECT_TRUE(parent->ShouldCheckForPaintInvalidation());
   EXPECT_TRUE(parent->NeedsPaintOffsetAndVisualRectUpdate());
   object->ClearPaintInvalidationFlags();
-  EXPECT_FALSE(object->MayNeedPaintInvalidation());
+  EXPECT_FALSE(object->ShouldCheckForPaintInvalidation());
   EXPECT_FALSE(object->NeedsPaintOffsetAndVisualRectUpdate());
   parent->ClearPaintInvalidationFlags();
-  EXPECT_FALSE(parent->MayNeedPaintInvalidation());
+  EXPECT_FALSE(parent->ShouldCheckForPaintInvalidation());
   EXPECT_FALSE(parent->NeedsPaintOffsetAndVisualRectUpdate());
 
   object->SetShouldDoFullPaintInvalidationWithoutGeometryChange();
   EXPECT_TRUE(object->ShouldDoFullPaintInvalidation());
   EXPECT_FALSE(object->NeedsPaintOffsetAndVisualRectUpdate());
-  EXPECT_TRUE(parent->MayNeedPaintInvalidation());
+  EXPECT_TRUE(parent->ShouldCheckForPaintInvalidation());
   EXPECT_FALSE(parent->NeedsPaintOffsetAndVisualRectUpdate());
-  object->SetMayNeedPaintInvalidation();
+  object->SetShouldCheckForPaintInvalidation();
   EXPECT_TRUE(object->NeedsPaintOffsetAndVisualRectUpdate());
   EXPECT_TRUE(parent->NeedsPaintOffsetAndVisualRectUpdate());
   object->ClearPaintInvalidationFlags();
-  EXPECT_FALSE(object->MayNeedPaintInvalidation());
+  EXPECT_FALSE(object->ShouldCheckForPaintInvalidation());
   EXPECT_FALSE(object->NeedsPaintOffsetAndVisualRectUpdate());
   parent->ClearPaintInvalidationFlags();
-  EXPECT_FALSE(parent->MayNeedPaintInvalidation());
+  EXPECT_FALSE(parent->ShouldCheckForPaintInvalidation());
   EXPECT_FALSE(parent->NeedsPaintOffsetAndVisualRectUpdate());
 
-  object->SetMayNeedPaintInvalidationWithoutGeometryChange();
-  EXPECT_TRUE(object->MayNeedPaintInvalidation());
+  object->SetShouldCheckForPaintInvalidationWithoutGeometryChange();
+  EXPECT_TRUE(object->ShouldCheckForPaintInvalidation());
   EXPECT_FALSE(object->NeedsPaintOffsetAndVisualRectUpdate());
-  EXPECT_TRUE(parent->MayNeedPaintInvalidation());
+  EXPECT_TRUE(parent->ShouldCheckForPaintInvalidation());
   EXPECT_FALSE(parent->NeedsPaintOffsetAndVisualRectUpdate());
-  object->SetMayNeedPaintInvalidation();
+  object->SetShouldCheckForPaintInvalidation();
   EXPECT_TRUE(object->NeedsPaintOffsetAndVisualRectUpdate());
   EXPECT_TRUE(parent->NeedsPaintOffsetAndVisualRectUpdate());
   object->ClearPaintInvalidationFlags();
-  EXPECT_FALSE(object->MayNeedPaintInvalidation());
+  EXPECT_FALSE(object->ShouldCheckForPaintInvalidation());
   EXPECT_FALSE(object->NeedsPaintOffsetAndVisualRectUpdate());
   parent->ClearPaintInvalidationFlags();
-  EXPECT_FALSE(parent->MayNeedPaintInvalidation());
+  EXPECT_FALSE(parent->ShouldCheckForPaintInvalidation());
   EXPECT_FALSE(parent->NeedsPaintOffsetAndVisualRectUpdate());
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_table.cc b/third_party/blink/renderer/core/layout/layout_table.cc
index d123137..abc5986 100644
--- a/third_party/blink/renderer/core/layout/layout_table.cc
+++ b/third_party/blink/renderer/core/layout/layout_table.cc
@@ -476,7 +476,7 @@
     UpdateFragmentationInfoForChild(caption);
 
   if (!SelfNeedsLayout())
-    caption.SetMayNeedPaintInvalidation();
+    caption.SetShouldCheckForPaintInvalidation();
 
   SetLogicalHeight(LogicalHeight() + caption.LogicalHeight() +
                    CollapsedMarginBeforeForChild(caption) +
@@ -878,7 +878,7 @@
   collapsed_borders_valid_ = false;
   needs_invalidate_collapsed_borders_for_all_cells_ = true;
   collapsed_outer_borders_valid_ = false;
-  SetMayNeedPaintInvalidation();
+  SetShouldCheckForPaintInvalidation();
 }
 
 void LayoutTable::InvalidateCollapsedBordersForAllCellsIfNeeded() {
@@ -1638,9 +1638,9 @@
   }
 }
 
-PaintInvalidationReason LayoutTable::InvalidatePaint(
+void LayoutTable::InvalidatePaint(
     const PaintInvalidatorContext& context) const {
-  return TablePaintInvalidator(*this, context).InvalidatePaint();
+  TablePaintInvalidator(*this, context).InvalidatePaint();
 }
 
 LayoutUnit LayoutTable::PaddingTop() const {
diff --git a/third_party/blink/renderer/core/layout/layout_table.h b/third_party/blink/renderer/core/layout/layout_table.h
index 6b9835b..2c5835d 100644
--- a/third_party/blink/renderer/core/layout/layout_table.h
+++ b/third_party/blink/renderer/core/layout/layout_table.h
@@ -428,8 +428,7 @@
   void SimplifiedNormalFlowLayout() override;
   bool RecalcOverflowAfterStyleChange() override;
   void EnsureIsReadyForPaintInvalidation() override;
-  PaintInvalidationReason InvalidatePaint(
-      const PaintInvalidatorContext&) const override;
+  void InvalidatePaint(const PaintInvalidatorContext&) const override;
   bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override;
 
  private:
diff --git a/third_party/blink/renderer/core/layout/layout_table_cell.cc b/third_party/blink/renderer/core/layout/layout_table_cell.cc
index e08a856c..07472c7e 100644
--- a/third_party/blink/renderer/core/layout/layout_table_cell.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_cell.cc
@@ -1215,9 +1215,9 @@
   return LayoutBlock::HasLineIfEmpty();
 }
 
-PaintInvalidationReason LayoutTableCell::InvalidatePaint(
+void LayoutTableCell::InvalidatePaint(
     const PaintInvalidatorContext& context) const {
-  return TableCellPaintInvalidator(*this, context).InvalidatePaint();
+  TableCellPaintInvalidator(*this, context).InvalidatePaint();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_table_cell.h b/third_party/blink/renderer/core/layout/layout_table_cell.h
index 859bb4f..2299fe6 100644
--- a/third_party/blink/renderer/core/layout/layout_table_cell.h
+++ b/third_party/blink/renderer/core/layout/layout_table_cell.h
@@ -356,8 +356,7 @@
       const LayoutRect& container_rect,
       TouchAction container_whitelisted_touch_action) const override;
 
-  PaintInvalidationReason InvalidatePaint(
-      const PaintInvalidatorContext&) const override;
+  void InvalidatePaint(const PaintInvalidatorContext&) const override;
 
   LayoutSize OffsetFromContainerInternal(
       const LayoutObject*,
diff --git a/third_party/blink/renderer/core/layout/layout_table_section.cc b/third_party/blink/renderer/core/layout/layout_table_section.cc
index 07b1f20a..48c2d52 100644
--- a/third_party/blink/renderer/core/layout/layout_table_section.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_section.cc
@@ -1271,7 +1271,7 @@
         // need a layout. In this case, we know we're going to issue paint
         // invalidations ourselves (and the child) anyway.
         if (!Table()->SelfNeedsLayout())
-          cell->SetMayNeedPaintInvalidation();
+          cell->SetShouldCheckForPaintInvalidation();
       }
     }
     if (row)
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index 9aea5af..3241d32 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -477,13 +477,13 @@
 
 void LayoutView::SetShouldDoFullPaintInvalidationForViewAndAllDescendants() {
   if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
-    SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
+    SetSubtreeShouldDoFullPaintInvalidation();
   else
     SetShouldDoFullPaintInvalidationForViewAndAllDescendantsInternal(this);
 }
 
 void LayoutView::InvalidatePaintForViewAndCompositedLayers() {
-  SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
+  SetSubtreeShouldDoFullPaintInvalidation();
 
   // The only way we know how to hit these ASSERTS below this point is via the
   // Chromium OS login screen.
@@ -886,7 +886,7 @@
     if (NeedsLayout())
       return result;
     if (GetFrameView()->VisualViewportSuppliesScrollbars())
-      SetMayNeedPaintInvalidation();
+      SetShouldCheckForPaintInvalidation();
     GetFrameView()->AdjustViewSize();
     SetNeedsPaintPropertyUpdate();
     if (Layer())
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
index 2198215..37d63ad 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -268,7 +268,7 @@
 
   // When paint fragment is replaced, the subtree needs paint invalidation to
   // re-compute paint properties in NGPaintFragment.
-  Base::SetShouldDoFullPaintInvalidation(PaintInvalidationReason::kSubtree);
+  Base::SetSubtreeShouldDoFullPaintInvalidation();
 }
 
 template <typename Base>
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index d120691..1c399d52 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -207,7 +207,7 @@
       // -dynamic.html
       // TODO(layoutng): See if we can optimize this. When we natively
       // support relative positioning in NG we can probably remove this,
-      box_->SetMayNeedPaintInvalidation();
+      box_->SetShouldCheckForPaintInvalidation();
 
       // We have to re-set the cached result here, because it is used for
       // LayoutNGMixin::CurrentFragment and therefore has to be up-to-date.
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
index c1d14a5..ba9064af 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
@@ -115,7 +115,7 @@
     // if the client is one that could have a LayoutSVGInlineText use a
     // paint invalidation reason that will force paint invalidation of the
     // entire <text>/<tspan>/... subtree.
-    client.SetShouldDoFullPaintInvalidation(
+    client.SetSubtreeShouldDoFullPaintInvalidation(
         PaintInvalidationReason::kSVGResource);
     client.InvalidateClipPathCache();
     // Invalidate paint properties to update effects if any.
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc
index 9e256694..659ee17 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc
@@ -127,7 +127,6 @@
 }
 
 void LayoutSVGResourceMarker::SetNeedsTransformUpdate() {
-  SetMayNeedPaintInvalidationSubtree();
   // The transform paint property relies on the SVG transform being up-to-date
   // (see: PaintPropertyTreeBuilder::updateTransformForNonRootSVG).
   SetNeedsPaintPropertyUpdate();
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
index af6a5074..6de0dd4 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
@@ -196,7 +196,7 @@
   // mark the entire subtree as needing paint invalidation checking.
   if (transform_change != SVGTransformChange::kNone ||
       viewport_may_have_changed) {
-    SetMayNeedPaintInvalidationSubtree();
+    SetSubtreeShouldCheckForPaintInvalidation();
     SetNeedsPaintPropertyUpdate();
 
     if (Layer())
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_transformable_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_transformable_container.cc
index 5ed2cc5..9859027 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_transformable_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_transformable_container.cc
@@ -68,7 +68,6 @@
 }
 
 void LayoutSVGTransformableContainer::SetNeedsTransformUpdate() {
-  SetMayNeedPaintInvalidationSubtree();
   // The transform paint property relies on the SVG transform being up-to-date
   // (see: PaintPropertyTreeBuilder::updateTransformForNonRootSVG).
   SetNeedsPaintPropertyUpdate();
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.cc
index d86c420..e03a672e 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.cc
@@ -56,7 +56,6 @@
 }
 
 void LayoutSVGViewportContainer::SetNeedsTransformUpdate() {
-  SetMayNeedPaintInvalidationSubtree();
   // The transform paint property relies on the SVG transform being up-to-date
   // (see: PaintPropertyTreeBuilder::updateTransformForNonRootSVG).
   SetNeedsPaintPropertyUpdate();
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 7ef8deca..5fae42f6 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1160,7 +1160,7 @@
       parser_->AsScriptableDocumentParser();
   if (scriptable_parser && GetResource()) {
     scriptable_parser->SetInlineScriptCacheHandler(
-        ToRawResource(GetResource())->CacheHandler());
+        ToRawResource(GetResource())->InlineScriptCacheHandler());
   }
 
   // FeaturePolicy is reset in the browser process on commit, so this needs to
diff --git a/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc b/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
index 248409d..a013c09 100644
--- a/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
+++ b/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
@@ -118,7 +118,7 @@
 
   // This manual invalidation is necessary to avoid a DCHECK failure in
   // FindVisualRectNeedingUpdateScopeBase::CheckVisualRect().
-  FrameView().GetLayoutView()->SetMayNeedPaintInvalidationSubtree();
+  FrameView().GetLayoutView()->SetSubtreeShouldCheckForPaintInvalidation();
 
   FrameView().UpdateAllLifecyclePhases();
 }
diff --git a/third_party/blink/renderer/core/paint/BUILD.gn b/third_party/blink/renderer/core/paint/BUILD.gn
index d8e2d9b4f..e85edd0 100644
--- a/third_party/blink/renderer/core/paint/BUILD.gn
+++ b/third_party/blink/renderer/core/paint/BUILD.gn
@@ -78,8 +78,6 @@
     "ellipsis_box_painter.h",
     "embedded_content_painter.cc",
     "embedded_content_painter.h",
-    "embedded_object_paint_invalidator.cc",
-    "embedded_object_paint_invalidator.h",
     "embedded_object_painter.cc",
     "embedded_object_painter.h",
     "fallback_theme.cc",
@@ -102,8 +100,6 @@
     "frame_set_painter.h",
     "grid_painter.cc",
     "grid_painter.h",
-    "html_canvas_paint_invalidator.cc",
-    "html_canvas_paint_invalidator.h",
     "html_canvas_painter.cc",
     "html_canvas_painter.h",
     "image_painter.cc",
diff --git a/third_party/blink/renderer/core/paint/block_paint_invalidator.cc b/third_party/blink/renderer/core/paint/block_paint_invalidator.cc
index f9a48ba5..633e8e8 100644
--- a/third_party/blink/renderer/core/paint/block_paint_invalidator.cc
+++ b/third_party/blink/renderer/core/paint/block_paint_invalidator.cc
@@ -20,15 +20,12 @@
   block_.GetFrame()->GetPage()->GetDragCaret().ClearPreviousVisualRect(block_);
 }
 
-PaintInvalidationReason BlockPaintInvalidator::InvalidatePaint(
+void BlockPaintInvalidator::InvalidatePaint(
     const PaintInvalidatorContext& context) {
-  PaintInvalidationReason reason =
-      BoxPaintInvalidator(block_, context).InvalidatePaint();
+  BoxPaintInvalidator(block_, context).InvalidatePaint();
 
   block_.GetFrame()->Selection().InvalidatePaint(block_, context);
   block_.GetFrame()->GetPage()->GetDragCaret().InvalidatePaint(block_, context);
-
-  return reason;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/block_paint_invalidator.h b/third_party/blink/renderer/core/paint/block_paint_invalidator.h
index 988b0ce2..7101dbdc 100644
--- a/third_party/blink/renderer/core/paint/block_paint_invalidator.h
+++ b/third_party/blink/renderer/core/paint/block_paint_invalidator.h
@@ -20,7 +20,7 @@
   BlockPaintInvalidator(const LayoutBlock& block) : block_(block) {}
 
   void ClearPreviousVisualRects();
-  PaintInvalidationReason InvalidatePaint(const PaintInvalidatorContext&);
+  void InvalidatePaint(const PaintInvalidatorContext&);
 
  private:
   const LayoutBlock& block_;
diff --git a/third_party/blink/renderer/core/paint/box_paint_invalidator.cc b/third_party/blink/renderer/core/paint/box_paint_invalidator.cc
index 562dcfd..82ac60a 100644
--- a/third_party/blink/renderer/core/paint/box_paint_invalidator.cc
+++ b/third_party/blink/renderer/core/paint/box_paint_invalidator.cc
@@ -225,36 +225,17 @@
   }
 }
 
-PaintInvalidationReason BoxPaintInvalidator::InvalidatePaint() {
+void BoxPaintInvalidator::InvalidatePaint() {
   InvalidateBackground();
 
-  PaintInvalidationReason reason = ComputePaintInvalidationReason();
-  if (reason == PaintInvalidationReason::kIncremental) {
-    if (box_.PreviousSize() != box_.Size()) {
-      context_.painting_layer->SetNeedsRepaint();
-      box_.InvalidateDisplayItemClients(reason);
-    } else {
-      reason = PaintInvalidationReason::kNone;
-    }
-
-    // Though we have done incremental invalidation, we still need to call
-    // ObjectPaintInvalidator with PaintInvalidationNone to do any other
-    // required operations.
-    reason = std::max(reason, ObjectPaintInvalidatorWithContext(box_, context_)
-                                  .InvalidatePaintWithComputedReason(
-                                      PaintInvalidationReason::kNone));
-  } else {
-    reason = ObjectPaintInvalidatorWithContext(box_, context_)
-                 .InvalidatePaintWithComputedReason(reason);
-  }
+  ObjectPaintInvalidatorWithContext(box_, context_)
+      .InvalidatePaintWithComputedReason(ComputePaintInvalidationReason());
 
   if (PaintLayerScrollableArea* area = box_.GetScrollableArea())
     area->InvalidatePaintOfScrollControlsIfNeeded(context_);
 
   // This is for the next invalidatePaintIfNeeded so must be at the end.
   SavePreviousBoxGeometriesIfNeeded();
-
-  return reason;
 }
 
 bool BoxPaintInvalidator::
diff --git a/third_party/blink/renderer/core/paint/box_paint_invalidator.h b/third_party/blink/renderer/core/paint/box_paint_invalidator.h
index 120692d..9cd71dc 100644
--- a/third_party/blink/renderer/core/paint/box_paint_invalidator.h
+++ b/third_party/blink/renderer/core/paint/box_paint_invalidator.h
@@ -25,7 +25,7 @@
 
   static void BoxWillBeDestroyed(const LayoutBox&);
 
-  PaintInvalidationReason InvalidatePaint();
+  void InvalidatePaint();
 
  private:
   friend class BoxPaintInvalidatorTest;
diff --git a/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc b/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc
index 92c92a0..fd7efda 100644
--- a/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc
+++ b/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc
@@ -46,7 +46,7 @@
 
     GetDocument().View()->UpdateAllLifecyclePhases();
     auto& target = *GetDocument().getElementById("target");
-    const auto& box = *ToLayoutBox(target.GetLayoutObject());
+    auto& box = *ToLayoutBox(target.GetLayoutObject());
     LayoutRect visual_rect = box.FirstFragment().VisualRect();
     LayoutPoint paint_offset = box.FirstFragment().PaintOffset();
 
@@ -79,6 +79,9 @@
           height: 100px;
           transform-origin: 0 0;
         }
+        .background {
+          background: blue;
+        }
         .border {
           border-width: 20px 10px;
           border-style: solid;
@@ -105,7 +108,6 @@
 
   EXPECT_TRUE(box.PaintedOutputOfObjectHasNoEffectRegardlessOfSize());
   LayoutRect visual_rect = box.FirstFragment().VisualRect();
-  EXPECT_EQ(LayoutRect(0, 0, 50, 100), visual_rect);
 
   // No geometry change.
   EXPECT_EQ(
@@ -139,7 +141,7 @@
   target.setAttribute(HTMLNames::styleAttr, "background: blue");
   GetDocument().View()->UpdateAllLifecyclePhases();
 
-  box.SetMayNeedPaintInvalidation();
+  box.SetShouldCheckForPaintInvalidation();
   LayoutRect visual_rect = box.FirstFragment().VisualRect();
   EXPECT_EQ(LayoutRect(0, 0, 50, 100), visual_rect);
 
@@ -186,8 +188,8 @@
   // The target initially has border.
   ExpectFullPaintInvalidationOnGeometryChange("With border");
 
-  // Clear border.
-  target.setAttribute(HTMLNames::classAttr, "");
+  // Clear border, set background.
+  target.setAttribute(HTMLNames::classAttr, "background");
   target.setAttribute(HTMLNames::styleAttr, "border-radius: 5px");
   ExpectFullPaintInvalidationOnGeometryChange("With border-radius");
 
diff --git a/third_party/blink/renderer/core/paint/box_painter.cc b/third_party/blink/renderer/core/paint/box_painter.cc
index b856f70..473b45f 100644
--- a/third_party/blink/renderer/core/paint/box_painter.cc
+++ b/third_party/blink/renderer/core/paint/box_painter.cc
@@ -111,8 +111,7 @@
   // non-delayed-invalidation animated background, which should be ignored.
   if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
       (style.Appearance() == kMediaSliderPart ||
-       layout_box_.FullPaintInvalidationReason() ==
-           PaintInvalidationReason::kDelayedFull)) {
+       layout_box_.ShouldDelayFullPaintInvalidation())) {
     cache_skipper.emplace(paint_info.context);
   }
 
diff --git a/third_party/blink/renderer/core/paint/embedded_object_paint_invalidator.cc b/third_party/blink/renderer/core/paint/embedded_object_paint_invalidator.cc
deleted file mode 100644
index c81ef41..0000000
--- a/third_party/blink/renderer/core/paint/embedded_object_paint_invalidator.cc
+++ /dev/null
@@ -1,24 +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/core/paint/embedded_object_paint_invalidator.h"
-
-#include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
-#include "third_party/blink/renderer/core/layout/layout_embedded_object.h"
-#include "third_party/blink/renderer/core/paint/box_paint_invalidator.h"
-
-namespace blink {
-
-PaintInvalidationReason EmbeddedObjectPaintInvalidator::InvalidatePaint() {
-  PaintInvalidationReason reason =
-      BoxPaintInvalidator(embedded_object_, context_).InvalidatePaint();
-
-  WebPluginContainerImpl* plugin = embedded_object_.Plugin();
-  if (plugin)
-    plugin->InvalidatePaint();
-
-  return reason;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/embedded_object_paint_invalidator.h b/third_party/blink/renderer/core/paint/embedded_object_paint_invalidator.h
deleted file mode 100644
index d9c0a11..0000000
--- a/third_party/blink/renderer/core/paint/embedded_object_paint_invalidator.h
+++ /dev/null
@@ -1,33 +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_CORE_PAINT_EMBEDDED_OBJECT_PAINT_INVALIDATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_EMBEDDED_OBJECT_PAINT_INVALIDATOR_H_
-
-#include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
-#include "third_party/blink/renderer/platform/wtf/allocator.h"
-
-namespace blink {
-
-class LayoutEmbeddedObject;
-struct PaintInvalidatorContext;
-
-class EmbeddedObjectPaintInvalidator {
-  STACK_ALLOCATED();
-
- public:
-  EmbeddedObjectPaintInvalidator(const LayoutEmbeddedObject& embedded_object,
-                                 const PaintInvalidatorContext& context)
-      : embedded_object_(embedded_object), context_(context) {}
-
-  PaintInvalidationReason InvalidatePaint();
-
- private:
-  const LayoutEmbeddedObject& embedded_object_;
-  const PaintInvalidatorContext& context_;
-};
-
-}  // namespace blink
-
-#endif
diff --git a/third_party/blink/renderer/core/paint/html_canvas_paint_invalidator.cc b/third_party/blink/renderer/core/paint/html_canvas_paint_invalidator.cc
deleted file mode 100644
index c6e78348..0000000
--- a/third_party/blink/renderer/core/paint/html_canvas_paint_invalidator.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/paint/html_canvas_paint_invalidator.h"
-
-#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
-#include "third_party/blink/renderer/core/layout/layout_html_canvas.h"
-#include "third_party/blink/renderer/core/paint/box_paint_invalidator.h"
-#include "third_party/blink/renderer/core/paint/paint_invalidator.h"
-
-namespace blink {
-
-PaintInvalidationReason HTMLCanvasPaintInvalidator::InvalidatePaint() {
-  auto* element = ToHTMLCanvasElement(html_canvas_.GetNode());
-  if (element->IsDirty())
-    element->DoDeferredPaintInvalidation();
-
-  return BoxPaintInvalidator(html_canvas_, context_).InvalidatePaint();
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/html_canvas_paint_invalidator.h b/third_party/blink/renderer/core/paint/html_canvas_paint_invalidator.h
deleted file mode 100644
index fdd94b4..0000000
--- a/third_party/blink/renderer/core/paint/html_canvas_paint_invalidator.h
+++ /dev/null
@@ -1,33 +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_CORE_PAINT_HTML_CANVAS_PAINT_INVALIDATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_HTML_CANVAS_PAINT_INVALIDATOR_H_
-
-#include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
-#include "third_party/blink/renderer/platform/wtf/allocator.h"
-
-namespace blink {
-
-class LayoutHTMLCanvas;
-struct PaintInvalidatorContext;
-
-class HTMLCanvasPaintInvalidator {
-  STACK_ALLOCATED();
-
- public:
-  HTMLCanvasPaintInvalidator(const LayoutHTMLCanvas& html_canvas,
-                             const PaintInvalidatorContext& context)
-      : html_canvas_(html_canvas), context_(context) {}
-
-  PaintInvalidationReason InvalidatePaint();
-
- private:
-  const LayoutHTMLCanvas& html_canvas_;
-  const PaintInvalidatorContext& context_;
-};
-
-}  // namespace blink
-
-#endif
diff --git a/third_party/blink/renderer/core/paint/object_paint_invalidator.cc b/third_party/blink/renderer/core/paint/object_paint_invalidator.cc
index 88f1a5c6..eb66fe4 100644
--- a/third_party/blink/renderer/core/paint/object_paint_invalidator.cc
+++ b/third_party/blink/renderer/core/paint/object_paint_invalidator.cc
@@ -285,7 +285,7 @@
 }
 
 DISABLE_CFI_PERF
-void ObjectPaintInvalidatorWithContext::InvalidateSelection(
+PaintInvalidationReason ObjectPaintInvalidatorWithContext::InvalidateSelection(
     PaintInvalidationReason reason) {
   // In LayoutNG, if NGPaintFragment paints the selection, we invalidate for
   // selection change in PaintInvalidator.
@@ -294,15 +294,15 @@
       !object_.IsLayoutReplaced() &&
       NGPaintFragment::InlineFragmentsFor(&object_)
           .IsInLayoutNGInlineFormattingContext())
-    return;
+    return reason;
 
   // Update selection rect when we are doing full invalidation with geometry
   // change (in case that the object is moved, composite status changed, etc.)
   // or shouldInvalidationSelection is set (in case that the selection itself
   // changed).
-  bool full_invalidation = IsImmediateFullPaintInvalidationReason(reason);
+  bool full_invalidation = IsFullPaintInvalidationReason(reason);
   if (!full_invalidation && !object_.ShouldInvalidateSelection())
-    return;
+    return reason;
 
   LayoutRect old_selection_rect = object_.SelectionVisualRect();
   LayoutRect new_selection_rect;
@@ -320,70 +320,50 @@
   object_.GetMutableForPainting().SetSelectionVisualRect(new_selection_rect);
 
   if (full_invalidation)
-    return;
+    return reason;
 
   object_.GetMutableForPainting().SetPartialInvalidationVisualRect(
       UnionRect(object_.PartialInvalidationVisualRect(),
                 UnionRect(new_selection_rect, old_selection_rect)));
-  context_.painting_layer->SetNeedsRepaint();
-  object_.InvalidateDisplayItemClients(PaintInvalidationReason::kSelection);
-}
-
-DISABLE_CFI_PERF
-void ObjectPaintInvalidatorWithContext::InvalidatePartialRect(
-    PaintInvalidationReason reason) {
-  if (IsImmediateFullPaintInvalidationReason(reason))
-    return;
-
-  auto rect = object_.PartialInvalidationLocalRect();
-  if (rect.IsEmpty())
-    return;
-
-  context_.MapLocalRectToVisualRect(object_, rect);
-  if (rect.IsEmpty())
-    return;
-
-  object_.GetMutableForPainting().SetPartialInvalidationVisualRect(
-      UnionRect(object_.PartialInvalidationVisualRect(), rect));
-
-  context_.painting_layer->SetNeedsRepaint();
-  object_.InvalidateDisplayItemClients(PaintInvalidationReason::kRectangle);
+  return PaintInvalidationReason::kSelection;
 }
 
 DISABLE_CFI_PERF
 PaintInvalidationReason
-ObjectPaintInvalidatorWithContext::InvalidatePaintWithComputedReason(
+ObjectPaintInvalidatorWithContext::InvalidatePartialRect(
+    PaintInvalidationReason reason) {
+  if (IsFullPaintInvalidationReason(reason))
+    return reason;
+
+  auto rect = object_.PartialInvalidationLocalRect();
+  if (rect.IsEmpty())
+    return reason;
+
+  context_.MapLocalRectToVisualRect(object_, rect);
+  if (rect.IsEmpty())
+    return reason;
+
+  object_.GetMutableForPainting().SetPartialInvalidationVisualRect(
+      UnionRect(object_.PartialInvalidationVisualRect(), rect));
+
+  return PaintInvalidationReason::kRectangle;
+}
+
+DISABLE_CFI_PERF
+void ObjectPaintInvalidatorWithContext::InvalidatePaintWithComputedReason(
     PaintInvalidationReason reason) {
   DCHECK(!(context_.subtree_flags &
            PaintInvalidatorContext::kSubtreeNoInvalidation));
 
-  // This is before InvalidateSelection before the latter will accumulate
-  // selection visual rects to the partial rect mapped in the former.
-  InvalidatePartialRect(reason);
-
-  // We need to invalidate the selection before checking for whether we are
-  // doing a full invalidation.  This is because we need to update the previous
-  // selection rect regardless.
-  InvalidateSelection(reason);
-
-  switch (reason) {
-    case PaintInvalidationReason::kNone:
-      if (object_.IsSVG() &&
-          (context_.subtree_flags &
-           PaintInvalidatorContext::kSubtreeSVGResourceChange)) {
-        reason = PaintInvalidationReason::kSVGResource;
-        break;
-      }
-      return PaintInvalidationReason::kNone;
-    case PaintInvalidationReason::kDelayedFull:
-      return PaintInvalidationReason::kDelayedFull;
-    default:
-      DCHECK(IsImmediateFullPaintInvalidationReason(reason));
-  }
+  // InvalidatePartialRect is before InvalidateSelection because the latter will
+  // accumulate selection visual rects to the partial rect mapped in the former.
+  reason = InvalidatePartialRect(reason);
+  reason = InvalidateSelection(reason);
+  if (reason == PaintInvalidationReason::kNone)
+    return;
 
   context_.painting_layer->SetNeedsRepaint();
   object_.InvalidateDisplayItemClients(reason);
-  return reason;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/object_paint_invalidator.h b/third_party/blink/renderer/core/paint/object_paint_invalidator.h
index af5aad1..a0605c26 100644
--- a/third_party/blink/renderer/core/paint/object_paint_invalidator.h
+++ b/third_party/blink/renderer/core/paint/object_paint_invalidator.h
@@ -56,17 +56,16 @@
                                     const PaintInvalidatorContext& context)
       : ObjectPaintInvalidator(object), context_(context) {}
 
-  PaintInvalidationReason InvalidatePaint() {
-    return InvalidatePaintWithComputedReason(ComputePaintInvalidationReason());
+  void InvalidatePaint() {
+    InvalidatePaintWithComputedReason(ComputePaintInvalidationReason());
   }
 
   PaintInvalidationReason ComputePaintInvalidationReason();
-  PaintInvalidationReason InvalidatePaintWithComputedReason(
-      PaintInvalidationReason);
+  void InvalidatePaintWithComputedReason(PaintInvalidationReason);
 
  private:
-  void InvalidateSelection(PaintInvalidationReason);
-  void InvalidatePartialRect(PaintInvalidationReason);
+  PaintInvalidationReason InvalidateSelection(PaintInvalidationReason);
+  PaintInvalidationReason InvalidatePartialRect(PaintInvalidationReason);
 
   const PaintInvalidatorContext& context_;
 };
diff --git a/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc b/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc
index 5f053347..88c8cfd 100644
--- a/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc
+++ b/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc
@@ -272,7 +272,7 @@
   EXPECT_EQ(LayoutRect(10, 10, 50, 50), target->PartialInvalidationLocalRect());
   target->InvalidatePaintRectangle(LayoutRect(30, 30, 60, 60));
   EXPECT_EQ(LayoutRect(10, 10, 80, 80), target->PartialInvalidationLocalRect());
-  EXPECT_TRUE(target->MayNeedPaintInvalidation());
+  EXPECT_TRUE(target->ShouldCheckForPaintInvalidation());
 
   GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
   EXPECT_EQ(LayoutRect(), target->PartialInvalidationLocalRect());
@@ -331,7 +331,7 @@
 
   // Simulate a change without full invalidation or selection change.
   GetDocument().View()->SetTracksPaintInvalidations(true);
-  target->SetMayNeedPaintInvalidation();
+  target->SetShouldCheckForPaintInvalidation();
   GetDocument().View()->UpdateAllLifecyclePhases();
   EXPECT_TRUE(graphics_layer->GetRasterInvalidationTracking()
                   ->Invalidations()
diff --git a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
index b602bf0..367fbce 100644
--- a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
@@ -645,7 +645,8 @@
 
   ScrollableArea* scrollable_area = GetDocument().View()->LayoutViewport();
   ASSERT_EQ(scrollable_area->MaximumScrollOffset().Height(), 0);
-  EXPECT_FALSE(GetDocument().GetLayoutView()->MayNeedPaintInvalidation());
+  EXPECT_FALSE(
+      GetDocument().GetLayoutView()->ShouldCheckForPaintInvalidation());
 
   Element* container = GetDocument().getElementById("container");
   container->setAttribute(HTMLNames::styleAttr,
@@ -653,7 +654,7 @@
   GetDocument().UpdateStyleAndLayoutTree();
 
   EXPECT_EQ(scrollable_area->MaximumScrollOffset().Height(), 1000);
-  EXPECT_TRUE(GetDocument().GetLayoutView()->MayNeedPaintInvalidation());
+  EXPECT_TRUE(GetDocument().GetLayoutView()->ShouldCheckForPaintInvalidation());
 }
 
 TEST_P(PaintAndRasterInvalidationTest,
@@ -696,17 +697,26 @@
 
   auto* target = GetLayoutObjectByElementId("target");
   target->SetShouldDoFullPaintInvalidationWithoutGeometryChange(
-      PaintInvalidationReason::kDelayedFull);
-  EXPECT_EQ(PaintInvalidationReason::kDelayedFull,
+      PaintInvalidationReason::kForTesting);
+  target->SetShouldDelayFullPaintInvalidation();
+  EXPECT_FALSE(target->ShouldDoFullPaintInvalidation());
+  EXPECT_TRUE(target->ShouldDelayFullPaintInvalidation());
+  EXPECT_EQ(PaintInvalidationReason::kForTesting,
             target->FullPaintInvalidationReason());
   EXPECT_FALSE(target->NeedsPaintOffsetAndVisualRectUpdate());
+  EXPECT_TRUE(target->ShouldCheckForPaintInvalidation());
+  EXPECT_TRUE(target->Parent()->ShouldCheckForPaintInvalidation());
 
   GetDocument().View()->SetTracksPaintInvalidations(true);
   GetDocument().View()->UpdateAllLifecyclePhases();
   EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations());
-  EXPECT_EQ(PaintInvalidationReason::kDelayedFull,
+  EXPECT_FALSE(target->ShouldDoFullPaintInvalidation());
+  EXPECT_TRUE(target->ShouldDelayFullPaintInvalidation());
+  EXPECT_EQ(PaintInvalidationReason::kForTesting,
             target->FullPaintInvalidationReason());
   EXPECT_FALSE(target->NeedsPaintOffsetAndVisualRectUpdate());
+  EXPECT_TRUE(target->ShouldCheckForPaintInvalidation());
+  EXPECT_TRUE(target->Parent()->ShouldCheckForPaintInvalidation());
   GetDocument().View()->SetTracksPaintInvalidations(false);
 
   GetDocument().View()->SetTracksPaintInvalidations(true);
@@ -716,9 +726,12 @@
   EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
               UnorderedElementsAre(RasterInvalidationInfo{
                   target, target->DebugName(), IntRect(0, 4000, 100, 100),
-                  PaintInvalidationReason::kFull}));
+                  PaintInvalidationReason::kForTesting}));
   EXPECT_EQ(PaintInvalidationReason::kNone,
             target->FullPaintInvalidationReason());
+  EXPECT_FALSE(target->ShouldDelayFullPaintInvalidation());
+  EXPECT_FALSE(target->ShouldCheckForPaintInvalidation());
+  EXPECT_FALSE(target->Parent()->ShouldCheckForPaintInvalidation());
   EXPECT_FALSE(target->NeedsPaintOffsetAndVisualRectUpdate());
   GetDocument().View()->SetTracksPaintInvalidations(false);
 };
@@ -748,14 +761,6 @@
 
   GetDocument().View()->SetTracksPaintInvalidations(true);
   ToElement(mask_rect->GetNode())->setAttribute("x", "20");
-  GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
-
-  EXPECT_EQ(PaintInvalidationReason::kFull,
-            real_rect->GetPaintInvalidationReason());
-  // mask_rect is not cached and validated by any PaintController.
-  EXPECT_EQ(PaintInvalidationReason::kJustCreated,
-            mask_rect->GetPaintInvalidationReason());
-
   GetDocument().View()->UpdateAllLifecyclePhases();
 
   EXPECT_EQ(LayoutRect(), mask_rect->FirstFragment().VisualRect());
diff --git a/third_party/blink/renderer/core/paint/paint_invalidator.cc b/third_party/blink/renderer/core/paint/paint_invalidator.cc
index 26487286..29de612ed 100644
--- a/third_party/blink/renderer/core/paint/paint_invalidator.cc
+++ b/third_party/blink/renderer/core/paint/paint_invalidator.cc
@@ -501,27 +501,22 @@
       UpdateVisualRect(object, *fragment_data, context);
     }
 
-    PaintInvalidationReason reason = object.InvalidatePaint(context);
-    switch (reason) {
-      case PaintInvalidationReason::kDelayedFull:
-        pending_delayed_paint_invalidations_.push_back(&object);
-        break;
-      case PaintInvalidationReason::kSubtree:
-        context.subtree_flags |=
-            (PaintInvalidatorContext::kSubtreeFullInvalidation |
-             PaintInvalidatorContext::
-                 kSubtreeFullInvalidationForStackedContents);
-        break;
-      case PaintInvalidationReason::kSVGResource:
-        context.subtree_flags |=
-            PaintInvalidatorContext::kSubtreeSVGResourceChange;
-        break;
-      default:
-        break;
-    }
+    object.InvalidatePaint(context);
   }
 
-  if (object.MayNeedPaintInvalidationSubtree()) {
+  auto reason = static_cast<const DisplayItemClient&>(object)
+                    .GetPaintInvalidationReason();
+  if (object.ShouldDelayFullPaintInvalidation() &&
+      !IsFullPaintInvalidationReason(reason))
+    pending_delayed_paint_invalidations_.push_back(&object);
+
+  if (object.SubtreeShouldDoFullPaintInvalidation()) {
+    context.subtree_flags |=
+        PaintInvalidatorContext::kSubtreeFullInvalidation |
+        PaintInvalidatorContext::kSubtreeFullInvalidationForStackedContents;
+  }
+
+  if (object.SubtreeShouldCheckForPaintInvalidation()) {
     context.subtree_flags |=
         PaintInvalidatorContext::kSubtreeInvalidationChecking;
   }
@@ -547,16 +542,13 @@
   // TODO(wangxianzhu): Do we need this for SPv2?
   if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled() &&
       !context.paint_invalidation_container->IsPaintInvalidationContainer() &&
-      object.GetPaintInvalidationReason() != PaintInvalidationReason::kNone)
+      reason != PaintInvalidationReason::kNone)
     InvalidateChromeClient(*context.paint_invalidation_container);
 }
 
 void PaintInvalidator::ProcessPendingDelayedPaintInvalidations() {
-  for (auto* target : pending_delayed_paint_invalidations_) {
-    target->GetMutableForPainting()
-        .SetShouldDoFullPaintInvalidationWithoutGeometryChange(
-            PaintInvalidationReason::kDelayedFull);
-  }
+  for (auto* target : pending_delayed_paint_invalidations_)
+    target->GetMutableForPainting().SetShouldDelayFullPaintInvalidation();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_invalidator.h b/third_party/blink/renderer/core/paint/paint_invalidator.h
index 7cb3e82..d6e96c5 100644
--- a/third_party/blink/renderer/core/paint/paint_invalidator.h
+++ b/third_party/blink/renderer/core/paint/paint_invalidator.h
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/paint/paint_property_tree_builder.h"
 #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
@@ -58,8 +59,7 @@
     return subtree_flags &
            (kSubtreeInvalidationChecking | kSubtreeVisualRectUpdate |
             kSubtreeFullInvalidation |
-            kSubtreeFullInvalidationForStackedContents |
-            kSubtreeSVGResourceChange);
+            kSubtreeFullInvalidationForStackedContents);
   }
 
   const PaintInvalidatorContext* ParentContext() const {
@@ -78,10 +78,9 @@
     kSubtreeVisualRectUpdate = 1 << 1,
     kSubtreeFullInvalidation = 1 << 2,
     kSubtreeFullInvalidationForStackedContents = 1 << 3,
-    kSubtreeSVGResourceChange = 1 << 4,
 
     // For repeated objects inside multicolumn.
-    kSubtreeSlowPathRect = 1 << 5,
+    kSubtreeSlowPathRect = 1 << 4,
 
     // When this flag is set, no paint or raster invalidation will be issued
     // for the subtree.
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 77b9981..a881988 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -811,11 +811,11 @@
 
   if (HasVisibleContent() != previously_has_visible_content) {
     SetNeedsCompositingInputsUpdateInternal();
-    // We need to tell m_layoutObject to recheck its rect because we
+    // We need to tell layout_object_ to recheck its rect because we
     // pretend that invisible LayoutObjects have 0x0 rects. Changing
     // visibility therefore changes our rect and we need to visit
-    // this LayoutObject during the invalidateTreeIfNeeded walk.
-    layout_object_.SetMayNeedPaintInvalidation();
+    // this LayoutObject during the PrePaintTreeWalk.
+    layout_object_.SetShouldCheckForPaintInvalidation();
   }
 
   Update3DTransformedDescendantStatus();
@@ -1455,8 +1455,7 @@
       // can't see this layer (which has been removed) so won't do this for us.
       ObjectPaintInvalidator(GetLayoutObject())
           .InvalidatePaintIncludingNonCompositingDescendants();
-      GetLayoutObject()
-          .SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
+      GetLayoutObject().SetSubtreeShouldDoFullPaintInvalidation();
       did_set_paint_invalidation = true;
     }
   }
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index 5455935c..adea385 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -565,7 +565,7 @@
 
   if (requires_paint_invalidation) {
     GetLayoutBox()->SetShouldDoFullPaintInvalidation();
-    GetLayoutBox()->SetMayNeedPaintInvalidationSubtree();
+    GetLayoutBox()->SetSubtreeShouldCheckForPaintInvalidation();
   } else if (!UsesCompositedScrolling()) {
     // If any scrolling content might have ben clipped by a cull rect, then
     // that cull rect could be affected by scroll offset. For composited
@@ -1892,10 +1892,8 @@
 
 void PaintLayerScrollableArea::InvalidatePaintForStickyDescendants() {
   if (PaintLayerScrollableAreaRareData* d = RareData()) {
-    for (PaintLayer* sticky_layer : d->sticky_constraints_map_.Keys()) {
-      sticky_layer->GetLayoutObject()
-          .SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
-    }
+    for (PaintLayer* sticky_layer : d->sticky_constraints_map_.Keys())
+      sticky_layer->GetLayoutObject().SetSubtreeShouldDoFullPaintInvalidation();
   }
 }
 
@@ -2779,7 +2777,7 @@
 }
 
 void PaintLayerScrollableArea::ScrollControlWasSetNeedsPaintInvalidation() {
-  GetLayoutBox()->SetMayNeedPaintInvalidation();
+  GetLayoutBox()->SetShouldCheckForPaintInvalidation();
 }
 
 void PaintLayerScrollableArea::DidScrollWithScrollbar(
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 76a55ce6..7b7403e5 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -1806,7 +1806,7 @@
     // the entire subtree on paint offset changes.
     full_context_.force_subtree_update = true;
 
-    object_.GetMutableForPainting().SetMayNeedPaintInvalidation();
+    object_.GetMutableForPainting().SetShouldCheckForPaintInvalidation();
     fragment_data_.SetPaintOffset(context_.current.paint_offset);
     fragment_data_.InvalidateClipPathCache();
 
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index 78fa248..38354e90 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -167,6 +167,12 @@
   }
 
   auto* node = object.GetNode();
+  if (!node && object.IsLayoutBlockFlow() &&
+      ToLayoutBlockFlow(object).IsAnonymousBlockContinuation()) {
+    // An anonymous continuation does not have handlers so we need to check the
+    // DOM ancestor for handlers using |NodeForHitTest|.
+    node = object.NodeForHitTest();
+  }
   if (!node)
     return false;
   return HasBlockingTouchEventHandler(*object.GetFrame(), *node);
diff --git a/third_party/blink/renderer/core/paint/table_cell_paint_invalidator.cc b/third_party/blink/renderer/core/paint/table_cell_paint_invalidator.cc
index db26d502..f15db7c6 100644
--- a/third_party/blink/renderer/core/paint/table_cell_paint_invalidator.cc
+++ b/third_party/blink/renderer/core/paint/table_cell_paint_invalidator.cc
@@ -16,12 +16,16 @@
 
 namespace blink {
 
+static bool DisplayItemClientIsFullyInvalidated(
+    const DisplayItemClient& client) {
+  return IsFullPaintInvalidationReason(client.GetPaintInvalidationReason());
+}
+
 void TableCellPaintInvalidator::InvalidateContainerForCellGeometryChange(
     const LayoutObject& container,
     const PaintInvalidatorContext& container_context) {
   // We only need to do this if the container hasn't been fully invalidated.
-  DCHECK(
-      !IsFullPaintInvalidationReason(container.GetPaintInvalidationReason()));
+  DCHECK(!DisplayItemClientIsFullyInvalidated(container));
 
   // At this time we have already walked the container for paint invalidation,
   // so we should invalidate the container immediately here instead of setting
@@ -30,7 +34,7 @@
   container.InvalidateDisplayItemClients(PaintInvalidationReason::kGeometry);
 }
 
-PaintInvalidationReason TableCellPaintInvalidator::InvalidatePaint() {
+void TableCellPaintInvalidator::InvalidatePaint() {
   // The cell's containing row and section paint backgrounds behind the cell,
   // and the row or table paints collapsed borders. If the cell's geometry
   // changed and the containers which will paint backgrounds and/or collapsed
@@ -40,7 +44,7 @@
     const auto& row = *cell_.Row();
     const auto& section = *row.Section();
     const auto& table = *section.Table();
-    if (!IsFullPaintInvalidationReason(row.GetPaintInvalidationReason()) &&
+    if (!DisplayItemClientIsFullyInvalidated(row) &&
         (row.StyleRef().HasBackground() ||
          (table.HasCollapsedBorders() &&
           LIKELY(!table.ShouldPaintAllCollapsedBorders())))) {
@@ -48,13 +52,13 @@
     }
 
     if (UNLIKELY(table.ShouldPaintAllCollapsedBorders()) &&
-        !IsFullPaintInvalidationReason(table.GetPaintInvalidationReason())) {
+        !DisplayItemClientIsFullyInvalidated(table)) {
       DCHECK(table.HasCollapsedBorders());
       InvalidateContainerForCellGeometryChange(
           table, *context_.ParentContext()->ParentContext()->ParentContext());
     }
 
-    if (!IsFullPaintInvalidationReason(section.GetPaintInvalidationReason())) {
+    if (!DisplayItemClientIsFullyInvalidated(section)) {
       bool section_paints_background = section.StyleRef().HasBackground();
       if (!section_paints_background) {
         auto col_and_colgroup = section.Table()->ColElementAtAbsoluteColumn(
@@ -72,7 +76,7 @@
     }
   }
 
-  return BlockPaintInvalidator(cell_).InvalidatePaint(context_);
+  BlockPaintInvalidator(cell_).InvalidatePaint(context_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/table_cell_paint_invalidator.h b/third_party/blink/renderer/core/paint/table_cell_paint_invalidator.h
index b2d129e1..e5a3086 100644
--- a/third_party/blink/renderer/core/paint/table_cell_paint_invalidator.h
+++ b/third_party/blink/renderer/core/paint/table_cell_paint_invalidator.h
@@ -22,7 +22,7 @@
                             const PaintInvalidatorContext& context)
       : cell_(cell), context_(context) {}
 
-  PaintInvalidationReason InvalidatePaint();
+  void InvalidatePaint();
 
  private:
   void InvalidateContainerForCellGeometryChange(
diff --git a/third_party/blink/renderer/core/paint/table_paint_invalidator.cc b/third_party/blink/renderer/core/paint/table_paint_invalidator.cc
index ac25e88..51529225 100644
--- a/third_party/blink/renderer/core/paint/table_paint_invalidator.cc
+++ b/third_party/blink/renderer/core/paint/table_paint_invalidator.cc
@@ -14,9 +14,8 @@
 
 namespace blink {
 
-PaintInvalidationReason TablePaintInvalidator::InvalidatePaint() {
-  PaintInvalidationReason reason =
-      BoxPaintInvalidator(table_, context_).InvalidatePaint();
+void TablePaintInvalidator::InvalidatePaint() {
+  BoxPaintInvalidator(table_, context_).InvalidatePaint();
 
   // If any col changed background, we need to invalidate all sections because
   // col background paints into section's background display item.
@@ -29,7 +28,7 @@
       // LayoutTableCol uses the table's localVisualRect(). Should check column
       // for paint invalidation when table's visual rect changed.
       if (visual_rect_changed)
-        col->SetMayNeedPaintInvalidation();
+        col->SetShouldCheckForPaintInvalidation();
       // This ensures that the backgroundChangedSinceLastPaintInvalidation flag
       // is up-to-date.
       col->EnsureIsReadyForPaintInvalidation();
@@ -52,8 +51,6 @@
               *section, PaintInvalidationReason::kStyle);
     }
   }
-
-  return reason;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/table_paint_invalidator.h b/third_party/blink/renderer/core/paint/table_paint_invalidator.h
index 315a9bd3..2bcadfb 100644
--- a/third_party/blink/renderer/core/paint/table_paint_invalidator.h
+++ b/third_party/blink/renderer/core/paint/table_paint_invalidator.h
@@ -21,7 +21,7 @@
                         const PaintInvalidatorContext& context)
       : table_(table), context_(context) {}
 
-  PaintInvalidationReason InvalidatePaint();
+  void InvalidatePaint();
 
  private:
   const LayoutTable& table_;
diff --git a/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc b/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
index f739924..dd9ee9b 100644
--- a/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
@@ -204,10 +204,9 @@
   inner_view->SetShouldDoFullPaintInvalidation(
       PaintInvalidationReason::kForTesting);
   inner_view->Layer()->SetNeedsRepaint();
-  EXPECT_FALSE(inner_frame_document->View()
-                   ->GetLayoutView()
-                   ->FullPaintInvalidationReason() ==
-               PaintInvalidationReason::kNone);
+  EXPECT_TRUE(inner_frame_document->View()
+                  ->GetLayoutView()
+                  ->ShouldDoFullPaintInvalidation());
   inner_view->Compositor()->SetNeedsCompositingUpdate(
       kCompositingUpdateRebuildTree);
   EXPECT_EQ(kCompositingUpdateRebuildTree,
@@ -219,10 +218,9 @@
   EXPECT_TRUE(inner_frame_document->View()->ShouldThrottleRendering());
 
   EXPECT_FALSE(inner_view->NeedsLayout());
-  EXPECT_FALSE(inner_frame_document->View()
-                   ->GetLayoutView()
-                   ->FullPaintInvalidationReason() ==
-               PaintInvalidationReason::kNone);
+  EXPECT_TRUE(inner_frame_document->View()
+                  ->GetLayoutView()
+                  ->ShouldDoFullPaintInvalidation());
   EXPECT_EQ(kCompositingUpdateRebuildTree,
             inner_view->Compositor()->pending_update_type_);
   EXPECT_TRUE(inner_view->Layer()->NeedsRepaint());
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index eb8b539e..b384e8e8 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -312,15 +312,16 @@
   if (!layout_object_)
     return kUnknownRole;
 
+  native_role_ = NativeAccessibilityRoleIgnoringAria();
+
   if ((aria_role_ = DetermineAriaRoleAttribute()) != kUnknownRole)
     return aria_role_;
 
-  AccessibilityRole role = NativeAccessibilityRoleIgnoringAria();
   // Anything that needs to still be exposed but doesn't have a more specific
   // role should be considered a generic container. Examples are
   // layout blocks with no node, in-page link targets, and plain elements
   // such as a <span> with ARIA markup.
-  return role == kUnknownRole ? kGenericContainerRole : role;
+  return native_role_ == kUnknownRole ? kGenericContainerRole : native_role_;
 }
 
 Node* AXLayoutObject::GetNodeOrContainingBlockNode() const {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 7dca85c..04f04f1 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -87,6 +87,7 @@
 AXNodeObject::AXNodeObject(Node* node, AXObjectCacheImpl& ax_object_cache)
     : AXObject(ax_object_cache),
       children_dirty_(false),
+      native_role_(kUnknownRole),
       node_(node) {}
 
 AXNodeObject* AXNodeObject::Create(Node* node,
@@ -453,6 +454,7 @@
   if (GetNode()->HasTagName(sectionTag))
     return kRegionRole;
 
+  // TODO(accessibility): http://crbug.com/873118
   if (GetNode()->HasTagName(addressTag))
     return kContentInfoRole;
 
@@ -519,13 +521,14 @@
   if (!GetNode())
     return kUnknownRole;
 
+  native_role_ = NativeAccessibilityRoleIgnoringAria();
+
   if ((aria_role_ = DetermineAriaRoleAttribute()) != kUnknownRole)
     return aria_role_;
   if (GetNode()->IsTextNode())
     return kStaticTextRole;
 
-  AccessibilityRole role = NativeAccessibilityRoleIgnoringAria();
-  return role == kUnknownRole ? kGenericContainerRole : role;
+  return native_role_ == kUnknownRole ? kGenericContainerRole : native_role_;
 }
 
 void AXNodeObject::AccessibilityChildrenFromAOMProperty(
@@ -2156,7 +2159,7 @@
     return false;
   }
 
-  switch (NativeAccessibilityRoleIgnoringAria()) {
+  switch (native_role_) {
     case kButtonRole:
     case kCheckBoxRole:
     case kImageRole:
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.h b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
index 840aa9c..a36cf324 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -54,6 +54,8 @@
 #if DCHECK_IS_ON()
   bool initialized_ = false;
 #endif
+  // The accessibility role, not taking ARIA into account.
+  AccessibilityRole native_role_;
 
   bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const override;
   const AXObject* InheritsPresentationalRoleFrom() const override;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_certificate.cc b/third_party/blink/renderer/modules/peerconnection/rtc_certificate.cc
index 317ee0b..4fd68ad6 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_certificate.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_certificate.cc
@@ -36,29 +36,31 @@
 
 namespace blink {
 
-RTCCertificate::RTCCertificate(std::unique_ptr<WebRTCCertificate> certificate)
-    : certificate_(base::WrapUnique(certificate.release())) {}
-
-std::unique_ptr<WebRTCCertificate> RTCCertificate::CertificateShallowCopy()
-    const {
-  return certificate_->ShallowCopy();
-}
+RTCCertificate::RTCCertificate(
+    rtc::scoped_refptr<rtc::RTCCertificate> certificate)
+    : certificate_(std::move(certificate)) {}
 
 DOMTimeStamp RTCCertificate::expires() const {
   return static_cast<DOMTimeStamp>(certificate_->Expires());
 }
 
 HeapVector<RTCDtlsFingerprint> RTCCertificate::getFingerprints() {
-  WebVector<WebRTCDtlsFingerprint> web_fingerprints =
-      certificate_->GetFingerprints();
-  DCHECK(!web_fingerprints.IsEmpty());
-  HeapVector<RTCDtlsFingerprint> fingerprints(web_fingerprints.size());
-  for (size_t i = 0; i < fingerprints.size(); ++i) {
-    DCHECK(!web_fingerprints[i].Algorithm().IsEmpty());
-    DCHECK(!web_fingerprints[i].Value().IsEmpty());
-    fingerprints[i].setAlgorithm(web_fingerprints[i].Algorithm());
-    fingerprints[i].setValue(web_fingerprints[i].Value());
+  std::unique_ptr<rtc::SSLCertificateStats> first_certificate_stats =
+      certificate_->ssl_certificate().GetStats();
+
+  HeapVector<RTCDtlsFingerprint> fingerprints;
+  for (rtc::SSLCertificateStats* certificate_stats =
+           first_certificate_stats.get();
+       certificate_stats; certificate_stats = certificate_stats->issuer.get()) {
+    fingerprints.emplace_back();
+    auto& fingerprint = fingerprints.back();
+    fingerprint.setAlgorithm(WTF::String::FromUTF8(
+        certificate_stats->fingerprint_algorithm.c_str()));
+    fingerprint.setValue(
+        WTF::String::FromUTF8(certificate_stats->fingerprint.c_str())
+            .LowerASCII());
   }
+
   return fingerprints;
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h b/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h
index 47fcbf5aa..4b3aa2e 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h
@@ -33,7 +33,6 @@
 
 #include <memory>
 
-#include "third_party/blink/public/platform/web_rtc_certificate.h"
 #include "third_party/blink/renderer/core/dom/dom_time_stamp.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_dtls_fingerprint.h"
@@ -41,6 +40,7 @@
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/webrtc/rtc_base/rtccertificate.h"
 
 namespace blink {
 
@@ -49,18 +49,18 @@
 
  public:
   // Takes ownership of the certificate.
-  RTCCertificate(std::unique_ptr<WebRTCCertificate>);
+  RTCCertificate(rtc::scoped_refptr<rtc::RTCCertificate>);
 
-  // Returns a new WebRTCCertificate shallow copy.
-  std::unique_ptr<WebRTCCertificate> CertificateShallowCopy() const;
-  const WebRTCCertificate& Certificate() const { return *certificate_; }
+  const rtc::scoped_refptr<rtc::RTCCertificate>& Certificate() const {
+    return certificate_;
+  }
 
   // Returns the expiration time in ms relative to epoch, 1970-01-01T00:00:00Z.
   DOMTimeStamp expires() const;
   HeapVector<RTCDtlsFingerprint> getFingerprints();
 
  private:
-  std::unique_ptr<WebRTCCertificate> certificate_;
+  rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index c50af6c..63ab728 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -43,7 +43,6 @@
 #include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_rtc_answer_options.h"
-#include "third_party/blink/public/platform/web_rtc_certificate.h"
 #include "third_party/blink/public/platform/web_rtc_certificate_generator.h"
 #include "third_party/blink/public/platform/web_rtc_configuration.h"
 #include "third_party/blink/public/platform/web_rtc_data_channel_handler.h"
@@ -114,6 +113,7 @@
 #include "third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/webrtc/api/peerconnectioninterface.h"
 
 namespace blink {
 
@@ -241,7 +241,7 @@
   explicit WebRTCCertificateObserver(ScriptPromiseResolver* resolver)
       : resolver_(resolver) {}
 
-  void OnSuccess(std::unique_ptr<WebRTCCertificate> certificate) override {
+  void OnSuccess(rtc::scoped_refptr<rtc::RTCCertificate> certificate) override {
     resolver_->Resolve(new RTCCertificate(std::move(certificate)));
   }
 
@@ -364,10 +364,10 @@
   if (configuration.hasCertificates()) {
     const HeapVector<Member<RTCCertificate>>& certificates =
         configuration.certificates();
-    WebVector<std::unique_ptr<WebRTCCertificate>> certificates_copy(
+    WebVector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates_copy(
         certificates.size());
     for (size_t i = 0; i < certificates.size(); ++i) {
-      certificates_copy[i] = certificates[i]->CertificateShallowCopy();
+      certificates_copy[i] = certificates[i]->Certificate();
     }
     web_configuration.certificates = std::move(certificates_copy);
   }
@@ -496,7 +496,7 @@
   // Make sure no certificates have expired.
   if (configuration.certificates.size() > 0) {
     DOMTimeStamp now = ConvertSecondsToDOMTimeStamp(CurrentTime());
-    for (const std::unique_ptr<WebRTCCertificate>& certificate :
+    for (const rtc::scoped_refptr<rtc::RTCCertificate>& certificate :
          configuration.certificates) {
       DOMTimeStamp expires = certificate->Expires();
       if (expires <= now) {
diff --git a/third_party/blink/renderer/modules/peerconnection/testing/internals_rtc_certificate.cc b/third_party/blink/renderer/modules/peerconnection/testing/internals_rtc_certificate.cc
index 0b28fec..8853bde 100644
--- a/third_party/blink/renderer/modules/peerconnection/testing/internals_rtc_certificate.cc
+++ b/third_party/blink/renderer/modules/peerconnection/testing/internals_rtc_certificate.cc
@@ -9,7 +9,7 @@
 bool InternalsRTCCertificate::rtcCertificateEquals(Internals& internals,
                                                    RTCCertificate* a,
                                                    RTCCertificate* b) {
-  return a->Certificate().Equals(b->Certificate());
+  return a->Certificate() == b->Certificate();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 5b18e7b..2d5d1db 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1861,6 +1861,7 @@
     "//services/viz/public/interfaces",
     "//services/viz/public/interfaces:interfaces_blink",
     "//skia",
+    "//skia:skcms",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party:freetype_harfbuzz",
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 5f5687c..2d22e00 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -76,10 +76,6 @@
   RuntimeEnabledFeatures::SetOriginTrialControlledFeaturesEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableOriginPolicy(bool enable) {
-  RuntimeEnabledFeatures::SetOriginPolicyEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableOutOfBlinkCORS(bool enable) {
   RuntimeEnabledFeatures::SetOutOfBlinkCORSEnabled(enable);
 }
@@ -320,6 +316,10 @@
   RuntimeEnabledFeatures::SetPictureInPictureAPIEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnablePortals(bool enable) {
+  RuntimeEnabledFeatures::SetPortalsEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnablePreloadDefaultIsMetadata(bool enable) {
   RuntimeEnabledFeatures::SetPreloadDefaultIsMetadataEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc b/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc
index 37861d19..8fd5c50d 100644
--- a/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc
+++ b/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc
@@ -72,8 +72,6 @@
       return "full layer";
     case PaintInvalidationReason::kForTesting:
       return "for testing";
-    case PaintInvalidationReason::kDelayedFull:
-      return "delayed full";
   }
   NOTREACHED();
   return "";
diff --git a/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h b/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h
index 8afacba3..c81b0d2 100644
--- a/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h
+++ b/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h
@@ -31,6 +31,8 @@
   // Scroll bars, scroll corner, etc.
   kScrollControl,
   kOutline,
+  // The object is invalidated as a part of a subtree full invalidation (forced
+  // by LayoutObject::SetSubtreeShouldDoFullPaintInvalidation()).
   kSubtree,
   kSVGResource,
   kBackground,
@@ -51,13 +53,7 @@
   // invalidation may be implicit, e.g. when a layer is created.
   kFullLayer,
   kForTesting,
-  // kDelayedFull means that kFull is needed in order to fully paint the
-  // content, but that painting of the object can be delayed until a future
-  // frame. This can be the case for an object whose content is not visible to
-  // the user.
-  kDelayedFull,
-
-  kMax = kDelayedFull
+  kMax = kForTesting,
 };
 
 PLATFORM_EXPORT const char* PaintInvalidationReasonToString(
@@ -71,12 +67,6 @@
   return reason >= PaintInvalidationReason::kFull;
 }
 
-inline bool IsImmediateFullPaintInvalidationReason(
-    PaintInvalidationReason reason) {
-  return IsFullPaintInvalidationReason(reason) &&
-         reason != PaintInvalidationReason::kDelayedFull;
-}
-
 PLATFORM_EXPORT std::ostream& operator<<(std::ostream&,
                                          PaintInvalidationReason);
 
diff --git a/third_party/blink/renderer/platform/graphics/paint_invalidation_reason_test.cc b/third_party/blink/renderer/platform/graphics/paint_invalidation_reason_test.cc
index 7ecdd389..547928a 100644
--- a/third_party/blink/renderer/platform/graphics/paint_invalidation_reason_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint_invalidation_reason_test.cc
@@ -9,18 +9,25 @@
 
 namespace blink {
 
+TEST(PaintInvalidationReasonTest, ToString) {
+  for (auto i = PaintInvalidationReason::kNone;
+       i <= PaintInvalidationReason::kMax;
+       i = static_cast<PaintInvalidationReason>(static_cast<int>(i) + 1))
+    EXPECT_STRNE("", PaintInvalidationReasonToString(i));
+
+  EXPECT_STREQ("none",
+               PaintInvalidationReasonToString(PaintInvalidationReason::kNone));
+  EXPECT_STREQ("full",
+               PaintInvalidationReasonToString(PaintInvalidationReason::kFull));
+}
+
 TEST(PaintInvalidationReasonTest, StreamOutput) {
-  {
+  for (auto i = PaintInvalidationReason::kNone;
+       i <= PaintInvalidationReason::kMax;
+       i = static_cast<PaintInvalidationReason>(static_cast<int>(i) + 1)) {
     std::stringstream string_stream;
-    PaintInvalidationReason reason = PaintInvalidationReason::kNone;
-    string_stream << reason;
-    EXPECT_EQ("none", string_stream.str());
-  }
-  {
-    std::stringstream string_stream;
-    PaintInvalidationReason reason = PaintInvalidationReason::kDelayedFull;
-    string_stream << reason;
-    EXPECT_EQ("delayed full", string_stream.str());
+    string_stream << i;
+    EXPECT_EQ(PaintInvalidationReasonToString(i), string_stream.str());
   }
 }
 
diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
index 0f74520..b4de4a17 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
@@ -64,6 +64,8 @@
 }
 
 static constexpr size_t kLongestSignatureLength = sizeof("RIFF????WEBPVP") - 1;
+static const size_t k4BytesPerPixel = 4;
+static const size_t k8BytesPerPixel = 8;
 
 std::unique_ptr<ImageDecoder> ImageDecoder::Create(
     scoped_refptr<SegmentReader> data,
@@ -75,15 +77,24 @@
   // At least kLongestSignatureLength bytes are needed to sniff the signature.
   if (data->size() < kLongestSignatureLength)
     return nullptr;
+  // On low end devices, always decode to 8888.
+  if (high_bit_depth_decoding_option == kHighBitDepthToHalfFloat &&
+      Platform::Current() && Platform::Current()->IsLowEndDevice()) {
+    high_bit_depth_decoding_option = kDefaultBitDepth;
+  }
 
   size_t max_decoded_bytes = Platform::Current()
                                  ? Platform::Current()->MaxDecodedImageBytes()
                                  : kNoDecodedImageByteLimit;
   if (!desired_size.isEmpty()) {
-    static const size_t kBytesPerPixels = 4;
-    size_t requested_decoded_bytes =
-        kBytesPerPixels * desired_size.width() * desired_size.height();
-    max_decoded_bytes = std::min(requested_decoded_bytes, max_decoded_bytes);
+    size_t num_pixels = desired_size.width() * desired_size.height();
+    if (high_bit_depth_decoding_option == kDefaultBitDepth) {
+      max_decoded_bytes =
+          std::min(k4BytesPerPixel * num_pixels, max_decoded_bytes);
+    } else {  // kHighBitDepthToHalfFloat
+      max_decoded_bytes =
+          std::min(k8BytesPerPixel * num_pixels, max_decoded_bytes);
+    }
   }
 
   // Access the first kLongestSignatureLength chars to sniff the signature.
@@ -188,8 +199,12 @@
     uint64_t area;
   };
 
-  return ImageSize(FrameSizeAtIndex(index)).area *
-         sizeof(ImageFrame::PixelData);
+  size_t decoded_bytes_per_pixel = k4BytesPerPixel;
+  if (frame_buffer_cache_[index].GetPixelFormat() ==
+      ImageFrame::PixelFormat::kRGBA_F16) {
+    decoded_bytes_per_pixel = k8BytesPerPixel;
+  }
+  return ImageSize(FrameSizeAtIndex(index)).area * decoded_bytes_per_pixel;
 }
 
 size_t ImageDecoder::ClearCacheExceptFrame(size_t clear_except_frame) {
@@ -391,12 +406,18 @@
   // As we decode we will learn the total number of frames, and thus total
   // possible image memory used.
 
+  size_t decoded_bytes_per_pixel = k4BytesPerPixel;
+
+  if (frame_buffer_cache_.size() && frame_buffer_cache_[0].GetPixelFormat() ==
+                                        ImageFrame::PixelFormat::kRGBA_F16) {
+    decoded_bytes_per_pixel = k8BytesPerPixel;
+  }
   const uint64_t frame_memory_usage =
-      DecodedSize().Area() * 4;  // 4 bytes per pixel
+      DecodedSize().Area() * decoded_bytes_per_pixel;
 
   // This condition never fails in the current code. Our existing image decoders
   // parse for the image size and SetFailed() if that size overflows
-  DCHECK_EQ(frame_memory_usage / 4, DecodedSize().Area());
+  DCHECK_EQ(frame_memory_usage / decoded_bytes_per_pixel, DecodedSize().Area());
 
   const uint64_t total_memory_usage = frame_memory_usage * index;
   if (total_memory_usage / frame_memory_usage != index) {  // overflow occurred
diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder.h b/third_party/blink/renderer/platform/image-decoders/image_decoder.h
index dff162e..1242a2e 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_decoder.h
+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder.h
@@ -153,6 +153,12 @@
 
   bool IsAllDataReceived() const { return is_all_data_received_; }
 
+  // Returns true if the decoder supports decoding to high bit depth. The
+  // decoded output will be high bit depth (half float backed bitmap) iff
+  // encoded image is high bit depth and high_bit_depth_decoding_option_ is set
+  // to kHighBitDepthToHalfFloat.
+  virtual bool ImageIsHighBitDepth() { return false; }
+
   // Returns true if the buffer holds enough data to instantiate a decoder.
   // This is useful for callers to determine whether a decoder instantiation
   // failure is due to insufficient or bad data.
@@ -214,7 +220,11 @@
   // Returns whether the size is legal (i.e. not going to result in
   // overflow elsewhere).  If not, marks decoding as failed.
   virtual bool SetSize(unsigned width, unsigned height) {
-    if (SizeCalculationMayOverflow(width, height))
+    unsigned decoded_bytes_per_pixel = 4;
+    if (ImageIsHighBitDepth() &&
+        high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat)
+      decoded_bytes_per_pixel = 8;
+    if (SizeCalculationMayOverflow(width, height, decoded_bytes_per_pixel))
       return SetFailed();
 
     size_ = IntSize(width, height);
@@ -447,12 +457,16 @@
   }
 
  private:
-  // Some code paths compute the size of the image as "width * height * 4"
+  // Some code paths compute the size of the image as "width * height * 4 or 8"
   // and return it as a (signed) int.  Avoid overflow.
-  static bool SizeCalculationMayOverflow(unsigned width, unsigned height) {
+  static bool SizeCalculationMayOverflow(unsigned width,
+                                         unsigned height,
+                                         unsigned decoded_bytes_per_pixel) {
     unsigned long long total_size = static_cast<unsigned long long>(width) *
                                     static_cast<unsigned long long>(height);
-    return total_size > ((1 << 29) - 1);
+    if (decoded_bytes_per_pixel == 4)
+      return total_size > ((1 << 29) - 1);
+    return total_size > ((1 << 28) - 1);
   }
 
   bool purge_aggressively_;
diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc
index c6fc45b..0ae6fa60 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc
@@ -39,12 +39,15 @@
 
 class TestImageDecoder : public ImageDecoder {
  public:
-  TestImageDecoder()
+  TestImageDecoder(
+      ImageDecoder::HighBitDepthDecodingOption high_bit_depth_decoding_option)
       : ImageDecoder(kAlphaNotPremultiplied,
-                     ImageDecoder::kDefaultBitDepth,
+                     high_bit_depth_decoding_option,
                      ColorBehavior::TransformToSRGB(),
                      kNoDecodedImageByteLimit) {}
 
+  TestImageDecoder() : TestImageDecoder(ImageDecoder::kDefaultBitDepth) {}
+
   String FilenameExtension() const override { return ""; }
 
   Vector<ImageFrame, 1>& FrameBufferCache() { return frame_buffer_cache_; }
@@ -64,20 +67,52 @@
       frame_buffer_cache_[i].SetOriginalFrameRect(IntRect(0, 0, width, height));
   }
 
+  bool ImageIsHighBitDepth() override { return image_is_high_bit_depth_; }
+  void SetImageToHighBitDepthForTest() { image_is_high_bit_depth_ = true; }
+
  private:
+  bool image_is_high_bit_depth_ = false;
   void DecodeSize() override {}
   void Decode(size_t index) override {}
 };
 
 TEST(ImageDecoderTest, sizeCalculationMayOverflow) {
-  std::unique_ptr<TestImageDecoder> decoder(
-      std::make_unique<TestImageDecoder>());
-  EXPECT_FALSE(decoder->SetSize(1 << 29, 1));
-  EXPECT_FALSE(decoder->SetSize(1, 1 << 29));
-  EXPECT_FALSE(decoder->SetSize(1 << 15, 1 << 15));
-  EXPECT_TRUE(decoder->SetSize(1 << 28, 1));
-  EXPECT_TRUE(decoder->SetSize(1, 1 << 28));
-  EXPECT_TRUE(decoder->SetSize(1 << 14, 1 << 14));
+  // Test coverage:
+  // Regular bit depth image with regular decoder
+  // Regular bit depth image with high bit depth decoder
+  // High bit depth image with regular decoder
+  // High bit depth image with high bit depth decoder
+  bool high_bit_depth_decoder_status[] = {false, true};
+  bool high_bit_depth_image_status[] = {false, true};
+
+  for (bool high_bit_depth_decoder : high_bit_depth_decoder_status) {
+    for (bool high_bit_depth_image : high_bit_depth_image_status) {
+      std::unique_ptr<TestImageDecoder> decoder;
+      if (high_bit_depth_decoder) {
+        decoder = std::make_unique<TestImageDecoder>(
+            ImageDecoder::kHighBitDepthToHalfFloat);
+      } else {
+        decoder = std::make_unique<TestImageDecoder>();
+      }
+      if (high_bit_depth_image)
+        decoder->SetImageToHighBitDepthForTest();
+
+      unsigned log_pixel_size = 2;  // pixel is 4 bytes
+      if (high_bit_depth_decoder && high_bit_depth_image)
+        log_pixel_size = 3;  // pixel is 8 byts
+      unsigned overflow_dim_shift = 31 - log_pixel_size;
+      unsigned overflow_dim_shift_half = (overflow_dim_shift + 1) / 2;
+
+      EXPECT_FALSE(decoder->SetSize(1 << overflow_dim_shift, 1));
+      EXPECT_FALSE(decoder->SetSize(1, 1 << overflow_dim_shift));
+      EXPECT_FALSE(decoder->SetSize(1 << overflow_dim_shift_half,
+                                    1 << overflow_dim_shift_half));
+      EXPECT_TRUE(decoder->SetSize(1 << (overflow_dim_shift - 1), 1));
+      EXPECT_TRUE(decoder->SetSize(1, 1 << (overflow_dim_shift - 1)));
+      EXPECT_TRUE(decoder->SetSize(1 << (overflow_dim_shift_half - 1),
+                                   1 << (overflow_dim_shift_half - 1)));
+    }
+  }
 }
 
 TEST(ImageDecoderTest, requiredPreviousFrameIndex) {
diff --git a/third_party/blink/renderer/platform/image-decoders/image_frame.cc b/third_party/blink/renderer/platform/image-decoders/image_frame.cc
index b93d6f1..02ed602 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_frame.cc
+++ b/third_party/blink/renderer/platform/image-decoders/image_frame.cc
@@ -86,6 +86,7 @@
 bool ImageFrame::CopyBitmapData(const ImageFrame& other) {
   DCHECK_NE(this, &other);
   has_alpha_ = other.has_alpha_;
+  pixel_format_ = other.pixel_format_;
   bitmap_.reset();
   SkImageInfo info = other.bitmap_.info();
   return bitmap_.tryAllocPixels(info) &&
@@ -101,6 +102,7 @@
   if (other->bitmap_.isImmutable())
     return false;
   has_alpha_ = other->has_alpha_;
+  pixel_format_ = other->pixel_format_;
   bitmap_.reset();
   bitmap_.swap(other->bitmap_);
   other->status_ = kFrameEmpty;
@@ -157,24 +159,6 @@
   SetHasAlpha(true);
 }
 
-void ImageFrame::SetRGBAPremultiplyF16Buffer(PixelDataF16* dst,
-                                             PixelDataF16* src,
-                                             size_t num_pixels) {
-  sk_sp<SkColorSpace> color_space = SkColorSpace::MakeSRGBLinear();
-  auto color_format = SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat;
-  SkColorSpaceXform::Apply(color_space.get(), color_format, dst,
-                           color_space.get(), color_format, src, num_pixels,
-                           SkColorSpaceXform::AlphaOp::kPremul_AlphaOp);
-}
-
-void ImageFrame::SetPixelsOpaqueF16Buffer(PixelDataF16* dst,
-                                          PixelDataF16* src,
-                                          size_t num_pixels) {
-  // We set the alpha half float to 0x3c00, which is equal to 1.
-  while (num_pixels-- > 0)
-    *dst++ = (*src++ & 0x0000ffffffffffff) | 0x3c00000000000000;
-}
-
 static void BlendRGBAF16Buffer(ImageFrame::PixelDataF16* dst,
                                ImageFrame::PixelDataF16* src,
                                size_t num_pixels,
diff --git a/third_party/blink/renderer/platform/image-decoders/image_frame.h b/third_party/blink/renderer/platform/image-decoders/image_frame.h
index 0aec08e..9562391d 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_frame.h
+++ b/third_party/blink/renderer/platform/image-decoders/image_frame.h
@@ -81,10 +81,6 @@
 
   ImageFrame();
 
-  ImageFrame(PixelFormat pixel_format) : ImageFrame() {
-    pixel_format_ = pixel_format;
-  }
-
   // The assignment operator reads has_alpha_ (inside SetStatus()) before it
   // sets it (in SetHasAlpha()).  This doesn't cause any problems, since the
   // SetHasAlpha() call ensures all state is set correctly, but it means we
@@ -157,6 +153,7 @@
     return required_previous_frame_index_;
   }
   void SetHasAlpha(bool alpha);
+  void SetPixelFormat(PixelFormat format) { pixel_format_ = format; }
   void SetOriginalFrameRect(const IntRect& r) { original_frame_rect_ = r; }
   void SetStatus(Status);
   void SetDuration(TimeDelta duration) { duration_ = duration; }
@@ -234,10 +231,6 @@
     *dest = SkPackARGB32NoCheck(a, r, g, b);
   }
 
-  static void SetRGBAPremultiplyF16Buffer(PixelDataF16* dst,
-                                          PixelDataF16* src,
-                                          size_t num_pixels);
-
   static inline void SetRGBARaw(PixelData* dest,
                                 unsigned r,
                                 unsigned g,
@@ -246,10 +239,6 @@
     *dest = SkPackARGB32NoCheck(a, r, g, b);
   }
 
-  static void SetPixelsOpaqueF16Buffer(PixelDataF16* dst,
-                                       PixelDataF16* src,
-                                       size_t num_pixels);
-
   // Blend the RGBA pixel provided by |red|, |green|, |blue| and |alpha| over
   // the pixel in |dest|, without premultiplication, and overwrite |dest| with
   // the result.
diff --git a/third_party/blink/renderer/platform/image-decoders/image_frame_test.cc b/third_party/blink/renderer/platform/image-decoders/image_frame_test.cc
index 07a506a..908a772 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_frame_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/image_frame_test.cc
@@ -65,80 +65,6 @@
   }
 };
 
-TEST_F(ImageFrameTest, TestF16API) {
-  ImageFrame::PixelFormat kN32 = ImageFrame::PixelFormat::kN32;
-  ImageFrame::PixelFormat kRGBA_F16 = ImageFrame::PixelFormat::kRGBA_F16;
-
-  ImageFrame frame_no_pixel_format;
-  ASSERT_EQ(kN32, frame_no_pixel_format.GetPixelFormat());
-
-  ImageFrame frame_pixel_format_n32(kN32);
-  ASSERT_EQ(kN32, frame_pixel_format_n32.GetPixelFormat());
-
-  ImageFrame frame_pixel_format_f16(kRGBA_F16);
-  ASSERT_EQ(kRGBA_F16, frame_pixel_format_f16.GetPixelFormat());
-
-  ImageFrame frame_copy_ctor_n32(frame_pixel_format_n32);
-  ASSERT_EQ(kN32, frame_copy_ctor_n32.GetPixelFormat());
-
-  ImageFrame frame_copy_ctor_f16(frame_pixel_format_f16);
-  ASSERT_EQ(kRGBA_F16, frame_copy_ctor_f16.GetPixelFormat());
-
-  ImageFrame frame_test_assignment;
-  frame_test_assignment = frame_pixel_format_n32;
-  ASSERT_EQ(kN32, frame_test_assignment.GetPixelFormat());
-  frame_test_assignment = frame_pixel_format_f16;
-  ASSERT_EQ(kRGBA_F16, frame_test_assignment.GetPixelFormat());
-
-  SkBitmap bitmap(frame_pixel_format_f16.Bitmap());
-  ASSERT_EQ(0, bitmap.width());
-  ASSERT_EQ(0, bitmap.height());
-  ASSERT_EQ(nullptr, bitmap.colorSpace());
-
-  TestAllocator allocator;
-  frame_pixel_format_f16.SetMemoryAllocator(&allocator);
-  sk_sp<SkColorSpace> srgb_linear = SkColorSpace::MakeSRGBLinear();
-
-  ASSERT_TRUE(frame_pixel_format_f16.AllocatePixelData(2, 2, srgb_linear));
-  bitmap = frame_pixel_format_f16.Bitmap();
-  ASSERT_EQ(2, bitmap.width());
-  ASSERT_EQ(2, bitmap.height());
-  ASSERT_TRUE(SkColorSpace::Equals(srgb_linear.get(), bitmap.colorSpace()));
-}
-
-TEST_F(ImageFrameTest, SetRGBAPremultiplyF16Buffer) {
-  ImageFrame::PixelDataF16 premul_f16;
-  ImageFrame::SetRGBAPremultiplyF16Buffer(&premul_f16, &src_f16, 1);
-
-  float f32_from_src_f16[4];
-  ConvertF16ToF32(f32_from_src_f16, src_f16);
-  for (int i = 0; i < 3; i++)
-    f32_from_src_f16[i] *= f32_from_src_f16[3];
-
-  float f32_from_premul_f16[4];
-  ConvertF16ToF32(f32_from_premul_f16, premul_f16);
-
-  for (int i = 0; i < 4; i++) {
-    ASSERT_TRUE(fabs(f32_from_src_f16[i] - f32_from_premul_f16[i]) <
-                color_compoenent_tolerance);
-  }
-}
-
-TEST_F(ImageFrameTest, SetPixelsOpaqueF16Buffer) {
-  ImageFrame::PixelDataF16 opaque_f16;
-  ImageFrame::SetPixelsOpaqueF16Buffer(&opaque_f16, &src_f16, 1);
-
-  float f32_from_src_f16[4];
-  ConvertF16ToF32(f32_from_src_f16, src_f16);
-
-  float f32_from_opaque_f16[4];
-  ConvertF16ToF32(f32_from_opaque_f16, opaque_f16);
-
-  for (int i = 0; i < 3; i++)
-    ASSERT_EQ(f32_from_src_f16[i], f32_from_opaque_f16[i]);
-  ASSERT_EQ(1.0f, f32_from_opaque_f16[3]);
-}
-
 TEST_F(ImageFrameTest, BlendRGBARawF16Buffer) {
   ImageFrame::PixelData blended_8888(dst_8888);
   ImageFrame::BlendRGBARaw(&blended_8888, src_8888_r, src_8888_g, src_8888_b,
diff --git a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc
index 67e0bba5..f81c0fe0 100644
--- a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h"
 
 #include <memory>
+#include "third_party/skia/third_party/skcms/skcms.h"
 
 #if (defined(__ARM_NEON__) || defined(__ARM_NEON))
 #include <arm_neon.h>
@@ -63,7 +64,9 @@
       // never be respected.
       repetition_count_(kAnimationLoopOnce),
       has_alpha_channel_(false),
-      current_buffer_saw_alpha_(false) {}
+      current_buffer_saw_alpha_(false),
+      decode_to_half_float_(false),
+      bit_depth_(0) {}
 
 PNGImageDecoder::~PNGImageDecoder() = default;
 
@@ -139,6 +142,8 @@
 void PNGImageDecoder::InitializeNewFrame(size_t index) {
   const PNGImageReader::FrameInfo& frame_info = reader_->GetFrameInfo(index);
   ImageFrame& buffer = frame_buffer_cache_[index];
+  if (decode_to_half_float_)
+    buffer.SetPixelFormat(ImageFrame::PixelFormat::kRGBA_F16);
 
   DCHECK(IntRect(IntPoint(), Size()).Contains(frame_info.frame_rect));
   buffer.SetOriginalFrameRect(frame_info.frame_rect);
@@ -226,6 +231,26 @@
   }
 }
 
+void PNGImageDecoder::SetBitDepth() {
+  if (bit_depth_)
+    return;
+  png_structp png = reader_->PngPtr();
+  png_infop info = reader_->InfoPtr();
+  bit_depth_ = png_get_bit_depth(png, info);
+  decode_to_half_float_ =
+      (bit_depth_ == 16) &&
+      (high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat) &&
+      // TODO(zakerinasab): https://crbug.com/874057
+      // Due to a lack of 16 bit APNG encoders, multi-frame 16 bit APNGs are not
+      // supported. In this case the decoder falls back to 8888 decode mode.
+      (repetition_count_ == kAnimationNone);
+}
+
+bool PNGImageDecoder::ImageIsHighBitDepth() {
+  SetBitDepth();
+  return bit_depth_ == 16;
+}
+
 bool PNGImageDecoder::SetSize(unsigned width, unsigned height) {
   DCHECK(!IsDecodedSizeAvailable());
   // Protect against large PNGs. See http://bugzil.la/251381 for more details.
@@ -255,7 +280,7 @@
   if (png_get_valid(png, info, PNG_INFO_tRNS))
     png_set_expand(png);
 
-  if (bit_depth == 16)
+  if (!decode_to_half_float_)
     png_set_strip_16(png);
 
   if (color_type == PNG_COLOR_TYPE_GRAY ||
@@ -503,7 +528,10 @@
     if (PNG_INTERLACE_ADAM7 ==
         png_get_interlace_type(png, reader_->InfoPtr())) {
       unsigned color_channels = has_alpha_channel_ ? 4 : 3;
-      reader_->CreateInterlaceBuffer(color_channels * Size().Area());
+      unsigned interlace_buffer_size = color_channels * Size().Area();
+      if (decode_to_half_float_)
+        interlace_buffer_size *= 2;
+      reader_->CreateInterlaceBuffer(interlace_buffer_size);
       if (!reader_->InterlaceBuffer()) {
         longjmp(JMPBUF(png), 1);
         return;
@@ -575,120 +603,143 @@
 
   // Write the decoded row pixels to the frame buffer. The repetitive
   // form of the row write loops is for speed.
-  ImageFrame::PixelData* const dst_row = buffer.GetAddr(frame_rect.X(), y);
   const int width = frame_rect.Width();
-
   png_bytep src_ptr = row;
-  if (has_alpha) {
-    // Here we apply the color space transformation to the dst space.
-    // It does not really make sense to transform to a gamma-encoded
-    // space and then immediately after, perform a linear premultiply.
-    // Ideally we would pass kPremul_SkAlphaType to xform->apply(),
-    // instructing SkColorSpaceXform to perform the linear premultiply
-    // while the pixels are a linear space.
-    // We cannot do this because when we apply the gamma encoding after
-    // the premultiply, we will very likely end up with valid pixels
-    // where R, G, and/or B are greater than A.  The legacy drawing
-    // pipeline does not know how to handle this.
-    if (ColorProfileTransform* xform = ColorTransform()) {
-      ImageFrame::PixelData* xform_dst = dst_row;
-      // If we're blending over the previous frame, we can't overwrite that
-      // when we do the color transform. So we allocate another row of pixels
-      // to hold the temporary result before blending. In all other cases,
-      // we can safely transform directly to the destination buffer, then do
-      // any operations in-place (premul, swizzle).
+
+  if (!decode_to_half_float_) {
+    ImageFrame::PixelData* const dst_row = buffer.GetAddr(frame_rect.X(), y);
+    if (has_alpha) {
+      if (ColorProfileTransform* xform = ColorTransform()) {
+        ImageFrame::PixelData* xform_dst = dst_row;
+        // If we're blending over the previous frame, we can't overwrite that
+        // when we do the color transform. So we allocate another row of pixels
+        // to hold the temporary result before blending. In all other cases,
+        // we can safely transform directly to the destination buffer, then do
+        // any operations in-place (premul, swizzle).
+        if (frame_buffer_cache_[current_frame_].GetAlphaBlendSource() ==
+            ImageFrame::kBlendAtopPreviousFrame) {
+          if (!color_transform_scanline_) {
+            // This buffer may be wider than necessary for this frame, but by
+            // allocating the full width of the PNG, we know it will be able to
+            // hold temporary data for any subsequent frame.
+            color_transform_scanline_.reset(
+                new ImageFrame::PixelData[Size().Width()]);
+          }
+          xform_dst = color_transform_scanline_.get();
+        }
+        skcms_PixelFormat color_format = skcms_PixelFormat_RGBA_8888;
+        skcms_AlphaFormat alpha_format = skcms_AlphaFormat_Unpremul;
+        bool color_conversion_successful = skcms_Transform(
+            src_ptr, color_format, alpha_format, xform->SrcProfile(), xform_dst,
+            color_format, alpha_format, xform->DstProfile(), width);
+        DCHECK(color_conversion_successful);
+        src_ptr = png_bytep(xform_dst);
+      }
+
+      unsigned alpha_mask = 255;
       if (frame_buffer_cache_[current_frame_].GetAlphaBlendSource() ==
-          ImageFrame::kBlendAtopPreviousFrame) {
-        if (!color_transform_scanline_) {
-          // This buffer may be wider than necessary for this frame, but by
-          // allocating the full width of the PNG, we know it will be able to
-          // hold temporary data for any subsequent frame.
-          color_transform_scanline_.reset(
-              new ImageFrame::PixelData[Size().Width()]);
-        }
-        xform_dst = color_transform_scanline_.get();
-      }
-      skcms_PixelFormat color_format = skcms_PixelFormat_RGBA_8888;
-      skcms_AlphaFormat alpha_format = skcms_AlphaFormat_Unpremul;
-      bool color_conversion_successful = skcms_Transform(
-          src_ptr, color_format, alpha_format, xform->SrcProfile(), xform_dst,
-          color_format, alpha_format, xform->DstProfile(), width);
-      DCHECK(color_conversion_successful);
-      src_ptr = png_bytep(xform_dst);
-    }
-
-    unsigned alpha_mask = 255;
-    if (frame_buffer_cache_[current_frame_].GetAlphaBlendSource() ==
-        ImageFrame::kBlendAtopBgcolor) {
-      if (buffer.PremultiplyAlpha()) {
+          ImageFrame::kBlendAtopBgcolor) {
+        if (buffer.PremultiplyAlpha()) {
 #if (defined(__ARM_NEON__) || defined(__ARM_NEON))
-        SetRGBAPremultiplyRowNeon(src_ptr, width, dst_row, &alpha_mask);
+          SetRGBAPremultiplyRowNeon(src_ptr, width, dst_row, &alpha_mask);
 #else
-        for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
-             dst_pixel++, src_ptr += 4) {
-          ImageFrame::SetRGBAPremultiply(dst_pixel, src_ptr[0], src_ptr[1],
-                                         src_ptr[2], src_ptr[3]);
-          alpha_mask &= src_ptr[3];
-        }
+          for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+               dst_pixel++, src_ptr += 4) {
+            ImageFrame::SetRGBAPremultiply(dst_pixel, src_ptr[0], src_ptr[1],
+                                           src_ptr[2], src_ptr[3]);
+            alpha_mask &= src_ptr[3];
+          }
 #endif
-      } else {
+        } else {
 #if (defined(__ARM_NEON__) || defined(__ARM_NEON))
-        SetRGBARawRowNeon(src_ptr, width, dst_row, &alpha_mask);
+          SetRGBARawRowNeon(src_ptr, width, dst_row, &alpha_mask);
 #else
-        for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
-             dst_pixel++, src_ptr += 4) {
-          ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2],
-                                 src_ptr[3]);
-          alpha_mask &= src_ptr[3];
-        }
-#endif
-      }
-    } else {
-      // Now, the blend method is ImageFrame::BlendAtopPreviousFrame. Since the
-      // frame data of the previous frame is copied at InitFrameBuffer, we can
-      // blend the pixel of this frame, stored in |src_ptr|, over the previous
-      // pixel stored in |dst_pixel|.
-      if (buffer.PremultiplyAlpha()) {
-        for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
-             dst_pixel++, src_ptr += 4) {
-          ImageFrame::BlendRGBAPremultiplied(dst_pixel, src_ptr[0], src_ptr[1],
-                                             src_ptr[2], src_ptr[3]);
-          alpha_mask &= src_ptr[3];
-        }
-      } else {
-        for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
-             dst_pixel++, src_ptr += 4) {
-          ImageFrame::BlendRGBARaw(dst_pixel, src_ptr[0], src_ptr[1],
+          for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+               dst_pixel++, src_ptr += 4) {
+            ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1],
                                    src_ptr[2], src_ptr[3]);
-          alpha_mask &= src_ptr[3];
+            alpha_mask &= src_ptr[3];
+          }
+#endif
+        }
+      } else {
+        // Now, the blend method is ImageFrame::BlendAtopPreviousFrame. Since
+        // the frame data of the previous frame is copied at InitFrameBuffer, we
+        // can blend the pixel of this frame, stored in |src_ptr|, over the
+        // previous pixel stored in |dst_pixel|.
+        if (buffer.PremultiplyAlpha()) {
+          for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+               dst_pixel++, src_ptr += 4) {
+            ImageFrame::BlendRGBAPremultiplied(
+                dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]);
+            alpha_mask &= src_ptr[3];
+          }
+        } else {
+          for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+               dst_pixel++, src_ptr += 4) {
+            ImageFrame::BlendRGBARaw(dst_pixel, src_ptr[0], src_ptr[1],
+                                     src_ptr[2], src_ptr[3]);
+            alpha_mask &= src_ptr[3];
+          }
         }
       }
-    }
 
-    if (alpha_mask != 255)
-      current_buffer_saw_alpha_ = true;
+      if (alpha_mask != 255)
+        current_buffer_saw_alpha_ = true;
 
-  } else {
+    } else {
 #if (defined(__ARM_NEON__) || defined(__ARM_NEON))
-    SetRGBARawRowNoAlphaNeon(src_ptr, width, dst_row);
+      SetRGBARawRowNoAlphaNeon(src_ptr, width, dst_row);
 #else
-    for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
-         src_ptr += 3, ++dst_pixel) {
-      ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2],
-                             255);
-    }
+      for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+           src_ptr += 3, ++dst_pixel) {
+        ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2],
+                               255);
+      }
 #endif
-    // We'll apply the color space xform to opaque pixels after they have been
-    // written to the ImageFrame.
-    // TODO: Apply the xform to the RGB pixels, skipping second pass over data.
-    if (ColorProfileTransform* xform = ColorTransform()) {
-      skcms_AlphaFormat alpha_format = skcms_AlphaFormat_Opaque;
-      bool color_conversion_successful =
-          skcms_Transform(dst_row, XformColorFormat(), alpha_format,
-                          xform->SrcProfile(), dst_row, XformColorFormat(),
-                          alpha_format, xform->DstProfile(), width);
-      DCHECK(color_conversion_successful);
+      // We'll apply the color space xform to opaque pixels after they have been
+      // written to the ImageFrame.
+      // TODO: Apply the xform to the RGB pixels, skipping second pass over
+      // data.
+      if (ColorProfileTransform* xform = ColorTransform()) {
+        skcms_AlphaFormat alpha_format = skcms_AlphaFormat_Opaque;
+        bool color_conversion_successful =
+            skcms_Transform(dst_row, XformColorFormat(), alpha_format,
+                            xform->SrcProfile(), dst_row, XformColorFormat(),
+                            alpha_format, xform->DstProfile(), width);
+        DCHECK(color_conversion_successful);
+      }
     }
+  } else {  // for if (!decode_to_half_float_)
+    ImageFrame::PixelDataF16* const dst_row_f16 =
+        buffer.GetAddrF16(frame_rect.X(), y);
+
+    // TODO(zakerinasab): https://crbug.com/874057
+    // Due to a lack of 16 bit APNG encoders, multi-frame 16 bit APNGs are not
+    // supported. Hence, we expect the blending mode always be
+    // kBlendAtopBgcolor.
+    DCHECK(frame_buffer_cache_[current_frame_].GetAlphaBlendSource() ==
+           ImageFrame::kBlendAtopBgcolor);
+
+    // Color space transformation to the dst space and converting the decoded
+    // color componenets from uint16 to float16.
+    auto* xform = ColorTransform();
+    auto* src_profile = xform ? xform->SrcProfile() : nullptr;
+    auto* dst_profile = xform ? xform->DstProfile() : nullptr;
+    auto src_format = has_alpha ? skcms_PixelFormat_RGBA_16161616
+                                : skcms_PixelFormat_RGB_161616;
+    auto src_alpha_format =
+        has_alpha ? skcms_AlphaFormat_Unpremul : skcms_AlphaFormat_Opaque;
+    auto dst_alpha_format = has_alpha ? (buffer.PremultiplyAlpha()
+                                             ? skcms_AlphaFormat_PremulAsEncoded
+                                             : skcms_AlphaFormat_Unpremul)
+                                      : skcms_AlphaFormat_Opaque;
+    bool success = skcms_Transform(
+        src_ptr, src_format, src_alpha_format, src_profile, dst_row_f16,
+        skcms_PixelFormat_RGBA_hhhh, dst_alpha_format, dst_profile, width);
+    DCHECK(success);
+
+    current_buffer_saw_alpha_ = has_alpha;
   }
 
   buffer.SetPixelsChanged(true);
diff --git a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h
index a82f8a0..8c44bfe 100644
--- a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h
+++ b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h
@@ -48,6 +48,7 @@
   String FilenameExtension() const override { return "png"; }
   bool SetSize(unsigned, unsigned) override;
   int RepetitionCount() const override;
+  bool ImageIsHighBitDepth() override;
   bool FrameIsReceivedAtIndex(size_t) const override;
   TimeDelta FrameDurationAtIndex(size_t) const override;
   bool SetFailed() override;
@@ -59,6 +60,7 @@
 
   void SetColorSpace();
   void SetRepetitionCount(int);
+  void SetBitDepth();
 
  private:
   using ParseQuery = PNGImageReader::ParseQuery;
@@ -78,6 +80,8 @@
   int repetition_count_;
   bool has_alpha_channel_;
   bool current_buffer_saw_alpha_;
+  bool decode_to_half_float_;
+  size_t bit_depth_;
   std::unique_ptr<ImageFrame::PixelData[]> color_transform_scanline_;
 };
 
diff --git a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
index c24afa1..26ab2543 100644
--- a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
@@ -9,6 +9,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/skia/include/core/SkImage.h"
 
 // /LayoutTests/images/resources/png-animated-idat-part-of-animation.png
 // is modified in multiple tests to simulate erroneous PNGs. As a reference,
@@ -43,6 +44,13 @@
   return CreatePNGDecoder(ImageDecoder::kAlphaNotPremultiplied);
 }
 
+std::unique_ptr<ImageDecoder> Create16BitPNGDecoder() {
+  return std::make_unique<PNGImageDecoder>(
+      ImageDecoder::kAlphaNotPremultiplied,
+      ImageDecoder::kHighBitDepthToHalfFloat, ColorBehavior::Tag(),
+      ImageDecoder::kNoDecodedImageByteLimit);
+}
+
 std::unique_ptr<ImageDecoder> CreatePNGDecoderWithPngData(
     const char* png_file) {
   auto decoder = CreatePNGDecoder();
@@ -994,6 +1002,252 @@
       "/images/resources/png-simple.png", 1000u);
 }
 
+struct PNGSample {
+  String filename;
+  String color_space;
+  bool is_transparent;
+  bool is_high_bit_depth;
+  scoped_refptr<SharedBuffer> png_contents;
+  std::vector<float> expected_pixels;
+};
+
+static void TestHighBitDepthPNGDecoding(const PNGSample& png_sample,
+                                        ImageDecoder* decoder) {
+  scoped_refptr<SharedBuffer> png = png_sample.png_contents;
+  ASSERT_TRUE(png.get());
+  decoder->SetData(png.get(), true);
+  ASSERT_TRUE(decoder->IsSizeAvailable());
+  ASSERT_TRUE(decoder->IsDecodedSizeAvailable());
+
+  IntSize size(2, 2);
+  ASSERT_EQ(size, decoder->Size());
+  ASSERT_EQ(size, decoder->DecodedSize());
+  ASSERT_EQ(true, decoder->ImageIsHighBitDepth());
+
+  ASSERT_TRUE(decoder->FrameIsReceivedAtIndex(0));
+  ASSERT_EQ(size, decoder->FrameSizeAtIndex(0));
+
+  ASSERT_EQ(1u, decoder->FrameCount());
+  ASSERT_EQ(kAnimationNone, decoder->RepetitionCount());
+
+  auto* frame = decoder->DecodeFrameBufferAtIndex(0);
+  ASSERT_TRUE(frame);
+  ASSERT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+  ASSERT_EQ(ImageFrame::kRGBA_F16, frame->GetPixelFormat());
+
+  sk_sp<SkImage> image = frame->FinalizePixelsAndGetImage();
+  ASSERT_TRUE(image);
+
+  ASSERT_EQ(2, image->width());
+  ASSERT_EQ(2, image->height());
+  ASSERT_EQ(kRGBA_F16_SkColorType, image->colorType());
+
+  // Readback pixels and convert color components from half float to float.
+  SkImageInfo info =
+      SkImageInfo::Make(2, 2, kRGBA_F16_SkColorType, kUnpremul_SkAlphaType,
+                        image->refColorSpace());
+  std::unique_ptr<uint8_t[]> decoded_pixels(
+      new uint8_t[info.computeMinByteSize()]());
+  ASSERT_TRUE(
+      image->readPixels(info, decoded_pixels.get(), info.minRowBytes(), 0, 0));
+
+  float decoded_pixels_float_32[16];
+  ASSERT_TRUE(skcms_Transform(
+      decoded_pixels.get(), skcms_PixelFormat_RGBA_hhhh,
+      skcms_AlphaFormat_Unpremul, nullptr, decoded_pixels_float_32,
+      skcms_PixelFormat_RGBA_ffff, skcms_AlphaFormat_Unpremul, nullptr, 4));
+
+  std::vector<float> expected_pixels = png_sample.expected_pixels;
+  bool test_succeed = true;
+  const float decoding_tolerance = 0.001;
+  for (int i = 0; i < 16; i++) {
+    if (fabs(decoded_pixels_float_32[i] - expected_pixels[i]) >
+        decoding_tolerance) {
+      DLOG(DCHECK) << "Pixel comparison failed. File: " << png_sample.filename
+                   << ", component index: " << i
+                   << ", actual: " << decoded_pixels_float_32[i]
+                   << ", expected: " << expected_pixels[i]
+                   << ", tolerance: " << decoding_tolerance;
+      test_succeed = false;
+    }
+  }
+  ASSERT_TRUE(test_succeed);
+}
+
+static void FillPNGSamplesSourcePixels(std::vector<PNGSample>& png_samples) {
+  // Color components of opaque and transparent 16 bit PNG, read with libpng
+  // in BigEndian and scaled to [0,1]. The values are read from non-interlaced
+  // samples, but used for both interlaced and non-interlaced test cases.
+  static const std::vector<float> source_pixels_opaque_srgb = {
+      0.4986953536, 0.5826657511, 0.7013199054, 1,   // Top left pixel
+      0.907988098,  0.8309605554, 0.492011902,  1,   // Top right pixel
+      0.6233157855, 0.9726558328, 0.9766536965, 1,   // Bottom left pixel
+      0.8946517128, 0.9663080797, 0.9053025101, 1};  // Bottom right pixel
+  static const std::vector<float> source_pixels_opaque_adobe_rgb = {
+      0.4448004883, 0.5216296635, 0.6506294347, 1,   // Top left pixel
+      0.8830548562, 0.7978179599, 0.4323186084, 1,   // Top right pixel
+      0.6841992828, 0.9704280156, 0.9711299306, 1,   // Bottom left pixel
+      0.8874799725, 0.96099794,   0.8875715267, 1};  // Bottom right pixel
+  static const std::vector<float> source_pixels_opaque_p3 = {
+      0.515648127,  0.5802243076, 0.6912489509, 1,   // Top left pixel
+      0.8954146639, 0.8337987335, 0.5691767758, 1,   // Top right pixel
+      0.772121767,  0.9671625849, 0.973510338,  1,   // Bottom left pixel
+      0.9118944076, 0.9645685512, 0.9110704204, 1};  // Bottom right pixel
+  static const std::vector<float> source_pixels_opaque_e_srgb = {
+      0.6414435035, 0.6857862211, 0.747005417,  1,   // Top left pixel
+      0.877347982,  0.8382848859, 0.6494087129, 1,   // Top right pixel
+      0.735194934,  0.9353933013, 0.9374380102, 1,   // Bottom left pixel
+      0.9209277485, 0.9575799191, 0.9264515145, 1};  // Bottom right pixel
+  static const std::vector<float> source_pixels_opaque_prophoto = {
+      0.5032883192, 0.5191271839, 0.6309147784, 1,   // Top left pixel
+      0.8184176394, 0.8002899214, 0.5526970321, 1,   // Top right pixel
+      0.842526894,  0.945616846,  0.9667048142, 1,   // Bottom left pixel
+      0.9119554437, 0.9507133593, 0.9001754788, 1};  // Bottom right pixel
+  static const std::vector<float> source_pixels_opaque_rec2020 = {
+      0.5390554665, 0.5766842145, 0.6851758602, 1,   // Top left pixel
+      0.871061265,  0.831326772,  0.5805294881, 1,   // Top right pixel
+      0.8386205844, 0.9599603265, 0.9727168688, 1,   // Bottom left pixel
+      0.9235217823, 0.9611200122, 0.9112840467, 1};  // Bottom right pixel
+
+  static const std::vector<float> source_pixels_transparent_srgb = {
+      0.3733272297,  0.4783093004, 0.6266422522, 0.8,   // Top left pixel
+      0.8466468299,  0.7182879377, 0.153322652,  0.6,   // Top right pixel
+      0.05831998169, 0.9316395819, 0.9416495003, 0.4,   // Bottom left pixel
+      0.4733043412,  0.8316319524, 0.5266346227, 0.2};  // Bottom right pixel
+  static const std::vector<float> source_pixels_transparent_adobe_rgb = {
+      0.305943389,  0.4019836728, 0.5632867933,  0.8,   // Top left pixel
+      0.8051117723, 0.6630197604, 0.05374227512, 0.6,   // Top right pixel
+      0.210482948,  0.926115816,  0.9278248264,  0.4,   // Bottom left pixel
+      0.4374456397, 0.8050812543, 0.4379644465,  0.2};  // Bottom right pixel
+  static const std::vector<float> source_pixels_transparent_p3 = {
+      0.3945372702, 0.475257496,  0.6140383001, 0.8,   // Top left pixel
+      0.8257114519, 0.7230182345, 0.2819256886, 0.6,   // Top right pixel
+      0.4302738994, 0.9179064622, 0.933806363,  0.4,   // Bottom left pixel
+      0.5595330739, 0.8228122377, 0.5554436561, 0.2};  // Bottom right pixel
+  static const std::vector<float> source_pixels_transparent_e_srgb = {
+      0.5517814908, 0.6072327764, 0.6837415122, 0.8,   // Top left pixel
+      0.7955901427, 0.7304646372, 0.4156557565, 0.6,   // Top right pixel
+      0.3380178531, 0.8385290303, 0.8435950256, 0.4,   // Bottom left pixel
+      0.6046997787, 0.7879606317, 0.6323186084, 0.2};  // Bottom right pixel
+  static const std::vector<float> source_pixels_transparent_prophoto = {
+      0.379064622,  0.3988708324, 0.5386282139, 0.8,   // Top left pixel
+      0.6973525597, 0.6671396963, 0.2544289311, 0.6,   // Top right pixel
+      0.6063477531, 0.864103151,  0.9168078126, 0.4,   // Bottom left pixel
+      0.5598077363, 0.7536278325, 0.5009384298, 0.2};  // Bottom right pixel
+  static const std::vector<float> source_pixels_transparent_rec2020 = {
+      0.4237735561, 0.4708323796, 0.6064698253, 0.8,   // Top left pixel
+      0.7851224537, 0.7188677806, 0.3008468757, 0.6,   // Top right pixel
+      0.5965819791, 0.8999618524, 0.9318532082, 0.4,   // Bottom left pixel
+      0.6176699474, 0.805600061,  0.5565117876, 0.2};  // Bottom right pixel
+
+  for (PNGSample& png_sample : png_samples) {
+    if (png_sample.color_space == "sRGB") {
+      png_sample.expected_pixels = png_sample.is_transparent
+                                       ? source_pixels_transparent_srgb
+                                       : source_pixels_opaque_srgb;
+    } else if (png_sample.color_space == "AdobeRGB") {
+      png_sample.expected_pixels = png_sample.is_transparent
+                                       ? source_pixels_transparent_adobe_rgb
+                                       : source_pixels_opaque_adobe_rgb;
+    } else if (png_sample.color_space == "DisplayP3") {
+      png_sample.expected_pixels = png_sample.is_transparent
+                                       ? source_pixels_transparent_p3
+                                       : source_pixels_opaque_p3;
+    } else if (png_sample.color_space == "e-sRGB") {
+      png_sample.expected_pixels = png_sample.is_transparent
+                                       ? source_pixels_transparent_e_srgb
+                                       : source_pixels_opaque_e_srgb;
+    } else if (png_sample.color_space == "ProPhoto") {
+      png_sample.expected_pixels = png_sample.is_transparent
+                                       ? source_pixels_transparent_prophoto
+                                       : source_pixels_opaque_prophoto;
+    } else if (png_sample.color_space == "Rec2020") {
+      png_sample.expected_pixels = png_sample.is_transparent
+                                       ? source_pixels_transparent_rec2020
+                                       : source_pixels_opaque_rec2020;
+    } else {
+      NOTREACHED();
+    }
+  }
+}
+
+static std::vector<PNGSample> GetPNGSamplesInfo(bool include_8bit_pngs) {
+  std::vector<PNGSample> png_samples;
+  std::vector<String> interlace_status = {"", "_interlaced"};
+  // TODO(zakerinasab) https://crbug.com/874939:
+  // e-sRGB decodes fine to 8888, but fails to decode to F16, hence not tested.
+  std::vector<String> color_spaces = {"sRGB", "AdobeRGB", "DisplayP3",
+                                      "ProPhoto", "Rec2020"};
+  std::vector<String> alpha_status = {"_opaque", "_transparent"};
+
+  for (String color_space : color_spaces) {
+    for (String alpha : alpha_status) {
+      PNGSample png_sample;
+      png_sample.filename.append("_");
+      png_sample.filename.append(color_space);
+      png_sample.filename.append(alpha);
+      png_sample.filename.append(".png");
+      png_sample.color_space = color_space;
+      png_sample.is_transparent = (alpha == "_transparent");
+
+      for (String interlace : interlace_status) {
+        PNGSample high_bit_depth_sample(png_sample);
+        high_bit_depth_sample.filename.insert(interlace, 0);
+        high_bit_depth_sample.filename.insert("2x2_16bit", 0);
+        high_bit_depth_sample.is_high_bit_depth = true;
+        png_samples.push_back(high_bit_depth_sample);
+      }
+      if (include_8bit_pngs) {
+        PNGSample regular_bit_depth_sample(png_sample);
+        regular_bit_depth_sample.filename.insert("2x2_8bit", 0);
+        regular_bit_depth_sample.is_high_bit_depth = false;
+        png_samples.push_back(regular_bit_depth_sample);
+      }
+    }
+  }
+
+  return png_samples;
+}
+
+TEST(StaticPNGTests, DecodeHighBitDepthPngToHalfFloat) {
+  const bool include_8bit_pngs = false;
+  std::vector<PNGSample> png_samples = GetPNGSamplesInfo(include_8bit_pngs);
+  FillPNGSamplesSourcePixels(png_samples);
+  String path = "/images/resources/png-16bit/";
+  for (PNGSample& png_sample : png_samples) {
+    String full_path = path;
+    full_path.append(png_sample.filename);
+    png_sample.png_contents = ReadFile(full_path.Ascii().data());
+    auto decoder = Create16BitPNGDecoder();
+    TestHighBitDepthPNGDecoding(png_sample, decoder.get());
+  }
+}
+
+TEST(StaticPNGTests, ImageIsHighBitDepth) {
+  const bool include_8bit_pngs = true;
+  std::vector<PNGSample> png_samples = GetPNGSamplesInfo(include_8bit_pngs);
+  IntSize size(2, 2);
+
+  String path = "/images/resources/png-16bit/";
+  for (PNGSample& png_sample : png_samples) {
+    String full_path = path;
+    full_path.append(png_sample.filename);
+    png_sample.png_contents = ReadFile(full_path.Ascii().data());
+    ASSERT_TRUE(png_sample.png_contents.get());
+
+    std::unique_ptr<ImageDecoder> decoders[] = {CreatePNGDecoder(),
+                                                Create16BitPNGDecoder()};
+    for (auto& decoder : decoders) {
+      decoder->SetData(png_sample.png_contents.get(), true);
+      ASSERT_TRUE(decoder->IsSizeAvailable());
+      ASSERT_TRUE(decoder->IsDecodedSizeAvailable());
+      ASSERT_EQ(size, decoder->Size());
+      ASSERT_EQ(size, decoder->DecodedSize());
+      ASSERT_EQ(png_sample.is_high_bit_depth, decoder->ImageIsHighBitDepth());
+    }
+  }
+}
+
 TEST(PNGTests, VerifyFrameCompleteBehavior) {
   struct {
     const char* name;
@@ -1071,14 +1325,15 @@
   auto decoder =
       CreatePNGDecoderWithPngData("/images/resources/crbug807324.png");
 
-  // An update to libpng (without using the libpng-provided workaround) resulted
-  // in truncating this image. It has no transparency, so no pixel should be
-  // transparent.
+  // An update to libpng (without using the libpng-provided workaround)
+  // resulted in truncating this image. It has no transparency, so no pixel
+  // should be transparent.
   auto* frame = decoder->DecodeFrameBufferAtIndex(0);
   auto size = decoder->Size();
-  for (int i = 0; i < size.Width();  ++i)
-  for (int j = 0; j < size.Height(); ++j) {
-    ASSERT_NE(SK_ColorTRANSPARENT, *frame->GetAddr(i, j));
+  for (int i = 0; i < size.Width(); ++i) {
+    for (int j = 0; j < size.Height(); ++j) {
+      ASSERT_NE(SK_ColorTRANSPARENT, *frame->GetAddr(i, j));
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc b/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc
index a0b617fb..fe55f76 100644
--- a/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc
+++ b/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc
@@ -543,8 +543,14 @@
       fctl_needs_dat_chunk_ = false;
       if (ignore_animation_)
         is_animated_ = false;
+      // SetSize() requires bit depth information to correctly fallback to 8888
+      // decoding if there is not enough memory to decode to f16 pixel format.
+      // SetBitDepth() requires repition count to correctly fallback to 8888
+      // decoding for multi-frame APNGs (https://crbug.com/874057). Therefore,
+      // the order of the next three calls matters.
       if (!is_animated_ || 1 == reported_frame_count_)
         decoder_->SetRepetitionCount(kAnimationNone);
+      decoder_->SetBitDepth();
       if (!decoder_->SetSize(width_, height_))
         return false;
       decoder_->SetColorSpace();
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
index d43122fc6..71f36d3 100644
--- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
@@ -194,7 +194,8 @@
     c->RedirectBlocked();
 }
 
-SourceKeyedCachedMetadataHandler* RawResource::CacheHandler() {
+SourceKeyedCachedMetadataHandler* RawResource::InlineScriptCacheHandler() {
+  DCHECK_EQ(kMainResource, GetType());
   return static_cast<SourceKeyedCachedMetadataHandler*>(
       Resource::CacheHandler());
 }
@@ -227,16 +228,24 @@
 
 CachedMetadataHandler* RawResource::CreateCachedMetadataHandler(
     std::unique_ptr<CachedMetadataSender> send_callback) {
-  return new SourceKeyedCachedMetadataHandler(Encoding(),
-                                              std::move(send_callback));
+  // If this is the document resource, create a cache handler that can handle
+  // multiple inline scripts.
+  if (GetType() == kMainResource) {
+    return new SourceKeyedCachedMetadataHandler(Encoding(),
+                                                std::move(send_callback));
+  }
+  return Resource::CreateCachedMetadataHandler(std::move(send_callback));
 }
 
 void RawResource::SetSerializedCachedMetadata(const char* data, size_t size) {
   Resource::SetSerializedCachedMetadata(data, size);
 
-  SourceKeyedCachedMetadataHandler* cache_handler = CacheHandler();
-  if (cache_handler) {
-    cache_handler->SetSerializedCachedMetadata(data, size);
+  if (GetType() == kMainResource) {
+    SourceKeyedCachedMetadataHandler* cache_handler =
+        InlineScriptCacheHandler();
+    if (cache_handler) {
+      cache_handler->SetSerializedCachedMetadata(data, size);
+    }
   }
 
   ResourceClientWalker<RawResourceClient> w(Clients());
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
index 950e634..88a5f01 100644
--- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
@@ -90,8 +90,9 @@
 
   // Used for code caching of scripts with source code inline in the HTML.
   // Returns a cache handler which can store multiple cache metadata entries,
-  // keyed by the source code of the script.
-  SourceKeyedCachedMetadataHandler* CacheHandler();
+  // keyed by the source code of the script. This is valid only if type is
+  // kMainResource.
+  SourceKeyedCachedMetadataHandler* InlineScriptCacheHandler();
 
   scoped_refptr<BlobDataHandle> DownloadedBlob() const {
     return downloaded_blob_;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a96e2bb8..e732d04 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -843,10 +843,6 @@
       name: "OrientationEvent",
     },
     {
-      name: "OriginPolicy",
-      status: "test",
-    },
-    {
       name: "OriginTrials",
       status: "stable",
     },
@@ -946,6 +942,10 @@
       status: "experimental",
     },
     {
+      name: "Portals",
+      status: "test",
+    },
+    {
       name: "PostMessageOptions",
       status: "experimental",
     },
diff --git a/third_party/blink/tools/audit_non_blink_usage.py b/third_party/blink/tools/audit_non_blink_usage.py
index 1c0fe38..7b7df05 100755
--- a/third_party/blink/tools/audit_non_blink_usage.py
+++ b/third_party/blink/tools/audit_non_blink_usage.py
@@ -260,9 +260,6 @@
 
             # Blink uses UKM for logging e.g. always-on leak detection (crbug/757374)
             'ukm::.+',
-
-            # WebRTC classes
-            'webrtc::.+',
         ],
         'disallowed': ['.+'],
     },
@@ -395,6 +392,16 @@
         ],
         'allowed': ['crypto::.+'],
     },
+    {
+        'paths': [
+            'third_party/blink/renderer/modules/peerconnection',
+            'third_party/blink/renderer/bindings/modules/v8/serialization',
+        ],
+        'allowed': [
+            'webrtc::.+',
+            'rtc::.+',
+        ]
+    }
 ]
 
 
diff --git a/tools/binary_size/libsupersize/apkanalyzer.py b/tools/binary_size/libsupersize/apkanalyzer.py
index 1efc52f..3dac7ce 100644
--- a/tools/binary_size/libsupersize/apkanalyzer.py
+++ b/tools/binary_size/libsupersize/apkanalyzer.py
@@ -111,9 +111,11 @@
       else:
         # Sibling or higher nodes
         break
-    assert total_child_size <= size, (
-        'Child node total size exceeded parent node total size')
-    node_size = size - total_child_size
+    # Disabled for perf bots: https://crbug.com/874956
+    # TODO(wnwen): Investigate reason
+    #assert total_child_size <= size, (
+    #    'Child node total size exceeded parent node total size')
+    node_size = max(0, size - total_child_size)
     nodes.append((name, node_size))
     return next_idx, size
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index df52ce48..7e3f777 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -51,6 +51,14 @@
       'Cast Android (dbg)': 'android_cast_debug_static_bot',
       'Deterministic Android': 'android_without_codecs_release_bot_minimal_symbols',
       'Deterministic Android (dbg)': 'android_debug_bot',
+
+      'android-cronet-arm-dbg': 'android_cronet_debug_static_bot_arm_no_neon',
+      'android-cronet-arm-rel': 'android_cronet_release_bot_minimal_symbols_arm_no_neon',
+      'android-cronet-arm64-dbg': 'android_cronet_debug_static_bot_arm64',
+      'android-cronet-arm64-rel': 'android_cronet_release_bot_minimal_symbols_arm64',
+      'android-cronet-asan-arm-rel': 'android_cronet_release_bot_minimal_symbols_arm_no_neon_clang_asan',
+      'android-cronet-x86-dbg': 'android_cronet_debug_static_bot_x86',
+      'android-cronet-x86-rel': 'android_cronet_release_bot_minimal_symbols_x86',
       'android-kitkat-arm-rel': 'android_release_bot_minimal_symbols',
       'android-marshmallow-arm64-rel': 'android_release_bot_minimal_symbols_arm64',
     },
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 1080cd3..65c7164 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3663,6 +3663,159 @@
   <int value="8" label="Unknown or unhandler error"/>
 </enum>
 
+<enum name="BluetoothSocketServiceHash">
+  <summary>
+    31-bit hash of the service UUID. Values here are populated from
+    https://www.bluetooth.com/specifications/assigned-numbers/service-discovery
+  </summary>
+  <int value="27577045"
+      label="SIM_Access; 0000112d-0000-1000-8000-00805f9b34fb"/>
+  <int value="46048502"
+      label="VideoSink; 00001304-0000-1000-8000-00805f9b34fb"/>
+  <int value="53242880"
+      label="3D Glasses; 00001138-0000-1000-8000-00805f9b34fb"/>
+  <int value="80025460"
+      label="AdvancedAudioDistribution; 0000110d-0000-1000-8000-00805f9b34fb"/>
+  <int value="80662886"
+      label="A/V_RemoteControlTarget; 0000110c-0000-1000-8000-00805f9b34fb"/>
+  <int value="89015451"
+      label="HDP Source; 00001401-0000-1000-8000-00805f9b34fb"/>
+  <int value="93024408"
+      label="ReferencePrinting; 00001119-0000-1000-8000-00805f9b34fb"/>
+  <int value="134662076" label="GN; 00001117-0000-1000-8000-00805f9b34fb"/>
+  <int value="153517886"
+      label="DirectPrintingReferenceObjectsService;
+             00001120-0000-1000-8000-00805f9b34fb"/>
+  <int value="164698689"
+      label="DirectPrinting; 00001118-0000-1000-8000-00805f9b34fb"/>
+  <int value="181138738" label="WAP; 00001113-0000-1000-8000-00805f9b34fb"/>
+  <int value="191537385"
+      label="BasicPrinting; 00001122-0000-1000-8000-00805f9b34fb"/>
+  <int value="219461847"
+      label="Basic Imaging Profile; 0000111a-0000-1000-8000-00805f9b34fb"/>
+  <int value="223205883"
+      label="HCR_Scan; 00001127-0000-1000-8000-00805f9b34fb"/>
+  <int value="230869022"
+      label="ESDP_UPNP_IP_LAP; 00001301-0000-1000-8000-00805f9b34fb"/>
+  <int value="237750726"
+      label="AudioSource; 0000110a-0000-1000-8000-00805f9b34fb"/>
+  <int value="270998158"
+      label="OBEXFileTransfer; 00001106-0000-1000-8000-00805f9b34fb"/>
+  <int value="308706592"
+      label="Handsfree; 0000111e-0000-1000-8000-00805f9b34fb"/>
+  <int value="312472359"
+      label="ESDP_UPNP_L2CAP; 00001302-0000-1000-8000-00805f9b34fb"/>
+  <int value="325643766"
+      label="CTN Notification Service; 0000113d-0000-1000-8000-00805f9b34fb"/>
+  <int value="414065238"
+      label="Headset - HS; 00001131-0000-1000-8000-00805f9b34fb"/>
+  <int value="414260640"
+      label="UPNP_IP_Service; 00001206-0000-1000-8000-00805f9b34fb"/>
+  <int value="474133479"
+      label="Message Access Server; 00001132-0000-1000-8000-00805f9b34fb"/>
+  <int value="481017904"
+      label="GenericNetworking; 00001201-0000-1000-8000-00805f9b34fb"/>
+  <int value="488312511"
+      label="Phonebook Access - PCE; 0000112e-0000-1000-8000-00805f9b34fb"/>
+  <int value="556074802"
+      label="3D Display; 00001137-0000-1000-8000-00805f9b34fb"/>
+  <int value="559272304"
+      label="GenericFileTransfer; 00001202-0000-1000-8000-00805f9b34fb"/>
+  <int value="575067238"
+      label="AudioSink; 0000110b-0000-1000-8000-00805f9b34fb"/>
+  <int value="579644444"
+      label="PnPInformation; 00001200-0000-1000-8000-00805f9b34fb"/>
+  <int value="618213861"
+      label="HCR_Print; 00001126-0000-1000-8000-00805f9b34fb"/>
+  <int value="687640090"
+      label="HardcopyCableReplacement; 00001125-0000-1000-8000-00805f9b34fb"/>
+  <int value="690920522"
+      label="HumanInterfaceDeviceService;
+             00001124-0000-1000-8000-00805f9b34fb"/>
+  <int value="726709874"
+      label="MPS SC UUID; 0000113b-0000-1000-8000-00805f9b34fb"/>
+  <int value="772096015" label="GNSS; 00001135-0000-1000-8000-00805f9b34fb"/>
+  <int value="788291396"
+      label="ImagingResponder; 0000111b-0000-1000-8000-00805f9b34fb"/>
+  <int value="826986046"
+      label="GenericAudio; 00001203-0000-1000-8000-00805f9b34fb"/>
+  <int value="917447240"
+      label="GenericTelephony; 00001204-0000-1000-8000-00805f9b34fb"/>
+  <int value="932610720"
+      label="HDP Sink; 00001402-0000-1000-8000-00805f9b34fb"/>
+  <int value="1048137004"
+      label="Common_ISDN_Access; 00001128-0000-1000-8000-00805f9b34fb"/>
+  <int value="1089394723"
+      label="ReflectedUI; 00001121-0000-1000-8000-00805f9b34fb"/>
+  <int value="1173742133"
+      label="Phonebook Access; 00001130-0000-1000-8000-00805f9b34fb"/>
+  <int value="1185226354"
+      label="ImagingReferencedObjects; 0000111d-0000-1000-8000-00805f9b34fb"/>
+  <int value="1269421948" label="NAP; 00001116-0000-1000-8000-00805f9b34fb"/>
+  <int value="1297730173"
+      label="PrintingStatus; 00001123-0000-1000-8000-00805f9b34fb"/>
+  <int value="1322541006"
+      label="CTN Access Service; 0000113c-0000-1000-8000-00805f9b34fb"/>
+  <int value="1346822007"
+      label="A/V_RemoteControl; 0000110e-0000-1000-8000-00805f9b34fb"/>
+  <int value="1353247390"
+      label="IrMCSync; 00001104-0000-1000-8000-00805f9b34fb"/>
+  <int value="1391113544"
+      label="Intercom; 00001110-0000-1000-8000-00805f9b34fb"/>
+  <int value="1406818563"
+      label="MPS Profile UUID; 0000113a-0000-1000-8000-00805f9b34fb"/>
+  <int value="1416242305"
+      label="3D Synchronization; 00001139-0000-1000-8000-00805f9b34fb"/>
+  <int value="1539513745"
+      label="CTN Profile; 0000113e-0000-1000-8000-00805f9b34fb"/>
+  <int value="1559400847"
+      label="Phonebook Access - PSE; 0000112f-0000-1000-8000-00805f9b34fb"/>
+  <int value="1563279259"
+      label="GNSS_Server; 00001136-0000-1000-8000-00805f9b34fb"/>
+  <int value="1564925248"
+      label="LANAccessUsingPPP; 00001102-0000-1000-8000-00805f9b34fb"/>
+  <int value="1603721693"
+      label="ESDP_UPNP_IP_PAN; 00001300-0000-1000-8000-00805f9b34fb"/>
+  <int value="1608206008"
+      label="A/V_RemoteControlController;
+             0000110f-0000-1000-8000-00805f9b34fb"/>
+  <int value="1608308139"
+      label="UPNP_Service; 00001205-0000-1000-8000-00805f9b34fb"/>
+  <int value="1621920090"
+      label="ImagingAutomaticArchive; 0000111c-0000-1000-8000-00805f9b34fb"/>
+  <int value="1642311038"
+      label="HandsfreeAudioGateway; 0000111f-0000-1000-8000-00805f9b34fb"/>
+  <int value="1665040985"
+      label="Message Access Profile; 00001134-0000-1000-8000-00805f9b34fb"/>
+  <int value="1679985708"
+      label="SerialPort; 00001101-0000-1000-8000-00805f9b34fb"/>
+  <int value="1684180024"
+      label="Headset - Audio Gateway (AG);
+             00001112-0000-1000-8000-00805f9b34fb"/>
+  <int value="1686752071"
+      label="WAP_CLIENT; 00001114-0000-1000-8000-00805f9b34fb"/>
+  <int value="1692867207"
+      label="IrMCSyncCommand; 00001107-0000-1000-8000-00805f9b34fb"/>
+  <int value="1857661865" label="HDP; 00001400-0000-1000-8000-00805f9b34fb"/>
+  <int value="1914606342"
+      label="VideoSource; 00001303-0000-1000-8000-00805f9b34fb"/>
+  <int value="1917084049" label="PANU; 00001115-0000-1000-8000-00805f9b34fb"/>
+  <int value="1923026611"
+      label="OBEXObjectPush; 00001105-0000-1000-8000-00805f9b34fb"/>
+  <int value="1931905004"
+      label="Message Notification Server;
+             00001133-0000-1000-8000-00805f9b34fb"/>
+  <int value="1979370118"
+      label="DialupNetworking; 00001103-0000-1000-8000-00805f9b34fb"/>
+  <int value="2002893898"
+      label="Headset; 00001108-0000-1000-8000-00805f9b34fb"/>
+  <int value="2035822117" label="Fax; 00001111-0000-1000-8000-00805f9b34fb"/>
+  <int value="2039617417"
+      label="VideoDistribution; 00001305-0000-1000-8000-00805f9b34fb"/>
+  <int value="2060171182"
+      label="CordlessTelephony; 00001109-0000-1000-8000-00805f9b34fb"/>
+</enum>
+
 <enum name="BluetoothStatus">
   <int value="0" label="Disabled"/>
   <int value="1" label="Enabled"/>
@@ -28857,6 +29010,7 @@
   <int value="217455219" label="SyncStandaloneTransport:enabled"/>
   <int value="218890378" label="ManualSaving:disabled"/>
   <int value="219117936" label="AllowReaderForAccessibility:enabled"/>
+  <int value="219682005" label="AutoplaySoundSettings:disabled"/>
   <int value="222184258"
       label="AutofillEnforceMinRequiredFieldsForHeuristics:disabled"/>
   <int value="223662457" label="BackgroundLoadingForDownloads:enabled"/>
@@ -29547,6 +29701,7 @@
   <int value="1612446645" label="enable-weak-memorycache"/>
   <int value="1612871297" label="WebPayments:disabled"/>
   <int value="1612974229" label="allow-insecure-localhost"/>
+  <int value="1614528057" label="AutoplaySoundSettings:enabled"/>
   <int value="1614596813" label="CloseButtonsInactiveTabs:disabled"/>
   <int value="1615988672" label="GrantNotificationsToDSE:enabled"/>
   <int value="1617187093" label="enable-improved-a2hs"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 709a767..e4c7e87 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -27691,6 +27691,42 @@
   </summary>
 </histogram>
 
+<histogram name="Extensions.BluetoothSocket.Connect.Service"
+    enum="BluetoothSocketServiceHash" expires_after="M72">
+  <owner>ortuno@chromium.org</owner>
+  <owner>reillyg@chromium.org</owner>
+  <summary>
+    Records the UUID of the service to which a Chrome App opens a socket
+    connection. The recorded value is a 31-bit hash of the UUID. These results
+    will help us better understand the uses of the API and make changes
+    according to developers' behavior.
+  </summary>
+</histogram>
+
+<histogram name="Extensions.BluetoothSocket.ListenL2CAP.Service"
+    enum="BluetoothSocketServiceHash" expires_after="M72">
+  <owner>ortuno@chromium.org</owner>
+  <owner>reillyg@chromium.org</owner>
+  <summary>
+    Records the UUID of a service created by a Chrome App listening for L2CAP
+    connections. The recorded value is a 31-bit hash of the UUID. These results
+    will help us better understand the uses of the API and make changes
+    according to developers' behavior.
+  </summary>
+</histogram>
+
+<histogram name="Extensions.BluetoothSocket.ListenRFCOMM.Service"
+    enum="BluetoothSocketServiceHash" expires_after="M72">
+  <owner>ortuno@chromium.org</owner>
+  <owner>reillyg@chromium.org</owner>
+  <summary>
+    Records the UUID of a service created by a Chrome App listening for RFCOMM
+    connections. The recorded value is a 31-bit hash of the UUID. These results
+    will help us better understand the uses of the API and make changes
+    according to developers' behavior.
+  </summary>
+</histogram>
+
 <histogram name="Extensions.BookmarkApp.GetAppForCurrentURLDuration" units="ms">
   <owner>mgiuca@chromium.org</owner>
   <owner>ortuno@chromium.org</owner>
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index d38ec837..468aa9d 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -164,6 +164,8 @@
 crbug.com/338838 [ All ] rendering.mobile/basic_stream [ Skip ]
 crbug.com/873013 [ Android_Webview ] rendering.mobile/yahoo_answers_mobile_2018 [ Skip ]
 crbug.com/873014 [ Android_Webview ] rendering.mobile/yahoo_answers_mobile_sync_scroll_2018 [ Skip ]
+crbug.com/874935 [ Android_Webview ] rendering.mobile/yahoo_answers_mobile_2018 [ Skip ]
+crbug.com/874935 [ Android_Webview ] rendering.mobile/yahoo_answers_mobile_sync_scroll_2018 [ Skip ]
 
 # Benchmark: rasterize_and_record_micro.top_25
 crbug.com/764543 [ All ] rasterize_and_record_micro.top_25/file://static_top_25/wikipedia.html [ Skip ]
@@ -180,6 +182,7 @@
 
 # Benchmark: system_health.common_mobile
 crbug.com/714650 [ Android ] system_health.common_mobile/browse:news:globo [ Skip ]
+crbug.com/787001 [ Android_Webview ] system_health.common_mobile/load:media:soundcloud [ Skip ]
 crbug.com/708300 [ Android ] system_health.common_mobile/browse:shopping:flipkart [ Skip ]
 crbug.com/738854 [ Nexus_5X ] system_health.common_mobile/load:tools:drive [ Skip ]
 crbug.com/738854 [ Android_Webview ] system_health.common_mobile/load:tools:drive [ Skip ]
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index ce8dbda..ec91450 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -185,8 +185,12 @@
 
 #if defined(ATK_216)
 constexpr AtkRole kStaticRole = ATK_ROLE_STATIC;
+constexpr AtkRole kSubscriptRole = ATK_ROLE_SUBSCRIPT;
+constexpr AtkRole kSuperscriptRole = ATK_ROLE_SUPERSCRIPT;
 #else
 constexpr AtkRole kStaticRole = ATK_ROLE_TEXT;
+constexpr AtkRole kSubscriptRole = ATK_ROLE_TEXT;
+constexpr AtkRole kSuperscriptRole = ATK_ROLE_TEXT;
 #endif
 
 #if defined(ATK_226)
@@ -982,6 +986,8 @@
     case ax::mojom::Role::kAnchor:
       return ATK_ROLE_LINK;
     case ax::mojom::Role::kAnnotation:
+      // TODO(accessibility) Panels are generally for containers of widgets.
+      // This should probably be a section (if a container) or static if text.
       return ATK_ROLE_PANEL;
     case ax::mojom::Role::kApplication:
       // Only use ATK_ROLE_APPLICATION for elements with no parent, since it
@@ -1014,7 +1020,7 @@
     case ax::mojom::Role::kSwitch:
       return ATK_ROLE_TOGGLE_BUTTON;
     case ax::mojom::Role::kColorWell:
-      return ATK_ROLE_COLOR_CHOOSER;
+      return ATK_ROLE_PUSH_BUTTON;
     case ax::mojom::Role::kColumn:
       return ATK_ROLE_UNKNOWN;
     case ax::mojom::Role::kColumnHeader:
@@ -1026,9 +1032,9 @@
     case ax::mojom::Role::kComplementary:
       return ATK_ROLE_LANDMARK;
     case ax::mojom::Role::kContentDeletion:
-      return ATK_ROLE_PANEL;  // TODO(accessibility) text attribute diff:del.
     case ax::mojom::Role::kContentInsertion:
-      return ATK_ROLE_PANEL;  // TODO(accessibility) text attribute diff:ins.
+      // TODO(accessibility) https://github.com/w3c/html-aam/issues/141
+      return ATK_ROLE_SECTION;
     case ax::mojom::Role::kContentInfo:
       return ATK_ROLE_LANDMARK;
     case ax::mojom::Role::kDate:
@@ -1101,13 +1107,17 @@
     case ax::mojom::Role::kDocument:
       return ATK_ROLE_DOCUMENT_WEB;
     case ax::mojom::Role::kEmbeddedObject:
-      return ATK_ROLE_PANEL;
+      return ATK_ROLE_EMBEDDED;
     case ax::mojom::Role::kForm:
-      return ATK_ROLE_FORM;  // Spec says ATK_ROLE_LANDMARK, checking.
+      // TODO(accessibility) Forms which lack an accessible name are no longer
+      // exposed as forms. http://crbug.com/874384. Forms which have accessible
+      // names should be exposed as ATK_ROLE_LANDMARK according to Core AAM.
+      return ATK_ROLE_FORM;
     case ax::mojom::Role::kFigure:
     case ax::mojom::Role::kFeed:
-    case ax::mojom::Role::kGenericContainer:
       return ATK_ROLE_PANEL;
+    case ax::mojom::Role::kGenericContainer:
+      return ATK_ROLE_SECTION;
     case ax::mojom::Role::kGraphicsDocument:
       return ATK_ROLE_DOCUMENT_WEB;
     case ax::mojom::Role::kGraphicsObject:
@@ -1122,7 +1132,7 @@
       return ATK_ROLE_HEADING;
     case ax::mojom::Role::kIframe:
     case ax::mojom::Role::kIframePresentational:
-      return ATK_ROLE_DOCUMENT_WEB;
+      return ATK_ROLE_INTERNAL_FRAME;
     case ax::mojom::Role::kIgnored:
       return ATK_ROLE_REDUNDANT_OBJECT;
     case ax::mojom::Role::kImage:
@@ -1134,17 +1144,19 @@
     case ax::mojom::Role::kLabelText:
       return ATK_ROLE_LABEL;
     case ax::mojom::Role::kLegend:
-      return ATK_ROLE_TEXT;
+      return ATK_ROLE_LABEL;
     // Layout table objects are treated the same as Role::kGenericContainer.
     case ax::mojom::Role::kLayoutTable:
-      return ATK_ROLE_PANEL;
+      return ATK_ROLE_SECTION;
     case ax::mojom::Role::kLayoutTableCell:
-      return ATK_ROLE_PANEL;
+      return ATK_ROLE_SECTION;
     case ax::mojom::Role::kLayoutTableColumn:
-      return ATK_ROLE_PANEL;
+      return ATK_ROLE_SECTION;
     case ax::mojom::Role::kLayoutTableRow:
-      return ATK_ROLE_PANEL;
+      return ATK_ROLE_SECTION;
     case ax::mojom::Role::kLineBreak:
+      // TODO(Accessibility) Having a separate accessible object for line breaks
+      // is inconsistent with other implementations. http://crbug.com/873144#c1.
       return ATK_ROLE_TEXT;
     case ax::mojom::Role::kLink:
       return ATK_ROLE_LINK;
@@ -1157,6 +1169,8 @@
     case ax::mojom::Role::kListBoxOption:
       return ATK_ROLE_LIST_ITEM;
     case ax::mojom::Role::kListMarker:
+      // TODO(Accessibility) Having a separate accessible object for the marker
+      // is inconsistent with other implementations. http://crbug.com/873144.
       return kStaticRole;
     case ax::mojom::Role::kListItem:
       return ATK_ROLE_LIST_ITEM;
@@ -1165,7 +1179,7 @@
     case ax::mojom::Role::kMain:
       return ATK_ROLE_LANDMARK;
     case ax::mojom::Role::kMark:
-      return ATK_ROLE_TEXT;
+      return kStaticRole;
     case ax::mojom::Role::kMath:
       return ATK_ROLE_MATH;
     case ax::mojom::Role::kMarquee:
@@ -1187,7 +1201,7 @@
     case ax::mojom::Role::kMenuListOption:
       return ATK_ROLE_MENU_ITEM;
     case ax::mojom::Role::kMeter:
-      return ATK_ROLE_PROGRESS_BAR;
+      return ATK_ROLE_LEVEL_BAR;
     case ax::mojom::Role::kNavigation:
       return ATK_ROLE_LANDMARK;
     case ax::mojom::Role::kNote:
@@ -1197,10 +1211,15 @@
       return ATK_ROLE_PANEL;
     case ax::mojom::Role::kParagraph:
       return ATK_ROLE_PARAGRAPH;
-    case ax::mojom::Role::kPopUpButton:
+    case ax::mojom::Role::kPopUpButton: {
+      std::string html_tag =
+          GetData().GetStringAttribute(ax::mojom::StringAttribute::kHtmlTag);
+      if (html_tag == "select")
+        return ATK_ROLE_COMBO_BOX;
       return ATK_ROLE_PUSH_BUTTON;
+    }
     case ax::mojom::Role::kPre:
-      return ATK_ROLE_TEXT;
+      return ATK_ROLE_SECTION;
     case ax::mojom::Role::kProgressIndicator:
       return ATK_ROLE_PROGRESS_BAR;
     case ax::mojom::Role::kRadioButton:
@@ -1226,9 +1245,9 @@
     case ax::mojom::Role::kRow:
       return ATK_ROLE_TABLE_ROW;
     case ax::mojom::Role::kRowHeader:
-      return ATK_ROLE_TABLE_ROW_HEADER;  // ATK_ROLE_ROW_HEADER also exists.
+      return ATK_ROLE_ROW_HEADER;
     case ax::mojom::Role::kRuby:
-      return ATK_ROLE_TEXT;
+      return kStaticRole;
     case ax::mojom::Role::kScrollBar:
       return ATK_ROLE_SCROLL_BAR;
     case ax::mojom::Role::kSearch:
@@ -1240,29 +1259,46 @@
       return ATK_ROLE_SPIN_BUTTON;
     case ax::mojom::Role::kSplitter:
       return ATK_ROLE_SEPARATOR;
-    case ax::mojom::Role::kStaticText:
+    case ax::mojom::Role::kStaticText: {
+      switch (static_cast<ax::mojom::TextPosition>(
+          GetIntAttribute(ax::mojom::IntAttribute::kTextPosition))) {
+        case ax::mojom::TextPosition::kSubscript:
+          return kSubscriptRole;
+        case ax::mojom::TextPosition::kSuperscript:
+          return kSuperscriptRole;
+        default:
+          break;
+      }
       return ATK_ROLE_TEXT;
+    }
     case ax::mojom::Role::kStatus:
       return ATK_ROLE_STATUSBAR;
     case ax::mojom::Role::kSvgRoot:
-      return ATK_ROLE_IMAGE;
+      return ATK_ROLE_DOCUMENT_FRAME;
     case ax::mojom::Role::kTab:
       return ATK_ROLE_PAGE_TAB;
     case ax::mojom::Role::kTable:
       return ATK_ROLE_TABLE;
     case ax::mojom::Role::kTableHeaderContainer:
+      // TODO(accessibility) This mapping is correct, but it doesn't seem to be
+      // used. We don't necessarily want to always expose these containers, but
+      // we must do so if they are focusable. http://crbug.com/874043
       return ATK_ROLE_PANEL;
     case ax::mojom::Role::kTabList:
       return ATK_ROLE_PAGE_TAB_LIST;
     case ax::mojom::Role::kTabPanel:
       return ATK_ROLE_SCROLL_PANE;
     case ax::mojom::Role::kTerm:
+      // TODO(accessibility) This mapping should also be applied to the dfn
+      // element. http://crbug.com/874411
       return ATK_ROLE_DESCRIPTION_TERM;
     case ax::mojom::Role::kTitleBar:
       return ATK_ROLE_TITLE_BAR;
     case ax::mojom::Role::kInlineTextBox:
     case ax::mojom::Role::kTextField:
     case ax::mojom::Role::kSearchBox:
+      if (GetData().HasState(ax::mojom::State::kProtected))
+        return ATK_ROLE_PASSWORD_TEXT;
       if (!GetStringAttribute(ax::mojom::StringAttribute::kAutoComplete)
                .empty() ||
           IsFocusedInputWithSuggestions()) {
@@ -1273,7 +1309,7 @@
       return ATK_ROLE_COMBO_BOX;
     case ax::mojom::Role::kAbbr:
     case ax::mojom::Role::kTime:
-      return ATK_ROLE_TEXT;
+      return kStaticRole;
     case ax::mojom::Role::kTimer:
       return ATK_ROLE_TIMER;
     case ax::mojom::Role::kToggleButton:
@@ -1300,8 +1336,9 @@
       return ATK_ROLE_FRAME;
     case ax::mojom::Role::kClient:
     case ax::mojom::Role::kDesktop:
-    case ax::mojom::Role::kFigcaption:
       return ATK_ROLE_PANEL;
+    case ax::mojom::Role::kFigcaption:
+      return ATK_ROLE_CAPTION;
     case ax::mojom::Role::kFooter:
       return ATK_ROLE_FOOTER;
     case ax::mojom::Role::kKeyboard:
@@ -1318,8 +1355,10 @@
     atk_state_set_add_state(atk_state_set, ATK_STATE_EXPANDABLE);
   if (data.HasState(ax::mojom::State::kDefault))
     atk_state_set_add_state(atk_state_set, ATK_STATE_DEFAULT);
-  if (data.HasState(ax::mojom::State::kEditable))
+  if (data.HasState(ax::mojom::State::kEditable) &&
+      data.GetRestriction() != ax::mojom::Restriction::kReadOnly) {
     atk_state_set_add_state(atk_state_set, ATK_STATE_EDITABLE);
+  }
   if (data.HasState(ax::mojom::State::kExpanded)) {
     atk_state_set_add_state(atk_state_set, ATK_STATE_EXPANDABLE);
     atk_state_set_add_state(atk_state_set, ATK_STATE_EXPANDED);
@@ -1464,6 +1503,13 @@
       states->AppendString(atk_state_type_get_name(state_type));
   }
   dict->Set("states", std::move(states));
+
+  AtkAttributeSet* attributes = atk_object_get_attributes(atk_object_);
+  for (AtkAttributeSet* attr = attributes; attr; attr = attr->next) {
+    AtkAttribute* attribute = static_cast<AtkAttribute*>(attr->data);
+    dict->SetString(attribute->name, attribute->value);
+  }
+  atk_attribute_set_free(attributes);
 }
 
 gfx::NativeViewAccessible AXPlatformNodeAuraLinux::GetNativeViewAccessible() {
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
index 192df8e..3c2d50c 100644
--- a/ui/ozone/platform/drm/BUILD.gn
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -60,10 +60,10 @@
     "gpu/drm_window.h",
     "gpu/drm_window_proxy.cc",
     "gpu/drm_window_proxy.h",
-    "gpu/gbm_overlay_surface.cc",
-    "gpu/gbm_overlay_surface.h",
     "gpu/gbm_pixmap.cc",
     "gpu/gbm_pixmap.h",
+    "gpu/gbm_overlay_surface.cc",
+    "gpu/gbm_overlay_surface.h",
     "gpu/gbm_surface.cc",
     "gpu/gbm_surface.h",
     "gpu/gbm_surface_factory.cc",
@@ -90,6 +90,8 @@
     "gpu/page_flip_request.h",
     "gpu/proxy_helpers.cc",
     "gpu/proxy_helpers.h",
+    "gpu/drm_framebuffer.h",
+    "gpu/drm_framebuffer_generator.h",
     "gpu/screen_manager.cc",
     "gpu/screen_manager.h",
     "host/drm_cursor.cc",
@@ -182,8 +184,10 @@
     "gpu/hardware_display_plane_manager_unittest.cc",
     "gpu/mock_drm_device.cc",
     "gpu/mock_drm_device.h",
-    "gpu/mock_gbm_device.cc",
-    "gpu/mock_gbm_device.h",
+    "gpu/mock_dumb_buffer_generator.cc",
+    "gpu/mock_dumb_buffer_generator.h",
+    "gpu/mock_drm_framebuffer_generator.cc",
+    "gpu/mock_drm_framebuffer_generator.h",
     "gpu/proxy_helpers_unittest.cc",
     "gpu/screen_manager_unittest.cc",
   ]
diff --git a/ui/ozone/platform/drm/gpu/drm_framebuffer.cc b/ui/ozone/platform/drm/gpu/drm_framebuffer.cc
index b632257..0109b28 100644
--- a/ui/ozone/platform/drm/gpu/drm_framebuffer.cc
+++ b/ui/ozone/platform/drm/gpu/drm_framebuffer.cc
@@ -5,7 +5,6 @@
 #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 
 #include "ui/ozone/common/linux/drm_util_linux.h"
-#include "ui/ozone/common/linux/gbm_buffer.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
 
@@ -49,35 +48,6 @@
       gfx::Size(params.width, params.height));
 }
 
-// static
-scoped_refptr<DrmFramebuffer> DrmFramebuffer::AddFramebuffer(
-    scoped_refptr<DrmDevice> drm,
-    const GbmBuffer* buffer) {
-  gfx::Size size = buffer->GetSize();
-  AddFramebufferParams params;
-  params.format = buffer->GetFormat();
-  params.modifier = buffer->GetFormatModifier();
-  params.width = size.width();
-  params.height = size.height();
-  params.num_planes = buffer->GetNumPlanes();
-  for (size_t i = 0; i < params.num_planes; ++i) {
-    params.handles[i] = buffer->GetPlaneHandle(i);
-    params.strides[i] = buffer->GetPlaneStride(i);
-    params.offsets[i] = buffer->GetPlaneOffset(i);
-  }
-
-  // AddFramebuffer2 only considers the modifiers if addfb_flags has
-  // DRM_MODE_FB_MODIFIERS set. We only set that when we've created
-  // a bo with modifiers, otherwise, we rely on the "no modifiers"
-  // behavior doing the right thing.
-  params.flags = 0;
-  if (drm->allow_addfb2_modifiers() &&
-      params.modifier != DRM_FORMAT_MOD_INVALID)
-    params.flags |= DRM_MODE_FB_MODIFIERS;
-
-  return AddFramebuffer(std::move(drm), params);
-}
-
 DrmFramebuffer::DrmFramebuffer(scoped_refptr<DrmDevice> drm_device,
                                uint32_t framebuffer_id,
                                uint32_t framebuffer_pixel_format,
diff --git a/ui/ozone/platform/drm/gpu/drm_framebuffer.h b/ui/ozone/platform/drm/gpu/drm_framebuffer.h
index bb5e6e3..c089cb3 100644
--- a/ui/ozone/platform/drm/gpu/drm_framebuffer.h
+++ b/ui/ozone/platform/drm/gpu/drm_framebuffer.h
@@ -15,7 +15,6 @@
 namespace ui {
 
 class DrmDevice;
-class GbmBuffer;
 
 // Abstraction for a DRM buffer that can be scanned-out of.
 class DrmFramebuffer : public base::RefCountedThreadSafe<DrmFramebuffer> {
@@ -36,10 +35,6 @@
       scoped_refptr<DrmDevice> drm_device,
       AddFramebufferParams params);
 
-  static scoped_refptr<DrmFramebuffer> AddFramebuffer(
-      scoped_refptr<DrmDevice> drm_device,
-      const GbmBuffer* buffer);
-
   DrmFramebuffer(scoped_refptr<DrmDevice> drm_device,
                  uint32_t framebuffer_id,
                  uint32_t framebuffer_pixel_format,
diff --git a/ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h b/ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h
new file mode 100644
index 0000000..04d8847
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h
@@ -0,0 +1,31 @@
+// 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 UI_OZONE_PLATFORM_DRM_GPU_SCANOUT_BUFFER_GENERATOR_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_SCANOUT_BUFFER_GENERATOR_H_
+
+#include <vector>
+
+#include "base/memory/scoped_refptr.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace ui {
+
+class DrmDevice;
+class DrmFramebuffer;
+
+class DrmFramebufferGenerator {
+ public:
+  virtual ~DrmFramebufferGenerator() {}
+
+  virtual scoped_refptr<DrmFramebuffer> Create(
+      const scoped_refptr<DrmDevice>& drm,
+      uint32_t format,
+      const std::vector<uint64_t>& modifiers,
+      const gfx::Size& size) = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_SCANOUT_BUFFER_GENERATOR_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc b/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
index ea0947a..300aabe 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
@@ -10,10 +10,10 @@
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/gpu_fence.h"
 #include "ui/ozone/common/linux/drm_util_linux.h"
-#include "ui/ozone/common/linux/gbm_buffer.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
 #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_window.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
 
@@ -25,6 +25,7 @@
     const scoped_refptr<DrmDevice>& drm_device,
     const gfx::Size& size,
     uint32_t format,
+    DrmFramebufferGenerator* buffer_generator,
     std::vector<scoped_refptr<DrmFramebuffer>>* reusable_buffers) {
   // Check if we can re-use existing buffers.
   for (const auto& buffer : *reusable_buffers) {
@@ -34,24 +35,22 @@
     }
   }
 
-  // TODO(dcastagna): use the right modifiers.
-  std::unique_ptr<GbmBuffer> buffer =
-      drm_device->gbm_device()->CreateBuffer(format, size, GBM_BO_USE_SCANOUT);
-  if (!buffer)
-    return nullptr;
-
+  const std::vector<uint64_t>
+      modifiers;  // TODO(dcastagna): use the right modifiers.
   scoped_refptr<DrmFramebuffer> drm_framebuffer =
-      DrmFramebuffer::AddFramebuffer(drm_device, buffer.get());
-  if (!drm_framebuffer)
-    return nullptr;
+      buffer_generator->Create(drm_device, format, modifiers, size);
+  if (drm_framebuffer)
+    reusable_buffers->push_back(drm_framebuffer);
 
-  reusable_buffers->push_back(drm_framebuffer);
   return drm_framebuffer;
 }
 
 }  // namespace
 
-DrmOverlayValidator::DrmOverlayValidator(DrmWindow* window) : window_(window) {}
+DrmOverlayValidator::DrmOverlayValidator(
+    DrmWindow* window,
+    DrmFramebufferGenerator* buffer_generator)
+    : window_(window), buffer_generator_(buffer_generator) {}
 
 DrmOverlayValidator::~DrmOverlayValidator() {}
 
@@ -83,7 +82,8 @@
 
     scoped_refptr<DrmFramebuffer> buffer = GetBufferForPageFlipTest(
         drm, params[i].buffer_size,
-        GetFourCCFormatFromBufferFormat(params[i].format), &reusable_buffers);
+        GetFourCCFormatFromBufferFormat(params[i].format), buffer_generator_,
+        &reusable_buffers);
 
     DrmOverlayPlane plane(buffer, params[i].plane_z_order, params[i].transform,
                           params[i].display_rect, params[i].crop_rect,
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator.h b/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
index 5030650..1304559 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
@@ -11,12 +11,14 @@
 namespace ui {
 
 class DrmWindow;
+class DrmFramebufferGenerator;
 struct OverlayCheck_Params;
 struct OverlayCheckReturn_Params;
 
 class DrmOverlayValidator {
  public:
-  DrmOverlayValidator(DrmWindow* window);
+  DrmOverlayValidator(DrmWindow* window,
+                      DrmFramebufferGenerator* buffer_generator);
   ~DrmOverlayValidator();
 
   // Tests if configurations |params| are compatible with |window_| and finds
@@ -28,6 +30,7 @@
 
  private:
   DrmWindow* window_;  // Not owned.
+  DrmFramebufferGenerator* buffer_generator_;  // Not owned.
 
   DISALLOW_COPY_AND_ASSIGN(DrmOverlayValidator);
 };
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 c2a99d7..977207d 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
@@ -15,16 +15,14 @@
 #include "ui/gfx/gpu_fence.h"
 #include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
 #include "ui/ozone/common/linux/drm_util_linux.h"
-#include "ui/ozone/common/linux/gbm_buffer.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
 #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_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_window.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_gbm_device.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
 
 namespace {
@@ -64,16 +62,8 @@
   void AddPlane(const ui::OverlayCheck_Params& params);
 
   scoped_refptr<ui::DrmFramebuffer> CreateBuffer() {
-    auto gbm_buffer = drm_->gbm_device()->CreateBuffer(
-        DRM_FORMAT_XRGB8888, primary_rect_.size(), GBM_BO_USE_SCANOUT);
-    return ui::DrmFramebuffer::AddFramebuffer(drm_, gbm_buffer.get());
-  }
-
-  scoped_refptr<ui::DrmFramebuffer> CreateOverlayBuffer(uint32_t format,
-                                                        const gfx::Size& size) {
-    auto gbm_buffer =
-        drm_->gbm_device()->CreateBuffer(format, size, GBM_BO_USE_SCANOUT);
-    return ui::DrmFramebuffer::AddFramebuffer(drm_, gbm_buffer.get());
+    return buffer_generator_->CreateWithModifier(
+        drm_, DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_NONE, primary_rect_.size());
   }
 
  protected:
@@ -89,7 +79,7 @@
 
   std::unique_ptr<base::MessageLoop> message_loop_;
   scoped_refptr<ui::MockDrmDevice> drm_;
-  ui::MockGbmDevice* gbm_ = nullptr;
+  std::unique_ptr<ui::MockDrmFramebufferGenerator> buffer_generator_;
   std::unique_ptr<ui::ScreenManager> screen_manager_;
   std::unique_ptr<ui::DrmDeviceManager> drm_device_manager_;
   ui::DrmWindow* window_;
@@ -111,16 +101,15 @@
   last_swap_buffers_result_ = gfx::SwapResult::SWAP_FAILED;
 
   message_loop_.reset(new base::MessageLoopForUI);
-  auto gbm = std::make_unique<ui::MockGbmDevice>();
-  gbm_ = gbm.get();
-  drm_ = new ui::MockDrmDevice(std::move(gbm));
+  drm_ = new ui::MockDrmDevice;
 
   CrtcState crtc_state = {.planes = {
                               {.formats = {DRM_FORMAT_XRGB8888}},
                           }};
   InitializeDrmState({crtc_state});
 
-  screen_manager_.reset(new ui::ScreenManager());
+  buffer_generator_.reset(new ui::MockDrmFramebufferGenerator());
+  screen_manager_.reset(new ui::ScreenManager(buffer_generator_.get()));
   screen_manager_->AddDisplayController(drm_, kCrtcIdBase, kConnectorIdBase);
   screen_manager_->ConfigureDisplayController(
       drm_, kCrtcIdBase, kConnectorIdBase, gfx::Point(), kDefaultMode);
@@ -129,12 +118,13 @@
 
   std::unique_ptr<ui::DrmWindow> window(new ui::DrmWindow(
       kDefaultWidgetHandle, drm_device_manager_.get(), screen_manager_.get()));
-  window->Initialize();
+  window->Initialize(buffer_generator_.get());
   window->SetBounds(
       gfx::Rect(gfx::Size(kDefaultMode.hdisplay, kDefaultMode.vdisplay)));
   screen_manager_->AddWindow(kDefaultWidgetHandle, std::move(window));
   window_ = screen_manager_->GetWindow(kDefaultWidgetHandle);
-  overlay_validator_.reset(new ui::DrmOverlayValidator(window_));
+  overlay_validator_.reset(
+      new ui::DrmOverlayValidator(window_, buffer_generator_.get()));
 
   overlay_rect_ =
       gfx::Rect(0, 0, kDefaultMode.hdisplay / 2, kDefaultMode.vdisplay / 2);
@@ -220,9 +210,9 @@
 
 void DrmOverlayValidatorTest::AddPlane(const ui::OverlayCheck_Params& params) {
   scoped_refptr<ui::DrmDevice> drm = window_->GetController()->GetDrmDevice();
-
-  scoped_refptr<ui::DrmFramebuffer> drm_framebuffer = CreateOverlayBuffer(
-      ui::GetFourCCFormatFromBufferFormat(params.format), params.buffer_size);
+  scoped_refptr<ui::DrmFramebuffer> drm_framebuffer = buffer_generator_->Create(
+      drm, ui::GetFourCCFormatFromBufferFormat(params.format), {},
+      params.buffer_size);
   plane_list_.push_back(ui::DrmOverlayPlane(
       std::move(drm_framebuffer), params.plane_z_order, params.transform,
       params.display_rect, params.crop_rect, true, nullptr));
@@ -479,7 +469,7 @@
 TEST_F(DrmOverlayValidatorTest, RejectBufferAllocationFail) {
   // Buffer allocation for scanout might fail.
   // In that case we should reject the overlay candidate.
-  gbm_->set_allocation_failure(true);
+  buffer_generator_->set_allocation_failure(true);
 
   std::vector<ui::OverlayCheckReturn_Params> returns =
       overlay_validator_->TestPageFlip(overlay_params_,
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.cc b/ui/ozone/platform/drm/gpu/drm_thread.cc
index 3205de1..62567655 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread.cc
@@ -22,6 +22,7 @@
 #include "ui/ozone/platform/drm/gpu/drm_buffer.h"
 #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_framebuffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
 #include "ui/ozone/platform/drm/gpu/drm_window.h"
 #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h"
@@ -35,6 +36,34 @@
 
 namespace {
 
+scoped_refptr<DrmFramebuffer> AddFramebuffersForBuffer(
+    const scoped_refptr<DrmDevice>& drm,
+    GbmBuffer* buffer) {
+  gfx::Size size = buffer->GetSize();
+  DrmFramebuffer::AddFramebufferParams params;
+  params.format = buffer->GetFormat();
+  params.modifier = buffer->GetFormatModifier();
+  params.width = size.width();
+  params.height = size.height();
+  params.num_planes = buffer->GetNumPlanes();
+  for (size_t i = 0; i < params.num_planes; ++i) {
+    params.handles[i] = buffer->GetPlaneHandle(i);
+    params.strides[i] = buffer->GetPlaneStride(i);
+    params.offsets[i] = buffer->GetPlaneOffset(i);
+  }
+
+  // AddFramebuffer2 only considers the modifiers if addfb_flags has
+  // DRM_MODE_FB_MODIFIERS set. We only set that when we've created
+  // a bo with modifiers, otherwise, we rely on the "no modifiers"
+  // behavior doing the right thing.
+  params.flags = 0;
+  if (drm->allow_addfb2_modifiers() &&
+      params.modifier != DRM_FORMAT_MOD_INVALID)
+    params.flags |= DRM_MODE_FB_MODIFIERS;
+
+  return DrmFramebuffer::AddFramebuffer(drm, params);
+}
+
 uint32_t BufferUsageToGbmFlags(gfx::BufferUsage usage) {
   switch (usage) {
     case gfx::BufferUsage::GPU_READ:
@@ -77,7 +106,7 @@
 
   scoped_refptr<DrmFramebuffer> framebuffer;
   if (flags & GBM_BO_USE_SCANOUT) {
-    framebuffer = DrmFramebuffer::AddFramebuffer(drm, buffer.get());
+    framebuffer = AddFramebuffersForBuffer(drm, buffer.get());
     if (!framebuffer)
       return;
   }
@@ -86,6 +115,36 @@
   *out_framebuffer = std::move(framebuffer);
 }
 
+class GbmBufferGenerator : public DrmFramebufferGenerator {
+ public:
+  GbmBufferGenerator() {}
+  ~GbmBufferGenerator() override {}
+
+  // DrmFramebufferGenerator:
+  scoped_refptr<DrmFramebuffer> Create(const scoped_refptr<DrmDevice>& drm,
+                                       uint32_t format,
+                                       const std::vector<uint64_t>& modifiers,
+                                       const gfx::Size& size) override {
+    std::unique_ptr<GbmBuffer> buffer;
+
+    if (modifiers.size() > 0) {
+      buffer = drm->gbm_device()->CreateBufferWithModifiers(
+          format, size, GBM_BO_USE_SCANOUT, modifiers);
+    } else {
+      buffer =
+          drm->gbm_device()->CreateBuffer(format, size, GBM_BO_USE_SCANOUT);
+    }
+
+    if (!buffer)
+      return nullptr;
+
+    return AddFramebuffersForBuffer(drm, buffer.get());
+  }
+
+ protected:
+  DISALLOW_COPY_AND_ASSIGN(GbmBufferGenerator);
+};
+
 class GbmDeviceGenerator : public DrmDeviceGenerator {
  public:
   GbmDeviceGenerator() {}
@@ -134,7 +193,8 @@
 void DrmThread::Init() {
   device_manager_.reset(
       new DrmDeviceManager(std::make_unique<GbmDeviceGenerator>()));
-  screen_manager_.reset(new ScreenManager());
+  buffer_generator_.reset(new GbmBufferGenerator());
+  screen_manager_.reset(new ScreenManager(buffer_generator_.get()));
 
   display_manager_.reset(
       new DrmGpuDisplayManager(screen_manager_.get(), device_manager_.get()));
@@ -204,7 +264,7 @@
   if (buffer->GetFlags() & GBM_BO_USE_SCANOUT) {
     // NB: This is not required to succeed; framebuffers are added for
     // imported buffers on a best effort basis.
-    framebuffer = DrmFramebuffer::AddFramebuffer(drm, buffer.get());
+    framebuffer = AddFramebuffersForBuffer(drm, buffer.get());
   }
 
   *out_buffer = std::move(buffer);
@@ -268,7 +328,7 @@
 void DrmThread::CreateWindow(gfx::AcceleratedWidget widget) {
   std::unique_ptr<DrmWindow> window(
       new DrmWindow(widget, device_manager_.get(), screen_manager_.get()));
-  window->Initialize();
+  window->Initialize(buffer_generator_.get());
   screen_manager_->AddWindow(widget, std::move(window));
 }
 
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.h b/ui/ozone/platform/drm/gpu/drm_thread.h
index 79685b1..b1c954eb 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.h
+++ b/ui/ozone/platform/drm/gpu/drm_thread.h
@@ -44,6 +44,7 @@
 class DrmFramebuffer;
 class DrmGpuDisplayManager;
 class GbmBuffer;
+class DrmFramebufferGenerator;
 class ScreenManager;
 
 struct DrmOverlayPlane;
@@ -154,6 +155,7 @@
                                 std::vector<DrmOverlayPlane> planes);
 
   std::unique_ptr<DrmDeviceManager> device_manager_;
+  std::unique_ptr<DrmFramebufferGenerator> buffer_generator_;
   std::unique_ptr<ScreenManager> screen_manager_;
   std::unique_ptr<DrmGpuDisplayManager> display_manager_;
 
diff --git a/ui/ozone/platform/drm/gpu/drm_window.cc b/ui/ozone/platform/drm/gpu/drm_window.cc
index 04f31690..1c0dc3dc 100644
--- a/ui/ozone/platform/drm/gpu/drm_window.cc
+++ b/ui/ozone/platform/drm/gpu/drm_window.cc
@@ -37,11 +37,12 @@
 DrmWindow::~DrmWindow() {
 }
 
-void DrmWindow::Initialize() {
+void DrmWindow::Initialize(DrmFramebufferGenerator* buffer_generator) {
   TRACE_EVENT1("drm", "DrmWindow::Initialize", "widget", widget_);
 
   device_manager_->UpdateDrmDevice(widget_, nullptr);
-  overlay_validator_ = std::make_unique<DrmOverlayValidator>(this);
+  overlay_validator_ =
+      std::make_unique<DrmOverlayValidator>(this, buffer_generator);
 }
 
 void DrmWindow::Shutdown() {
diff --git a/ui/ozone/platform/drm/gpu/drm_window.h b/ui/ozone/platform/drm/gpu/drm_window.h
index b44d416..236eefa0 100644
--- a/ui/ozone/platform/drm/gpu/drm_window.h
+++ b/ui/ozone/platform/drm/gpu/drm_window.h
@@ -33,6 +33,7 @@
 class HardwareDisplayController;
 struct OverlayCheck_Params;
 struct OverlayCheckReturn_Params;
+class DrmFramebufferGenerator;
 class ScreenManager;
 
 // The GPU object representing a window.
@@ -54,7 +55,7 @@
 
   gfx::Rect bounds() const { return bounds_; }
 
-  void Initialize();
+  void Initialize(DrmFramebufferGenerator* buffer_generator);
 
   void Shutdown();
 
diff --git a/ui/ozone/platform/drm/gpu/drm_window_unittest.cc b/ui/ozone/platform/drm/gpu/drm_window_unittest.cc
index fdd4af7..fd5d379c 100644
--- a/ui/ozone/platform/drm/gpu/drm_window_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/drm_window_unittest.cc
@@ -21,13 +21,12 @@
 #include "third_party/skia/include/core/SkSurface.h"
 #include "ui/gfx/gpu_fence.h"
 #include "ui/gfx/presentation_feedback.h"
-#include "ui/ozone/common/linux/gbm_buffer.h"
 #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_framebuffer.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_gbm_device.h"
+#include "ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
 #include "ui/ozone/public/surface_ozone_canvas.h"
 
@@ -86,6 +85,7 @@
  protected:
   std::unique_ptr<base::MessageLoop> message_loop_;
   scoped_refptr<ui::MockDrmDevice> drm_;
+  std::unique_ptr<ui::MockDumbBufferGenerator> buffer_generator_;
   std::unique_ptr<ui::ScreenManager> screen_manager_;
   std::unique_ptr<ui::DrmDeviceManager> drm_device_manager_;
 
@@ -102,9 +102,9 @@
   last_swap_buffers_result_ = gfx::SwapResult::SWAP_FAILED;
 
   message_loop_.reset(new base::MessageLoopForUI);
-  auto gbm_device = std::make_unique<ui::MockGbmDevice>();
-  drm_ = new ui::MockDrmDevice(std::move(gbm_device));
-  screen_manager_.reset(new ui::ScreenManager());
+  drm_ = new ui::MockDrmDevice;
+  buffer_generator_.reset(new ui::MockDumbBufferGenerator());
+  screen_manager_.reset(new ui::ScreenManager(buffer_generator_.get()));
   screen_manager_->AddDisplayController(drm_, kDefaultCrtc, kDefaultConnector);
   screen_manager_->ConfigureDisplayController(
       drm_, kDefaultCrtc, kDefaultConnector, gfx::Point(), kDefaultMode);
@@ -113,7 +113,7 @@
 
   std::unique_ptr<ui::DrmWindow> window(new ui::DrmWindow(
       kDefaultWidgetHandle, drm_device_manager_.get(), screen_manager_.get()));
-  window->Initialize();
+  window->Initialize(buffer_generator_.get());
   window->SetBounds(
       gfx::Rect(gfx::Size(kDefaultMode.hdisplay, kDefaultMode.vdisplay)));
   screen_manager_->AddWindow(kDefaultWidgetHandle, std::move(window));
@@ -159,9 +159,7 @@
                   gfx::Point(4, 2), 0);
 
   // Add another device.
-  auto gbm_device = std::make_unique<ui::MockGbmDevice>();
-  scoped_refptr<ui::MockDrmDevice> drm =
-      new ui::MockDrmDevice(std::move(gbm_device));
+  scoped_refptr<ui::MockDrmDevice> drm = new ui::MockDrmDevice;
   screen_manager_->AddDisplayController(drm, kDefaultCrtc, kDefaultConnector);
   screen_manager_->ConfigureDisplayController(
       drm, kDefaultCrtc, kDefaultConnector,
@@ -179,13 +177,11 @@
 
 TEST_F(DrmWindowTest, CheckDeathOnFailedSwap) {
   const gfx::Size window_size(6, 4);
+  ui::MockDumbBufferGenerator buffer_generator;
   ui::DrmWindow* window = screen_manager_->GetWindow(kDefaultWidgetHandle);
-
-  std::unique_ptr<ui::GbmBuffer> buffer = drm_->gbm_device()->CreateBuffer(
-      DRM_FORMAT_XRGB8888, window_size, GBM_BO_USE_SCANOUT);
-  scoped_refptr<ui::DrmFramebuffer> framebuffer =
-      ui::DrmFramebuffer::AddFramebuffer(drm_, buffer.get());
-  ui::DrmOverlayPlane plane(framebuffer, nullptr);
+  ui::DrmOverlayPlane plane(
+      buffer_generator.Create(drm_, DRM_FORMAT_XRGB8888, {}, window_size),
+      nullptr);
 
   drm_->set_page_flip_expectation(false);
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc
index 662d7232..0a4c75cb 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc
@@ -13,14 +13,12 @@
 #include "ui/gfx/gpu_fence.h"
 #include "ui/gfx/native_pixmap.h"
 #include "ui/gfx/presentation_feedback.h"
-#include "ui/ozone/common/linux/gbm_buffer.h"
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
-#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_gpu_util.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
 #include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
-#include "ui/ozone/platform/drm/gpu/mock_gbm_device.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h"
 
 namespace {
 
@@ -59,18 +57,17 @@
                                  const std::string& property_name);
 
   scoped_refptr<ui::DrmFramebuffer> CreateBuffer() {
-    std::unique_ptr<ui::GbmBuffer> buffer = drm_->gbm_device()->CreateBuffer(
-        DRM_FORMAT_XRGB8888, kDefaultModeSize, GBM_BO_USE_SCANOUT);
-    return ui::DrmFramebuffer::AddFramebuffer(drm_, buffer.get());
+    return buffer_generator_->CreateWithModifier(
+        drm_, DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_NONE, kDefaultModeSize);
   }
 
   scoped_refptr<ui::DrmFramebuffer> CreateOverlayBuffer() {
-    std::unique_ptr<ui::GbmBuffer> buffer = drm_->gbm_device()->CreateBuffer(
-        DRM_FORMAT_XRGB8888, kOverlaySize, GBM_BO_USE_SCANOUT);
-    return ui::DrmFramebuffer::AddFramebuffer(drm_, buffer.get());
+    return buffer_generator_->CreateWithModifier(
+        drm_, DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_NONE, kOverlaySize);
   }
 
  protected:
+  std::unique_ptr<ui::MockDrmFramebufferGenerator> buffer_generator_;
   std::unique_ptr<ui::HardwareDisplayController> controller_;
   scoped_refptr<ui::MockDrmDevice> drm_;
 
@@ -86,10 +83,10 @@
   page_flips_ = 0;
   last_swap_result_ = gfx::SwapResult::SWAP_FAILED;
 
-  auto gbm_device = std::make_unique<ui::MockGbmDevice>();
-  drm_ = new ui::MockDrmDevice(std::move(gbm_device));
+  drm_ = new ui::MockDrmDevice;
   InitializeDrmDevice(/* use_atomic= */ true);
 
+  buffer_generator_.reset(new ui::MockDrmFramebufferGenerator());
   controller_.reset(new ui::HardwareDisplayController(
       std::unique_ptr<ui::CrtcController>(
           new ui::CrtcController(drm_.get(), kPrimaryCrtc, kPrimaryConnector)),
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 177a262..7944c60 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
@@ -17,15 +17,13 @@
 #include "ui/display/types/gamma_ramp_rgb_entry.h"
 #include "ui/gfx/gpu_fence.h"
 #include "ui/gfx/gpu_fence_handle.h"
-#include "ui/ozone/common/linux/gbm_buffer.h"
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
-#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_gpu_util.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"
 #include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
-#include "ui/ozone/platform/drm/gpu/mock_gbm_device.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h"
 
 namespace {
 
@@ -64,20 +62,21 @@
   void SetUp() override;
 
   scoped_refptr<ui::DrmFramebuffer> CreateBuffer(const gfx::Size& size) {
-    return CreateBufferWithFormat(size, DRM_FORMAT_XRGB8888);
+    return buffer_generator_->CreateWithModifier(fake_drm_, DRM_FORMAT_XRGB8888,
+                                                 DRM_FORMAT_MOD_NONE, size);
   }
 
   scoped_refptr<ui::DrmFramebuffer> CreateBufferWithFormat(
       const gfx::Size& size,
       uint32_t format) {
-    std::unique_ptr<ui::GbmBuffer> buffer =
-        fake_drm_->gbm_device()->CreateBuffer(format, size, GBM_BO_USE_SCANOUT);
-    return ui::DrmFramebuffer::AddFramebuffer(fake_drm_, buffer.get());
+    return buffer_generator_->CreateWithModifier(fake_drm_, format,
+                                                 DRM_FORMAT_MOD_NONE, size);
   }
 
  protected:
   ui::HardwareDisplayPlaneList state_;
   scoped_refptr<ui::DrmFramebuffer> fake_buffer_;
+  std::unique_ptr<ui::MockDrmFramebufferGenerator> buffer_generator_;
   scoped_refptr<ui::MockDrmDevice> fake_drm_;
 
   std::vector<ui::MockDrmDevice::CrtcProperties> crtc_properties_;
@@ -92,9 +91,9 @@
 
 void HardwareDisplayPlaneManagerTest::SetUp() {
   use_atomic_ = GetParam();
+  buffer_generator_.reset(new ui::MockDrmFramebufferGenerator());
 
-  auto gbm_device = std::make_unique<ui::MockGbmDevice>();
-  fake_drm_ = new ui::MockDrmDevice(std::move(gbm_device));
+  fake_drm_ = new ui::MockDrmDevice;
   fake_drm_->SetPropertyBlob(ui::MockDrmDevice::AllocateInFormatsBlob(
       kInFormatsBlobPropId, {DRM_FORMAT_XRGB8888}, {}));
 
@@ -299,7 +298,7 @@
 TEST_P(HardwareDisplayPlaneManagerLegacyTest, CheckFramebufferFormatMatch) {
   ui::DrmOverlayPlaneList assigns;
   scoped_refptr<ui::DrmFramebuffer> buffer =
-      CreateBufferWithFormat(kDefaultBufferSize, DRM_FORMAT_UYVY);
+      CreateBufferWithFormat(kDefaultBufferSize, DRM_FORMAT_NV12);
   assigns.push_back(ui::DrmOverlayPlane(buffer, nullptr));
 
   InitializeDrmState(/*crtc_count=*/2, /*planes_per_crtc=*/1);
@@ -783,8 +782,8 @@
   HardwareDisplayPlaneManagerPlanesReadyTest() {}
 
   void SetUp() override {
-    auto gbm_device = std::make_unique<ui::MockGbmDevice>();
-    fake_drm_ = new ui::MockDrmDevice(std::move(gbm_device));
+    fake_drm_ = new ui::MockDrmDevice;
+    buffer_generator_.reset(new ui::MockDrmFramebufferGenerator());
     drm_framebuffer_ = CreateBuffer(kDefaultBufferSize);
     planes_without_fences_ = CreatePlanesWithoutFences();
     planes_with_fences_ = CreatePlanesWithFences();
@@ -795,10 +794,8 @@
   void RequestPlanesReady(ui::DrmOverlayPlaneList planes);
 
   scoped_refptr<ui::DrmFramebuffer> CreateBuffer(const gfx::Size& size) {
-    std::unique_ptr<ui::GbmBuffer> buffer =
-        fake_drm_->gbm_device()->CreateBuffer(DRM_FORMAT_XRGB8888, size,
-                                              GBM_BO_USE_SCANOUT);
-    return ui::DrmFramebuffer::AddFramebuffer(fake_drm_, buffer.get());
+    return buffer_generator_->CreateWithModifier(fake_drm_, DRM_FORMAT_XRGB8888,
+                                                 DRM_FORMAT_MOD_NONE, size);
   }
 
   ui::DrmOverlayPlaneList CreatePlanesWithoutFences() {
@@ -820,6 +817,7 @@
   }
 
   scoped_refptr<ui::MockDrmDevice> fake_drm_;
+  std::unique_ptr<ui::MockDrmFramebufferGenerator> buffer_generator_;
   std::unique_ptr<ui::HardwareDisplayPlaneManager> plane_manager_;
   bool callback_called = false;
   base::test::ScopedTaskEnvironment task_env_{
@@ -928,26 +926,24 @@
 TEST(HardwareDisplayPlaneManagerAtomic, EnableBlend) {
   auto plane_manager =
       std::make_unique<ui::HardwareDisplayPlaneManagerAtomic>();
-  auto gbm_device = std::make_unique<ui::MockGbmDevice>();
-  auto drm_device =
-      base::MakeRefCounted<ui::MockDrmDevice>(std::move(gbm_device));
+  auto drm_device = base::MakeRefCounted<ui::MockDrmDevice>();
+  auto buffer_generator = std::make_unique<ui::MockDrmFramebufferGenerator>();
   ui::HardwareDisplayPlaneList plane_list;
   HardwareDisplayPlaneAtomicMock hw_plane;
-  std::unique_ptr<ui::GbmBuffer> buffer =
-      drm_device->gbm_device()->CreateBuffer(
-          DRM_FORMAT_XRGB8888, kDefaultBufferSize, GBM_BO_USE_SCANOUT);
-  scoped_refptr<ui::DrmFramebuffer> framebuffer =
-      ui::DrmFramebuffer::AddFramebuffer(drm_device, buffer.get());
-  ui::DrmOverlayPlane overlay(framebuffer, nullptr);
+  scoped_refptr<ui::DrmFramebuffer> buffer =
+      buffer_generator->CreateWithModifier(
+          drm_device.get(), DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_NONE,
+          kDefaultBufferSize);
+  ui::DrmOverlayPlane overlay(buffer, nullptr);
   overlay.enable_blend = true;
   plane_manager->SetPlaneData(&plane_list, &hw_plane, overlay, 1, gfx::Rect(),
                               nullptr);
-  EXPECT_EQ(hw_plane.framebuffer(), framebuffer->framebuffer_id());
+  EXPECT_EQ(hw_plane.framebuffer(), buffer->framebuffer_id());
 
   overlay.enable_blend = false;
   plane_manager->SetPlaneData(&plane_list, &hw_plane, overlay, 1, gfx::Rect(),
                               nullptr);
-  EXPECT_EQ(hw_plane.framebuffer(), framebuffer->opaque_framebuffer_id());
+  EXPECT_EQ(hw_plane.framebuffer(), buffer->opaque_framebuffer_id());
 }
 
 }  // namespace
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.cc b/ui/ozone/platform/drm/gpu/mock_drm_device.cc
index 4b7f5a46..abdc7adc 100644
--- a/ui/ozone/platform/drm/gpu/mock_drm_device.cc
+++ b/ui/ozone/platform/drm/gpu/mock_drm_device.cc
@@ -73,11 +73,11 @@
     default;
 MockDrmDevice::PlaneProperties::~PlaneProperties() = default;
 
-MockDrmDevice::MockDrmDevice(std::unique_ptr<GbmDevice> gbm_device)
+MockDrmDevice::MockDrmDevice()
     : DrmDevice(base::FilePath(),
                 base::File(),
                 true /* is_primary_device */,
-                std::move(gbm_device)),
+                nullptr),
       get_crtc_call_count_(0),
       set_crtc_call_count_(0),
       restore_crtc_call_count_(0),
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.h b/ui/ozone/platform/drm/gpu/mock_drm_device.h
index 2e705d0..95ad0398 100644
--- a/ui/ozone/platform/drm/gpu/mock_drm_device.h
+++ b/ui/ozone/platform/drm/gpu/mock_drm_device.h
@@ -44,7 +44,7 @@
     std::vector<DrmDevice::Property> properties;
   };
 
-  MockDrmDevice(std::unique_ptr<GbmDevice> gbm_device);
+  MockDrmDevice();
 
   static ScopedDrmPropertyBlobPtr AllocateInFormatsBlob(
       uint32_t id,
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.cc b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.cc
new file mode 100644
index 0000000..41fb7ba
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.cc
@@ -0,0 +1,53 @@
+// 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 "ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h"
+
+#include <drm_fourcc.h>
+
+#include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+
+namespace ui {
+
+namespace {
+
+uint32_t g_current_mock_buffer_handle = 0x1111;
+
+}  // namespace
+
+MockDrmFramebufferGenerator::MockDrmFramebufferGenerator() {}
+
+MockDrmFramebufferGenerator::~MockDrmFramebufferGenerator() {}
+
+scoped_refptr<DrmFramebuffer> MockDrmFramebufferGenerator::Create(
+    const scoped_refptr<DrmDevice>& drm,
+    uint32_t format,
+    const std::vector<uint64_t>& modifiers,
+    const gfx::Size& size) {
+  return CreateWithModifier(
+      drm, format, modifiers.empty() ? DRM_FORMAT_MOD_NONE : modifiers.front(),
+      size);
+}
+
+scoped_refptr<DrmFramebuffer> MockDrmFramebufferGenerator::CreateWithModifier(
+    const scoped_refptr<DrmDevice>& drm,
+    uint32_t format,
+    uint64_t modifier,
+    const gfx::Size& size) {
+  if (allocation_failure_)
+    return nullptr;
+
+  DrmFramebuffer::AddFramebufferParams params;
+  params.format = format;
+  params.modifier = modifier;
+  params.width = size.width();
+  params.height = size.height();
+  params.num_planes = 1;
+  params.handles[0] = g_current_mock_buffer_handle++;
+
+  return DrmFramebuffer::AddFramebuffer(drm, params);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h
new file mode 100644
index 0000000..4b3e8cb
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h
@@ -0,0 +1,44 @@
+// 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_MOCK_SCANOUT_BUFFER_GENERATOR_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_MOCK_SCANOUT_BUFFER_GENERATOR_H_
+
+#include "base/macros.h"
+
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h"
+
+namespace ui {
+
+class MockDrmFramebufferGenerator : public DrmFramebufferGenerator {
+ public:
+  MockDrmFramebufferGenerator();
+  ~MockDrmFramebufferGenerator() override;
+
+  // DrmFramebufferGenerator:
+  scoped_refptr<DrmFramebuffer> Create(const scoped_refptr<DrmDevice>& drm,
+                                       uint32_t format,
+                                       const std::vector<uint64_t>& modifiers,
+                                       const gfx::Size& size) override;
+
+  scoped_refptr<DrmFramebuffer> CreateWithModifier(
+      const scoped_refptr<DrmDevice>& drm,
+      uint32_t format,
+      uint64_t modifier,
+      const gfx::Size& size);
+
+  void set_allocation_failure(bool allocation_failure) {
+    allocation_failure_ = allocation_failure;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockDrmFramebufferGenerator);
+
+  bool allocation_failure_ = false;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_MOCK_SCANOUT_BUFFER_GENERATOR_H_
diff --git a/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.cc b/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.cc
new file mode 100644
index 0000000..2934559
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.cc
@@ -0,0 +1,69 @@
+// 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 "ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.h"
+
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+
+namespace ui {
+
+namespace {
+
+uint32_t GetFourCCCodeForSkColorType(SkColorType type) {
+  switch (type) {
+    case kUnknown_SkColorType:
+    case kAlpha_8_SkColorType:
+      return 0;
+    case kRGB_565_SkColorType:
+      return DRM_FORMAT_RGB565;
+    case kARGB_4444_SkColorType:
+      return DRM_FORMAT_ARGB4444;
+    case kN32_SkColorType:
+      return DRM_FORMAT_ARGB8888;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+scoped_refptr<DrmFramebuffer> AddFramebufferForDumbBuffer(
+    const scoped_refptr<DrmDevice>& drm,
+    uint32_t handle,
+    uint32_t stride,
+    const SkImageInfo& info) {
+  DrmFramebuffer::AddFramebufferParams params;
+  params.flags = 0;
+  params.format = GetFourCCCodeForSkColorType(info.colorType());
+  params.modifier = DRM_FORMAT_MOD_INVALID;
+  params.width = info.width();
+  params.height = info.height();
+  params.num_planes = 1;
+  params.handles[0] = handle;
+  params.strides[0] = stride;
+  return DrmFramebuffer::AddFramebuffer(drm, params);
+}
+
+}  // namespace
+
+MockDumbBufferGenerator::MockDumbBufferGenerator() {}
+
+MockDumbBufferGenerator::~MockDumbBufferGenerator() {}
+
+scoped_refptr<DrmFramebuffer> MockDumbBufferGenerator::Create(
+    const scoped_refptr<DrmDevice>& drm,
+    uint32_t format,
+    const std::vector<uint64_t>& modifiers,
+    const gfx::Size& size) {
+  std::unique_ptr<DrmBuffer> buffer(new DrmBuffer(drm));
+  SkImageInfo info = SkImageInfo::MakeN32Premul(size.width(), size.height());
+  if (!buffer->Initialize(info))
+    return NULL;
+
+  return AddFramebufferForDumbBuffer(drm, buffer->GetHandle(), buffer->stride(),
+                                     info);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.h b/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.h
new file mode 100644
index 0000000..9fe8f4d
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.h
@@ -0,0 +1,34 @@
+// 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_MOCK_DUMB_BUFFER_GENERATOR_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_MOCK_DUMB_BUFFER_GENERATOR_H_
+
+#include "base/macros.h"
+
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h"
+
+namespace ui {
+
+class DrmFramebuffer;
+
+class MockDumbBufferGenerator : public DrmFramebufferGenerator {
+ public:
+  MockDumbBufferGenerator();
+  ~MockDumbBufferGenerator() override;
+
+  // DrmFramebufferGenerator:
+  scoped_refptr<DrmFramebuffer> Create(const scoped_refptr<DrmDevice>& drm,
+                                       uint32_t format,
+                                       const std::vector<uint64_t>& modifiers,
+                                       const gfx::Size& size) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockDumbBufferGenerator);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_MOCK_DUMB_BUFFER_GENERATOR_H_
diff --git a/ui/ozone/platform/drm/gpu/mock_gbm_device.cc b/ui/ozone/platform/drm/gpu/mock_gbm_device.cc
deleted file mode 100644
index d7b298f..0000000
--- a/ui/ozone/platform/drm/gpu/mock_gbm_device.cc
+++ /dev/null
@@ -1,161 +0,0 @@
-// 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 "ui/ozone/platform/drm/gpu/mock_gbm_device.h"
-
-#include <drm_fourcc.h>
-#include <xf86drm.h>
-
-#include "base/logging.h"
-#include "base/numerics/safe_math.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/ozone/common/linux/drm_util_linux.h"
-#include "ui/ozone/common/linux/gbm_buffer.h"
-
-namespace ui {
-namespace {
-
-class MockGbmBuffer final : public ui::GbmBuffer {
- public:
-  MockGbmBuffer(uint32_t format,
-                uint32_t flags,
-                uint64_t modifier,
-                const gfx::Size& size,
-                std::vector<gfx::NativePixmapPlane> planes,
-                std::vector<uint32_t> handles)
-      : format_(format),
-        format_modifier_(modifier),
-        flags_(flags),
-        size_(size),
-        planes_(std::move(planes)),
-        handles_(std::move(handles)) {}
-
-  ~MockGbmBuffer() override {}
-
-  uint32_t GetFormat() const override { return format_; }
-  uint64_t GetFormatModifier() const override { return format_modifier_; }
-  uint32_t GetFlags() const override { return flags_; }
-  size_t GetFdCount() const override { return 0; }
-  gfx::Size GetSize() const override { return size_; }
-  gfx::BufferFormat GetBufferFormat() const override {
-    return ui::GetBufferFormatFromFourCCFormat(format_);
-  }
-  bool AreFdsValid() const override { return false; }
-  size_t GetNumPlanes() const override { return planes_.size(); }
-  int GetPlaneFd(size_t plane) const override {
-    NOTREACHED();
-    return -1;
-  }
-  int GetPlaneStride(size_t plane) const override {
-    DCHECK_LT(plane, planes_.size());
-    return planes_[plane].stride;
-  }
-  int GetPlaneOffset(size_t plane) const override {
-    DCHECK_LT(plane, planes_.size());
-    return planes_[plane].offset;
-  }
-  size_t GetPlaneSize(size_t plane) const override {
-    DCHECK_LT(plane, planes_.size());
-    return planes_[plane].size;
-  }
-  uint32_t GetPlaneHandle(size_t plane) const override {
-    DCHECK_LT(plane, planes_.size());
-    return handles_[plane];
-  }
-  uint32_t GetHandle() const override { return GetPlaneHandle(0); }
-  gfx::NativePixmapHandle ExportHandle() const override {
-    NOTIMPLEMENTED();
-    return gfx::NativePixmapHandle();
-  }
-
- private:
-  uint32_t format_ = 0;
-  uint64_t format_modifier_ = 0;
-  uint32_t flags_ = 0;
-  std::vector<base::ScopedFD> fds_;
-  gfx::Size size_;
-  std::vector<gfx::NativePixmapPlane> planes_;
-  std::vector<uint32_t> handles_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockGbmBuffer);
-};
-
-}  // namespace
-
-MockGbmDevice::MockGbmDevice() {}
-
-MockGbmDevice::~MockGbmDevice() {}
-
-void MockGbmDevice::set_allocation_failure(bool should_fail_allocations) {
-  should_fail_allocations_ = should_fail_allocations;
-}
-
-std::unique_ptr<GbmBuffer> MockGbmDevice::CreateBuffer(uint32_t format,
-                                                       const gfx::Size& size,
-                                                       uint32_t flags) {
-  if (should_fail_allocations_)
-    return nullptr;
-
-  return CreateBufferWithModifiers(format, size, flags, {});
-}
-
-std::unique_ptr<GbmBuffer> MockGbmDevice::CreateBufferWithModifiers(
-    uint32_t format,
-    const gfx::Size& size,
-    uint32_t flags,
-    const std::vector<uint64_t>& modifiers) {
-  uint32_t bytes_per_pixel;
-  switch (format) {
-    case DRM_FORMAT_XRGB8888:
-    case DRM_FORMAT_ARGB8888:
-      bytes_per_pixel = 4;
-      break;
-    case DRM_FORMAT_UYVY:
-      bytes_per_pixel = 2;
-      break;
-    default:
-      NOTREACHED() << "Unsupported format: " << format;
-      return nullptr;
-  }
-
-  if (modifiers.size() > 1)
-    return nullptr;
-
-  uint64_t format_modifier =
-      modifiers.size() ? modifiers[0] : DRM_FORMAT_MOD_NONE;
-  switch (format_modifier) {
-    case DRM_FORMAT_MOD_NONE:
-    case I915_FORMAT_MOD_X_TILED:
-      break;
-    default:
-      NOTREACHED() << "Unsupported format modifier: " << format_modifier;
-      return nullptr;
-  }
-
-  uint32_t width = base::checked_cast<uint32_t>(size.width());
-  uint32_t height = base::checked_cast<uint32_t>(size.height());
-  uint32_t plane_stride = base::CheckMul(bytes_per_pixel, width).ValueOrDie();
-  uint32_t plane_size = base::CheckMul(plane_stride, height).ValueOrDie();
-  uint32_t plane_offset = 0;
-
-  std::vector<gfx::NativePixmapPlane> planes;
-  planes.push_back(gfx::NativePixmapPlane(plane_stride, plane_offset,
-                                          plane_size, format_modifier));
-  std::vector<uint32_t> handles;
-  handles.push_back(next_handle_++);
-
-  return std::make_unique<MockGbmBuffer>(format, flags, format_modifier, size,
-                                         std::move(planes), std::move(handles));
-}
-
-std::unique_ptr<GbmBuffer> MockGbmDevice::CreateBufferFromFds(
-    uint32_t format,
-    const gfx::Size& size,
-    std::vector<base::ScopedFD> fds,
-    const std::vector<gfx::NativePixmapPlane>& planes) {
-  NOTREACHED();
-  return nullptr;
-}
-
-}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/mock_gbm_device.h b/ui/ozone/platform/drm/gpu/mock_gbm_device.h
deleted file mode 100644
index b07c63e51..0000000
--- a/ui/ozone/platform/drm/gpu/mock_gbm_device.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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 UI_OZONE_PLATFORM_DRM_GPU_MOCK_GBM_DEVICE_H_
-#define UI_OZONE_PLATFORM_DRM_GPU_MOCK_GBM_DEVICE_H_
-
-#include "ui/ozone/common/linux/gbm_device.h"
-
-namespace ui {
-
-// The real DrmDevice makes actual DRM calls which we can't use in unit tests.
-class MockGbmDevice : public GbmDevice {
- public:
-  MockGbmDevice();
-  ~MockGbmDevice() override;
-
-  void set_allocation_failure(bool should_fail_allocations);
-
-  // GbmDevice:
-  std::unique_ptr<GbmBuffer> CreateBuffer(uint32_t format,
-                                          const gfx::Size& size,
-                                          uint32_t flags) override;
-  std::unique_ptr<GbmBuffer> CreateBufferWithModifiers(
-      uint32_t format,
-      const gfx::Size& size,
-      uint32_t flags,
-      const std::vector<uint64_t>& modifiers) override;
-  std::unique_ptr<GbmBuffer> CreateBufferFromFds(
-      uint32_t format,
-      const gfx::Size& size,
-      std::vector<base::ScopedFD> fds,
-      const std::vector<gfx::NativePixmapPlane>& planes) override;
-
- private:
-  uint32_t next_handle_ = 0;
-  bool should_fail_allocations_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(MockGbmDevice);
-};
-
-}  // namespace ui
-
-#endif  // UI_OZONE_PLATFORM_DRM_GPU_MOCK_GBM_DEVICE_H_
diff --git a/ui/ozone/platform/drm/gpu/screen_manager.cc b/ui/ozone/platform/drm/gpu/screen_manager.cc
index a228fda..3cd0ad0 100644
--- a/ui/ozone/platform/drm/gpu/screen_manager.cc
+++ b/ui/ozone/platform/drm/gpu/screen_manager.cc
@@ -15,12 +15,12 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/gpu_fence.h"
-#include "ui/ozone/common/linux/gbm_buffer.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
 #include "ui/ozone/platform/drm/gpu/drm_console_buffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
 #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_window.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
 
@@ -96,7 +96,8 @@
 
 }  // namespace
 
-ScreenManager::ScreenManager() {}
+ScreenManager::ScreenManager(DrmFramebufferGenerator* buffer_generator)
+    : buffer_generator_(buffer_generator) {}
 
 ScreenManager::~ScreenManager() {
   DCHECK(window_map_.empty());
@@ -386,11 +387,8 @@
   }
 
   scoped_refptr<DrmDevice> drm = controller->GetDrmDevice();
-  std::unique_ptr<GbmBuffer> buffer =
-      drm->gbm_device()->CreateBufferWithModifiers(
-          fourcc_format, bounds.size(), GBM_BO_USE_SCANOUT, modifiers);
-  scoped_refptr<DrmFramebuffer> framebuffer =
-      DrmFramebuffer::AddFramebuffer(drm, buffer.get());
+  scoped_refptr<DrmFramebuffer> buffer =
+      buffer_generator_->Create(drm, fourcc_format, modifiers, bounds.size());
   if (!buffer) {
     LOG(ERROR) << "Failed to create scanout buffer";
     return DrmOverlayPlane(nullptr, 0, gfx::OVERLAY_TRANSFORM_INVALID,
@@ -398,8 +396,8 @@
                            /* gpu_fence */ nullptr);
   }
 
-  FillModesetBuffer(drm, controller, framebuffer.get());
-  return DrmOverlayPlane(framebuffer, nullptr);
+  FillModesetBuffer(drm, controller, buffer.get());
+  return DrmOverlayPlane(buffer, nullptr);
 }
 
 bool ScreenManager::EnableController(HardwareDisplayController* controller) {
diff --git a/ui/ozone/platform/drm/gpu/screen_manager.h b/ui/ozone/platform/drm/gpu/screen_manager.h
index e5c2760d..c21dbde 100644
--- a/ui/ozone/platform/drm/gpu/screen_manager.h
+++ b/ui/ozone/platform/drm/gpu/screen_manager.h
@@ -25,11 +25,12 @@
 
 class DrmDevice;
 class DrmWindow;
+class DrmFramebufferGenerator;
 
 // Responsible for keeping track of active displays and configuring them.
 class ScreenManager {
  public:
-  ScreenManager();
+  ScreenManager(DrmFramebufferGenerator* surface_generator);
   virtual ~ScreenManager();
 
   // Register a display controller. This must be called before trying to
@@ -130,6 +131,7 @@
 
   DrmWindow* FindWindowAt(const gfx::Rect& bounds) const;
 
+  DrmFramebufferGenerator* buffer_generator_;  // Not owned.
   // List of display controllers (active and disabled).
   HardwareDisplayControllers controllers_;
 
diff --git a/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc b/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
index 1d05b1d9..d88806e 100644
--- a/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/gpu_fence.h"
-#include "ui/ozone/common/linux/gbm_buffer.h"
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
@@ -20,7 +19,7 @@
 #include "ui/ozone/platform/drm/gpu/drm_window.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_gbm_device.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
 
 namespace ui {
@@ -57,36 +56,20 @@
   }
 
   void SetUp() override {
-    auto gbm = std::make_unique<ui::MockGbmDevice>();
-    drm_ = new ui::MockDrmDevice(std::move(gbm));
+    drm_ = new ui::MockDrmDevice;
     device_manager_.reset(new ui::DrmDeviceManager(nullptr));
-    screen_manager_.reset(new ui::ScreenManager());
+    buffer_generator_.reset(new ui::MockDrmFramebufferGenerator());
+    screen_manager_.reset(new ui::ScreenManager(buffer_generator_.get()));
   }
   void TearDown() override {
     screen_manager_.reset();
     drm_ = nullptr;
   }
 
-  scoped_refptr<DrmFramebuffer> CreateBuffer(uint32_t format,
-                                             const gfx::Size& size) {
-    return CreateBufferWithModifier(format, DRM_FORMAT_MOD_NONE, size);
-  }
-
-  scoped_refptr<DrmFramebuffer> CreateBufferWithModifier(
-      uint32_t format,
-      uint64_t format_modifier,
-      const gfx::Size& size) {
-    std::vector<uint64_t> modifiers;
-    if (format_modifier != DRM_FORMAT_MOD_NONE)
-      modifiers.push_back(format_modifier);
-    auto buffer = drm_->gbm_device()->CreateBufferWithModifiers(
-        format, size, GBM_BO_USE_SCANOUT, modifiers);
-    return DrmFramebuffer::AddFramebuffer(drm_, buffer.get());
-  }
-
  protected:
   scoped_refptr<ui::MockDrmDevice> drm_;
   std::unique_ptr<ui::DrmDeviceManager> device_manager_;
+  std::unique_ptr<ui::MockDrmFramebufferGenerator> buffer_generator_;
   std::unique_ptr<ui::ScreenManager> screen_manager_;
 
  private:
@@ -377,9 +360,7 @@
 
 TEST_F(ScreenManagerTest,
        CheckProperConfigurationWithDifferentDeviceAndSameCrtc) {
-  auto gbm_device = std::make_unique<ui::MockGbmDevice>();
-  scoped_refptr<ui::MockDrmDevice> drm2 =
-      new ui::MockDrmDevice(std::move(gbm_device));
+  scoped_refptr<ui::MockDrmDevice> drm2 = new ui::MockDrmDevice;
 
   screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
   screen_manager_->AddDisplayController(drm2, kPrimaryCrtc, kPrimaryConnector);
@@ -404,7 +385,7 @@
 TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithSameBounds) {
   std::unique_ptr<ui::DrmWindow> window(
       new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get()));
-  window->Initialize();
+  window->Initialize(buffer_generator_.get());
   window->SetBounds(GetPrimaryBounds());
   screen_manager_->AddWindow(1, std::move(window));
 
@@ -422,7 +403,7 @@
 TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithDifferentBounds) {
   std::unique_ptr<ui::DrmWindow> window(
       new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get()));
-  window->Initialize();
+  window->Initialize(buffer_generator_.get());
   gfx::Rect new_bounds = GetPrimaryBounds();
   new_bounds.Inset(0, 0, 1, 1);
   window->SetBounds(new_bounds);
@@ -445,7 +426,7 @@
   for (size_t i = 1; i < kWindowCount + 1; ++i) {
     std::unique_ptr<ui::DrmWindow> window(
         new ui::DrmWindow(i, device_manager_.get(), screen_manager_.get()));
-    window->Initialize();
+    window->Initialize(buffer_generator_.get());
     window->SetBounds(GetPrimaryBounds());
     screen_manager_->AddWindow(i, std::move(window));
   }
@@ -470,7 +451,7 @@
   gfx::AcceleratedWidget window_id = 1;
   std::unique_ptr<ui::DrmWindow> window(new ui::DrmWindow(
       window_id, device_manager_.get(), screen_manager_.get()));
-  window->Initialize();
+  window->Initialize(buffer_generator_.get());
   window->SetBounds(GetPrimaryBounds());
   screen_manager_->AddWindow(window_id, std::move(window));
 
@@ -492,7 +473,7 @@
 TEST_F(ScreenManagerTest, EnableControllerWhenWindowHasNoBuffer) {
   std::unique_ptr<ui::DrmWindow> window(
       new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get()));
-  window->Initialize();
+  window->Initialize(buffer_generator_.get());
   window->SetBounds(GetPrimaryBounds());
   screen_manager_->AddWindow(1, std::move(window));
 
@@ -521,10 +502,11 @@
 TEST_F(ScreenManagerTest, EnableControllerWhenWindowHasBuffer) {
   std::unique_ptr<ui::DrmWindow> window(
       new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get()));
-  window->Initialize();
+  window->Initialize(buffer_generator_.get());
   window->SetBounds(GetPrimaryBounds());
-  scoped_refptr<DrmFramebuffer> buffer =
-      CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size());
+
+  scoped_refptr<ui::DrmFramebuffer> buffer = buffer_generator_->Create(
+      drm_, DRM_FORMAT_XRGB8888, {}, GetPrimaryBounds().size());
   ui::DrmOverlayPlaneList planes;
   planes.push_back(ui::DrmOverlayPlane(buffer, nullptr));
   window->SchedulePageFlip(std::move(planes), base::DoNothing(),
@@ -546,10 +528,13 @@
 TEST_F(ScreenManagerTest, DISABLED_RejectBufferWithIncompatibleModifiers) {
   std::unique_ptr<ui::DrmWindow> window(
       new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get()));
-  window->Initialize();
+  window->Initialize(buffer_generator_.get());
   window->SetBounds(GetPrimaryBounds());
-  auto buffer = CreateBufferWithModifier(
-      DRM_FORMAT_XRGB8888, I915_FORMAT_MOD_X_TILED, GetPrimaryBounds().size());
+  scoped_refptr<ui::DrmFramebuffer> buffer =
+      buffer_generator_->CreateWithModifier(drm_, DRM_FORMAT_XRGB8888,
+                                            I915_FORMAT_MOD_X_TILED,
+                                            GetPrimaryBounds().size());
+
   ui::DrmOverlayPlaneList planes;
   planes.push_back(ui::DrmOverlayPlane(buffer, nullptr));
   window->SchedulePageFlip(std::move(planes), base::DoNothing(),
@@ -573,14 +558,11 @@
 }
 
 TEST(ScreenManagerTest2, ShouldNotHardwareMirrorDifferentDrmDevices) {
-  auto gbm_device1 = std::make_unique<MockGbmDevice>();
-  auto drm_device1 =
-      base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device1));
-  auto gbm_device2 = std::make_unique<MockGbmDevice>();
-  auto drm_device2 =
-      base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device2));
+  auto drm_device1 = base::MakeRefCounted<MockDrmDevice>();
+  auto drm_device2 = base::MakeRefCounted<MockDrmDevice>();
   DrmDeviceManager drm_device_manager(nullptr);
-  ScreenManager screen_manager;
+  MockDrmFramebufferGenerator buffer_generator;
+  ScreenManager screen_manager(&buffer_generator);
 
   constexpr uint32_t kCrtc19 = 19;
   constexpr uint32_t kConnector28 = 28;
@@ -599,7 +581,7 @@
   {
     auto window1 =
         std::make_unique<DrmWindow>(1, &drm_device_manager, &screen_manager);
-    window1->Initialize();
+    window1->Initialize(&buffer_generator);
     screen_manager.AddWindow(1, std::move(window1));
     screen_manager.GetWindow(1)->SetBounds(gfx::Rect(0, 0, 1920, 1080));
     screen_manager.AddDisplayController(drm_device1, kCrtc19, kConnector28);
@@ -611,7 +593,7 @@
                                               Mode(1920, 1080));
     auto window2 =
         std::make_unique<DrmWindow>(2, &drm_device_manager, &screen_manager);
-    window2->Initialize();
+    window2->Initialize(&buffer_generator);
     screen_manager.AddWindow(2, std::move(window2));
     screen_manager.GetWindow(2)->SetBounds(gfx::Rect(0, 1140, 1920, 1080));
   }
@@ -657,7 +639,7 @@
                                               Mode(1920, 1080));
     auto window3 =
         std::make_unique<DrmWindow>(3, &drm_device_manager, &screen_manager);
-    window3->Initialize();
+    window3->Initialize(&buffer_generator);
     screen_manager.AddWindow(3, std::move(window3));
     screen_manager.GetWindow(3)->SetBounds(gfx::Rect(0, 0, 1920, 1080));
     screen_manager.GetWindow(1)->SetBounds(gfx::Rect(0, 1140, 1920, 1080));
diff --git a/ui/views/controls/button/checkbox.cc b/ui/views/controls/button/checkbox.cc
index 1038a13f..91e398e 100644
--- a/ui/views/controls/button/checkbox.cc
+++ b/ui/views/controls/button/checkbox.cc
@@ -45,6 +45,10 @@
   // Limit the checkbox height to match the legacy appearance.
   const gfx::Size preferred_size(LabelButton::CalculatePreferredSize());
   SetMinSize(gfx::Size(0, preferred_size.height() + 4));
+
+  // Checkboxes always have a focus ring, even when the platform otherwise
+  // doesn't generally use them for buttons.
+  SetInstallFocusRingOnFocus(true);
 }
 
 Checkbox::~Checkbox() {