diff --git a/DEPS b/DEPS
index 810493c0..e0155b37 100644
--- a/DEPS
+++ b/DEPS
@@ -105,11 +105,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '3ad86d0c8bc41007d96a8d7335bc97ba08369741',
+  'skia_revision': '8ef78eae93114236b6eea4f525ea8cb9570da1e1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'd46dfc574b2090b0ffa7cd0d3cea901deddb3bd7',
+  'v8_revision': '6d7c52f868ecedb5e6ca0ac3da5917fcadb9c108',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -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': '5ddbdbf70c50068b9682750454d9fd339ed805c2',
+  'angle_revision': '624fbdcf55b017215693a1817d89d3e9f1ea5af1',
   # 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.
@@ -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': 'ac93684421fa0fb860b74e4339253378ee9066ab',
+  'catapult_revision': '69f64b27039708e61e1e9ce5d3a580235c667332',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -213,7 +213,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': 'fe90a1d2dcbeaffd7cb87fa50860c6741e76eaef',
+  'spv_tools_revision': '146eb3bdcf5b7dc021edb3ca9bb9aac18ae904c0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -954,7 +954,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '5cfa93810a407da48fe72b9e11df6e2bb7f17d9b',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '7f61afaa6ec3420f840df61d6f46d355a71317f3',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1106,7 +1106,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6d2f3f4cb8bac1f7c4a945c73d07a33df74f22f9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '3ff52ffa22ae08a487482c4adc6bbf53f334f947',
+    Var('webrtc_git') + '/src.git' + '@' + '5f45e6651864ffc14a5adfc64d8c5248e123d1a4',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1137,7 +1137,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8dfa5c869e0823cae9bd4c6cec863741fdef23c3',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1b448050d611114d6afbab145368ba3248f9234e',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 8a85cbf..351e1f6 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -600,6 +600,7 @@
     r".*[\\/]BuildHooksAndroidImpl\.java",
     r".*[\\/]LicenseContentProvider\.java",
     r".*[\\/]PlatformServiceBridgeImpl.java",
+    r".*chrome[\\\/]android[\\\/]feed[\\\/]dummy[\\\/].*\.java",
 ]
 
 # These paths contain test data and other known invalid JSON files.
diff --git a/WATCHLISTS b/WATCHLISTS
index 25b1a8a..ffe011f 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2304,6 +2304,7 @@
     'origin_trials': ['chasej+watch@chromium.org',
                       'iclelland+watch@chromium.org'],
     'ozone': ['kalyan.kondapally@intel.com',
+              'msisov@igalia.com',
               'ozone-reviews@chromium.org'],
     'page_info' : ['raymes+watch@chromium.org'],
     'page_load_metrics' : ['bmcquade+watch@chromium.org',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 7ce5e92..13b97a9 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1466,8 +1466,6 @@
     "content/keyboard_overlay/keyboard_overlay_delegate.h",
     "content/keyboard_overlay/keyboard_overlay_view.cc",
     "content/keyboard_overlay/keyboard_overlay_view.h",
-    "content/screen_orientation_delegate_chromeos.cc",
-    "content/screen_orientation_delegate_chromeos.h",
   ]
 
   defines = [ "ASH_WITH_CONTENT_IMPLEMENTATION" ]
@@ -1619,7 +1617,7 @@
 # need or use content and should be placed in |ash_unittests| instead.
 test("ash_content_unittests") {
   sources = [
-    "content/display/screen_orientation_controller_chromeos_unittest.cc",
+    "content/display/screen_orientation_controller_unittest.cc",
     "content/keyboard_overlay/keyboard_overlay_delegate_unittest.cc",
     "content/keyboard_overlay/keyboard_overlay_view_unittest.cc",
     "test/ash_unittests.cc",
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index a290e5ae..9e15ccc 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -450,10 +450,6 @@
   }
 }
 
-gfx::Size AppListView::CalculatePreferredSize() const {
-  return app_list_main_view_->GetPreferredSize();
-}
-
 void AppListView::OnPaint(gfx::Canvas* canvas) {
   views::WidgetDelegateView::OnPaint(canvas);
   if (!next_paint_callback_.is_null()) {
@@ -646,23 +642,6 @@
 }
 
 void AppListView::InitializeFullscreen(gfx::NativeView parent) {
-  const display::Display display_nearest_view = GetDisplayNearestView();
-  const gfx::Rect display_work_area_bounds = display_nearest_view.work_area();
-
-  // Set the widget height to the shelf height to replace the shelf background
-  // on show animation with no flicker. In shelf mode we set the bounds to the
-  // top of the screen because the widget does not animate.
-  const int overlay_view_bounds_y = is_side_shelf_
-                                        ? display_work_area_bounds.y()
-                                        : display_work_area_bounds.bottom();
-  gfx::Rect app_list_overlay_view_bounds(
-      display_nearest_view.bounds().x(), overlay_view_bounds_y,
-      parent->bounds().width(), display_nearest_view.bounds().height());
-
-  // The app list container fills the screen, so convert to local coordinates.
-  gfx::Rect local_bounds = app_list_overlay_view_bounds;
-  local_bounds -= display_nearest_view.bounds().OffsetFromOrigin();
-
   fullscreen_widget_ = new views::Widget;
   views::Widget::InitParams app_list_overlay_view_params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
@@ -683,7 +662,7 @@
   // intersection.
   // TODO(mash): Redesign this animation to position the widget to cover the
   // entire screen, then animate the layer up into position. crbug.com/768437
-  fullscreen_widget_->GetNativeView()->SetBounds(local_bounds);
+  fullscreen_widget_->GetNativeView()->SetBounds(GetPreferredWidgetBounds());
 
   overlay_view_ = new AppListOverlayView(0 /* no corners */);
 
@@ -808,23 +787,19 @@
       }
     }
   } else {
-    const int display_height = GetDisplayNearestView().size().height();
-    int app_list_y_for_state = 0;
+    const int fullscreen_height = GetFullscreenStateHeight();
     int app_list_height = 0;
     switch (app_list_state_) {
       case AppListViewState::FULLSCREEN_ALL_APPS:
       case AppListViewState::FULLSCREEN_SEARCH:
-        app_list_y_for_state = 0;
-        app_list_height = display_height;
+        app_list_height = fullscreen_height;
         break;
       case AppListViewState::HALF:
-        app_list_y_for_state = display_height - kHalfAppListHeight;
         app_list_height = kHalfAppListHeight;
         break;
       case AppListViewState::PEEKING: {
         const int peeking_height =
             AppListConfig::instance().peeking_app_list_height();
-        app_list_y_for_state = display_height - peeking_height;
         app_list_height = peeking_height;
         break;
       }
@@ -839,12 +814,12 @@
     ConvertPointToScreen(this, &location_in_screen_coordinates);
     const int drag_delta =
         initial_drag_point_.y() - location_in_screen_coordinates.y();
-    const int location_y_in_current_display =
+    const int location_y_in_current_work_area =
         location_in_screen_coordinates.y() -
-        GetDisplayNearestView().bounds().y();
+        GetDisplayNearestView().work_area().y();
     // If the drag ended near the bezel, close the app list and return early.
-    if (location_y_in_current_display >=
-        (display_height - kAppListBezelMargin)) {
+    if (location_y_in_current_work_area >=
+        (fullscreen_height - kAppListBezelMargin)) {
       Dismiss();
       return;
     }
@@ -1477,9 +1452,8 @@
   background_opacity_ = background_opacity;
   gfx::Rect new_widget_bounds = fullscreen_widget_->GetWindowBoundsInScreen();
   app_list_y_position_in_screen_ = std::min(
-      std::max(y_position_in_screen, GetDisplayNearestView().bounds().y()),
-      GetDisplayNearestView().bounds().bottom() -
-          AppListConfig::instance().shelf_height());
+      std::max(y_position_in_screen, GetDisplayNearestView().work_area().y()),
+      GetScreenBottom() - AppListConfig::instance().shelf_height());
   new_widget_bounds.set_y(app_list_y_position_in_screen_);
   gfx::NativeView native_view = fullscreen_widget_->GetNativeView();
   ::wm::ConvertRectFromScreen(native_view->parent(), &new_widget_bounds);
@@ -1500,7 +1474,7 @@
 }
 
 gfx::Rect AppListView::GetAppInfoDialogBounds() const {
-  gfx::Rect app_info_bounds(GetDisplayNearestView().bounds());
+  gfx::Rect app_info_bounds(GetDisplayNearestView().work_area());
   app_info_bounds.ClampToCenteredSize(
       gfx::Size(kAppInfoDialogWidth, kAppInfoDialogHeight));
   return app_info_bounds;
@@ -1524,8 +1498,7 @@
 int AppListView::GetCurrentAppListHeight() const {
   if (!fullscreen_widget_)
     return AppListConfig::instance().shelf_height();
-  return GetDisplayNearestView().bounds().bottom() -
-         fullscreen_widget_->GetWindowBoundsInScreen().y();
+  return GetScreenBottom() - fullscreen_widget_->GetWindowBoundsInScreen().y();
 }
 
 float AppListView::GetAppListTransitionProgress() const {
@@ -1548,13 +1521,19 @@
   // Currently transition progress is between peeking and fullscreen state.
   // Calculate the progress of this transition.
   const float fullscreen_height_above_peeking =
-      GetDisplayNearestView().size().height() - peeking_height;
+      GetFullscreenStateHeight() - peeking_height;
   const float current_height_above_peeking = current_height - peeking_height;
   DCHECK_GT(fullscreen_height_above_peeking, 0);
   DCHECK_LE(current_height_above_peeking, fullscreen_height_above_peeking);
   return 1 + current_height_above_peeking / fullscreen_height_above_peeking;
 }
 
+int AppListView::GetFullscreenStateHeight() const {
+  const display::Display display = GetDisplayNearestView();
+  const gfx::Rect display_bounds = display.bounds();
+  return display_bounds.height() - display.work_area().y() + display_bounds.y();
+}
+
 bool AppListView::IsHomeLauncherEnabledInTabletMode() const {
   return is_tablet_mode_ && is_home_launcher_enabled_;
 }
@@ -1644,7 +1623,7 @@
 void AppListView::OnDisplayMetricsChanged(const display::Display& display,
                                           uint32_t changed_metrics) {
   // Set the |fullscreen_widget_| size to fit the new display metrics.
-  fullscreen_widget_->SetSize(GetDisplayNearestView().size());
+  fullscreen_widget_->GetNativeView()->SetBounds(GetPreferredWidgetBounds());
 
   // Update the |fullscreen_widget_| bounds to accomodate the new work
   // area.
@@ -1714,4 +1693,25 @@
          GetRootAppsGridView()->pagination_model()->has_transition();
 }
 
+gfx::Rect AppListView::GetPreferredWidgetBounds() {
+  const display::Display display = GetDisplayNearestView();
+  const gfx::Rect work_area_bounds = display.work_area();
+
+  // Set the widget height to the shelf height to replace the shelf background
+  // on show animation with no flicker. In shelf mode we set the bounds to the
+  // top of the screen because the widget does not animate. Then convert to
+  // local coordinates since the app list container fills the screen.
+  const int overlay_view_bounds_y =
+      (is_side_shelf_ ? work_area_bounds.y() : work_area_bounds.bottom()) -
+      display.bounds().y();
+
+  // Use parent's width instead of display width to avoid 1 px gap (See
+  // https://crbug.com/884889).
+  CHECK(fullscreen_widget_);
+  aura::Window* parent = fullscreen_widget_->GetNativeView()->parent();
+  CHECK(parent);
+  return gfx::Rect(0, overlay_view_bounds_y, parent->bounds().width(),
+                   GetFullscreenStateHeight());
+}
+
 }  // namespace app_list
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h
index f215ab01..4d74721 100644
--- a/ash/app_list/views/app_list_view.h
+++ b/ash/app_list/views/app_list_view.h
@@ -133,7 +133,6 @@
   void SetAppListOverlayVisible(bool visible);
 
   // views::View:
-  gfx::Size CalculatePreferredSize() const override;
   void OnPaint(gfx::Canvas* canvas) override;
   const char* GetClassName() const override;
   bool CanProcessEventsWithinSubtree() const override;
@@ -213,6 +212,9 @@
   // while [1.0, 2.0] means the progress between peeking and fullscreen state.
   float GetAppListTransitionProgress() const;
 
+  // Returns the height of app list in fullscreen state.
+  int GetFullscreenStateHeight() const;
+
   views::Widget* get_fullscreen_widget_for_test() const {
     return fullscreen_widget_;
   }
@@ -337,6 +339,10 @@
   // Returns true if scroll events should be ignored.
   bool ShouldIgnoreScrollEvents();
 
+  // Returns preferred fullscreen widget bounds in parent window. Note that this
+  // function should only be called after the widget is initialized.
+  gfx::Rect GetPreferredWidgetBounds();
+
   AppListViewDelegate* delegate_;    // Weak. Owned by AppListService.
   AppListModel* const model_;        // Not Owned.
   SearchModel* const search_model_;  // Not Owned.
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index a562c6d..f4390e0 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -1862,7 +1862,7 @@
   // |kAllAppsIndicatorOpacityEndFraction|, the opacity of all apps indicator
   // changes from 0.f to 1.0f.
   const float peeking_to_fullscreen_height =
-      contents_view_->GetDisplaySize().height() - peeking_height;
+      app_list_view->GetFullscreenStateHeight() - peeking_height;
   DCHECK_GT(peeking_to_fullscreen_height, 0);
   const float drag_amount = current_height - peeking_height;
   fraction = std::max(drag_amount / peeking_to_fullscreen_height, 0.f);
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index 99db7fa..b4dae04 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -479,8 +479,8 @@
 gfx::Size ContentsView::CalculatePreferredSize() const {
   // If shelf is set auto-hide, the work area will become fullscreen. The bottom
   // row of apps will be partially blocked by the shelf when it becomes shown.
-  // So always cut the shelf bounds from display bounds.
-  gfx::Size size = GetDisplaySize();
+  // So always cut the shelf bounds from widget bounds.
+  gfx::Size size = GetWidget()->GetNativeView()->bounds().size();
   if (!app_list_view_->is_side_shelf())
     size.set_height(size.height() - AppListConfig::instance().shelf_height());
   return size;
@@ -526,12 +526,6 @@
 
 void ContentsView::TransitionEnded() {}
 
-gfx::Size ContentsView::GetDisplaySize() const {
-  return display::Screen::GetScreen()
-      ->GetDisplayNearestView(GetWidget()->GetNativeView())
-      .size();
-}
-
 void ContentsView::FadeOutOnClose(base::TimeDelta animation_duration) {
   DoAnimation(animation_duration, layer(), 0.0f);
   DoAnimation(animation_duration, GetSearchBoxView()->layer(), 0.0f);
diff --git a/ash/app_list/views/contents_view.h b/ash/app_list/views/contents_view.h
index 28a52d2..c7e3bfb 100644
--- a/ash/app_list/views/contents_view.h
+++ b/ash/app_list/views/contents_view.h
@@ -157,9 +157,6 @@
   void TransitionChanged() override;
   void TransitionEnded() override;
 
-  // Returns the size of current display.
-  gfx::Size GetDisplaySize() const;
-
   // Starts the fade out animation when the app list is closed.
   void FadeOutOnClose(base::TimeDelta animation_duration);
 
diff --git a/ash/content/display/DEPS b/ash/content/display/DEPS
index 2069ccc75..9fa848d1 100644
--- a/ash/content/display/DEPS
+++ b/ash/content/display/DEPS
@@ -1,5 +1,5 @@
 specific_include_rules = {
-  "screen_orientation_controller_chromeos_unittest.cc": [
+  "screen_orientation_controller_unittest.cc": [
     "+content/public/browser/browser_context.h",
     "+content/public/browser/web_contents.h",
     "+third_party/blink/public/common/screen_orientation/web_screen_orientation_lock_type.h",
diff --git a/ash/content/display/screen_orientation_controller_chromeos_unittest.cc b/ash/content/display/screen_orientation_controller_unittest.cc
similarity index 85%
rename from ash/content/display/screen_orientation_controller_chromeos_unittest.cc
rename to ash/content/display/screen_orientation_controller_unittest.cc
index 074eee52..c76455c9 100644
--- a/ash/content/display/screen_orientation_controller_chromeos_unittest.cc
+++ b/ash/content/display/screen_orientation_controller_unittest.cc
@@ -7,7 +7,6 @@
 #include <memory>
 #include <vector>
 
-#include "ash/content/screen_orientation_delegate_chromeos.h"
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
 #include "ash/public/cpp/app_types.h"
@@ -43,6 +42,8 @@
 
 namespace ash {
 
+using WebContents = content::WebContents;
+
 namespace {
 
 const float kDegreesToRadians = 3.1415926f / 180.0f;
@@ -86,8 +87,7 @@
 
 // Attaches the NativeView of |web_contents| to |parent| without changing the
 // currently active window.
-void AttachWebContents(content::WebContents* web_contents,
-                       aura::Window* parent) {
+void AttachWebContents(WebContents* web_contents, aura::Window* parent) {
   aura::Window* window = web_contents->GetNativeView();
   window->Show();
   parent->AddChild(window);
@@ -95,12 +95,48 @@
 
 // Attaches the NativeView of |web_contents| to |parent|, ensures that it is
 // visible, and activates the parent window.
-void AttachAndActivateWebContents(content::WebContents* web_contents,
+void AttachAndActivateWebContents(WebContents* web_contents,
                                   aura::Window* parent) {
   AttachWebContents(web_contents, parent);
   Shell::Get()->activation_client()->ActivateWindow(parent);
 }
 
+ash::OrientationLockType ToAshOrientationLockType(
+    blink::WebScreenOrientationLockType blink_orientation_lock) {
+  switch (blink_orientation_lock) {
+    case blink::kWebScreenOrientationLockDefault:
+    case blink::kWebScreenOrientationLockAny:
+      return ash::OrientationLockType::kAny;
+    case blink::kWebScreenOrientationLockPortrait:
+      return ash::OrientationLockType::kPortrait;
+    case blink::kWebScreenOrientationLockPortraitPrimary:
+      return ash::OrientationLockType::kPortraitPrimary;
+    case blink::kWebScreenOrientationLockPortraitSecondary:
+      return ash::OrientationLockType::kPortraitSecondary;
+    case blink::kWebScreenOrientationLockLandscape:
+      return ash::OrientationLockType::kLandscape;
+    case blink::kWebScreenOrientationLockLandscapePrimary:
+      return ash::OrientationLockType::kLandscapePrimary;
+    case blink::kWebScreenOrientationLockLandscapeSecondary:
+      return ash::OrientationLockType::kLandscapeSecondary;
+    case blink::kWebScreenOrientationLockNatural:
+      return ash::OrientationLockType::kNatural;
+  }
+  return ash::OrientationLockType::kAny;
+}
+
+void Lock(WebContents* web_contents,
+          blink::WebScreenOrientationLockType orientation_lock) {
+  Shell::Get()->screen_orientation_controller()->LockOrientationForWindow(
+      web_contents->GetNativeView(),
+      ToAshOrientationLockType(orientation_lock));
+}
+
+void Unlock(WebContents* web_contents) {
+  Shell::Get()->screen_orientation_controller()->UnlockOrientationForWindow(
+      web_contents->GetNativeView());
+}
+
 }  // namespace
 
 class ScreenOrientationControllerTest : public AshTestBase {
@@ -108,15 +144,13 @@
   ScreenOrientationControllerTest();
   ~ScreenOrientationControllerTest() override;
 
-  content::ScreenOrientationDelegate* delegate() { return &delegate_; }
-
-  // Creates and initializes and empty content::WebContents that is backed by a
+  // Creates and initializes and empty WebContents that is backed by a
   // content::BrowserContext and that has an aura::Window.
-  std::unique_ptr<content::WebContents> CreateWebContents();
+  std::unique_ptr<WebContents> CreateWebContents();
 
-  // Creates a secondary content::WebContents, with a separate
+  // Creates a secondary WebContents, with a separate
   // content::BrowserContext.
-  std::unique_ptr<content::WebContents> CreateSecondaryWebContents();
+  std::unique_ptr<WebContents> CreateSecondaryWebContents();
 
   // AshTestBase:
   void SetUp() override;
@@ -149,14 +183,12 @@
   }
 
  private:
-  ScreenOrientationDelegateChromeos delegate_;
-
   content::TestBrowserContext browser_context_;
 
   // Optional content::BrowserContext used for two window tests.
   std::unique_ptr<content::BrowserContext> secondary_browser_context_;
 
-  // Setups underlying content layer so that content::WebContents can be
+  // Setups underlying content layer so that WebContents can be
   // generated.
   std::unique_ptr<views::WebViewTestHelper> webview_test_helper_;
 
@@ -169,13 +201,13 @@
 
 ScreenOrientationControllerTest::~ScreenOrientationControllerTest() = default;
 
-std::unique_ptr<content::WebContents>
+std::unique_ptr<WebContents>
 ScreenOrientationControllerTest::CreateWebContents() {
   return content::WebContentsTester::CreateTestWebContents(&browser_context_,
                                                            nullptr);
 }
 
-std::unique_ptr<content::WebContents>
+std::unique_ptr<WebContents>
 ScreenOrientationControllerTest::CreateSecondaryWebContents() {
   secondary_browser_context_.reset(new content::TestBrowserContext());
   return content::WebContentsTester::CreateTestWebContents(
@@ -191,108 +223,108 @@
   SetRunningOutsideAsh();
 }
 
-// Tests that a content::WebContents can lock rotation.
+// Tests that a WebContents can lock rotation.
 TEST_F(ScreenOrientationControllerTest, LockOrientation) {
   EnableTabletMode(true);
 
-  std::unique_ptr<content::WebContents> content(CreateWebContents());
+  std::unique_ptr<WebContents> content(CreateWebContents());
   std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
   ASSERT_NE(nullptr, content->GetNativeView());
   ASSERT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
   ASSERT_FALSE(RotationLocked());
 
   AttachAndActivateWebContents(content.get(), focus_window.get());
-  delegate()->Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
+  Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
   EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
   EXPECT_TRUE(RotationLocked());
 }
 
-// Tests that a content::WebContents can unlock rotation.
+// Tests that a WebContents can unlock rotation.
 TEST_F(ScreenOrientationControllerTest, Unlock) {
   EnableTabletMode(true);
 
-  std::unique_ptr<content::WebContents> content(CreateWebContents());
+  std::unique_ptr<WebContents> content(CreateWebContents());
   std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
   ASSERT_NE(nullptr, content->GetNativeView());
   ASSERT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
   ASSERT_FALSE(RotationLocked());
 
   AttachAndActivateWebContents(content.get(), focus_window.get());
-  delegate()->Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
+  Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
   EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
   EXPECT_TRUE(RotationLocked());
 
-  delegate()->Unlock(content.get());
+  Unlock(content.get());
   EXPECT_FALSE(RotationLocked());
 }
 
-// Tests that a content::WebContents is able to change the orientation of the
+// Tests that a WebContents is able to change the orientation of the
 // display after having locked rotation.
 TEST_F(ScreenOrientationControllerTest, OrientationChanges) {
   EnableTabletMode(true);
 
-  std::unique_ptr<content::WebContents> content(CreateWebContents());
+  std::unique_ptr<WebContents> content(CreateWebContents());
   std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
   ASSERT_NE(nullptr, content->GetNativeView());
   ASSERT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
   ASSERT_FALSE(RotationLocked());
 
   AttachAndActivateWebContents(content.get(), focus_window.get());
-  delegate()->Lock(content.get(), blink::kWebScreenOrientationLockPortrait);
+  Lock(content.get(), blink::kWebScreenOrientationLockPortrait);
   EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
   EXPECT_TRUE(RotationLocked());
 
-  delegate()->Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
+  Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
   EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
 }
 
-// Tests that orientation can only be set by the first content::WebContents that
+// Tests that orientation can only be set by the first WebContents that
 // has set a rotation lock.
 TEST_F(ScreenOrientationControllerTest, SecondContentCannotChangeOrientation) {
   EnableTabletMode(true);
 
-  std::unique_ptr<content::WebContents> content1(CreateWebContents());
-  std::unique_ptr<content::WebContents> content2(CreateSecondaryWebContents());
+  std::unique_ptr<WebContents> content1(CreateWebContents());
+  std::unique_ptr<WebContents> content2(CreateSecondaryWebContents());
   std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
   std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
   ASSERT_NE(content1->GetNativeView(), content2->GetNativeView());
 
   AttachAndActivateWebContents(content1.get(), focus_window1.get());
   AttachWebContents(content2.get(), focus_window2.get());
-  delegate()->Lock(content1.get(), blink::kWebScreenOrientationLockLandscape);
-  delegate()->Lock(content2.get(), blink::kWebScreenOrientationLockPortrait);
+  Lock(content1.get(), blink::kWebScreenOrientationLockLandscape);
+  Lock(content2.get(), blink::kWebScreenOrientationLockPortrait);
   EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
 }
 
-// Tests that only the content::WebContents that set a rotation lock can perform
+// Tests that only the WebContents that set a rotation lock can perform
 // an unlock.
 TEST_F(ScreenOrientationControllerTest, SecondContentCannotUnlock) {
   EnableTabletMode(true);
 
-  std::unique_ptr<content::WebContents> content1(CreateWebContents());
-  std::unique_ptr<content::WebContents> content2(CreateSecondaryWebContents());
+  std::unique_ptr<WebContents> content1(CreateWebContents());
+  std::unique_ptr<WebContents> content2(CreateSecondaryWebContents());
   std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
   std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
   ASSERT_NE(content1->GetNativeView(), content2->GetNativeView());
 
   AttachAndActivateWebContents(content1.get(), focus_window1.get());
   AttachWebContents(content2.get(), focus_window2.get());
-  delegate()->Lock(content1.get(), blink::kWebScreenOrientationLockLandscape);
-  delegate()->Unlock(content2.get());
+  Lock(content1.get(), blink::kWebScreenOrientationLockLandscape);
+  Unlock(content2.get());
   EXPECT_TRUE(RotationLocked());
 }
 
-// Tests that a rotation lock is applied only while the content::WebContents are
+// Tests that a rotation lock is applied only while the WebContents are
 // a part of the active window.
 TEST_F(ScreenOrientationControllerTest, ActiveWindowChangesUpdateLock) {
   EnableTabletMode(true);
 
-  std::unique_ptr<content::WebContents> content(CreateWebContents());
+  std::unique_ptr<WebContents> content(CreateWebContents());
   std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
   std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
 
   AttachAndActivateWebContents(content.get(), focus_window1.get());
-  delegate()->Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
+  Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
   ASSERT_TRUE(RotationLocked());
 
   ::wm::ActivationClient* activation_client = Shell::Get()->activation_client();
@@ -308,15 +340,15 @@
 TEST_F(ScreenOrientationControllerTest, ActiveWindowChangesUpdateOrientation) {
   EnableTabletMode(true);
 
-  std::unique_ptr<content::WebContents> content1(CreateWebContents());
-  std::unique_ptr<content::WebContents> content2(CreateSecondaryWebContents());
+  std::unique_ptr<WebContents> content1(CreateWebContents());
+  std::unique_ptr<WebContents> content2(CreateSecondaryWebContents());
   std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
   std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
   AttachAndActivateWebContents(content1.get(), focus_window1.get());
   AttachWebContents(content2.get(), focus_window2.get());
 
-  delegate()->Lock(content1.get(), blink::kWebScreenOrientationLockLandscape);
-  delegate()->Lock(content2.get(), blink::kWebScreenOrientationLockPortrait);
+  Lock(content1.get(), blink::kWebScreenOrientationLockLandscape);
+  Lock(content2.get(), blink::kWebScreenOrientationLockPortrait);
   EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
 
   ::wm::ActivationClient* activation_client = Shell::Get()->activation_client();
@@ -334,10 +366,10 @@
 TEST_F(ScreenOrientationControllerTest, VisibilityChangesLock) {
   EnableTabletMode(true);
 
-  std::unique_ptr<content::WebContents> content(CreateWebContents());
+  std::unique_ptr<WebContents> content(CreateWebContents());
   std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
   AttachAndActivateWebContents(content.get(), focus_window.get());
-  delegate()->Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
+  Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
   EXPECT_TRUE(RotationLocked());
 
   aura::Window* window = content->GetNativeView();
@@ -353,12 +385,12 @@
 TEST_F(ScreenOrientationControllerTest, WindowDestructionRemovesLock) {
   EnableTabletMode(true);
 
-  std::unique_ptr<content::WebContents> content(CreateWebContents());
+  std::unique_ptr<WebContents> content(CreateWebContents());
   std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
   std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
 
   AttachAndActivateWebContents(content.get(), focus_window1.get());
-  delegate()->Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
+  Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
   ASSERT_TRUE(RotationLocked());
 
   focus_window1->RemoveChild(content->GetNativeView());
@@ -548,12 +580,12 @@
 // Tests that when the orientation lock is set to Landscape, that rotation can
 // be done between the two angles of the orientation.
 TEST_F(ScreenOrientationControllerTest, LandscapeOrientationAllowsRotation) {
-  std::unique_ptr<content::WebContents> content(CreateWebContents());
+  std::unique_ptr<WebContents> content(CreateWebContents());
   std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
   EnableTabletMode(true);
 
   AttachAndActivateWebContents(content.get(), focus_window.get());
-  delegate()->Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
+  Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
   EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
   EXPECT_TRUE(RotationLocked());
 
@@ -568,15 +600,15 @@
   EXPECT_EQ(display::Display::ROTATE_180, GetCurrentInternalDisplayRotation());
 }
 
-// Tests that when the orientation lock is set to Portrait, that rotaiton can be
+// Tests that when the orientation lock is set to Portrait, that rotation can be
 // done between the two angles of the orientation.
 TEST_F(ScreenOrientationControllerTest, PortraitOrientationAllowsRotation) {
-  std::unique_ptr<content::WebContents> content(CreateWebContents());
+  std::unique_ptr<WebContents> content(CreateWebContents());
   std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
   EnableTabletMode(true);
 
   AttachAndActivateWebContents(content.get(), focus_window.get());
-  delegate()->Lock(content.get(), blink::kWebScreenOrientationLockPortrait);
+  Lock(content.get(), blink::kWebScreenOrientationLockPortrait);
   EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
   EXPECT_TRUE(RotationLocked());
 
@@ -594,13 +626,12 @@
 // Tests that for an orientation lock which does not allow rotation, that the
 // display rotation remains constant.
 TEST_F(ScreenOrientationControllerTest, OrientationLockDisallowsRotation) {
-  std::unique_ptr<content::WebContents> content(CreateWebContents());
+  std::unique_ptr<WebContents> content(CreateWebContents());
   std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
   EnableTabletMode(true);
 
   AttachAndActivateWebContents(content.get(), focus_window.get());
-  delegate()->Lock(content.get(),
-                   blink::kWebScreenOrientationLockPortraitPrimary);
+  Lock(content.get(), blink::kWebScreenOrientationLockPortraitPrimary);
   EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
   EXPECT_TRUE(RotationLocked());
 
@@ -613,16 +644,16 @@
   EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
 }
 
-// Tests that after a content::WebContents has applied an orientation lock which
+// Tests that after a WebContents has applied an orientation lock which
 // supports rotation, that a user rotation lock does not allow rotation.
 TEST_F(ScreenOrientationControllerTest, UserRotationLockDisallowsRotation) {
-  std::unique_ptr<content::WebContents> content(CreateWebContents());
+  std::unique_ptr<WebContents> content(CreateWebContents());
   std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
   EnableTabletMode(true);
 
   AttachAndActivateWebContents(content.get(), focus_window.get());
-  delegate()->Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
-  delegate()->Unlock(content.get());
+  Lock(content.get(), blink::kWebScreenOrientationLockLandscape);
+  Unlock(content.get());
 
   SetUserRotationLocked(true);
   EXPECT_TRUE(RotationLocked());
@@ -723,8 +754,8 @@
 TEST_F(ScreenOrientationControllerTest, UserRotationLock) {
   EnableTabletMode(true);
 
-  std::unique_ptr<content::WebContents> content1(CreateWebContents());
-  std::unique_ptr<content::WebContents> content2(CreateSecondaryWebContents());
+  std::unique_ptr<WebContents> content1(CreateWebContents());
+  std::unique_ptr<WebContents> content2(CreateSecondaryWebContents());
   std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
   std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
   ASSERT_NE(content1->GetNativeView(), content2->GetNativeView());
@@ -742,7 +773,7 @@
   orientation_controller->ToggleUserRotationLock();
   ASSERT_TRUE(orientation_controller->user_rotation_locked());
 
-  delegate()->Lock(content1.get(), blink::kWebScreenOrientationLockPortrait);
+  Lock(content1.get(), blink::kWebScreenOrientationLockPortrait);
 
   EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
 
@@ -764,10 +795,10 @@
   EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
 
   // Application forced to be landscape.
-  delegate()->Lock(content2.get(), blink::kWebScreenOrientationLockLandscape);
+  Lock(content2.get(), blink::kWebScreenOrientationLockLandscape);
   EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
 
-  delegate()->Lock(content1.get(), blink::kWebScreenOrientationLockAny);
+  Lock(content1.get(), blink::kWebScreenOrientationLockAny);
   activation_client->ActivateWindow(focus_window1.get());
   // Switching back to any will rotate to user rotation.
   EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
diff --git a/ash/public/interfaces/voice_interaction_controller.mojom b/ash/public/interfaces/voice_interaction_controller.mojom
index c4990eb..6c1a9d21 100644
--- a/ash/public/interfaces/voice_interaction_controller.mojom
+++ b/ash/public/interfaces/voice_interaction_controller.mojom
@@ -106,6 +106,9 @@
   // Called when the locale is changed.
   NotifyLocaleChanged(string locale);
 
+  // Called when the launch with mic open state is changed.
+  NotifyLaunchWithMicOpen(bool launch_with_mic_open);
+
   // Return if the voice interaction setting is enabled/disabled.
   IsSettingEnabled() => (bool enabled);
 
diff --git a/ash/voice_interaction/voice_interaction_controller.cc b/ash/voice_interaction/voice_interaction_controller.cc
index bede95b03..5fca0ae7 100644
--- a/ash/voice_interaction/voice_interaction_controller.cc
+++ b/ash/voice_interaction/voice_interaction_controller.cc
@@ -77,6 +77,11 @@
       [locale](auto* observer) { observer->OnLocaleChanged(locale); });
 }
 
+void VoiceInteractionController::NotifyLaunchWithMicOpen(
+    bool launch_with_mic_open) {
+  launch_with_mic_open_ = launch_with_mic_open;
+}
+
 void VoiceInteractionController::IsSettingEnabled(
     IsSettingEnabledCallback callback) {
   std::move(callback).Run(settings_enabled_);
diff --git a/ash/voice_interaction/voice_interaction_controller.h b/ash/voice_interaction/voice_interaction_controller.h
index 6d1e8a3..59b38c93 100644
--- a/ash/voice_interaction/voice_interaction_controller.h
+++ b/ash/voice_interaction/voice_interaction_controller.h
@@ -31,6 +31,7 @@
   void NotifyFeatureAllowed(mojom::AssistantAllowedState state) override;
   void NotifyNotificationEnabled(bool enabled) override;
   void NotifyLocaleChanged(const std::string& locale) override;
+  void NotifyLaunchWithMicOpen(bool launch_with_mic_open) override;
   void IsSettingEnabled(IsSettingEnabledCallback callback) override;
   void IsSetupCompleted(IsSetupCompletedCallback callback) override;
   void IsContextEnabled(IsContextEnabledCallback callback) override;
@@ -51,6 +52,8 @@
 
   bool notification_enabled() const { return notification_enabled_; }
 
+  bool launch_with_mic_open() const { return launch_with_mic_open_; }
+
   void FlushForTesting();
 
  private:
@@ -80,6 +83,9 @@
 
   std::string locale_;
 
+  // Whether the Assistant should launch with mic open;
+  bool launch_with_mic_open_ = false;
+
   mojo::BindingSet<mojom::VoiceInteractionController> bindings_;
 
   mojo::InterfacePtrSet<mojom::VoiceInteractionObserver> observers_;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 8a8f69a..3076ed8 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3116,8 +3116,6 @@
       "test/android/junit/src/org/chromium/base/task/test/BackgroundShadowAsyncTask.java",
       "test/android/junit/src/org/chromium/base/task/test/CustomShadowAsyncTask.java",
       "test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java",
-      "test/android/junit/src/org/chromium/base/task/test/BackgroundShadowAsyncTask.java",
-      "test/android/junit/src/org/chromium/base/task/test/CustomShadowAsyncTask.java",
       "test/android/junit/src/org/chromium/base/test/util/TestRunnerTestRule.java",
       "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/task/test/ShadowAsyncTask.java",
       "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/task/test/ShadowAsyncTaskBridge.java",
diff --git a/base/android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java b/base/android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java
index 015b077..b72e6e1 100644
--- a/base/android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java
+++ b/base/android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java
@@ -5,6 +5,9 @@
 package org.chromium.base.task;
 
 /**
- * Tasks posted will be run in order on a single thread.
+ * Tasks posted will be run in order on a single thread. Multiple SingleThreadTaskRunners
+ * can share a single thread. When sharing a thread, mutual exclusion is guaranteed but
+ * unless specified otherwise by the provider of a given SingleThreadTaskRunner there are
+ * no ordering guarantees w.r.t. other SingleThreadTaskRunner.
  */
 public interface SingleThreadTaskRunner extends SequencedTaskRunner {}
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index 009c88f..4166adf 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -168,6 +168,7 @@
       return DummyHistogram::GetInstance();
     // To avoid racy destruction at shutdown, the following will be leaked.
     const BucketRanges* created_ranges = CreateRanges();
+    CHECK(created_ranges->HasValidChecksum()) << name_;
 
 // Temporary check for https://crbug.com/836238
 #if defined(OS_WIN)  // Only Windows has a debugger that makes this useful.
@@ -206,13 +207,17 @@
         DEBUG_ALIAS_FOR_CSTR(h_name, name_.c_str(), 100);
         HistogramType h_type = histogram_type_;
         uint32_t b_count = bucket_count_;
-        size_t c_count = recreated_ranges->size() - 1;
+        size_t c_count = created_ranges->size() - 1;
         size_t r_count = recreated_ranges->size() - 1;
+        bool c_valid = created_ranges->HasValidChecksum();
+        bool r_valid = recreated_ranges->HasValidChecksum();
         CHECK(recreated_ranges->Equals(created_ranges)) << name_;
         debug::Alias(&h_type);
         debug::Alias(&b_count);
         debug::Alias(&c_count);
         debug::Alias(&r_count);
+        debug::Alias(&c_valid);
+        debug::Alias(&r_valid);
         CHECK(false) << name_;
       }
     }
@@ -241,11 +246,15 @@
         uint32_t b_count = bucket_count_;
         size_t c_count = recreated_ranges->size() - 1;
         size_t r_count = registered_ranges->size() - 1;
+        bool c_valid = recreated_ranges->HasValidChecksum();
+        bool r_valid = registered_ranges->HasValidChecksum();
         CHECK(recreated_ranges->Equals(registered_ranges)) << name_;
         debug::Alias(&h_type);
         debug::Alias(&b_count);
         debug::Alias(&c_count);
         debug::Alias(&r_count);
+        debug::Alias(&c_valid);
+        debug::Alias(&r_valid);
         CHECK(false) << name_;
       }
     }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index a545949..a6d0595e 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-98ba6c8440c2afb334433de9bb901a0f97efe1e1
\ No newline at end of file
+065fd902445d69829dddf465c524700b375de112
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index da4623e..abc96619 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-bd8834505d919018dd7ffdc5fa42eb04616ca9f5
\ No newline at end of file
+4dfe28e996ad104738f8abdb1f27680b369dba9a
\ No newline at end of file
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index 3ee5b67..58407f3 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -33,6 +33,9 @@
 #include "components/viz/common/resources/platform_color.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/client/shared_image_interface.h"
+#include "gpu/command_buffer/common/shared_image_trace_utils.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkPaint.h"
@@ -101,12 +104,13 @@
 class HudGpuBacking : public ResourcePool::GpuBacking {
  public:
   ~HudGpuBacking() override {
-    gpu::gles2::GLES2Interface* gl = compositor_context_provider->ContextGL();
+    if (mailbox.IsZero())
+      return;
+    auto* sii = compositor_context_provider->SharedImageInterface();
     if (returned_sync_token.HasData())
-      gl->WaitSyncTokenCHROMIUM(returned_sync_token.GetConstData());
-    if (mailbox_sync_token.HasData())
-      gl->WaitSyncTokenCHROMIUM(mailbox_sync_token.GetConstData());
-    gl->DeleteTextures(1, &texture_id);
+      sii->DestroySharedImage(returned_sync_token, mailbox);
+    else if (mailbox_sync_token.HasData())
+      sii->DestroySharedImage(mailbox_sync_token, mailbox);
   }
 
   void OnMemoryDump(
@@ -114,15 +118,15 @@
       const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
       uint64_t tracing_process_id,
       int importance) const override {
-    auto texture_tracing_guid = gl::GetGLTextureClientGUIDForTracing(
-        compositor_context_provider->ContextSupport()->ShareGroupTracingGUID(),
-        texture_id);
-    pmd->CreateSharedGlobalAllocatorDump(texture_tracing_guid);
-    pmd->AddOwnershipEdge(buffer_dump_guid, texture_tracing_guid, importance);
+    if (mailbox.IsZero())
+      return;
+
+    auto tracing_guid = gpu::GetSharedImageGUIDForTracing(mailbox);
+    pmd->CreateSharedGlobalAllocatorDump(tracing_guid);
+    pmd->AddOwnershipEdge(buffer_dump_guid, tracing_guid, importance);
   }
 
   viz::ContextProvider* compositor_context_provider;
-  GLuint texture_id;
 };
 
 class HudSoftwareBacking : public ResourcePool::SoftwareBacking {
@@ -229,22 +233,22 @@
     if (!pool_resource.gpu_backing()) {
       auto backing = std::make_unique<HudGpuBacking>();
       backing->compositor_context_provider = context_provider;
-      auto alloc = viz::TextureAllocation::MakeTextureId(
-          context_provider->ContextGL(),
-          context_provider->ContextCapabilities(), pool_resource.format(),
+      backing->InitOverlayCandidateAndTextureTarget(
+          pool_resource.format(), context_provider->ContextCapabilities(),
           layer_tree_impl()
               ->settings()
-              .resource_settings.use_gpu_memory_buffer_resources,
-          gpu_raster);
-      viz::TextureAllocation::AllocateStorage(
-          context_provider->ContextGL(),
-          context_provider->ContextCapabilities(), pool_resource.format(),
-          pool_resource.size(), alloc, pool_resource.color_space());
-      backing->texture_id = alloc.texture_id;
-      backing->texture_target = alloc.texture_target;
-      backing->overlay_candidate = alloc.overlay_candidate;
-      context_provider->ContextGL()->ProduceTextureDirectCHROMIUM(
-          backing->texture_id, backing->mailbox.name);
+              .resource_settings.use_gpu_memory_buffer_resources);
+      auto* sii = context_provider->SharedImageInterface();
+      uint32_t flags = gpu::SHARED_IMAGE_USAGE_GLES2;
+      if (gpu_raster)
+        flags |= gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
+      if (backing->overlay_candidate)
+        flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
+      backing->mailbox =
+          sii->CreateSharedImage(pool_resource.format(), pool_resource.size(),
+                                 pool_resource.color_space(), flags);
+      gpu::gles2::GLES2Interface* gl = context_provider->ContextGL();
+      gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
       pool_resource.set_gpu_backing(std::move(backing));
     } else if (pool_resource.gpu_backing()->returned_sync_token.HasData()) {
       context_provider->ContextGL()->WaitSyncTokenCHROMIUM(
@@ -287,8 +291,10 @@
 
     {
       ScopedGpuRaster gpu_raster(context_provider);
+      GLuint mailbox_texture_id =
+          gl->CreateAndConsumeTextureCHROMIUM(backing->mailbox.name);
       viz::ClientResourceProvider::ScopedSkSurface scoped_surface(
-          context_provider->GrContext(), backing->texture_id,
+          context_provider->GrContext(), mailbox_texture_id,
           backing->texture_target, pool_resource.size(), pool_resource.format(),
           false /* can_use_lcd_text */, 0 /* msaa_sample_count */);
       SkSurface* surface = scoped_surface.surface();
@@ -297,6 +303,7 @@
         return;
       }
       DrawHudContents(surface->getCanvas());
+      gl->DeleteTextures(1, &mailbox_texture_id);
     }
 
     backing->mailbox_sync_token =
@@ -322,12 +329,15 @@
     TRACE_EVENT0("cc", "UploadHudTexture");
     SkPixmap pixmap;
     staging_surface_->peekPixels(&pixmap);
-    gl->BindTexture(backing->texture_target, backing->texture_id);
+    GLuint mailbox_texture_id =
+        gl->CreateAndConsumeTextureCHROMIUM(backing->mailbox.name);
+    gl->BindTexture(backing->texture_target, mailbox_texture_id);
     DCHECK(GLSupportsFormat(pool_resource.format()));
     gl->TexSubImage2D(
         backing->texture_target, 0, 0, 0, pool_resource.size().width(),
         pool_resource.size().height(), GLDataFormat(pool_resource.format()),
         GLDataType(pool_resource.format()), pixmap.addr());
+    gl->DeleteTextures(1, &mailbox_texture_id);
     backing->mailbox_sync_token =
         viz::ClientResourceProvider::GenerateSyncTokenHelper(gl);
   } else {
diff --git a/cc/raster/gpu_raster_buffer_provider.cc b/cc/raster/gpu_raster_buffer_provider.cc
index a7df553..4b7f6b89 100644
--- a/cc/raster/gpu_raster_buffer_provider.cc
+++ b/cc/raster/gpu_raster_buffer_provider.cc
@@ -8,7 +8,6 @@
 
 #include <algorithm>
 
-#include "base/format_macros.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
@@ -31,7 +30,6 @@
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/client/raster_interface.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
-#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
 #include "gpu/command_buffer/common/shared_image_trace_utils.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "third_party/skia/include/core/SkMultiPictureDraw.h"
@@ -367,16 +365,9 @@
   if (!resource.gpu_backing()) {
     auto backing = std::make_unique<GpuRasterBacking>();
     backing->worker_context_provider = worker_context_provider_;
-    const auto& caps = compositor_context_provider_->ContextCapabilities();
-    backing->overlay_candidate =
-        use_gpu_memory_buffer_resources_ && caps.texture_storage_image &&
-        IsGpuMemoryBufferFormatSupported(resource.format());
-    if (backing->overlay_candidate) {
-      backing->texture_target = gpu::GetBufferTextureTarget(
-          gfx::BufferUsage::SCANOUT, BufferFormat(resource.format()), caps);
-    } else {
-      backing->texture_target = GL_TEXTURE_2D;
-    }
+    backing->InitOverlayCandidateAndTextureTarget(
+        resource.format(), compositor_context_provider_->ContextCapabilities(),
+        use_gpu_memory_buffer_resources_);
     resource.set_gpu_backing(std::move(backing));
   }
   GpuRasterBacking* backing =
diff --git a/cc/raster/one_copy_raster_buffer_provider.cc b/cc/raster/one_copy_raster_buffer_provider.cc
index 433d230e..5764e17 100644
--- a/cc/raster/one_copy_raster_buffer_provider.cc
+++ b/cc/raster/one_copy_raster_buffer_provider.cc
@@ -13,21 +13,23 @@
 #include "base/debug/alias.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/base/histograms.h"
 #include "cc/base/math_util.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/gpu/raster_context_provider.h"
-#include "components/viz/common/gpu/texture_allocation.h"
 #include "components/viz/common/resources/platform_color.h"
 #include "components/viz/common/resources/resource_format.h"
 #include "components/viz/common/resources/resource_sizes.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/context_support.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
 #include "gpu/command_buffer/client/raster_interface.h"
+#include "gpu/command_buffer/client/shared_image_interface.h"
+#include "gpu/command_buffer/common/shared_image_trace_utils.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gl/trace_util.h"
 
@@ -46,13 +48,13 @@
     : public ResourcePool::GpuBacking {
  public:
   ~OneCopyGpuBacking() override {
-    gpu::gles2::GLES2Interface* gl = compositor_context_provider->ContextGL();
+    if (mailbox.IsZero())
+      return;
+    auto* sii = worker_context_provider->SharedImageInterface();
     if (returned_sync_token.HasData())
-      gl->WaitSyncTokenCHROMIUM(returned_sync_token.GetConstData());
-    if (mailbox_sync_token.HasData())
-      gl->WaitSyncTokenCHROMIUM(mailbox_sync_token.GetConstData());
-    if (texture_id)
-      gl->DeleteTextures(1, &texture_id);
+      sii->DestroySharedImage(returned_sync_token, mailbox);
+    else if (mailbox_sync_token.HasData())
+      sii->DestroySharedImage(mailbox_sync_token, mailbox);
   }
 
   void OnMemoryDump(
@@ -60,23 +62,16 @@
       const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
       uint64_t tracing_process_id,
       int importance) const override {
-    if (!storage_allocated)
+    if (mailbox.IsZero())
       return;
 
-    auto texture_tracing_guid = gl::GetGLTextureClientGUIDForTracing(
-        compositor_context_provider->ContextSupport()->ShareGroupTracingGUID(),
-        texture_id);
-    pmd->CreateSharedGlobalAllocatorDump(texture_tracing_guid);
-    pmd->AddOwnershipEdge(buffer_dump_guid, texture_tracing_guid, importance);
+    auto tracing_guid = gpu::GetSharedImageGUIDForTracing(mailbox);
+    pmd->CreateSharedGlobalAllocatorDump(tracing_guid);
+    pmd->AddOwnershipEdge(buffer_dump_guid, tracing_guid, importance);
   }
 
-  // The ContextProvider used to clean up the texture id.
-  viz::ContextProvider* compositor_context_provider = nullptr;
-  // The texture backing of the resource.
-  GLuint texture_id = 0;
-  // The allocation of storage for the |texture_id| is deferred, and this tracks
-  // if it has been done.
-  bool storage_allocated = false;
+  // The ContextProvider used to clean up the mailbox
+  viz::RasterContextProvider* worker_context_provider = nullptr;
 };
 
 OneCopyRasterBufferProvider::RasterBufferImpl::RasterBufferImpl(
@@ -94,8 +89,7 @@
       before_raster_sync_token_(backing->returned_sync_token),
       mailbox_(backing->mailbox),
       mailbox_texture_target_(backing->texture_target),
-      mailbox_texture_is_overlay_candidate_(backing->overlay_candidate),
-      mailbox_texture_storage_allocated_(backing->storage_allocated) {}
+      mailbox_texture_is_overlay_candidate_(backing->overlay_candidate) {}
 
 OneCopyRasterBufferProvider::RasterBufferImpl::~RasterBufferImpl() {
   // This SyncToken was created on the worker context after uploading the
@@ -106,7 +100,7 @@
     // happened if the |after_raster_sync_token_| was set.
     backing_->returned_sync_token = gpu::SyncToken();
   }
-  backing_->storage_allocated = mailbox_texture_storage_allocated_;
+  backing_->mailbox = mailbox_;
 }
 
 void OneCopyRasterBufferProvider::RasterBufferImpl::Playback(
@@ -123,12 +117,10 @@
   // returns another SyncToken generated on the worker thread to synchronize
   // with after the raster is complete.
   after_raster_sync_token_ = client_->PlaybackAndCopyOnWorkerThread(
-      mailbox_, mailbox_texture_target_, mailbox_texture_is_overlay_candidate_,
-      mailbox_texture_storage_allocated_, before_raster_sync_token_,
-      raster_source, raster_full_rect, raster_dirty_rect, transform,
-      resource_size_, resource_format_, color_space_, playback_settings,
-      previous_content_id_, new_content_id);
-  mailbox_texture_storage_allocated_ = true;
+      &mailbox_, mailbox_texture_target_, mailbox_texture_is_overlay_candidate_,
+      before_raster_sync_token_, raster_source, raster_full_rect,
+      raster_dirty_rect, transform, resource_size_, resource_format_,
+      color_space_, playback_settings, previous_content_id_, new_content_id);
 }
 
 OneCopyRasterBufferProvider::OneCopyRasterBufferProvider(
@@ -171,24 +163,10 @@
     uint64_t previous_content_id) {
   if (!resource.gpu_backing()) {
     auto backing = std::make_unique<OneCopyGpuBacking>();
-    backing->compositor_context_provider = compositor_context_provider_;
-
-    gpu::gles2::GLES2Interface* gl = compositor_context_provider_->ContextGL();
-    const auto& caps = compositor_context_provider_->ContextCapabilities();
-
-    viz::TextureAllocation alloc = viz::TextureAllocation::MakeTextureId(
-        gl, caps, resource.format(), use_gpu_memory_buffer_resources_,
-        /*for_framebuffer_attachment=*/false);
-    backing->texture_id = alloc.texture_id;
-    backing->texture_target = alloc.texture_target;
-    backing->overlay_candidate = alloc.overlay_candidate;
-    gl->ProduceTextureDirectCHROMIUM(backing->texture_id,
-                                     backing->mailbox.name);
-    // Save a sync token in the backing so that we always wait on it even if
-    // this task is cancelled between being scheduled and running.
-    backing->returned_sync_token =
-        viz::ClientResourceProvider::GenerateSyncTokenHelper(gl);
-
+    backing->worker_context_provider = worker_context_provider_;
+    backing->InitOverlayCandidateAndTextureTarget(
+        resource.format(), compositor_context_provider_->ContextCapabilities(),
+        use_gpu_memory_buffer_resources_);
     resource.set_gpu_backing(std::move(backing));
   }
   OneCopyGpuBacking* backing =
@@ -273,10 +251,9 @@
 }
 
 gpu::SyncToken OneCopyRasterBufferProvider::PlaybackAndCopyOnWorkerThread(
-    const gpu::Mailbox& mailbox,
+    gpu::Mailbox* mailbox,
     GLenum mailbox_texture_target,
     bool mailbox_texture_is_overlay_candidate,
-    bool mailbox_texture_storage_allocated,
     const gpu::SyncToken& sync_token,
     const RasterSource* raster_source,
     const gfx::Rect& raster_full_rect,
@@ -300,8 +277,7 @@
   gpu::SyncToken sync_token_after_upload = CopyOnWorkerThread(
       staging_buffer.get(), raster_source, raster_full_rect, resource_format,
       resource_size, mailbox, mailbox_texture_target,
-      mailbox_texture_is_overlay_candidate, mailbox_texture_storage_allocated,
-      sync_token, color_space);
+      mailbox_texture_is_overlay_candidate, sync_token, color_space);
   staging_pool_.ReleaseStagingBuffer(std::move(staging_buffer));
   return sync_token_after_upload;
 }
@@ -385,10 +361,9 @@
     const gfx::Rect& rect_to_copy,
     viz::ResourceFormat resource_format,
     const gfx::Size& resource_size,
-    const gpu::Mailbox& mailbox,
+    gpu::Mailbox* mailbox,
     GLenum mailbox_texture_target,
     bool mailbox_texture_is_overlay_candidate,
-    bool mailbox_texture_storage_allocated,
     const gpu::SyncToken& sync_token,
     const gfx::ColorSpace& color_space) {
   viz::RasterContextProvider::ScopedRasterContextLock scoped_context(
@@ -396,21 +371,21 @@
   gpu::raster::RasterInterface* ri = scoped_context.RasterInterface();
   DCHECK(ri);
 
-  // Wait on the SyncToken that was created on the compositor thread after
-  // making the mailbox. This ensures that the mailbox we consume here is valid
-  // by the time the consume command executes.
-  ri->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+  if (mailbox->IsZero()) {
+    auto* sii = worker_context_provider_->SharedImageInterface();
+    uint32_t flags = gpu::SHARED_IMAGE_USAGE_RASTER;
+    if (mailbox_texture_is_overlay_candidate)
+      flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
+    *mailbox = sii->CreateSharedImage(resource_format, resource_size,
+                                      color_space, flags);
+    ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
+  } else {
+    ri->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+  }
+
   GLuint mailbox_texture_id = ri->CreateAndConsumeTexture(
       mailbox_texture_is_overlay_candidate, gfx::BufferUsage::SCANOUT,
-      resource_format, mailbox.name);
-
-  if (!mailbox_texture_storage_allocated) {
-    viz::TextureAllocation alloc = {mailbox_texture_id, mailbox_texture_target,
-                                    mailbox_texture_is_overlay_candidate};
-    viz::TextureAllocation::AllocateStorage(
-        ri, worker_context_provider_->ContextCapabilities(), resource_format,
-        resource_size, alloc, color_space);
-  }
+      resource_format, mailbox->name);
 
   // Create and bind staging texture.
   if (!staging_buffer->texture_id) {
diff --git a/cc/raster/one_copy_raster_buffer_provider.h b/cc/raster/one_copy_raster_buffer_provider.h
index e23dc8dd..98bb90e 100644
--- a/cc/raster/one_copy_raster_buffer_provider.h
+++ b/cc/raster/one_copy_raster_buffer_provider.h
@@ -61,10 +61,9 @@
 
   // Playback raster source and copy result into |resource|.
   gpu::SyncToken PlaybackAndCopyOnWorkerThread(
-      const gpu::Mailbox& mailbox,
+      gpu::Mailbox* mailbox,
       GLenum mailbox_texture_target,
       bool mailbox_texture_is_overlay_candidate,
-      bool mailbox_texture_storage_allocated,
       const gpu::SyncToken& sync_token,
       const RasterSource* raster_source,
       const gfx::Rect& raster_full_rect,
@@ -109,11 +108,9 @@
     const gfx::ColorSpace color_space_;
     const uint64_t previous_content_id_;
     const gpu::SyncToken before_raster_sync_token_;
-    const gpu::Mailbox mailbox_;
+    gpu::Mailbox mailbox_;
     const GLenum mailbox_texture_target_;
     const bool mailbox_texture_is_overlay_candidate_;
-    // Set to true once allocation is done in the worker thread.
-    bool mailbox_texture_storage_allocated_;
     // A SyncToken to be returned from the worker thread, and waited on before
     // using the rastered resource.
     gpu::SyncToken after_raster_sync_token_;
@@ -137,10 +134,9 @@
                                     const gfx::Rect& rect_to_copy,
                                     viz::ResourceFormat resource_format,
                                     const gfx::Size& resource_size,
-                                    const gpu::Mailbox& mailbox,
+                                    gpu::Mailbox* mailbox,
                                     GLenum mailbox_texture_target,
                                     bool mailbox_texture_is_overlay_candidate,
-                                    bool mailbox_texture_storage_allocated,
                                     const gpu::SyncToken& sync_token,
                                     const gfx::ColorSpace& color_space);
   gfx::BufferUsage StagingBufferUsage() const;
diff --git a/cc/resources/resource_pool.cc b/cc/resources/resource_pool.cc
index cf6939b..2d47472 100644
--- a/cc/resources/resource_pool.cc
+++ b/cc/resources/resource_pool.cc
@@ -25,6 +25,8 @@
 #include "components/viz/common/resources/resource_sizes.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
 
 using base::trace_event::MemoryAllocatorDump;
 using base::trace_event::MemoryDumpLevelOfDetail;
@@ -67,6 +69,21 @@
 
 constexpr base::TimeDelta ResourcePool::kDefaultExpirationDelay;
 
+void ResourcePool::GpuBacking::InitOverlayCandidateAndTextureTarget(
+    const viz::ResourceFormat format,
+    const gpu::Capabilities& caps,
+    bool use_gpu_memory_buffer_resources) {
+  overlay_candidate = use_gpu_memory_buffer_resources &&
+                      caps.texture_storage_image &&
+                      IsGpuMemoryBufferFormatSupported(format);
+  if (overlay_candidate) {
+    texture_target = gpu::GetBufferTextureTarget(gfx::BufferUsage::SCANOUT,
+                                                 BufferFormat(format), caps);
+  } else {
+    texture_target = GL_TEXTURE_2D;
+  }
+}
+
 ResourcePool::ResourcePool(
     viz::ClientResourceProvider* resource_provider,
     viz::ContextProvider* context_provider,
diff --git a/cc/resources/resource_pool.h b/cc/resources/resource_pool.h
index 0fe162d..5a66a4c9 100644
--- a/cc/resources/resource_pool.h
+++ b/cc/resources/resource_pool.h
@@ -34,6 +34,10 @@
 class SingleThreadTaskRunner;
 }
 
+namespace gpu {
+struct Capabilities;
+}
+
 namespace viz {
 class ClientResourceProvider;
 class ContextProvider;
@@ -68,6 +72,11 @@
         uint64_t tracing_process_id,
         int importance) const = 0;
 
+    void InitOverlayCandidateAndTextureTarget(
+        const viz::ResourceFormat format,
+        const gpu::Capabilities& caps,
+        bool use_gpu_memory_buffer_resources);
+
     gpu::Mailbox mailbox;
     gpu::SyncToken mailbox_sync_token;
     GLenum texture_target = 0;
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index cc5f72b..806a6d8 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -728,6 +728,8 @@
     "//content/test/data/media/video-player.html",
     "//content/test/data/media/webrtc_test_utilities.js",
     "//media/test/data/bear.mp4",
+    "//media/test/data/bear-vp8-webvtt.webm",
+    "//media/test/data/bear-vp8a.webm",
     "//media/test/data/sfx.mp3",
   ]
 }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java
new file mode 100644
index 0000000..d2293df
--- /dev/null
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java
@@ -0,0 +1,190 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.feed;
+
+import android.app.Activity;
+import android.support.annotation.IntDef;
+
+import com.google.android.libraries.feed.api.lifecycle.AppLifecycleListener;
+
+import org.chromium.base.ActivityState;
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.signin.SigninManager;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * Aggregation point for application lifecycle events that the Feed cares about. Events that
+ * originate in Java flow directly to FeedAppLifecycle, while native-originating events arrive
+ * via {@link FeedLifecycleBridge}.
+ */
+public class FeedAppLifecycle
+        implements SigninManager.SignInStateObserver, ApplicationStatus.ActivityStateListener {
+    @IntDef({AppLifecycleEvent.ENTER_FOREGROUND, AppLifecycleEvent.ENTER_BACKGROUND,
+            AppLifecycleEvent.CLEAR_ALL, AppLifecycleEvent.INITIALIZE,
+            AppLifecycleEvent.NUM_ENTRIES})
+
+    // Intdef used to assign each event a number for metrics logging purposes. This maps directly to
+    // the AppLifecycleEvent enum defined in tools/metrics/enums.xml
+    public @interface AppLifecycleEvent {
+        int ENTER_FOREGROUND = 0;
+        int ENTER_BACKGROUND = 1;
+        int CLEAR_ALL = 2;
+        int INITIALIZE = 3;
+        int NUM_ENTRIES = 4;
+    }
+
+    private AppLifecycleListener mAppLifecycleListener;
+    private FeedLifecycleBridge mLifecycleBridge;
+    private FeedScheduler mFeedScheduler;
+
+    private int mTabbedActivityCount;
+    private boolean mInitializeCalled;
+
+    /**
+     * Create a FeedAppLifecycle instance. In normal use, this should only be called by {@link
+     * FeedAppLifecycleFactory}.
+     * @param appLifecycleListener The Feed-side instance of the {@link AppLifecycleListener}
+     *        interface that we will call into.
+     * @param lifecycleBridge FeedLifecycleBridge JNI bridge over which native lifecycle events are
+     *        delivered.
+     */
+    public FeedAppLifecycle(AppLifecycleListener appLifecycleListener,
+            FeedLifecycleBridge lifecycleBridge, FeedScheduler feedScheduler) {
+        mAppLifecycleListener = appLifecycleListener;
+        mLifecycleBridge = lifecycleBridge;
+        mFeedScheduler = feedScheduler;
+
+        int resumedActivityCount = 0;
+        List<WeakReference<Activity>> activities = ApplicationStatus.getRunningActivities();
+        for (final WeakReference<Activity> ref : activities) {
+            final Activity activity = ref.get();
+            if (activity != null && activity instanceof ChromeTabbedActivity) {
+                @ActivityState
+                int activityState = ApplicationStatus.getStateForActivity(activity);
+                if (activityState != ActivityState.STOPPED) {
+                    ++mTabbedActivityCount;
+                }
+                if (activityState == ActivityState.RESUMED) {
+                    ++resumedActivityCount;
+                }
+            }
+        }
+
+        if (mTabbedActivityCount > 0) {
+            onEnterForeground();
+        }
+        // The scheduler cares about Chrome entering the visual foreground, which corresponds to the
+        // RESUMED state. This state is entered regardless of whether or not the Activity was
+        // previously paused.
+        if (resumedActivityCount > 0) {
+            mFeedScheduler.onForegrounded();
+        }
+
+        SigninManager.get().addSignInStateObserver(this);
+        ApplicationStatus.registerStateListenerForAllActivities(this);
+    }
+
+    /**
+     * This is called when an NTP is shown.
+     */
+    public void onNTPOpened() {
+        initialize();
+    }
+
+    /**
+     * This is called when the user has deleted some non-trivial number of history entries.
+     * We call onClearAll to avoid presenting personalized suggestions based on deleted history.
+     */
+    public void onHistoryDeleted() {
+        onClearAll();
+    }
+
+    /**
+     * This is called when cached browsing data is cleared. We call onClearAll so that the
+     * Feed deletes its cached browsing data.
+     */
+    public void onCachedDataCleared() {
+        onClearAll();
+    }
+
+    /**
+     * Unregisters listeners and cleans up any native resources held by FeedAppLifecycle.
+     */
+    public void destroy() {
+        SigninManager.get().removeSignInStateObserver(this);
+        ApplicationStatus.unregisterActivityStateListener(this);
+        mLifecycleBridge.destroy();
+        mLifecycleBridge = null;
+        mAppLifecycleListener = null;
+        mFeedScheduler = null;
+    }
+
+    @Override
+    public void onActivityStateChange(Activity activity, @ActivityState int newState) {
+        // We only care about ChromeTabbedActivity since no other type of activity could potentially
+        // show the Feed.
+        if (activity != null && activity instanceof ChromeTabbedActivity) {
+            switch (newState) {
+                case ActivityState.STOPPED:
+                    --mTabbedActivityCount;
+                    if (mTabbedActivityCount == 0) {
+                        onEnterBackground();
+                    }
+                    break;
+                case ActivityState.STARTED:
+                    ++mTabbedActivityCount;
+                    if (mTabbedActivityCount == 1) {
+                        onEnterForeground();
+                    }
+                    break;
+                case ActivityState.RESUMED:
+                    mFeedScheduler.onForegrounded();
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public void onSignedIn() {
+        onClearAll();
+    }
+
+    @Override
+    public void onSignedOut() {
+        onClearAll();
+    }
+
+    private void onEnterForeground() {
+        reportEvent(AppLifecycleEvent.ENTER_FOREGROUND);
+        mAppLifecycleListener.onEnterForeground();
+    }
+
+    private void onEnterBackground() {
+        reportEvent(AppLifecycleEvent.ENTER_BACKGROUND);
+        mAppLifecycleListener.onEnterBackground();
+    }
+
+    private void onClearAll() {
+        reportEvent(AppLifecycleEvent.CLEAR_ALL);
+        mAppLifecycleListener.onClearAll();
+    }
+
+    private void initialize() {
+        if (!mInitializeCalled) {
+            reportEvent(AppLifecycleEvent.INITIALIZE);
+            mAppLifecycleListener.initialize();
+            mInitializeCalled = true;
+        }
+    }
+
+    private void reportEvent(@AppLifecycleEvent int event) {
+        RecordHistogram.recordEnumeratedHistogram(
+                "ContentSuggestions.Feed.AppLifecycleEvents", event, AppLifecycleEvent.NUM_ENTRIES);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedEventReporter.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedEventReporter.java
deleted file mode 100644
index 1f7f81b..0000000
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedEventReporter.java
+++ /dev/null
@@ -1,19 +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.
-
-package org.chromium.chrome.browser.feed;
-
-/** Provides static entry points to send notifications to Feed. */
-public final class FeedEventReporter {
-    /** Not meant to be instantiated. */
-    private FeedEventReporter() {}
-
-    /** Should be called when the browser is foregrounded. */
-    public static void onBrowserForegrounded() {
-        FeedScheduler scheduler = FeedProcessScopeFactory.getFeedScheduler();
-        if (scheduler != null) {
-            scheduler.onForegrounded();
-        }
-    }
-}
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLifecycleBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLifecycleBridge.java
new file mode 100644
index 0000000..92130af
--- /dev/null
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLifecycleBridge.java
@@ -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.
+
+package org.chromium.chrome.browser.feed;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.chrome.browser.profiles.Profile;
+
+/**
+ * Java-side bridge that receives native-initiated lifecycle events and forwards them to
+ * FeedAppLifecycle. These events include, e.g., cached data clearing and history deletion.
+ */
+@JNINamespace("feed")
+public class FeedLifecycleBridge {
+    private long mNativeBridge;
+
+    /**
+     * Creates a FeedLifecycleBridge, which is responsible for receiving and forwarding native
+     * lifecycle events.
+     * @param profile Profile of the user whose lifecycle events we care about.
+     */
+    public FeedLifecycleBridge(Profile profile) {
+        mNativeBridge = nativeInit(profile);
+    }
+
+    /*
+     * Cleans up native half of this bridge.
+     */
+    public void destroy() {
+        assert mNativeBridge != 0;
+        nativeDestroy(mNativeBridge);
+        mNativeBridge = 0;
+    }
+
+    @CalledByNative
+    private static void onCachedDataCleared() {
+        FeedProcessScopeFactory.getFeedAppLifecycle().onCachedDataCleared();
+    }
+
+    // This would ordinarily be non-static, but there's no instance state, so for simplicity it's
+    // isomorphic to onCachedDataCleared.
+    @CalledByNative
+    private static void onHistoryDeleted() {
+        FeedProcessScopeFactory.getFeedAppLifecycle().onHistoryDeleted();
+    }
+
+    private native long nativeInit(Profile profile);
+    private native void nativeDestroy(long nativeFeedLifecycleBridge);
+}
\ No newline at end of file
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
index d5890f0..7cc262c 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -18,11 +18,13 @@
 import android.widget.FrameLayout;
 import android.widget.ScrollView;
 
+import com.google.android.libraries.feed.api.common.ThreadUtils;
 import com.google.android.libraries.feed.api.scope.FeedProcessScope;
 import com.google.android.libraries.feed.api.scope.FeedStreamScope;
 import com.google.android.libraries.feed.api.stream.Header;
 import com.google.android.libraries.feed.api.stream.NonDismissibleHeader;
 import com.google.android.libraries.feed.api.stream.Stream;
+import com.google.android.libraries.feed.feedapplifecyclelistener.FeedAppLifecycleListener;
 import com.google.android.libraries.feed.host.action.ActionApi;
 import com.google.android.libraries.feed.host.stream.CardConfiguration;
 import com.google.android.libraries.feed.host.stream.SnackbarApi;
@@ -300,6 +302,9 @@
         FeedProcessScope feedProcessScope = FeedProcessScopeFactory.getFeedProcessScope();
         assert feedProcessScope != null;
 
+        FeedAppLifecycle appLifecycle = FeedProcessScopeFactory.getFeedAppLifecycle();
+        appLifecycle.onNTPOpened();
+
         Activity activity = mTab.getActivity();
         Profile profile = mTab.getProfile();
 
@@ -423,8 +428,15 @@
     @VisibleForTesting
     public static void setInTestMode(boolean inTestMode) {
         if (inTestMode) {
-            FeedProcessScopeFactory.createFeedProcessScopeForTesting(new TestFeedScheduler(),
-                    new TestNetworkClient(), new TestFeedOfflineIndicator());
+            FeedScheduler feedScheduler = new TestFeedScheduler();
+            FeedAppLifecycleListener lifecycleListener =
+                    new FeedAppLifecycleListener(new ThreadUtils());
+            Profile profile = Profile.getLastUsedProfile().getOriginalProfile();
+            FeedAppLifecycle feedAppLifecycle = new FeedAppLifecycle(
+                    lifecycleListener, new FeedLifecycleBridge(profile), feedScheduler);
+            FeedProcessScopeFactory.createFeedProcessScopeForTesting(feedScheduler,
+                    new TestNetworkClient(), new TestFeedOfflineIndicator(), feedAppLifecycle,
+                    lifecycleListener);
         } else {
             FeedProcessScopeFactory.clearFeedProcessScopeForTesting();
         }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
index e489ab4..eece2ac 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
@@ -29,6 +29,7 @@
     private static boolean sIsDisableForPolicy =
             !PrefServiceBridge.getInstance().getBoolean(Pref.NTP_ARTICLES_SECTION_ENABLED);
     private static PrefChangeRegistrar sPrefChangeRegistrar;
+    private static FeedAppLifecycle sFeedAppLifecycle;
     private static FeedProcessScope sFeedProcessScope;
     private static FeedScheduler sFeedScheduler;
     private static FeedOfflineIndicator sFeedOfflineIndicator;
@@ -60,6 +61,17 @@
         return sFeedOfflineIndicator;
     }
 
+    /*
+     * @return The global instance of {@link FeedAppLifecycle} for the process.
+     *         Null if the Feed is disabled.
+     */
+    public static @Nullable FeedAppLifecycle getFeedAppLifecycle() {
+        if (sFeedAppLifecycle == null) {
+            initialize();
+        }
+        return sFeedAppLifecycle;
+    }
+
     /**
      * @return Whether the dependencies provided by this class are allowed to be created. The feed
      *         process is disabled if supervised user or enterprise policy has once been added
@@ -71,7 +83,8 @@
     }
 
     private static void initialize() {
-        assert sFeedProcessScope == null && sFeedScheduler == null && sFeedOfflineIndicator == null;
+        assert sFeedProcessScope == null && sFeedScheduler == null && sFeedOfflineIndicator == null
+                && sFeedAppLifecycle == null;
         if (!isFeedProcessEnabled()) return;
 
         sPrefChangeRegistrar = new PrefChangeRegistrar();
@@ -103,6 +116,9 @@
 
         sFeedOfflineIndicator =
                 new FeedOfflineBridge(profile, sFeedProcessScope.getKnownContentApi());
+
+        sFeedAppLifecycle = new FeedAppLifecycle(sFeedProcessScope.getAppLifecycleListener(),
+                new FeedLifecycleBridge(profile), sFeedScheduler);
     }
 
     private static Configuration createConfiguration() {
@@ -124,10 +140,9 @@
      */
     @VisibleForTesting
     static void createFeedProcessScopeForTesting(FeedScheduler feedScheduler,
-            NetworkClient networkClient, FeedOfflineIndicator feedOfflineIndicator) {
+            NetworkClient networkClient, FeedOfflineIndicator feedOfflineIndicator,
+            FeedAppLifecycle feedAppLifecycle, FeedAppLifecycleListener lifecycleListener) {
         Configuration configHostApi = createConfiguration();
-        FeedAppLifecycleListener lifecycleListener =
-                new FeedAppLifecycleListener(new ThreadUtils());
         sFeedScheduler = feedScheduler;
         sFeedProcessScope = new FeedProcessScope
                                     .Builder(configHostApi, Executors.newSingleThreadExecutor(),
@@ -136,6 +151,7 @@
                                             ContextUtils.getApplicationContext())
                                     .build();
         sFeedOfflineIndicator = feedOfflineIndicator;
+        sFeedAppLifecycle = feedAppLifecycle;
     }
 
     /** Use supplied NetworkClient instead of real one, for tests. */
@@ -184,5 +200,9 @@
             sFeedOfflineIndicator.destroy();
             sFeedOfflineIndicator = null;
         }
+        if (sFeedAppLifecycle != null) {
+            sFeedAppLifecycle.destroy();
+            sFeedAppLifecycle = null;
+        }
     }
 }
diff --git a/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java b/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java
new file mode 100644
index 0000000..289bbf2
--- /dev/null
+++ b/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java
@@ -0,0 +1,21 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.feed;
+
+/**
+ * Aggregation point for application lifecycle events that the Feed cares about.
+ */
+public class FeedAppLifecycle {
+    /**
+     * Create a FeedAppLifecycle instance. In normal use, this should only be called by {@link
+     * FeedAppLifecycleFactory}.
+     */
+    public FeedAppLifecycle() {}
+
+    /**
+     * This is called when a tabbed activity is launched.
+     */
+    public void onActivityLaunched() {}
+}
diff --git a/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedEventReporter.java b/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedEventReporter.java
deleted file mode 100644
index 07733c2..0000000
--- a/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedEventReporter.java
+++ /dev/null
@@ -1,18 +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.
-
-package org.chromium.chrome.browser.feed;
-
-/**
- * Provides static entry points to send notifications to Feed.
- */
-public final class FeedEventReporter {
-    // Not meant to be instantiated.
-    private FeedEventReporter() {}
-
-    /*
-     * Should be called when the browser is foregrounded.
-     */
-    public static void onBrowserForegrounded() {}
-}
diff --git a/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java b/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
new file mode 100644
index 0000000..59c36386
--- /dev/null
+++ b/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.feed;
+
+/**
+ * Vends dummy Feed host implementations.
+ */
+public class FeedProcessScopeFactory {
+    private static FeedAppLifecycle sFeedAppLifecycle;
+
+    /**
+     * @return A dummy {@link FeedAppLifecycle} instance.
+     */
+    public static FeedAppLifecycle getFeedAppLifecycle() {
+        return new FeedAppLifecycle();
+    }
+}
diff --git a/chrome/android/feed/feed_java_sources.gni b/chrome/android/feed/feed_java_sources.gni
index 2b4debf..31067a2 100644
--- a/chrome/android/feed/feed_java_sources.gni
+++ b/chrome/android/feed/feed_java_sources.gni
@@ -9,14 +9,15 @@
   feed_deps = [ "//third_party/feed:feed_lib_java" ]
 
   feed_java_sources = [
+    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedBasicLogging.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentBridge.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentStorage.java",
-    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedEventReporter.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoader.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoaderBridge.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalStorage.java",
+    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLifecycleBridge.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNetworkBridge.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java",
@@ -46,6 +47,7 @@
   ]
 
   feed_test_java_sources = [
+    "//chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java",
     "//chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNetworkBridgeConformanceTest.java",
     "//chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java",
     "//chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedSchedulerBridgeConformanceTest.java",
@@ -59,8 +61,9 @@
 } else {
   feed_deps = []
   feed_java_sources = [
+    "//chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java",
     "//chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java",
-    "//chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedEventReporter.java",
+    "//chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java",
   ]
   feed_srcjar_deps = []
 }
diff --git a/chrome/android/java/res/layout/toolbar_phone_incognito_button.xml b/chrome/android/java/res/layout/incognito_toggle_tabs.xml
similarity index 79%
rename from chrome/android/java/res/layout/toolbar_phone_incognito_button.xml
rename to chrome/android/java/res/layout/incognito_toggle_tabs.xml
index 4c86eb3c..eb61a6e5 100644
--- a/chrome/android/java/res/layout/toolbar_phone_incognito_button.xml
+++ b/chrome/android/java/res/layout/incognito_toggle_tabs.xml
@@ -4,5 +4,5 @@
      found in the LICENSE file. -->
 
 <!-- The incognito toggle toolbar button. -->
-<org.chromium.chrome.browser.widget.incognitotoggle.IncognitoToggleButton
+<org.chromium.chrome.browser.toolbar.IncognitoToggleTabLayout
     style="@style/ToolbarButton" />
diff --git a/chrome/android/java/res/layout/toolbar_phone.xml b/chrome/android/java/res/layout/toolbar_phone.xml
index 30a5dce..1751858c 100644
--- a/chrome/android/java/res/layout/toolbar_phone.xml
+++ b/chrome/android/java/res/layout/toolbar_phone.xml
@@ -38,6 +38,14 @@
         android:layout_height="match_parent"
         android:layout_gravity="top" />
 
+    <ViewStub
+        android:id="@+id/incognito_tabs_stub"
+        android:inflatedId="@+id/incognito_toggle_tabs"
+        android:layout="@layout/incognito_toggle_tabs"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="center" />
+
     <LinearLayout android:id="@+id/toolbar_buttons"
         android:orientation="horizontal"
         android:layout_width="wrap_content"
@@ -55,14 +63,6 @@
                 style="@style/ToolbarButton"
                 android:paddingStart="8dp"
                 android:visibility="gone" />
-
-            <ViewStub
-                android:id="@+id/incognito_button_stub"
-                android:inflatedId="@+id/incognito_button"
-                android:layout="@layout/toolbar_phone_incognito_button"
-                style="@style/ToolbarButton"
-                android:contentDescription="@string/accessibility_tabstrip_btn_incognito_toggle_standard"
-                android:visibility="gone" />
         </FrameLayout>
 
         <ImageButton android:id="@+id/tab_switcher_button"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index bb9a6d4..a90be424 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -78,7 +78,7 @@
 import org.chromium.chrome.browser.feature_engagement.ScreenshotMonitorDelegate;
 import org.chromium.chrome.browser.feature_engagement.ScreenshotTabObserver;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
-import org.chromium.chrome.browser.feed.FeedEventReporter;
+import org.chromium.chrome.browser.feed.FeedProcessScopeFactory;
 import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor;
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
@@ -625,6 +625,12 @@
                 ToolbarButtonInProductHelpController.maybeShowColdStartIPH(this);
             }
 
+            if (ChromeFeatureList.isEnabled(ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS)) {
+                // We call getFeedAppLifecycle() here to ensure the app lifecycle is created so that
+                // it can start listening for state changes.
+                FeedProcessScopeFactory.getFeedAppLifecycle();
+            }
+
             super.finishNativeInitialization();
         } finally {
             TraceEvent.end("ChromeTabbedActivity.finishNativeInitialization");
@@ -670,15 +676,14 @@
         mLocaleManager.setSnackbarManager(getSnackbarManager());
         mLocaleManager.startObservingPhoneChanges();
 
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS)) {
-            FeedEventReporter.onBrowserForegrounded();
-        } else {
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS)) {
             if (isWarmOnResume()) {
                 SuggestionsEventReporterBridge.onActivityWarmResumed();
             } else {
                 SuggestionsEventReporterBridge.onColdStart();
             }
         }
+
         if (!isWarmOnResume()) {
             SuggestionsMetrics.recordArticlesListVisible();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java
index 9f0a554c..97b4f2d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java
@@ -36,6 +36,14 @@
         nativeGetIcon(profile, siteID, callback);
     }
 
+    public static void getCategoryImage(
+            Profile profile, int categoryID, int pixelSize, Callback<Bitmap> callback) {
+        // TODO(dewittj): Remove this when image decoding works correctly.
+        Bitmap image = Bitmap.createBitmap(pixelSize, pixelSize, Bitmap.Config.ARGB_8888);
+        image.eraseColor(android.graphics.Color.GREEN);
+        callback.onResult(image);
+    }
+
     /**
      * Causes a network request for updating the catalog.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategory.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategory.java
index 26dda12..0e1bb3f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategory.java
@@ -8,10 +8,13 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.IntDef;
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.browser.UrlConstants;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -22,7 +25,41 @@
     // The ID to use when creating the More button, that should not scroll the ESP when clicked.
     public static final int MORE_BUTTON_ID = -1;
 
+    // These constants should be kept in sync with
+    // //chrome/browser/android/explore_sites/catalog.proto's CategoryType.
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({CategoryType.DEFAULT, CategoryType.SOCIAL, CategoryType.ENTERTAINMENT,
+            CategoryType.SPORT, CategoryType.NEWS, CategoryType.SHOPPING, CategoryType.REFERENCE,
+            CategoryType.BANKING, CategoryType.GOVERNMENT, CategoryType.TRAVEL,
+            CategoryType.EDUCATION, CategoryType.JOBS, CategoryType.APPS_GAMES,
+            CategoryType.FAVORITE, CategoryType.GOOGLE, CategoryType.FOOD, CategoryType.HEALTH,
+            CategoryType.BOOKS, CategoryType.TECHNOLOGY, CategoryType.SCIENCE})
+    public @interface CategoryType {
+        int DEFAULT = 0;
+        int SOCIAL = 1;
+        int ENTERTAINMENT = 2;
+        int SPORT = 3;
+        int NEWS = 4;
+        int SHOPPING = 5;
+        int REFERENCE = 6;
+        int BANKING = 7;
+        int GOVERNMENT = 8;
+        int TRAVEL = 9;
+        int EDUCATION = 10;
+        int JOBS = 11;
+        int APPS_GAMES = 12;
+        int FAVORITE = 13;
+        int GOOGLE = 14;
+        int FOOD = 15;
+        int HEALTH = 16;
+        int BOOKS = 17;
+        int TECHNOLOGY = 18;
+        int SCIENCE = 19;
+    }
+
     private int mCategoryId;
+
+    private @CategoryType int mCategoryType;
     private String mCategoryTitle;
 
     // Populated only in NTP.
@@ -36,8 +73,9 @@
      *   proto, or -1 if this represents the More button.
      * @param title The string to display as the caption for this tile.
      */
-    public ExploreSitesCategory(int categoryId, String title) {
+    public ExploreSitesCategory(int categoryId, @CategoryType int categoryType, String title) {
         mCategoryId = categoryId;
+        mCategoryType = categoryType;
         mCategoryTitle = title;
         mSites = new ArrayList<>();
     }
@@ -45,6 +83,9 @@
     public int getId() {
         return mCategoryId;
     }
+    public @CategoryType int getType() {
+        return mCategoryType;
+    }
 
     public String getTitle() {
         return mCategoryTitle;
@@ -84,8 +125,8 @@
     // easily append sites to the category.
     @CalledByNative
     private static ExploreSitesCategory createAndAppendToList(
-            int categoryId, String title, List<ExploreSitesCategory> list) {
-        ExploreSitesCategory category = new ExploreSitesCategory(categoryId, title);
+            int categoryId, int categoryType, String title, List<ExploreSitesCategory> list) {
+        ExploreSitesCategory category = new ExploreSitesCategory(categoryId, categoryType, title);
         list.add(category);
         return category;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryTileView.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryTileView.java
index 5cf40427..6be3fb0f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryTileView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryTileView.java
@@ -6,7 +6,10 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.ImageView;
 
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ntp.TitleUtil;
 import org.chromium.chrome.browser.widget.tile.TileWithTextView;
 
@@ -37,6 +40,19 @@
         super.initialize(TitleUtil.getTitleForDisplay(category.getTitle(), category.getUrl()),
                 SUPPORTED_OFFLINE, category.getDrawable(), TITLE_LINES);
         mCategory = category;
+
+        // Correct the properties of the icon for categories, it should be the entire size of the
+        // icon background now.
+        ImageView tileViewIcon = (ImageView) findViewById(R.id.tile_view_icon);
+        tileViewIcon.setScaleType(ImageView.ScaleType.CENTER);
+        MarginLayoutParams layoutParams = (MarginLayoutParams) tileViewIcon.getLayoutParams();
+        int tileViewIconSize =
+                getContext().getResources().getDimensionPixelSize(R.dimen.tile_view_icon_size);
+        layoutParams.width = tileViewIconSize;
+        layoutParams.height = tileViewIconSize;
+        layoutParams.topMargin = getContext().getResources().getDimensionPixelSize(
+                R.dimen.tile_view_icon_background_margin_top_modern);
+        tileViewIcon.setLayoutParams(layoutParams);
     }
 
     /** Retrieves url associated with this view. */
@@ -44,6 +60,10 @@
         return mCategory.getUrl();
     }
 
+    public ExploreSitesCategory getCategory() {
+        return mCategory;
+    }
+
     /** Renders icon based on tile data.  */
     public void renderIcon(ExploreSitesCategory category) {
         mCategory = category;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java
index 79d3f36..b573acc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java
@@ -5,12 +5,14 @@
 package org.chromium.chrome.browser.explore_sites;
 
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.support.graphics.drawable.VectorDrawableCompat;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.explore_sites.ExploreSitesCategory.CategoryType;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle;
@@ -20,7 +22,9 @@
 import org.chromium.ui.mojom.WindowOpenDisposition;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Describes a portion of UI responsible for rendering a group of categories.
@@ -29,11 +33,6 @@
 public class ExploreSitesSection {
     private static final int MAX_CATEGORIES = 3;
 
-    /** These should be kept in sync with //chrome/browser/android/explore_sites/catalog.proto */
-    private static final int SPORT = 3;
-    private static final int SHOPPING = 5;
-    private static final int FOOD = 15;
-
     @TileStyle
     private int mStyle;
     private Profile mProfile;
@@ -52,7 +51,7 @@
     }
 
     private void initialize() {
-        ExploreSitesBridge.getEspCatalog(mProfile, this ::initializeCategoryTiles);
+        ExploreSitesBridge.getEspCatalog(mProfile, this::gotEspCatalog);
     }
 
     private Drawable getVectorDrawable(int resource) {
@@ -72,27 +71,28 @@
         List<ExploreSitesCategory> categoryList = new ArrayList<>();
 
         // Sport category.
-        ExploreSitesCategory category = new ExploreSitesCategory(
-                SPORT, getContext().getString(R.string.explore_sites_default_category_sports));
+        ExploreSitesCategory category =
+                new ExploreSitesCategory(-1 /* category_id */, CategoryType.SPORT,
+                        getContext().getString(R.string.explore_sites_default_category_sports));
         category.setDrawable(getVectorDrawable(R.drawable.ic_directions_run_blue_24dp));
         categoryList.add(category);
 
         // Shopping category.
-        category = new ExploreSitesCategory(
-                SHOPPING, getContext().getString(R.string.explore_sites_default_category_shopping));
+        category = new ExploreSitesCategory(-1 /* category_id */, CategoryType.SHOPPING,
+                getContext().getString(R.string.explore_sites_default_category_shopping));
         category.setDrawable(getVectorDrawable(R.drawable.ic_shopping_basket_blue_24dp));
         categoryList.add(category);
 
         // Food category.
-        category = new ExploreSitesCategory(
-                FOOD, getContext().getString(R.string.explore_sites_default_category_cooking));
+        category = new ExploreSitesCategory(-1 /* category_id */, CategoryType.FOOD,
+                getContext().getString(R.string.explore_sites_default_category_cooking));
         category.setDrawable(getVectorDrawable(R.drawable.ic_restaurant_menu_blue_24dp));
         categoryList.add(category);
         return categoryList;
     }
 
     private ExploreSitesCategory createMoreTileCategory() {
-        ExploreSitesCategory category = new ExploreSitesCategory(
+        ExploreSitesCategory category = new ExploreSitesCategory(-1 /* category_id */,
                 ExploreSitesCategory.MORE_BUTTON_ID, getContext().getString(R.string.more));
         category.setDrawable(getVectorDrawable(R.drawable.ic_arrow_forward_blue_24dp));
         return category;
@@ -114,9 +114,55 @@
         tileView.setOnClickListener((View v) -> onClicked(category, v));
     }
 
+    /**
+     * Checks the result, if it indicates that we don't have a valid catalog, request one from the
+     * network.  If the network request fails, just continue but otherwise retry getting the catalog
+     * from the ExploreSitesBridge.
+     */
+    private void gotEspCatalog(List<ExploreSitesCategory> categoryList) {
+        if (categoryList == null || categoryList.size() == 0) {
+            ExploreSitesBridge.updateCatalogFromNetwork(
+                    mProfile, (Boolean success) -> { updateCategoryIcons(); });
+        }
+        // Initialize with defaults right away.
+        initializeCategoryTiles(categoryList);
+    }
+
+    private void updateCategoryIcons() {
+        Map<Integer, ExploreSitesCategoryTileView> viewTypes = new HashMap<>();
+        for (int i = 0; i < mExploreSection.getChildCount(); i++) {
+            ExploreSitesCategoryTileView v =
+                    (ExploreSitesCategoryTileView) mExploreSection.getChildAt(i);
+            ExploreSitesCategory category = v.getCategory();
+            if (category == null || category.getType() == ExploreSitesCategory.MORE_BUTTON_ID)
+                continue;
+            viewTypes.put(category.getType(), v);
+        }
+
+        ExploreSitesBridge.getEspCatalog(mProfile, (List<ExploreSitesCategory> categoryList) -> {
+            for (ExploreSitesCategory category : categoryList) {
+                ExploreSitesCategoryTileView v = viewTypes.get(category.getType());
+                if (v == null) {
+                    continue;
+                }
+                ExploreSitesBridge.getCategoryImage(mProfile, category.getId(),
+                        v.getContext().getResources().getDimensionPixelSize(
+                                R.dimen.tile_view_icon_size),
+                        (Bitmap image) -> {
+                            if (image != null) {
+                                category.setIcon(mExploreSection.getContext(), image);
+                                v.renderIcon(category);
+                            }
+                        });
+            }
+        });
+    }
+
     private void initializeCategoryTiles(List<ExploreSitesCategory> categoryList) {
+        boolean needIcons = true;
         if (categoryList == null || categoryList.size() == 0) {
             categoryList = createDefaultCategoryTiles();
+            needIcons = false; // Icons are already prepared in the default tiles.
         }
 
         int tileCount = 0;
@@ -126,6 +172,9 @@
             createTileView(category);
         }
         createTileView(createMoreTileCategory());
+        if (needIcons) {
+            updateCategoryIcons();
+        }
     }
 
     private void onClicked(ExploreSitesCategory category, View v) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index bb77bdb..092c7efa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -1672,6 +1672,7 @@
     }
 
     private void maybeShowPreviewVerboseStatusInProductHelp(final Tracker tracker) {
+        if (!isPreview()) return;
         if (!(getActivity() instanceof ChromeTabbedActivity)) return;
         final View anchorView = getActivity().getToolbarManager().getSecurityIconView();
         if (anchorView == null) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/IncognitoToggleTabLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/IncognitoToggleTabLayout.java
new file mode 100644
index 0000000..99a9d5a
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/IncognitoToggleTabLayout.java
@@ -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.
+
+package org.chromium.chrome.browser.toolbar;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.support.design.widget.TabLayout;
+import android.support.v7.content.res.AppCompatResources;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.widget.TintedImageView;
+
+/**
+ * TabLayout shown in the Horizontal Tab Switcher.
+ */
+public class IncognitoToggleTabLayout extends TabLayout {
+    private TabLayout.Tab mStandardButton;
+    private TabLayout.Tab mIncognitoButton;
+    private TintedImageView mStandardButtonIcon;
+    private TintedImageView mIncognitoButtonIcon;
+    private TabSwitcherDrawable mTabSwitcherDrawable;
+
+    private ColorStateList mTabIconDarkColor;
+    private ColorStateList mTabIconLightColor;
+    private ColorStateList mTabIconSelectedDarkColor;
+    private ColorStateList mTabIconSelectedLightColor;
+
+    private TabModelSelector mTabModelSelector;
+
+    /**
+     * Constructor for inflating from XML.
+     */
+    public IncognitoToggleTabLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mTabIconDarkColor =
+                AppCompatResources.getColorStateList(getContext(), R.color.dark_mode_tint);
+        mTabIconSelectedDarkColor =
+                AppCompatResources.getColorStateList(getContext(), R.color.light_active_color);
+        mTabIconLightColor =
+                AppCompatResources.getColorStateList(getContext(), R.color.white_alpha_70);
+        mTabIconSelectedLightColor =
+                AppCompatResources.getColorStateList(getContext(), R.color.white_mode_tint);
+
+        mStandardButtonIcon = new TintedImageView(getContext());
+        mTabSwitcherDrawable = TabSwitcherDrawable.createTabSwitcherDrawable(getContext(), false);
+        mStandardButtonIcon.setImageDrawable(mTabSwitcherDrawable);
+        mIncognitoButtonIcon = new TintedImageView(getContext());
+        mIncognitoButtonIcon.setImageResource(R.drawable.incognito_simple);
+
+        mStandardButton =
+                newTab().setCustomView(mStandardButtonIcon)
+                        .setContentDescription(R.string.accessibility_tab_switcher_standard_stack);
+        addTab(mStandardButton);
+        mIncognitoButton =
+                newTab().setCustomView(mIncognitoButtonIcon)
+                        .setContentDescription(
+                                ChromeFeatureList.isEnabled(ChromeFeatureList.INCOGNITO_STRINGS)
+                                        ? R.string.accessibility_tab_switcher_private_stack
+                                        : R.string.accessibility_tab_switcher_incognito_stack);
+        addTab(mIncognitoButton);
+
+        addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+            @Override
+            public void onTabSelected(TabLayout.Tab tab) {
+                setSelectedModel(mIncognitoButton.isSelected());
+            }
+
+            @Override
+            public void onTabUnselected(TabLayout.Tab tab) {}
+
+            @Override
+            public void onTabReselected(TabLayout.Tab tab) {}
+        });
+    }
+
+    /**
+     * @param selector A {@link TabModelSelector} to provide information about open tabs.
+     */
+    public void setTabModelSelector(TabModelSelector selector) {
+        mTabModelSelector = selector;
+        if (mTabModelSelector == null) return;
+        mTabModelSelector.addObserver(new EmptyTabModelSelectorObserver() {
+            @Override
+            public void onTabModelSelected(TabModel newModel, TabModel oldModel) {
+                setStateBasedOnModel();
+            }
+        });
+        setStateBasedOnModel();
+    }
+
+    /**
+     * Update the visual state based on number of normal (non-incognito) tabs present.
+     * @param tabCount The number of normal tabs.
+     */
+    public void updateTabCount(int tabCount) {
+        mTabSwitcherDrawable.updateForTabCount(tabCount, false);
+    }
+
+    private void setStateBasedOnModel() {
+        if (mTabModelSelector == null) return;
+        final boolean isIncognitoSelected = mTabModelSelector.isIncognitoSelected();
+        if (isIncognitoSelected) {
+            setSelectedTabIndicatorColor(mTabIconSelectedLightColor.getDefaultColor());
+            mStandardButtonIcon.setTint(mTabIconLightColor);
+            mTabSwitcherDrawable.setTint(mTabIconLightColor);
+            mIncognitoButtonIcon.setTint(mTabIconSelectedLightColor);
+        } else {
+            setSelectedTabIndicatorColor(mTabIconSelectedDarkColor.getDefaultColor());
+            mStandardButtonIcon.setTint(mTabIconSelectedDarkColor);
+            mTabSwitcherDrawable.setTint(mTabIconSelectedDarkColor);
+            mIncognitoButtonIcon.setTint(mTabIconDarkColor);
+        }
+        // Ensure the tab in tab layout is correctly selected when tab switcher is
+        // first opened.
+        if (isIncognitoSelected && !mIncognitoButton.isSelected()) {
+            mIncognitoButton.select();
+        } else if (!isIncognitoSelected && !mStandardButton.isSelected()) {
+            mStandardButton.select();
+        }
+    }
+
+    private void setSelectedModel(boolean incognitoSelected) {
+        if (mTabModelSelector == null
+                || incognitoSelected == mTabModelSelector.isIncognitoSelected()) {
+            return;
+        }
+
+        mTabModelSelector.commitAllTabClosures();
+        mTabModelSelector.selectModel(incognitoSelected);
+    }
+}
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 04a7af1..e3ace30 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
@@ -79,7 +79,6 @@
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.chrome.browser.util.ViewUtils;
 import org.chromium.chrome.browser.widget.animation.CancelAwareAnimatorListener;
-import org.chromium.chrome.browser.widget.incognitotoggle.IncognitoToggleButton;
 import org.chromium.chrome.browser.widget.newtab.NewTabButton;
 import org.chromium.chrome.browser.widget.textbubble.TextBubble;
 import org.chromium.components.feature_engagement.EventConstants;
@@ -146,7 +145,7 @@
     protected LocationBarPhone mLocationBar;
 
     protected ViewGroup mToolbarButtonsContainer;
-    private IncognitoToggleButton mIncognitoToggleButton;
+    private IncognitoToggleTabLayout mIncognitoToggleTabLayout;
     protected ImageView mToggleTabStackButton;
     protected NewTabButton mNewTabButton;
     protected @Nullable AppCompatImageButton mHomeButton;
@@ -196,7 +195,6 @@
 
     private OnClickListener mTabSwitcherListener;
     private OnClickListener mNewTabListener;
-    private OnClickListener mIncognitoListener;
 
     @ViewDebug.ExportedProperty(category = "chrome")
     protected boolean mUrlFocusChangeInProgress;
@@ -610,8 +608,6 @@
                 RecordUserAction.record("MobileNewTabOpened");
                 // TODO(kkimlabs): Record UMA action for homepage button.
             }
-        } else if (mIncognitoToggleButton == v) {
-            if (mIncognitoListener != null) mIncognitoListener.onClick(v);
         } else if (mHomeButton != null && mHomeButton == v) {
             openHomepage();
             if (isNativeLibraryReady()
@@ -812,19 +808,7 @@
      * @return The right bounds of the location bar after accounting for any visible left buttons.
      */
     protected int getBoundsAfterAccountingForRightButtons() {
-        // We set the incognito toggle button's visibility from GONE to VISIBLE when the tab
-        // switcher starts to open, but we don't want this to affect the Omnibox's size during the
-        // animation, so we have to make an adjustment here.
-        // However, if the experimental button is showing it sits in a FrameLayout with the
-        // incognito button and the omnibox will be appropriately sized without an explicit
-        // adjustment.
-        int incognitoButtonWidth = 0;
-        if (mIncognitoToggleButton != null && mIncognitoToggleButton.getVisibility() == VISIBLE
-                && (mExperimentalButton == null || mExperimentalButton.getVisibility() == GONE)) {
-            incognitoButtonWidth += mIncognitoToggleButton.getMeasuredWidth();
-        }
-        return Math.max(mToolbarSidePadding,
-                mToolbarButtonsContainer.getMeasuredWidth() - incognitoButtonWidth);
+        return Math.max(mToolbarSidePadding, mToolbarButtonsContainer.getMeasuredWidth());
     }
 
     protected void updateToolbarBackground(int color) {
@@ -1836,24 +1820,21 @@
 
         // Don't inflate the incognito toggle button unless the horizontal tab switcher experiment
         // is enabled and the user actually enters the tab switcher.
-        if (!FeatureUtilities.isBottomToolbarEnabled() && mIncognitoToggleButton == null
+        if (!FeatureUtilities.isBottomToolbarEnabled() && mIncognitoToggleTabLayout == null
                 && mTabSwitcherState != STATIC_TAB && usingHorizontalTabSwitcher()
                 && PrefServiceBridge.getInstance().isIncognitoModeEnabled()) {
-            ViewStub incognitoToggleButtonStub = findViewById(R.id.incognito_button_stub);
-            mIncognitoToggleButton = (IncognitoToggleButton) incognitoToggleButtonStub.inflate();
-            mIncognitoToggleButton.setOnClickListener(this);
-            mIncognitoToggleButton.setTabModelSelector(mTabModelSelector);
-            mTabSwitcherModeViews.add(mIncognitoToggleButton);
+            ViewStub incognitoToggleTabsStub = findViewById(R.id.incognito_tabs_stub);
+            mIncognitoToggleTabLayout =
+                    (IncognitoToggleTabLayout) incognitoToggleTabsStub.inflate();
+            mIncognitoToggleTabLayout.setTabModelSelector(mTabModelSelector);
+            mTabSwitcherModeViews.add(mIncognitoToggleTabLayout);
+            mIncognitoToggleTabLayout.updateTabCount(mTabModelSelector.getModel(false).getCount());
+
+            mBrowsingModeViews.add(mToggleTabStackButton);
         }
 
         for (View view : mTabSwitcherModeViews) {
-            // The incognito toggle button needs to be set to GONE rather than INVISIBLE so it
-            // doesn't reduce the space available for the Omnibox.
-            if (view == mIncognitoToggleButton && tabSwitcherViewsVisibility == INVISIBLE) {
-                view.setVisibility(GONE);
-            } else {
-                view.setVisibility(tabSwitcherViewsVisibility);
-            }
+            view.setVisibility(tabSwitcherViewsVisibility);
         }
         for (View view : mBrowsingModeViews) {
             view.setVisibility(browsingViewsVisibility);
@@ -1923,7 +1904,8 @@
         } else {
             if (!mDelayingTabSwitcherAnimation) {
                 mTabSwitcherModeAnimation = createExitTabSwitcherAnimation(showToolbar);
-                if (mIncognitoToggleButton != null) mIncognitoToggleButton.setClickable(false);
+                if (mIncognitoToggleTabLayout != null)
+                    mIncognitoToggleTabLayout.setClickable(false);
             }
         }
 
@@ -1956,7 +1938,7 @@
 
     @Override
     protected void onTabSwitcherTransitionFinished() {
-        if (mIncognitoToggleButton != null) mIncognitoToggleButton.setClickable(true);
+        if (mIncognitoToggleTabLayout != null) mIncognitoToggleTabLayout.setClickable(true);
 
         setAlpha(1.f);
         mClipRect = null;
@@ -2001,11 +1983,6 @@
     }
 
     @Override
-    public void setIncognitoClickHandler(OnClickListener listener) {
-        mIncognitoListener = listener;
-    }
-
-    @Override
     protected void onAccessibilityStatusChanged(boolean enabled) {
         super.onAccessibilityStatusChanged(enabled);
         if (mNewTabButton != null) mNewTabButton.onAccessibilityStatusChanged();
@@ -2260,6 +2237,10 @@
         mTabSwitcherButtonDrawableLight.updateForTabCount(numberOfTabs, isIncognito());
         mTabSwitcherButtonDrawable.updateForTabCount(numberOfTabs, isIncognito());
 
+        if (!isIncognito() && mIncognitoToggleTabLayout != null) {
+            mIncognitoToggleTabLayout.updateTabCount(numberOfTabs);
+        }
+
         boolean useTabStackDrawableLight = isIncognito()
                 || ColorUtils.shouldUseLightForegroundOnBackground(getTabThemeColor());
         if (mTabSwitcherAnimationTabStackDrawable == null
@@ -2669,8 +2650,8 @@
     @Override
     public void setTabModelSelector(TabModelSelector selector) {
         mTabModelSelector = selector;
-        if (mIncognitoToggleButton != null) {
-            mIncognitoToggleButton.setTabModelSelector(mTabModelSelector);
+        if (mIncognitoToggleTabLayout != null) {
+            mIncognitoToggleTabLayout.setTabModelSelector(mTabModelSelector);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
index 46f332ce..fa09fbb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
@@ -229,7 +229,7 @@
      * Whether there is a new version of the //chrome/android/webapk/shell_apk code.
      */
     private static boolean isShellApkVersionOutOfDate(WebApkInfo info) {
-        return info.shellApkVersion() < WebApkVersion.CURRENT_SHELL_APK_VERSION;
+        return info.shellApkVersion() < WebApkVersion.REQUEST_UPDATE_FOR_SHELL_APK_VERSION;
     }
 
     /**
@@ -249,7 +249,7 @@
         if (!info.apkPackageName().startsWith(WEBAPK_PACKAGE_PREFIX)) return false;
 
         if (isShellApkVersionOutOfDate(info)
-                && WebApkVersion.CURRENT_SHELL_APK_VERSION
+                && WebApkVersion.REQUEST_UPDATE_FOR_SHELL_APK_VERSION
                         > mStorage.getLastRequestedShellApkVersion()) {
             return true;
         }
@@ -267,7 +267,8 @@
         mStorage.updateTimeOfLastWebApkUpdateRequestCompletion();
         mStorage.updateDidLastWebApkUpdateRequestSucceed(result == WebApkInstallResult.SUCCESS);
         mStorage.setRelaxedUpdates(relaxUpdates);
-        mStorage.updateLastRequestedShellApkVersion(WebApkVersion.CURRENT_SHELL_APK_VERSION);
+        mStorage.updateLastRequestedShellApkVersion(
+                WebApkVersion.REQUEST_UPDATE_FOR_SHELL_APK_VERSION);
     }
 
     /**
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index ee8a536..2e2b6ec 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -865,7 +865,7 @@
         All sites
       </message>
       <message name="IDS_INTRUSIVE_ADS_INFORMATION" desc="The extra information at the top of the Site Details page when the site tends to show intrusive ads">
-        This site tends to show intrusive ads
+        This site shows intrusive or misleading ads
       </message>
       <message name="IDS_ADS_PERMISSION_TITLE" desc="Title for the ads permission [CHAR-LIMIT=32]">
          Ads
@@ -1045,13 +1045,13 @@
         Block sites from showing pop-ups and redirects (recommended)
       </message>
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_ADS_BLOCKED" desc="Summary text explaining that ads are being blocked on some sites.">
-        Block ads from sites that tend to show intrusive ads
+        Block ads from sites that show intrusive or misleading ads
       </message>
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_ADS_BLOCKED_LIST" desc="Summary text explaining that the Ads permission is set to block ads on some sites. To be shown in the list of permission categories.">
         Blocked from some sites
       </message>
       <message name="IDS_WEBSITE_SETTINGS_PERMISSIONS_ADS_BLOCK" desc="The Blocked string for the ads permission on Site Details">
-         Block if site tends to show intrusive ads (recommended)
+         Block if site shows intrusive or misleading ads (recommended)
       </message>
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_LOCATION_ASK" desc="Summary text explaining that sites need to ask for permission before knowing location and that it is the recommended setting.">
         Ask before allowing sites to know your location (recommended)
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index a216bd58..0997cea 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1502,6 +1502,7 @@
   "java/src/org/chromium/chrome/browser/toolbar/MenuButton.java",
   "java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonSlotData.java",
   "java/src/org/chromium/chrome/browser/toolbar/HomePageButton.java",
+  "java/src/org/chromium/chrome/browser/toolbar/IncognitoToggleTabLayout.java",
   "java/src/org/chromium/chrome/browser/toolbar/KeyboardNavigationListener.java",
   "java/src/org/chromium/chrome/browser/toolbar/ScrollingBottomViewResourceFrameLayout.java",
   "java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
index dab21fd..b47009c9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
@@ -274,6 +274,7 @@
     @Test
     @MediumTest
     @Feature({"ContextualSuggestions"})
+    @DisabledTest(message = "https://crbug.com/890947")
     public void testInProductHelp() throws InterruptedException, TimeoutException {
         assertTrue(
                 "Help bubble should be showing.", mMediator.getHelpBubbleForTesting().isShowing());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMediaParserTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMediaParserTest.java
index e2ebade..96fe0a8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMediaParserTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMediaParserTest.java
@@ -44,7 +44,7 @@
     /**
      * Wraps result from download media parser.
      */
-    public static class MediaParseResult {
+    public static class MediaParserResult {
         public boolean done;
         public DownloadMediaData mediaData;
     }
@@ -54,21 +54,16 @@
         mTestRule.loadNativeLibraryAndInitBrowserProcess();
     }
 
-    @Test
-    @LargeTest
-    @Feature({"Download"})
-    @RetryOnFailure
-    public void testParseAudioMetatadata() throws InterruptedException {
-        String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/sfx.mp3";
-        File audioFile = new File(filePath);
-        Assert.assertTrue(audioFile.exists());
+    private MediaParserResult parseMediaFile(String filePath, String mimeType) {
+        File mediaFile = new File(filePath);
+        Assert.assertTrue(mediaFile.exists());
         boolean done = false;
-        MediaParseResult result = new MediaParseResult();
+        MediaParserResult result = new MediaParserResult();
 
         // The native DownloadMediaParser needs to be created on UI thread.
         ThreadUtils.runOnUiThreadBlocking(() -> {
             DownloadMediaParserBridge parser = new DownloadMediaParserBridge(
-                    "audio/mp3", filePath, audioFile.length(), (DownloadMediaData mediaData) -> {
+                    mimeType, filePath, mediaFile.length(), (DownloadMediaData mediaData) -> {
                         result.mediaData = mediaData;
                         result.done = true;
                     });
@@ -81,7 +76,20 @@
                 return result.done;
             }
         }, MAX_MEDIA_PARSER_POLL_TIME_MS, MEDIA_PARSER_POLL_INTERVAL_MS);
+        return result;
+    }
 
+    @Test
+    @LargeTest
+    @Feature({"Download"})
+    @RetryOnFailure
+    /**
+     * Verify that the metadata from audio file can be retrieved correctly.
+     * @throws InterruptedException
+     */
+    public void testParseAudioMetatadata() throws InterruptedException {
+        String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/sfx.mp3";
+        MediaParserResult result = parseMediaFile(filePath, "audio/mp3");
         Assert.assertTrue("Failed to parse audio metadata.", result.mediaData != null);
     }
 
@@ -89,30 +97,50 @@
     @LargeTest
     @Feature({"Download"})
     @RetryOnFailure
-    public void testParseVideoMetatadataThumbnail() throws InterruptedException {
+    /**
+     * Verify metadata and thumbnail can be retrieved correctly from h264 video file.
+     * @throws InterruptedException
+     */
+    public void testParseVideoH264() throws InterruptedException {
         String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/bear.mp4";
-        File videoFile = new File(filePath);
-        Assert.assertTrue(videoFile.exists());
-        boolean done = false;
-        MediaParseResult result = new MediaParseResult();
+        MediaParserResult result = parseMediaFile(filePath, "video/mp4");
+        Assert.assertTrue("Failed to parse video file.", result.mediaData != null);
+        Assert.assertTrue(
+                "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getWidth() > 0);
+        Assert.assertTrue(
+                "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getHeight() > 0);
+    }
 
-        // The native DownloadMediaParser needs to be created on UI thread.
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            DownloadMediaParserBridge parser = new DownloadMediaParserBridge(
-                    "video/mp4", filePath, videoFile.length(), (DownloadMediaData mediaData) -> {
-                        result.mediaData = mediaData;
-                        result.done = true;
-                    });
-            parser.start();
-        });
+    @Test
+    @LargeTest
+    @Feature({"Download"})
+    @RetryOnFailure
+    /**
+     * Verify metadata and thumbnail can be retrieved correctly from vp8 video file.
+     * @throws InterruptedException
+     */
+    public void testParseVideoThumbnailVp8() throws InterruptedException {
+        String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/bear-vp8-webvtt.webm";
+        MediaParserResult result = parseMediaFile(filePath, "video/webm");
+        Assert.assertTrue("Failed to parse video file.", result.mediaData != null);
+        Assert.assertTrue(
+                "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getWidth() > 0);
+        Assert.assertTrue(
+                "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getHeight() > 0);
+    }
 
-        CriteriaHelper.pollUiThread(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                return result.done;
-            }
-        }, MAX_MEDIA_PARSER_POLL_TIME_MS, MEDIA_PARSER_POLL_INTERVAL_MS);
-
+    @Test
+    @LargeTest
+    @Feature({"Download"})
+    @RetryOnFailure
+    /**
+     * Verify metadata and thumbnail can be retrieved correctly from vp8 video file with alpha
+     * plane.
+     * @throws InterruptedException
+     */
+    public void testParseVideoThumbnailVp8WithAlphaPlane() throws InterruptedException {
+        String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/bear-vp8a.webm";
+        MediaParserResult result = parseMediaFile(filePath, "video/webm");
         Assert.assertTrue("Failed to parse video file.", result.mediaData != null);
         Assert.assertTrue(
                 "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getWidth() > 0);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java
new file mode 100644
index 0000000..e4ee217
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java
@@ -0,0 +1,263 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.feed;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.support.test.filters.SmallTest;
+
+import com.google.android.libraries.feed.api.lifecycle.AppLifecycleListener;
+import com.google.android.libraries.feed.feedapplifecyclelistener.FeedAppLifecycleListener;
+import com.google.android.libraries.feed.host.network.NetworkClient;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.chromium.base.ActivityState;
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.feed.FeedAppLifecycle.AppLifecycleEvent;
+import org.chromium.chrome.browser.multiwindow.MultiWindowTestHelper;
+import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
+import org.chromium.chrome.browser.test.ChromeBrowserTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+import org.chromium.content_public.browser.LoadUrlParams;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Unit tests for {@link FeedAppLifecycle}
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
+@EnableFeatures({ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS})
+public class FeedAppLifecycleTest {
+    @Rule
+    public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
+    @Rule
+    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+    @Mock
+    private FeedLifecycleBridge mLifecycleBridge;
+    @Mock
+    private FeedScheduler mFeedScheduler;
+    @Mock
+    private NetworkClient mNetworkClient;
+    @Mock
+    private FeedOfflineIndicator mOfflineIndicator;
+    @Mock
+    private AppLifecycleListener mAppLifecycleListener;
+    private ChromeTabbedActivity mActivity;
+    private FeedAppLifecycle mAppLifecycle;
+
+    @Before
+    public void setUp() throws InterruptedException, TimeoutException {
+        MockitoAnnotations.initMocks(this);
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mAppLifecycle =
+                    new FeedAppLifecycle(mAppLifecycleListener, mLifecycleBridge, mFeedScheduler);
+            FeedProcessScopeFactory.createFeedProcessScopeForTesting(mFeedScheduler, mNetworkClient,
+                    mOfflineIndicator, mAppLifecycle,
+                    new FeedAppLifecycleListener(
+                            new com.google.android.libraries.feed.api.common.ThreadUtils()));
+        });
+
+        mActivityTestRule.startMainActivityOnBlankPage();
+        mActivity = mActivityTestRule.getActivity();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"InterestFeedContentSuggestions"})
+    public void construction_checks_active_tabbed_activities() {
+        verify(mAppLifecycleListener, times(1)).onEnterForeground();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"InterestFeedContentSuggestions"})
+    public void activity_state_changes_increment_state_counters()
+            throws InterruptedException, TimeoutException {
+        assertEquals(0,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "ContentSuggestions.Feed.AppLifecycleEvents",
+                        AppLifecycleEvent.ENTER_BACKGROUND));
+        verify(mAppLifecycleListener, times(1)).onEnterForeground();
+        signalActivityStop(mActivity);
+        signalActivityStart(mActivity);
+
+        verify(mAppLifecycleListener, times(1)).onEnterBackground();
+        verify(mAppLifecycleListener, times(2)).onEnterForeground();
+        assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "ContentSuggestions.Feed.AppLifecycleEvents",
+                        AppLifecycleEvent.ENTER_BACKGROUND));
+        assertEquals(2,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "ContentSuggestions.Feed.AppLifecycleEvents",
+                        AppLifecycleEvent.ENTER_FOREGROUND));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"InterestFeedContentSuggestions"})
+    public void ntp_opening_triggers_initialize_only_once() throws InterruptedException {
+        // We open to about:blank initially so we shouldn't have called initialize() yet.
+        verify(mAppLifecycleListener, times(0)).initialize();
+        mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
+        verify(mAppLifecycleListener, times(1)).initialize();
+
+        // Opening the NTP again shouldn't trigger another call to initialize().
+        mActivityTestRule.loadUrlInNewTab(UrlConstants.NTP_URL);
+        verify(mAppLifecycleListener, times(1)).initialize();
+        assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "ContentSuggestions.Feed.AppLifecycleEvents",
+                        AppLifecycleEvent.INITIALIZE));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"InterestFeedContentSuggestions"})
+    public void history_deletion_triggers_clear_all() throws InterruptedException {
+        verify(mAppLifecycleListener, times(0)).onClearAll();
+        mAppLifecycle.onHistoryDeleted();
+        verify(mAppLifecycleListener, times(1)).onClearAll();
+        assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "ContentSuggestions.Feed.AppLifecycleEvents", AppLifecycleEvent.CLEAR_ALL));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"InterestFeedContentSuggestions"})
+    public void cached_data_removal_triggers_clear_all() throws InterruptedException {
+        verify(mAppLifecycleListener, times(0)).onClearAll();
+        mAppLifecycle.onCachedDataCleared();
+        verify(mAppLifecycleListener, times(1)).onClearAll();
+        assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "ContentSuggestions.Feed.AppLifecycleEvents", AppLifecycleEvent.CLEAR_ALL));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"InterestFeedContentSuggestions"})
+    public void signout_triggers_clear_all() throws InterruptedException {
+        verify(mAppLifecycleListener, times(0)).onClearAll();
+        mAppLifecycle.onSignedOut();
+        verify(mAppLifecycleListener, times(1)).onClearAll();
+        assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "ContentSuggestions.Feed.AppLifecycleEvents", AppLifecycleEvent.CLEAR_ALL));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"InterestFeedContentSuggestions"})
+    public void signin_triggers_clear_all() throws InterruptedException {
+        verify(mAppLifecycleListener, times(0)).onClearAll();
+        mAppLifecycle.onSignedIn();
+        verify(mAppLifecycleListener, times(1)).onClearAll();
+        assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "ContentSuggestions.Feed.AppLifecycleEvents", AppLifecycleEvent.CLEAR_ALL));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"InterestFeedContentSuggestions"})
+    public void second_window_does_not_trigger_foreground_or_background()
+            throws InterruptedException, TimeoutException {
+        verify(mAppLifecycleListener, times(1)).onEnterForeground();
+
+        MultiWindowUtils.getInstance().setIsInMultiWindowModeForTesting(true);
+        ChromeTabbedActivity activity2 = MultiWindowTestHelper.createSecondChromeTabbedActivity(
+                mActivity, new LoadUrlParams(UrlConstants.NTP_URL));
+
+        signalActivityStop(activity2);
+
+        // Starting and then stopping the second activity shouldn't trigger either foreground or
+        // background.
+        verify(mAppLifecycleListener, times(0)).onEnterBackground();
+        verify(mAppLifecycleListener, times(1)).onEnterForeground();
+
+        signalActivityStop(mActivity);
+
+        // Only now that both TabbedActivity instances have stopped should we get a background
+        // event.
+        verify(mAppLifecycleListener, times(1)).onEnterBackground();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"InterestFeedContentSuggestions"})
+    public void multi_window_does_not_cause_multiple_initialize() throws InterruptedException {
+        mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
+        verify(mAppLifecycleListener, times(1)).initialize();
+
+        MultiWindowUtils.getInstance().setIsInMultiWindowModeForTesting(true);
+        ChromeTabbedActivity activity2 = MultiWindowTestHelper.createSecondChromeTabbedActivity(
+                mActivity, new LoadUrlParams(UrlConstants.NTP_URL));
+
+        verify(mAppLifecycleListener, times(1)).initialize();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"InterestFeedContentSuggestions"})
+    public void resume_triggers_scheduler_foregrounded()
+            throws InterruptedException, TimeoutException {
+        // Starting mActivity in setUp() triggers a resume.
+        verify(mFeedScheduler, times(1)).onForegrounded();
+        signalActivityResume(mActivity);
+        verify(mFeedScheduler, times(2)).onForegrounded();
+    }
+
+    private void signalActivityStart(Activity activity)
+            throws InterruptedException, TimeoutException {
+        signalActivityState(activity, ActivityState.STARTED);
+    }
+
+    private void signalActivityResume(Activity activity)
+            throws InterruptedException, TimeoutException {
+        signalActivityState(activity, ActivityState.RESUMED);
+    }
+
+    private void signalActivityStop(Activity activity)
+            throws InterruptedException, TimeoutException {
+        signalActivityState(activity, ActivityState.STOPPED);
+    }
+
+    private void signalActivityState(final Activity activity,
+            final @ActivityState int activityState) throws InterruptedException, TimeoutException {
+        final CallbackHelper waitForStateChangeHelper = new CallbackHelper();
+        ThreadUtils.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ApplicationStatus.onStateChangeForTesting(activity, activityState);
+                waitForStateChangeHelper.notifyCalled();
+            }
+        });
+
+        waitForStateChangeHelper.waitForCallback(0);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
index 7bf1d36e9..e69cad44 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -156,7 +156,8 @@
                 WebApkInfo info = WebApkInfo.create(WEBAPK_ID, "", creationData.scope, null, null,
                         null, creationData.name, creationData.shortName, creationData.displayMode,
                         creationData.orientation, 0, creationData.themeColor,
-                        creationData.backgroundColor, "", WebApkVersion.CURRENT_SHELL_APK_VERSION,
+                        creationData.backgroundColor, "",
+                        WebApkVersion.REQUEST_UPDATE_FOR_SHELL_APK_VERSION,
                         creationData.manifestUrl, creationData.startUrl,
                         WebApkInfo.WebApkDistributor.BROWSER, creationData.iconUrlToMurmur2HashMap,
                         null, false /* forceNavigation */);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryUnitTest.java
index 435605d..1b6a1f9 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryUnitTest.java
@@ -19,15 +19,18 @@
     @Test
     public void testAddSite() {
         final int id = 1;
+        @ExploreSitesCategory.CategoryType
+        final int type = ExploreSitesCategory.CategoryType.SCIENCE;
         final int siteId = 100;
         final String title = "test";
         final String url = "http://www.google.com";
         final String categoryTitle = "Movies";
 
-        ExploreSitesCategory category = new ExploreSitesCategory(id, categoryTitle);
+        ExploreSitesCategory category = new ExploreSitesCategory(id, type, categoryTitle);
         category.addSite(new ExploreSitesSite(siteId, title, url));
 
         assertEquals(id, category.getId());
+        assertEquals(type, category.getType());
         assertEquals(1, category.getSites().size());
         assertEquals(siteId, category.getSites().get(0).getId());
         assertEquals(title, category.getSites().get(0).getTitle());
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
index 4392f7c..fc531fcf 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
@@ -10,7 +10,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import static org.chromium.webapk.lib.client.WebApkVersion.CURRENT_SHELL_APK_VERSION;
+import static org.chromium.webapk.lib.client.WebApkVersion.REQUEST_UPDATE_FOR_SHELL_APK_VERSION;
 
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -347,7 +347,8 @@
      */
     private boolean checkUpdateNeededForFetchedManifest(
             ManifestData androidManifestData, ManifestData fetchedManifestData) {
-        registerWebApk(WEBAPK_PACKAGE_NAME, androidManifestData, CURRENT_SHELL_APK_VERSION);
+        registerWebApk(
+                WEBAPK_PACKAGE_NAME, androidManifestData, REQUEST_UPDATE_FOR_SHELL_APK_VERSION);
         mClockRule.advance(WebappDataStorage.UPDATE_INTERVAL);
 
         TestWebApkUpdateManager updateManager =
@@ -364,7 +365,8 @@
         PathUtils.setPrivateDataDirectorySuffix("chrome");
         CommandLine.init(null);
 
-        registerWebApk(WEBAPK_PACKAGE_NAME, defaultManifestData(), CURRENT_SHELL_APK_VERSION);
+        registerWebApk(
+                WEBAPK_PACKAGE_NAME, defaultManifestData(), REQUEST_UPDATE_FOR_SHELL_APK_VERSION);
 
         WebappRegistry.getInstance().register(getWebApkId(WEBAPK_PACKAGE_NAME),
                 new WebappRegistry.FetchWebappDataStorageCallback() {
@@ -545,7 +547,8 @@
      */
     @Test
     public void testShellApkOutOfDateNoWebManifest() {
-        registerWebApk(WEBAPK_PACKAGE_NAME, defaultManifestData(), CURRENT_SHELL_APK_VERSION - 1);
+        registerWebApk(WEBAPK_PACKAGE_NAME, defaultManifestData(),
+                REQUEST_UPDATE_FOR_SHELL_APK_VERSION - 1);
         mClockRule.advance(WebappDataStorage.UPDATE_INTERVAL);
 
         TestWebApkUpdateManager updateManager =
@@ -568,7 +571,8 @@
      */
     @Test
     public void testShellApkOutOfDateStillHasWebManifest() {
-        registerWebApk(WEBAPK_PACKAGE_NAME, defaultManifestData(), CURRENT_SHELL_APK_VERSION - 1);
+        registerWebApk(WEBAPK_PACKAGE_NAME, defaultManifestData(),
+                REQUEST_UPDATE_FOR_SHELL_APK_VERSION - 1);
         mClockRule.advance(WebappDataStorage.UPDATE_INTERVAL);
 
         TestWebApkUpdateManager updateManager =
@@ -658,7 +662,8 @@
     public void testUnboundWebApkDoesNotUpgrade() {
         ManifestData androidManifestData = defaultManifestData();
 
-        registerWebApk(UNBOUND_WEBAPK_PACKAGE_NAME, androidManifestData, CURRENT_SHELL_APK_VERSION);
+        registerWebApk(UNBOUND_WEBAPK_PACKAGE_NAME, androidManifestData,
+                REQUEST_UPDATE_FOR_SHELL_APK_VERSION);
         mClockRule.advance(WebappDataStorage.UPDATE_INTERVAL);
 
         TestWebApkUpdateManager updateManager =
@@ -819,7 +824,8 @@
      */
     @Test
     public void testShellApkOutOfDate() {
-        registerWebApk(WEBAPK_PACKAGE_NAME, defaultManifestData(), CURRENT_SHELL_APK_VERSION - 1);
+        registerWebApk(WEBAPK_PACKAGE_NAME, defaultManifestData(),
+                REQUEST_UPDATE_FOR_SHELL_APK_VERSION - 1);
         TestWebApkUpdateManager updateManager =
                 new TestWebApkUpdateManager(getStorage(WEBAPK_PACKAGE_NAME));
 
diff --git a/chrome/android/webapk/PRESUBMIT.py b/chrome/android/webapk/PRESUBMIT.py
index e2471e2..70d259d 100644
--- a/chrome/android/webapk/PRESUBMIT.py
+++ b/chrome/android/webapk/PRESUBMIT.py
@@ -9,20 +9,20 @@
 
 This presubmit checks for two rules:
 1. If anything in the webapk/libs/common or the webapk/shell_apk directories
-has changed (excluding test files), $WAM_MINT_TRIGGER_VARIABLE should be
-updated.
-2. If $CHROME_UPDATE_TIRGGER_VARIABLE is changed in
-$SHELL_APK_VERSION_LOCAL_PATH, $SHELL_APK_VERSION_LOCAL_PATH should be the
-only changed file and changing $CHROME_UPDATE_TRIGGER_VARIABLE should be
-the only change.
+has changed (excluding test files), $CURRENT_VERSION_VARIABLE should be updated.
+2. If $REQUEST_UPDATE_FOR_VERSION_VARIABLE in
+$REQUEST_UPDATE_FOR_VERSION_LOCAL_PATH is changed, the variable change should
+be the only change in the CL.
 """
 
-WAM_MINT_TRIGGER_VARIABLE = r'template_shell_apk_version'
-CHROME_UPDATE_TRIGGER_VARIABLE = r'expected_shell_apk_version'
+CURRENT_VERSION_VARIABLE = 'current_shell_apk_version'
+CURRENT_VERSION_LOCAL_PATH = 'shell_apk/current_version/current_version.gni'
 
-SHELL_APK_VERSION_LOCAL_PATH = r'shell_apk/shell_apk_version.gni'
+REQUEST_UPDATE_FOR_VERSION_VARIABLE = 'request_update_for_shell_apk_version'
+REQUEST_UPDATE_FOR_VERSION_LOCAL_PATH = (
+    'shell_apk/request_update_for_shell_apk_version.gni')
 
-WAM_MINT_TRIGGER_LOCAL_PATHS = [
+TRIGGER_CURRENT_VERSION_UPDATE_LOCAL_PATHS = [
     'libs/common/src/',
     'shell_apk/AndroidManifest.xml',
     'shell_apk/res/',
@@ -52,20 +52,21 @@
   Check that if |expected_shell_apk_version| is updated it is the only
   change in the CL.
   """
-  if _CheckVersionVariableChanged(input_api, SHELL_APK_VERSION_LOCAL_PATH,
-                                  CHROME_UPDATE_TRIGGER_VARIABLE):
+  if _CheckVersionVariableChanged(input_api,
+                                  REQUEST_UPDATE_FOR_VERSION_LOCAL_PATH,
+                                  REQUEST_UPDATE_FOR_VERSION_VARIABLE):
     if (len(input_api.AffectedFiles()) != 1 or
         len(input_api.AffectedFiles[0].ChangedContents()) != 1):
       return [
         output_api.PresubmitError(
             '{} in {} must be updated in a standalone CL.'.format(
-                CHROME_UPDATE_TRIGGER_VARIABLE,
-                SHELL_APK_VERSION_LOCAL_PATH))
+                REQUEST_UPDATE_FOR_VERSION_VARIABLE,
+                REQUEST_UPDATE_FOR_VERSION_LOCAL_PATH))
         ]
   return []
 
 
-def _CheckWamMintTriggerRule(input_api, output_api):
+def _CheckCurrentVersionIncreaseRule(input_api, output_api):
   """
   Check that if a file in $WAM_MINT_TRIGGER_LOCAL_PATHS is updated that
   |template_shell_apk_version| is updated as well.
@@ -74,18 +75,18 @@
   for f in input_api.AffectedFiles():
     local_path = input_api.os_path.relpath(f.AbsoluteLocalPath(),
                                            input_api.PresubmitLocalPath())
-    for wam_mint_trigger_local_path in WAM_MINT_TRIGGER_LOCAL_PATHS:
-      if local_path.startswith(wam_mint_trigger_local_path):
+    for trigger_local_path in TRIGGER_CURRENT_VERSION_UPDATE_LOCAL_PATHS:
+      if local_path.startswith(trigger_local_path):
         files_requiring_version_increase.append(local_path)
 
   if not files_requiring_version_increase:
     return []
 
-  if not _CheckVersionVariableChanged(input_api, SHELL_APK_VERSION_LOCAL_PATH,
-                                      WAM_MINT_TRIGGER_VARIABLE):
+  if not _CheckVersionVariableChanged(input_api, CURRENT_VERSION_LOCAL_PATH,
+                                      CURRENT_VERSION_VARIABLE):
     return [output_api.PresubmitPromptWarning(
         '{} in {} needs to updated due to changes in:'.format(
-            WAM_MINT_TRIGGER_VARIABLE, SHELL_APK_VERSION_LOCAL_PATH),
+            CURRENT_VERSION_VARIABLE, CURRENT_VERSION_LOCAL_PATH),
         items=files_requiring_version_increase)]
 
   return []
@@ -95,7 +96,7 @@
   """Checks common to both upload and commit."""
   result = []
   result.extend(_CheckChromeUpdateTriggerRule(input_api, output_api))
-  result.extend(_CheckWamMintTriggerRule(input_api, output_api))
+  result.extend(_CheckCurrentVersionIncreaseRule(input_api, output_api))
 
   return result
 
diff --git a/chrome/android/webapk/PRESUBMIT_test.py b/chrome/android/webapk/PRESUBMIT_test.py
index 03517af1..db2368d 100755
--- a/chrome/android/webapk/PRESUBMIT_test.py
+++ b/chrome/android/webapk/PRESUBMIT_test.py
@@ -15,9 +15,10 @@
 from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi
 
 class ShellApkVersion(unittest.TestCase):
-   UPDATE_TEMPLATE_SHELL_APK_VERSION_MESSAGE = (
-       'template_shell_apk_version in shell_apk/shell_apk_version.gni needs to '
-       'updated due to changes in:')
+   UPDATE_CURRENT_VERSION_MESSAGE = (
+       'current_shell_apk_version in '
+       'shell_apk/current_version/current_version.gni needs to updated due to '
+       'changes in:')
 
    def makeMockAffectedFiles(self, file_names):
      mock_files = []
@@ -41,7 +42,7 @@
      ]
 
      SHELL_APK_RES_FILE_PATH = 'shell_apk/res/mipmap-xxxxxxhdpi/app_icon.png'
-     SHELL_APK_VERSION_FILE_PATH = 'shell_apk/shell_apk_version.gni'
+     CURRENT_VERSION_FILE_PATH = 'shell_apk/current_version/current_version.gni'
 
      # template_shell_apk_version not updated. There should be a warning about
      # template_shell_apk_version needing to be updated.
@@ -49,13 +50,13 @@
      input_api.files = self.makeMockAffectedFiles(
          changed_java_file_paths + [SHELL_APK_RES_FILE_PATH])
      input_api.files += [
-         MockAffectedFile(SHELL_APK_VERSION_FILE_PATH, 'variable=O',
+         MockAffectedFile(CURRENT_VERSION_FILE_PATH, 'variable=O',
                           'variable=N', action='M')
      ]
-     warnings = PRESUBMIT._CheckWamMintTriggerRule(input_api, MockOutputApi())
+     warnings = PRESUBMIT._CheckCurrentVersionIncreaseRule(input_api,
+                                                           MockOutputApi())
      self.assertEqual(1, len(warnings))
-     self.assertEqual(self.UPDATE_TEMPLATE_SHELL_APK_VERSION_MESSAGE,
-                      warnings[0].message)
+     self.assertEqual(self.UPDATE_CURRENT_VERSION_MESSAGE, warnings[0].message)
      self.assertEqual([COMMON_SRC_FILE_PATH, SHELL_APK_SRC_FILE_PATH,
                        SHELL_APK_RES_FILE_PATH],
                       warnings[0].items)
@@ -64,11 +65,12 @@
      input_api.files = self.makeMockAffectedFiles(
          changed_java_file_paths + [SHELL_APK_RES_FILE_PATH])
      input_api.files += [
-         MockAffectedFile(SHELL_APK_VERSION_FILE_PATH,
-                          ['template_shell_apk_version=1'],
-                          ['template_shell_apk_version=2'], action='M')
+         MockAffectedFile(CURRENT_VERSION_FILE_PATH,
+                          ['current_shell_apk_version=1'],
+                          ['current_shell_apk_version=2'], action='M')
      ]
-     warnings = PRESUBMIT._CheckWamMintTriggerRule(input_api, MockOutputApi())
+     warnings = PRESUBMIT._CheckCurrentVersionIncreaseRule(input_api,
+                                                           MockOutputApi())
      self.assertEqual([], warnings)
 
 if __name__ == '__main__':
diff --git a/chrome/android/webapk/libs/client/BUILD.gn b/chrome/android/webapk/libs/client/BUILD.gn
index e2906ae7..92169504 100644
--- a/chrome/android/webapk/libs/client/BUILD.gn
+++ b/chrome/android/webapk/libs/client/BUILD.gn
@@ -4,7 +4,7 @@
 
 import("//build/config/android/rules.gni")
 import("//chrome/android/webapk/libs/runtime_library_version.gni")
-import("//chrome/android/webapk/shell_apk/shell_apk_version.gni")
+import("//chrome/android/webapk/shell_apk/request_update_for_version.gni")
 
 android_library("client_java") {
   java_files = [
@@ -31,7 +31,7 @@
   ]
   defines = [
     "CURRENT_RUNTIME_DEX_VERSION_VALUE=$runtime_library_version",
-    "CURRENT_SHELL_APK_VERSION_VALUE=$expected_shell_apk_version",
+    "REQUEST_UPDATE_FOR_SHELL_APK_VERSION_VALUE=$request_update_for_shell_apk_version",
   ]
 }
 
diff --git a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkVersion.template b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkVersion.template
index 489c470..77b92bf 100644
--- a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkVersion.template
+++ b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkVersion.template
@@ -8,6 +8,6 @@
 
 public static final int CURRENT_RUNTIME_DEX_VERSION = CURRENT_RUNTIME_DEX_VERSION_VALUE;
 
-public static final int CURRENT_SHELL_APK_VERSION = CURRENT_SHELL_APK_VERSION_VALUE;
+public static final int REQUEST_UPDATE_FOR_SHELL_APK_VERSION = REQUEST_UPDATE_FOR_SHELL_APK_VERSION_VALUE;
 
 }
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn
index 0702fc1..bcf1c6e 100644
--- a/chrome/android/webapk/shell_apk/BUILD.gn
+++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
+import("current_version/current_version.gni")
 import("manifest_processor.gni")
-import("shell_apk_version.gni")
 
 android_resources("new_splash_resources") {
   custom_package = "org.chromium.webapk.shell_apk.h2o"
@@ -84,7 +84,7 @@
     }
     output = _manifest_output
 
-    extra_variables = [ "shell_apk_version=$template_shell_apk_version" ]
+    extra_variables = [ "shell_apk_version=$current_shell_apk_version" ]
   }
 
   android_resources(_resources_target_name) {
diff --git a/chrome/android/webapk/shell_apk/current_version/OWNERS b/chrome/android/webapk/shell_apk/current_version/OWNERS
new file mode 100644
index 0000000..e1a50d65
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/current_version/OWNERS
@@ -0,0 +1 @@
+per-file current_version.gni=*
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
new file mode 100644
index 0000000..163111e
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -0,0 +1,15 @@
+# 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.
+
+# HOW IS THE VERSION USED?
+# |current_shell_apk_version| is compiled into the WebAPK. Chromium
+# compares the version number with a number compiled into Chromium to determine
+# whether the WebAPK should be updated.
+#
+# WHEN SHOULD THE VERSION BE INCREMENTED?
+# Must be incremented whenever code which is compiled into
+# //chrome/android/webapk/shell_apk:webapk is changed. This includes
+# Java files, Android resource files and AndroidManifest.xml. Does not affect
+# Chrome.apk
+current_shell_apk_version = 58
diff --git a/chrome/android/webapk/shell_apk/request_update_for_version.gni b/chrome/android/webapk/shell_apk/request_update_for_version.gni
new file mode 100644
index 0000000..88fbc9ce
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/request_update_for_version.gni
@@ -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.
+
+# The ShellAPK version expected by Chrome. Chrome will try to update the WebAPK
+# if the WebAPK's ShellAPK version is less than
+# |request_update_for_shell_apk_version|. The version should be incremented
+# after a new ShellAPK has been uploaded to the WebAPK Minting Server.
+request_update_for_shell_apk_version = 28
diff --git a/chrome/android/webapk/shell_apk/shell_apk_version.gni b/chrome/android/webapk/shell_apk/shell_apk_version.gni
deleted file mode 100644
index 728bdd7d..0000000
--- a/chrome/android/webapk/shell_apk/shell_apk_version.gni
+++ /dev/null
@@ -1,15 +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.
-
-# Must be incremented whenever code in //chrome/android/webapk/shell_apk
-# (including AndroidManifest.xml) is updated. This version should be incremented
-# prior to uploading a new ShellAPK to the WebAPK Minting Server.
-# Does not affect Chrome.apk
-template_shell_apk_version = 57
-
-# The ShellAPK version expected by Chrome. Chrome will try to update the WebAPK
-# if the WebAPK's ShellAPK version is less than |expected_shell_apk_version|.
-# The version should be incremented after a new ShellAPK has been uploaded to
-# the WebAPK Minting Server.
-expected_shell_apk_version = 28
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 078f4f2a..4941516 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -8889,8 +8889,8 @@
     <message name="IDS_BLOCKED_ADS_PROMPT_TITLE" desc="Title of the prompt shown to users in the omnibox or infobar when Chrome has blocked ads on the site because the site tends to show intrusive ads. The title will stand alone next to an icon so there is no need for a period." formatter_data="android_java">
       Ads blocked
     </message>
-    <message name="IDS_BLOCKED_ADS_PROMPT_EXPLANATION" desc="Longer explanation that Chrome has blocked ads on the site. To be shown in an expanded infobar / bubble">
-      Chrome blocked ads on this site because this site tends to show intrusive ads.
+    <message name="IDS_BLOCKED_ADS_PROMPT_EXPLANATION" desc="A warning that a site has, in the past, shown intrusive or misleading ads. To be shown in an expanded infobar / bubble">
+      This site shows intrusive or misleading ads.
     </message>
     <message name="IDS_BLOCKED_ADS_PROMPT_TOOLTIP" desc="Explanation that Chrome blocked ads on this site. To be shown as a tooltip on the Ads blocked desktop bubble">
       Ads blocked on this site
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index cde4aa2..5930028 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2919,11 +2919,11 @@
   <message name="IDS_SETTINGS_SITE_SETTINGS_HANDLERS_BLOCKED" desc="The block label for protocol handlers in site settings.">
     Do not allow any site to handle protocols
   </message>
-  <message name="IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK" desc="The block label for ads in site settings.">
-    Blocked on sites that tend to show intrusive ads
+  <message name="IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK" desc="A subtitle for the ‘Ads’ setting. ‘Blocked’ refers to ads being blocked.">
+    Blocked on sites that show intrusive or misleading ads
   </message>
-  <message name="IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_RECOMMENDED" desc="The block label for ads in site settings (with the 'recommended' suffix).">
-    Blocked on sites that tend to show intrusive ads (recommended)
+  <message name="IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_RECOMMENDED" desc="A label for the ‘Ads’ setting toggle. ‘Blocked’ refers to ads being blocked.">
+    Blocked on sites that show intrusive or misleading ads (recommended)
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_SOUND_ALLOW" desc="The allow label for sound in site settings.">
     Allow sites to play sound
@@ -3031,8 +3031,11 @@
   <message name="IDS_SETTINGS_SITE_SETTINGS_SOURCE_DRM_DISABLED" desc="A label shown when the protected content / protected media identifier permission on the Site Details page is disabled because the user has turned off using unique identifiers to access protected content.">
     To change this setting, first <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>turn on identifiers<ph name="END_LINK">&lt;/a&gt;</ph>
   </message>
-  <message name="IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_SINGULAR" desc="A label shown when the Ads permission on the Site Details page is blocked by the user, but the site is not blacklisted for having bad ads.">
-    Block if site tends to show intrusive ads
+  <message name="IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_BLACKLISTED_SINGULAR" desc="A subtitle for the ‘Ads’ setting, shown if a website is known to show ads that have a poor user experience (intrusive, misleading) and is blocked from showing ads by Chrome.">
+    Site shows intrusive or misleading ads
+  </message>
+  <message name="IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_NOT_BLACKLISTED_SINGULAR" desc="A subtitle for the ‘Ads’ setting, shown if user chooses to Block ads. ‘Block’ refers to the blocking of ads.">
+    Block if site shows intrusive or misleading ads
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_SOURCE_KILL_SWITCH" desc="A label shown when a permission on the Site Details page is temporarily blocked for the user's safety.">
     Temporarily blocked to protect your security
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4099de8..3d7e89e 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2127,8 +2127,6 @@
       "android/download/service/download_background_task.cc",
       "android/download/service/download_task_scheduler.cc",
       "android/download/service/download_task_scheduler.h",
-      "android/download/video_frame_thumbnail_converter.cc",
-      "android/download/video_frame_thumbnail_converter.h",
       "android/explore_sites/catalog.cc",
       "android/explore_sites/catalog.h",
       "android/explore_sites/explore_sites_bridge.cc",
@@ -3063,6 +3061,8 @@
     sources += [
       "ash_service_registry.cc",
       "ash_service_registry.h",
+      "badging/badge_service_impl.cc",
+      "badging/badge_service_impl.h",
       "component_updater/cros_component_installer_chromeos.cc",
       "component_updater/cros_component_installer_chromeos.h",
       "component_updater/metadata_table_chromeos.cc",
@@ -4507,6 +4507,8 @@
       "android/feed/feed_image_loader_bridge.h",
       "android/feed/feed_journal_bridge.cc",
       "android/feed/feed_journal_bridge.h",
+      "android/feed/feed_lifecycle_bridge.cc",
+      "android/feed/feed_lifecycle_bridge.h",
       "android/feed/feed_logging_bridge.cc",
       "android/feed/feed_logging_bridge.h",
       "android/feed/feed_network_bridge.cc",
@@ -4789,6 +4791,7 @@
         "../android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentBridge.java",
         "../android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoaderBridge.java",
         "../android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java",
+        "../android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLifecycleBridge.java",
         "../android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java",
         "../android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNetworkBridge.java",
         "../android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedOfflineBridge.java",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 7793cb5d..ffde01a 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -186,6 +186,7 @@
   "+third_party/blink/public/platform/modules/presentation/presentation.mojom.h",
   "+third_party/blink/public/platform/modules/webauthn/authenticator.mojom.h",
   "+third_party/blink/public/platform/modules/webshare/webshare.mojom.h",
+  "+third_party/blink/public/platform/modules/badging/badging.mojom.h",
   "+third_party/blink/public/platform/oom_intervention.mojom.h",
   "+third_party/blink/public/platform/site_engagement.mojom.h",
   "+third_party/blink/public/web/window_features.mojom.h",
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 9b8865a..bc14baf 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -13,6 +13,8 @@
 #include "base/android/jni_string.h"
 #include "base/command_line.h"
 #include "chrome/browser/android/chrome_feature_list.h"
+#include "chrome/browser/autofill/personal_data_manager_factory.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/channel_info.h"
 #include "components/autofill_assistant/browser/controller.h"
 #include "components/variations/variations_associated_data.h"
@@ -145,6 +147,11 @@
   return api_key;
 }
 
+autofill::PersonalDataManager* UiControllerAndroid::GetPersonalDataManager() {
+  return autofill::PersonalDataManagerFactory::GetForProfile(
+      ProfileManager::GetLastUsedProfile());
+}
+
 std::string UiControllerAndroid::GetServerUrl() {
   return variations::GetVariationParamValueByFeature(
       chrome::android::kAutofillAssistant, "url");
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index 64846ba..8ae9a1a 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -39,6 +39,7 @@
 
   // Overrides Client:
   std::string GetApiKey() override;
+  autofill::PersonalDataManager* GetPersonalDataManager() override;
   std::string GetServerUrl() override;
   UiController* GetUiController() override;
 
diff --git a/chrome/browser/android/download/download_media_parser.cc b/chrome/browser/android/download/download_media_parser.cc
index 8bb222e..41c4a6e 100644
--- a/chrome/browser/android/download/download_media_parser.cc
+++ b/chrome/browser/android/download/download_media_parser.cc
@@ -9,15 +9,17 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
+#include "cc/paint/skia_paint_canvas.h"
 #include "chrome/browser/android/download/local_media_data_source_factory.h"
-#include "chrome/browser/android/download/video_frame_thumbnail_converter.h"
 #include "content/public/browser/android/gpu_video_accelerator_factories_provider.h"
 #include "content/public/common/service_manager_connection.h"
 #include "media/base/overlay_info.h"
+#include "media/base/video_thumbnail_decoder.h"
 #include "media/mojo/clients/mojo_video_decoder.h"
 #include "media/mojo/interfaces/constants.mojom.h"
 #include "media/mojo/interfaces/media_service.mojom.h"
 #include "media/mojo/services/media_interface_provider.h"
+#include "media/renderers/paint_canvas_video_renderer.h"
 #include "media/video/gpu_video_accelerator_factories.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
@@ -76,7 +78,7 @@
                                        weak_factory_.GetWeakPtr()));
 
   media_parser()->ParseMediaMetadata(
-      mime_type_, size_, true /* get_attached_images */, std::move(source_ptr),
+      mime_type_, size_, false /* get_attached_images */, std::move(source_ptr),
       base::BindOnce(&DownloadMediaParser::OnMediaMetadataParsed,
                      weak_factory_.GetWeakPtr()));
 }
@@ -96,10 +98,6 @@
   metadata_ = std::move(metadata);
   DCHECK(metadata_);
 
-  // TODO(xingliu): Make |attached_images| movable and use this as a thumbnail
-  // source as well as video frame.
-  attached_images_ = attached_images;
-
   // For audio file, we only need metadata and poster.
   if (base::StartsWith(mime_type_, "audio/",
                        base::CompareCase::INSENSITIVE_ASCII)) {
@@ -128,22 +126,39 @@
 
   media_parser()->ExtractVideoFrame(
       mime_type_, base::saturated_cast<uint32_t>(size_), std::move(source_ptr),
-      base::BindOnce(&DownloadMediaParser::OnEncodedVideoFrameRetrieved,
+      base::BindOnce(&DownloadMediaParser::OnVideoFrameRetrieved,
                      weak_factory_.GetWeakPtr()));
 }
 
-void DownloadMediaParser::OnEncodedVideoFrameRetrieved(
+void DownloadMediaParser::OnVideoFrameRetrieved(
     bool success,
-    const std::vector<uint8_t>& data,
+    chrome::mojom::VideoFrameDataPtr video_frame_data,
     const media::VideoDecoderConfig& config) {
-  if (data.empty()) {
+  if (!success) {
     OnError();
     return;
   }
 
-  encoded_data_ = data;
+  video_frame_data_ = std::move(video_frame_data);
   config_ = config;
 
+  // For vp8, vp9 codec, we directly do software decoding in utility process.
+  // Render now.
+  if (video_frame_data_->which() ==
+      chrome::mojom::VideoFrameData::Tag::DECODED_FRAME) {
+    decode_done_ = true;
+    RenderVideoFrame(std::move(video_frame_data_->get_decoded_frame()));
+    return;
+  }
+
+  // For other codec, the encoded frame is retrieved in utility process, send
+  // the data to GPU process to do hardware decoding.
+  if (video_frame_data_->get_encoded_data().empty()) {
+    OnError();
+    return;
+  }
+
+  // Starts to decode with MojoVideoDecoder.
   content::CreateGpuVideoAcceleratorFactories(base::BindRepeating(
       &DownloadMediaParser::OnGpuVideoAcceleratorFactoriesReady,
       weak_factory_.GetWeakPtr()));
@@ -162,83 +177,50 @@
 
   // Build and config the decoder.
   DCHECK(gpu_factories_);
-  decoder_ = std::make_unique<media::MojoVideoDecoder>(
+  auto mojo_decoder = std::make_unique<media::MojoVideoDecoder>(
       base::ThreadTaskRunnerHandle::Get(), gpu_factories_.get(), this,
       std::move(video_decoder_ptr), base::BindRepeating(&OnRequestOverlayInfo),
       gfx::ColorSpace());
 
-  decoder_->Initialize(
-      config_, false, nullptr,
-      base::BindRepeating(&DownloadMediaParser::OnVideoDecoderInitialized,
-                          weak_factory_.GetWeakPtr()),
-      base::BindRepeating(&DownloadMediaParser::OnVideoFrameDecoded,
-                          weak_factory_.GetWeakPtr()),
-      base::RepeatingClosure());
-}
+  decoder_ = std::make_unique<media::VideoThumbnailDecoder>(
+      std::move(mojo_decoder), config_,
+      std::move(video_frame_data_->get_encoded_data()));
 
-void DownloadMediaParser::OnVideoDecoderInitialized(bool success) {
-  if (!success) {
-    OnError();
-    return;
-  }
-
-  // Build the video buffer to decode.
-  auto buffer =
-      media::DecoderBuffer::CopyFrom(&encoded_data_[0], encoded_data_.size());
-  encoded_data_.clear();
-
-  // Decode one frame buffer, followed by eos buffer.
-  DCHECK_GE(decoder_->GetMaxDecodeRequests(), 2);
-  decoder_->Decode(
-      buffer, base::BindRepeating(&DownloadMediaParser::OnVideoBufferDecoded,
-                                  weak_factory_.GetWeakPtr()));
-  decoder_->Decode(media::DecoderBuffer::CreateEOSBuffer(),
-                   base::BindRepeating(&DownloadMediaParser::OnEosBufferDecoded,
-                                       weak_factory_.GetWeakPtr()));
-}
-
-void DownloadMediaParser::OnVideoBufferDecoded(media::DecodeStatus status) {
-  if (status != media::DecodeStatus::OK)
-    OnError();
-}
-
-void DownloadMediaParser::OnEosBufferDecoded(media::DecodeStatus status) {
-  if (status != media::DecodeStatus::OK)
-    OnError();
-
-  // Fails if no decoded video frame is generated when eos arrives.
-  if (!decode_done_)
-    OnError();
+  decoder_->Start(base::BindOnce(&DownloadMediaParser::OnVideoFrameDecoded,
+                                 weak_factory_.GetWeakPtr()));
 }
 
 void DownloadMediaParser::OnVideoFrameDecoded(
-    const scoped_refptr<media::VideoFrame>& frame) {
-  DCHECK(frame);
-  DCHECK(frame->HasTextures());
-  decode_done_ = true;
-
-  RenderVideoFrame(frame);
-}
-
-void DownloadMediaParser::RenderVideoFrame(
-    const scoped_refptr<media::VideoFrame>& video_frame) {
-  auto converter = VideoFrameThumbnailConverter::Create(
-      config_.codec(), gpu_factories_->GetMediaContextProvider());
-  converter->ConvertToBitmap(
-      video_frame,
-      base::BindOnce(&DownloadMediaParser::OnBitmapGenerated,
-                     weak_factory_.GetWeakPtr(), std::move(converter)));
-}
-
-void DownloadMediaParser::OnBitmapGenerated(
-    std::unique_ptr<VideoFrameThumbnailConverter>,
-    bool success,
-    SkBitmap bitmap) {
-  if (!success) {
+    scoped_refptr<media::VideoFrame> frame) {
+  if (!frame) {
     OnError();
     return;
   }
 
+  DCHECK(frame->HasTextures());
+  decode_done_ = true;
+
+  RenderVideoFrame(std::move(frame));
+}
+
+void DownloadMediaParser::RenderVideoFrame(
+    scoped_refptr<media::VideoFrame> video_frame) {
+  auto context_provider =
+      gpu_factories_ ? gpu_factories_->GetMediaContextProvider() : nullptr;
+
+  media::PaintCanvasVideoRenderer renderer;
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(video_frame->visible_rect().width(),
+                        video_frame->visible_rect().height());
+
+  // Draw the video frame to |bitmap|.
+  cc::SkiaPaintCanvas canvas(bitmap);
+  media::Context3D context =
+      context_provider ? media::Context3D(context_provider->ContextGL(),
+                                          context_provider->GrContext())
+                       : media::Context3D();
+  renderer.Copy(video_frame, &canvas, context);
+
   NotifyComplete(std::move(bitmap));
 }
 
@@ -275,8 +257,6 @@
 }
 
 void DownloadMediaParser::NotifyComplete(SkBitmap bitmap) {
-  // TODO(xingliu): Return the metadata and video thumbnail data in
-  // |parse_complete_cb_|.
   DCHECK(metadata_);
   if (parse_complete_cb_)
     std::move(parse_complete_cb_)
diff --git a/chrome/browser/android/download/download_media_parser.h b/chrome/browser/android/download/download_media_parser.h
index 148298f..1b7c209 100644
--- a/chrome/browser/android/download/download_media_parser.h
+++ b/chrome/browser/android/download/download_media_parser.h
@@ -26,10 +26,10 @@
 class MediaInterfaceProvider;
 class MojoVideoDecoder;
 class VideoDecoderConfig;
+class VideoThumbnailDecoder;
 }  // namespace media
 
 class SkBitmap;
-class VideoFrameThumbnailConverter;
 
 // Parse local media files, including media metadata and thumbnails.
 // Metadata is always parsed in utility process for both audio and video files.
@@ -69,27 +69,21 @@
 
   // Retrieves an encoded video frame.
   void RetrieveEncodedVideoFrame();
-  void OnEncodedVideoFrameRetrieved(bool success,
-                                    const std::vector<uint8_t>& data,
-                                    const media::VideoDecoderConfig& config);
+  void OnVideoFrameRetrieved(bool success,
+                             chrome::mojom::VideoFrameDataPtr video_frame_data,
+                             const media::VideoDecoderConfig& config);
 
   // Decodes the video frame.
   void OnGpuVideoAcceleratorFactoriesReady(
       std::unique_ptr<media::GpuVideoAcceleratorFactories>);
   void DecodeVideoFrame();
-  void OnVideoDecoderInitialized(bool success);
-  void OnVideoBufferDecoded(media::DecodeStatus status);
-  void OnEosBufferDecoded(media::DecodeStatus status);
-  void OnVideoFrameDecoded(
-      const scoped_refptr<media::VideoFrame>& decoded_frame);
-  media::mojom::InterfaceFactory* GetMediaInterfaceFactory();
-  void OnDecoderConnectionError();
+  void OnVideoFrameDecoded(scoped_refptr<media::VideoFrame> decoded_frame);
 
   // Renders the video frame to bitmap.
-  void RenderVideoFrame(const scoped_refptr<media::VideoFrame>& video_frame);
-  void OnBitmapGenerated(std::unique_ptr<VideoFrameThumbnailConverter>,
-                         bool success,
-                         SkBitmap bitmap);
+  void RenderVideoFrame(scoped_refptr<media::VideoFrame> video_frame);
+
+  media::mojom::InterfaceFactory* GetMediaInterfaceFactory();
+  void OnDecoderConnectionError();
 
   // Overlays media data source read operation. Gradually read data from media
   // file.
@@ -106,21 +100,21 @@
   ParseCompleteCB parse_complete_cb_;
   chrome::mojom::MediaMetadataPtr metadata_;
 
-  // Poster images obtained with |metadata_|.
-  std::vector<metadata::AttachedImage> attached_images_;
-
+  // Used to read media files chunks to feed to IPC channel.
   std::unique_ptr<chrome::mojom::MediaDataSource> media_data_source_;
 
   // The task runner to do blocking disk IO.
   scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
 
-  // Encoded video frame to be decoded. This data can be large for high
-  // resolution video, should be std::move or cleared whenever possible.
-  std::vector<uint8_t> encoded_data_;
+  // Cached video frame data, which contains either encoded frame or decoded
+  // video frame. Encoded frame is extracted with ffmpeg, the data can be large
+  // for high resolution video.
+  chrome::mojom::VideoFrameDataPtr video_frame_data_;
 
-  // Objects used to decode the video into media::VideoFrame.
+  // Objects used to decode the video into media::VideoFrame with
+  // MojoVideoDecoder.
   media::VideoDecoderConfig config_;
-  std::unique_ptr<media::MojoVideoDecoder> decoder_;
+  std::unique_ptr<media::VideoThumbnailDecoder> decoder_;
   media::mojom::InterfaceFactoryPtr media_interface_factory_;
   std::unique_ptr<media::MediaInterfaceProvider> media_interface_provider_;
   std::unique_ptr<media::GpuVideoAcceleratorFactories> gpu_factories_;
diff --git a/chrome/browser/android/download/video_frame_thumbnail_converter.cc b/chrome/browser/android/download/video_frame_thumbnail_converter.cc
deleted file mode 100644
index 208c34e..0000000
--- a/chrome/browser/android/download/video_frame_thumbnail_converter.cc
+++ /dev/null
@@ -1,66 +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 "chrome/browser/android/download/video_frame_thumbnail_converter.h"
-
-#include "base/macros.h"
-#include "cc/paint/skia_paint_canvas.h"
-#include "components/viz/common/gl_helper.h"
-#include "media/base/video_frame.h"
-#include "media/renderers/paint_canvas_video_renderer.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
-
-namespace {
-
-// Class to generate bitmap based on video frame backed by texture in remote
-// process.
-class TextureFrameThumbnailConverter : public VideoFrameThumbnailConverter {
- public:
-  TextureFrameThumbnailConverter(
-      scoped_refptr<ws::ContextProviderCommandBuffer> context_provider)
-      : context_provider_(std::move(context_provider)) {}
-  TextureFrameThumbnailConverter() = default;
-  ~TextureFrameThumbnailConverter() override = default;
-
- private:
-  // VideoFrameThumbnailConverter implementation.
-  void ConvertToBitmap(const scoped_refptr<media::VideoFrame>& frame,
-                       BitmapCallback pixel_callback) override {
-    DCHECK(frame->HasTextures())
-        << "This implementation only do texture read backs.";
-
-    // Read back the texture data contained in the video frame.
-    media::PaintCanvasVideoRenderer renderer;
-    SkBitmap skbitmap;
-    skbitmap.allocN32Pixels(frame->visible_rect().width(),
-                            frame->visible_rect().height());
-    cc::SkiaPaintCanvas canvas(skbitmap);
-    renderer.Copy(frame, &canvas,
-                  media::Context3D(context_provider_->ContextGL(),
-                                   context_provider_->GrContext()));
-
-    std::move(pixel_callback).Run(true, std::move(skbitmap));
-  }
-
-  // Command buffer channel used to read back the pixel data from texture in
-  // remote process.
-  scoped_refptr<ws::ContextProviderCommandBuffer> context_provider_;
-
-  DISALLOW_COPY_AND_ASSIGN(TextureFrameThumbnailConverter);
-};
-
-}  // namespace
-
-// static
-std::unique_ptr<VideoFrameThumbnailConverter>
-VideoFrameThumbnailConverter::Create(
-    media::VideoCodec codec,
-    scoped_refptr<ws::ContextProviderCommandBuffer> context_provider) {
-  // TODO(xingliu): Implement vpx decode and render pipeline.
-  DCHECK_NE(codec, media::VideoCodec::kCodecVP8);
-  DCHECK_NE(codec, media::VideoCodec::kCodecVP9);
-
-  return std::make_unique<TextureFrameThumbnailConverter>(
-      std::move(context_provider));
-}
diff --git a/chrome/browser/android/download/video_frame_thumbnail_converter.h b/chrome/browser/android/download/video_frame_thumbnail_converter.h
deleted file mode 100644
index 13864be..0000000
--- a/chrome/browser/android/download/video_frame_thumbnail_converter.h
+++ /dev/null
@@ -1,46 +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 CHROME_BROWSER_ANDROID_DOWNLOAD_VIDEO_FRAME_THUMBNAIL_CONVERTER_H_
-#define CHROME_BROWSER_ANDROID_DOWNLOAD_VIDEO_FRAME_THUMBNAIL_CONVERTER_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/memory/scoped_refptr.h"
-#include "media/base/video_codecs.h"
-
-namespace media {
-class VideoFrame;
-}  // namespace media
-
-namespace ws {
-class ContextProviderCommandBuffer;
-}  // namespace ws
-
-class SkBitmap;
-
-// Generates video thumbnail from a video frame. For most video format such as
-// H264, the input video frame contains a texture created in GPU process, we do
-// a readback and generate the bitmap. For vp8, vp9 format, the video frame
-// contains in-memory YUV data.
-class VideoFrameThumbnailConverter {
- public:
-  using BitmapCallback = base::OnceCallback<void(bool, SkBitmap)>;
-
-  // Create the video thumbnail renderer for video type with |codec|. The
-  // decoder to generate video frame may yields video frames backed by texture
-  // in remote process or raw YUV data.
-  static std::unique_ptr<VideoFrameThumbnailConverter> Create(
-      media::VideoCodec codec,
-      scoped_refptr<ws::ContextProviderCommandBuffer> context_provider);
-
-  virtual void ConvertToBitmap(const scoped_refptr<media::VideoFrame>& frame,
-                               BitmapCallback pixel_callback) = 0;
-
-  virtual ~VideoFrameThumbnailConverter() = default;
-};
-
-#endif  // CHROME_BROWSER_ANDROID_DOWNLOAD_VIDEO_FRAME_THUMBNAIL_CONVERTER_H_
diff --git a/chrome/browser/android/explore_sites/explore_sites_bridge.cc b/chrome/browser/android/explore_sites/explore_sites_bridge.cc
index daba02a..fae0347 100644
--- a/chrome/browser/android/explore_sites/explore_sites_bridge.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_bridge.cc
@@ -31,8 +31,16 @@
 
 void CatalogReady(ScopedJavaGlobalRef<jobject>(j_result_obj),
                   ScopedJavaGlobalRef<jobject>(j_callback_obj),
+                  GetCatalogStatus status,
                   std::unique_ptr<std::vector<ExploreSitesCategory>> result) {
   JNIEnv* env = base::android::AttachCurrentThread();
+  // TODO(dewittj): Pass along the status to Java land.
+  if (status == GetCatalogStatus::kNoCatalog) {
+    // Don't fill in the result, just return empty.
+    base::android::RunObjectCallbackAndroid(j_callback_obj, j_result_obj);
+    return;
+  }
+
   if (!result) {
     DLOG(ERROR) << "Unable to fetch the ExploreSites catalog!";
     base::android::RunObjectCallbackAndroid(j_callback_obj, nullptr);
@@ -42,7 +50,7 @@
   for (auto& category : *result) {
     ScopedJavaLocalRef<jobject> j_category =
         Java_ExploreSitesCategory_createAndAppendToList(
-            env, category.category_id,
+            env, category.category_id, category.category_type,
             ConvertUTF8ToJavaString(env, category.label), j_result_obj);
     for (auto& site : category.sites) {
       Java_ExploreSitesSite_createSiteInCategory(
@@ -55,7 +63,7 @@
 
 void ImageReady(ScopedJavaGlobalRef<jobject>(j_callback_obj),
                 std::unique_ptr<SkBitmap> bitmap) {
-  if (!bitmap) {
+  if (!bitmap || bitmap->isNull()) {
     DVLOG(1) << "Site icon is empty.";
     base::android::RunObjectCallbackAndroid(j_callback_obj, nullptr);
     return;
diff --git a/chrome/browser/android/explore_sites/explore_sites_service_impl.cc b/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
index e90fb93..2b102346 100644
--- a/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
@@ -58,8 +58,6 @@
       explore_sites_store_.get(), category_id, kFaviconsPerCategoryImage,
       base::BindOnce(&ExploreSitesServiceImpl::DecodeImageBytes,
                      std::move(callback))));
-  // TODO(dewittj, freedjm): implement.
-  std::move(callback).Run(nullptr);
 }
 
 void ExploreSitesServiceImpl::GetSiteImage(int site_id,
@@ -136,6 +134,8 @@
 // static
 void ExploreSitesServiceImpl::OnDecodeDone(BitmapCallback callback,
                                            const SkBitmap& decoded_image) {
+  DVLOG(1) << "Decoded images, result "
+           << (decoded_image.isNull() ? "null" : "non-null");
   std::unique_ptr<SkBitmap> bitmap = std::make_unique<SkBitmap>(decoded_image);
   std::move(callback).Run(std::move(bitmap));
 }
@@ -145,8 +145,11 @@
                                                EncodedImageList images) {
   // TODO(freedjm) Fix to handle multiple images when support is added for
   // creating composite images for the NTP tiles.
-  DCHECK(images.size() > 0);
-  std::unique_ptr<EncodedImageBytes> image_data = std::move(images[0]);
+  DVLOG(1) << "Requested decoding for " << images.size() << " images";
+  if (images.size() == 0) {
+    std::move(callback).Run(nullptr);
+    return;
+  }
 
   service_manager::mojom::ConnectorRequest connector_request;
   std::unique_ptr<service_manager::Connector> connector =
@@ -155,7 +158,7 @@
       ->GetConnector()
       ->BindConnectorRequest(std::move(connector_request));
 
-  data_decoder::DecodeImage(connector.get(), *image_data,
+  data_decoder::DecodeImage(connector.get(), *images[0],
                             data_decoder::mojom::ImageCodec::DEFAULT, false,
                             data_decoder::kDefaultMaxSizeInBytes, gfx::Size(),
                             base::BindOnce(&OnDecodeDone, std::move(callback)));
diff --git a/chrome/browser/android/explore_sites/explore_sites_service_impl_unittest.cc b/chrome/browser/android/explore_sites/explore_sites_service_impl_unittest.cc
index 32d43db1..e109497 100644
--- a/chrome/browser/android/explore_sites/explore_sites_service_impl_unittest.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_service_impl_unittest.cc
@@ -42,7 +42,9 @@
 
   void UpdateCatalogDoneCallback(bool success) { success_ = success; }
   void CatalogCallback(
+      GetCatalogStatus status,
       std::unique_ptr<std::vector<ExploreSitesCategory>> categories) {
+    database_status_ = status;
     if (categories != nullptr) {
       database_categories_ = std::move(categories);
     }
@@ -50,6 +52,7 @@
 
   bool success() { return success_; }
 
+  GetCatalogStatus database_status() { return database_status_; }
   std::vector<ExploreSitesCategory>* database_categories() {
     return database_categories_.get();
   }
@@ -71,6 +74,7 @@
 
   std::unique_ptr<explore_sites::ExploreSitesServiceImpl> service_;
   bool success_;
+  GetCatalogStatus database_status_;
   std::unique_ptr<std::vector<ExploreSitesCategory>> database_categories_;
   std::string test_data_;
   network::TestURLLoaderFactory test_url_loader_factory_;
@@ -117,6 +121,7 @@
 std::string ExploreSitesServiceImplTest::CreateTestDataProto() {
   std::string serialized_protobuf;
   explore_sites::GetCatalogResponse catalog_response;
+  catalog_response.set_version_token("abcd");
   explore_sites::Catalog* catalog = catalog_response.mutable_catalog();
   explore_sites::Category* category = catalog->add_categories();
   explore_sites::Site* site1 = category->add_sites();
@@ -169,11 +174,9 @@
   // TODO(petewil): Fix get catalog so it always returns data if it has some.
   service()->GetCatalog(base::BindOnce(
       &ExploreSitesServiceImplTest::CatalogCallback, base::Unretained(this)));
-  // Second call is to get the actual catalog sata into the update callback.
-  service()->GetCatalog(base::BindOnce(
-      &ExploreSitesServiceImplTest::CatalogCallback, base::Unretained(this)));
   PumpLoop();
 
+  EXPECT_EQ(GetCatalogStatus::kSuccess, database_status());
   EXPECT_NE(nullptr, database_categories());
   EXPECT_EQ(1U, database_categories()->size());
 
diff --git a/chrome/browser/android/explore_sites/explore_sites_types.h b/chrome/browser/android/explore_sites/explore_sites_types.h
index cc81b29..1956db0 100644
--- a/chrome/browser/android/explore_sites/explore_sites_types.h
+++ b/chrome/browser/android/explore_sites/explore_sites_types.h
@@ -54,8 +54,10 @@
   DISALLOW_COPY_AND_ASSIGN(ExploreSitesCategory);
 };
 
-using CatalogCallback = base::OnceCallback<void(
-    std::unique_ptr<std::vector<ExploreSitesCategory>>)>;
+enum class GetCatalogStatus { kFailed, kNoCatalog, kSuccess };
+
+using CatalogCallback = base::OnceCallback<
+    void(GetCatalogStatus, std::unique_ptr<std::vector<ExploreSitesCategory>>)>;
 using BooleanCallback = base::OnceCallback<void(bool)>;
 using EncodedImageBytes = std::vector<uint8_t>;
 using EncodedImageList = std::vector<std::unique_ptr<EncodedImageBytes>>;
diff --git a/chrome/browser/android/explore_sites/get_catalog_task.cc b/chrome/browser/android/explore_sites/get_catalog_task.cc
index 115263e..beafc29 100644
--- a/chrome/browser/android/explore_sites/get_catalog_task.cc
+++ b/chrome/browser/android/explore_sites/get_catalog_task.cc
@@ -31,16 +31,25 @@
 
 }  // namespace
 
+// |current_version_token| represents the version in "current_catalog" key of
+// the meta table.  We check whether there exists a "downloading_catalog", and
+// if there doesn't, just return the |current_version_token|.  If there is, set
+// the current == the downloading catalog and return the downloading (aka the
+// new current.);
 std::string UpdateCurrentCatalogIfNewer(sql::MetaTable* meta_table,
                                         std::string current_version_token) {
   DCHECK(meta_table);
   std::string downloading_version_token;
+  // See if there is a downloading catalog.
   if (!meta_table->GetValue("downloading_catalog",
                             &downloading_version_token)) {
+    // No downloading catalog means no change required.
     return current_version_token;
   }
 
-  if (!meta_table->SetValue("current_catalog", downloading_version_token))
+  // Update the current version.
+  current_version_token = downloading_version_token;
+  if (!meta_table->SetValue("current_catalog", current_version_token))
     return "";
   meta_table->DeleteKey("downloading_catalog");
 
@@ -49,6 +58,8 @@
 
 void RemoveOutdatedCatalogEntries(sql::Database* db,
                                   std::string version_token) {
+  // Deletes sites and categories with a version that doesn't match
+  // |version_token|.
   sql::Statement delete_sites(
       db->GetCachedStatement(SQL_FROM_HERE, kDeleteSiteSql));
   delete_sites.BindString(0, version_token);
@@ -60,45 +71,60 @@
   delete_categories.Run();
 }
 
-std::unique_ptr<GetCatalogTask::CategoryList> GetCatalogSync(
-    bool update_current,
-    sql::Database* db) {
+std::pair<GetCatalogStatus, std::unique_ptr<GetCatalogTask::CategoryList>>
+GetCatalogSync(bool update_current, sql::Database* db) {
   DCHECK(db);
   sql::MetaTable meta_table;
   if (!ExploreSitesSchema::InitMetaTable(db, &meta_table))
-    return nullptr;
+    return std::make_pair(GetCatalogStatus::kFailed, nullptr);
 
   // If we are downloading a catalog that is the same version as the one
   // currently in use, don't change it.  This is an error, should have been
   // caught before we got here.
-  std::string catalog_timestamp;
-  meta_table.GetValue("current_catalog", &catalog_timestamp);
+  std::string catalog_version_token;
+  if (!meta_table.GetValue("current_catalog", &catalog_version_token) ||
+      catalog_version_token.empty()) {
+    DVLOG(1)
+        << "Didn't find current catalog value. Attempting to use downloading.";
+    // If there is no current catalog, use downloading catalog and mark it as
+    // current.  If there is no downloading catalog, return no catalog.
+    meta_table.GetValue("downloading_catalog", &catalog_version_token);
+    if (catalog_version_token.empty())
+      return std::make_pair(GetCatalogStatus::kNoCatalog, nullptr);
+
+    update_current = true;
+  }
 
   if (update_current) {
+    DVLOG(1) << "Updating current catalog from " << catalog_version_token;
     sql::Transaction transaction(db);
     transaction.Begin();
-    catalog_timestamp =
-        UpdateCurrentCatalogIfNewer(&meta_table, catalog_timestamp);
-    if (catalog_timestamp == "")
-      return nullptr;
-    RemoveOutdatedCatalogEntries(db, catalog_timestamp);
+    catalog_version_token =
+        UpdateCurrentCatalogIfNewer(&meta_table, catalog_version_token);
+    if (catalog_version_token == "")
+      return std::make_pair(GetCatalogStatus::kFailed, nullptr);
+
+    RemoveOutdatedCatalogEntries(db, catalog_version_token);
+
     if (!transaction.Commit())
-      return nullptr;
+      return std::make_pair(GetCatalogStatus::kFailed, nullptr);
   }
 
+  DVLOG(1) << "Done updating. Catalog to use: " << catalog_version_token;
+
   sql::Statement category_statement(
       db->GetCachedStatement(SQL_FROM_HERE, kSelectCategorySql));
-  category_statement.BindString(0, catalog_timestamp);
+  category_statement.BindString(0, catalog_version_token);
 
   auto result = std::make_unique<GetCatalogTask::CategoryList>();
   while (category_statement.Step()) {
     result->emplace_back(category_statement.ColumnInt(0),  // category_id
-                         catalog_timestamp,
+                         catalog_version_token,
                          category_statement.ColumnInt(1),      // type
                          category_statement.ColumnString(2));  // label
   }
   if (!category_statement.Succeeded())
-    return nullptr;
+    return std::make_pair(GetCatalogStatus::kFailed, nullptr);
 
   for (auto& category : *result) {
     sql::Statement site_statement(
@@ -112,10 +138,10 @@
                                   site_statement.ColumnString(2));  // title
     }
     if (!site_statement.Succeeded())
-      return nullptr;
+      return std::make_pair(GetCatalogStatus::kFailed, nullptr);
   }
 
-  return result;
+  return std::make_pair(GetCatalogStatus::kSuccess, std::move(result));
 }
 
 GetCatalogTask::GetCatalogTask(ExploreSitesStore* store,
@@ -132,14 +158,16 @@
   store_->Execute(base::BindOnce(&GetCatalogSync, update_current_),
                   base::BindOnce(&GetCatalogTask::FinishedExecuting,
                                  weak_ptr_factory_.GetWeakPtr()),
-                  std::unique_ptr<CategoryList>());
+                  std::make_pair(GetCatalogStatus::kFailed,
+                                 std::unique_ptr<CategoryList>()));
 }
 
 void GetCatalogTask::FinishedExecuting(
-    std::unique_ptr<CategoryList> categories) {
+    std::pair<GetCatalogStatus, std::unique_ptr<CategoryList>> result) {
   TaskComplete();
-  DVLOG(1) << "Finished getting the catalog, result: " << categories.get();
-  std::move(callback_).Run(std::move(categories));
+  DVLOG(1) << "Finished getting the catalog, result: "
+           << static_cast<int>(std::get<0>(result));
+  std::move(callback_).Run(std::get<0>(result), std::move(std::get<1>(result)));
 }
 
 }  // namespace explore_sites
diff --git a/chrome/browser/android/explore_sites/get_catalog_task.h b/chrome/browser/android/explore_sites/get_catalog_task.h
index e5479bf..e6442fc7 100644
--- a/chrome/browser/android/explore_sites/get_catalog_task.h
+++ b/chrome/browser/android/explore_sites/get_catalog_task.h
@@ -46,7 +46,8 @@
   // Task implementation:
   void Run() override;
 
-  void FinishedExecuting(std::unique_ptr<CategoryList> categories);
+  void FinishedExecuting(
+      std::pair<GetCatalogStatus, std::unique_ptr<CategoryList>> result);
 
   ExploreSitesStore* store_;  // outlives this class.
 
diff --git a/chrome/browser/android/explore_sites/get_catalog_task_unittest.cc b/chrome/browser/android/explore_sites/get_catalog_task_unittest.cc
index 1df7dfe..d47bb70 100644
--- a/chrome/browser/android/explore_sites/get_catalog_task_unittest.cc
+++ b/chrome/browser/android/explore_sites/get_catalog_task_unittest.cc
@@ -21,6 +21,59 @@
 using offline_pages::TaskTestBase;
 
 namespace explore_sites {
+namespace {
+
+void ValidateTestingCatalog(GetCatalogTask::CategoryList* catalog) {
+  EXPECT_FALSE(catalog == nullptr);
+
+  EXPECT_EQ(2U, catalog->size());
+  ExploreSitesCategory* cat = &catalog->at(0);
+  EXPECT_EQ(3, cat->category_id);
+  EXPECT_EQ("5678", cat->version_token);
+  EXPECT_EQ(1, cat->category_type);
+  EXPECT_EQ("label_1", cat->label);
+
+  EXPECT_EQ(1U, cat->sites.size());
+  ExploreSitesSite* site = &cat->sites[0];
+  EXPECT_EQ("https://www.example.com/1", site->url.spec());
+  EXPECT_EQ(3, site->category_id);
+  EXPECT_EQ("example_1", site->title);
+
+  cat = &catalog->at(1);
+  EXPECT_EQ(4, cat->category_id);
+  EXPECT_EQ("5678", cat->version_token);
+  EXPECT_EQ(2, cat->category_type);
+  EXPECT_EQ("label_2", cat->label);
+
+  EXPECT_EQ(1U, cat->sites.size());
+  site = &cat->sites[0];
+  EXPECT_EQ("https://www.example.com/2", site->url.spec());
+  EXPECT_EQ(4, site->category_id);
+  EXPECT_EQ("example_2", site->title);
+}
+
+void ExpectSuccessGetCatalogResult(
+    GetCatalogStatus status,
+    std::unique_ptr<GetCatalogTask::CategoryList> catalog) {
+  EXPECT_EQ(GetCatalogStatus::kSuccess, status);
+  ValidateTestingCatalog(catalog.get());
+}
+
+void ExpectEmptyGetCatalogResult(
+    GetCatalogStatus status,
+    std::unique_ptr<GetCatalogTask::CategoryList> catalog) {
+  EXPECT_EQ(GetCatalogStatus::kNoCatalog, status);
+  EXPECT_TRUE(catalog == nullptr);
+}
+
+void ExpectFailedGetCatalogResult(
+    GetCatalogStatus status,
+    std::unique_ptr<GetCatalogTask::CategoryList> catalog) {
+  EXPECT_EQ(GetCatalogStatus::kFailed, status);
+  EXPECT_TRUE(catalog == nullptr);
+}
+
+}  // namespace
 
 class ExploreSitesGetCatalogTaskTest : public TaskTestBase {
  public:
@@ -87,35 +140,6 @@
   }));
 }
 
-void ExploreSitesGetCatalogTaskTest::ValidateTestingCatalog(
-    GetCatalogTask::CategoryList* catalog) {
-  EXPECT_FALSE(catalog == nullptr);
-
-  EXPECT_EQ(2U, catalog->size());
-  ExploreSitesCategory* cat = &catalog->at(0);
-  EXPECT_EQ(3, cat->category_id);
-  EXPECT_EQ("5678", cat->version_token);
-  EXPECT_EQ(1, cat->category_type);
-  EXPECT_EQ("label_1", cat->label);
-
-  EXPECT_EQ(1U, cat->sites.size());
-  ExploreSitesSite* site = &cat->sites[0];
-  EXPECT_EQ("https://www.example.com/1", site->url.spec());
-  EXPECT_EQ(3, site->category_id);
-  EXPECT_EQ("example_1", site->title);
-
-  cat = &catalog->at(1);
-  EXPECT_EQ(4, cat->category_id);
-  EXPECT_EQ("5678", cat->version_token);
-  EXPECT_EQ(2, cat->category_type);
-  EXPECT_EQ("label_2", cat->label);
-
-  EXPECT_EQ(1U, cat->sites.size());
-  site = &cat->sites[0];
-  EXPECT_EQ("https://www.example.com/2", site->url.spec());
-  EXPECT_EQ(4, site->category_id);
-  EXPECT_EQ("example_2", site->title);
-}
 
 void ExploreSitesGetCatalogTaskTest::SetDownloadingAndCurrentVersion(
     std::string downloading_version_token,
@@ -123,8 +147,16 @@
   ExecuteSync(base::BindLambdaForTesting([&](sql::Database* db) {
     sql::MetaTable meta_table;
     ExploreSitesSchema::InitMetaTable(db, &meta_table);
-    meta_table.SetValue("downloading_catalog", downloading_version_token);
-    meta_table.SetValue("current_catalog", current_version_token);
+    if (downloading_version_token.empty()) {
+      meta_table.DeleteKey("downloading_catalog");
+    } else {
+      meta_table.SetValue("downloading_catalog", downloading_version_token);
+    }
+    if (current_version_token.empty()) {
+      meta_table.DeleteKey("current_catalog");
+    } else {
+      meta_table.SetValue("current_catalog", current_version_token);
+    }
     return true;
   }));
 }
@@ -170,20 +202,14 @@
 TEST_F(ExploreSitesGetCatalogTaskTest, StoreFailure) {
   store()->SetInitializationStatusForTest(InitializationStatus::FAILURE);
 
-  auto callback = base::BindLambdaForTesting(
-      [](std::unique_ptr<GetCatalogTask::CategoryList> catalog) {
-        EXPECT_TRUE(catalog == nullptr);
-      });
-  GetCatalogTask task(store(), false, callback);
+  GetCatalogTask task(store(), false,
+                      base::BindOnce(&ExpectFailedGetCatalogResult));
   RunTask(&task);
 }
 
 TEST_F(ExploreSitesGetCatalogTaskTest, EmptyTask) {
-  auto callback = base::BindLambdaForTesting(
-      [](std::unique_ptr<GetCatalogTask::CategoryList> catalog) {
-        EXPECT_FALSE(catalog == nullptr);
-      });
-  GetCatalogTask task(store(), false, callback);
+  GetCatalogTask task(store(), false,
+                      base::BindOnce(&ExpectEmptyGetCatalogResult));
   RunTask(&task);
 }
 
@@ -192,11 +218,8 @@
 // is the "current" catalog, and where it is the "downloading" catalog.
 TEST_F(ExploreSitesGetCatalogTaskTest, SimpleCatalog) {
   PopulateTestingCatalog();
-  auto callback = base::BindLambdaForTesting(
-      [&](std::unique_ptr<GetCatalogTask::CategoryList> catalog) {
-        ValidateTestingCatalog(catalog.get());
-      });
-  GetCatalogTask task(store(), false, callback);
+  GetCatalogTask task(store(), false,
+                      base::BindOnce(&ExpectSuccessGetCatalogResult));
   RunTask(&task);
   // Since |update_current| is false, we should not have changed any rows in the
   // DB.
@@ -209,11 +232,8 @@
   // Update the testing catalog so that the older catalog is current and the
   // downloading catalog is ready to upgrade.
   SetDownloadingAndCurrentVersion("5678", "1234");
-  auto callback = base::BindLambdaForTesting(
-      [&](std::unique_ptr<GetCatalogTask::CategoryList> catalog) {
-        ValidateTestingCatalog(catalog.get());
-      });
-  GetCatalogTask task(store(), true /* update_current */, callback);
+  GetCatalogTask task(store(), true /* update_current */,
+                      base::BindOnce(&ExpectSuccessGetCatalogResult));
   RunTask(&task);
 
   EXPECT_EQ(std::make_pair(std::string("5678"), std::string()),
@@ -228,11 +248,8 @@
   // Make "1234" the downloading version, we should not see any changes in the
   // DB if the |update_current| flag is false.
   SetDownloadingAndCurrentVersion("1234", "5678");
-  auto callback = base::BindLambdaForTesting(
-      [&](std::unique_ptr<GetCatalogTask::CategoryList> catalog) {
-        ValidateTestingCatalog(catalog.get());
-      });
-  GetCatalogTask task(store(), false /* update_current */, callback);
+  GetCatalogTask task(store(), false /* update_current */,
+                      base::BindOnce(&ExpectSuccessGetCatalogResult));
   RunTask(&task);
 
   EXPECT_EQ(std::make_pair(std::string("5678"), std::string("1234")),
@@ -244,12 +261,26 @@
 TEST_F(ExploreSitesGetCatalogTaskTest, InvalidCatalogVersions) {
   PopulateTestingCatalog();
   SetDownloadingAndCurrentVersion("", "");
+  GetCatalogTask task(store(), false /* update_current */,
+                      base::BindOnce(&ExpectEmptyGetCatalogResult));
+  RunTask(&task);
+}
+
+TEST_F(ExploreSitesGetCatalogTaskTest,
+       GetCatalogWhenOnlyDownloadingCatalogExists) {
+  PopulateTestingCatalog();
+  SetDownloadingAndCurrentVersion("1234", "");
+  ExecuteSync(base::BindLambdaForTesting([&](sql::Database* db) {
+    sql::Statement cat_count(db->GetUniqueStatement(
+        "DELETE FROM categories where version_token <> \"1234\";"));
+    return cat_count.Run();
+  }));
   auto callback = base::BindLambdaForTesting(
-      [&](std::unique_ptr<GetCatalogTask::CategoryList> catalog) {
-        EXPECT_EQ(0U, catalog->size());
+      [&](GetCatalogStatus status,
+          std::unique_ptr<GetCatalogTask::CategoryList> catalog) {
+        EXPECT_NE(0U, catalog->size());
       });
   GetCatalogTask task(store(), false /* update_current */, callback);
-  RunTask(&task);
 }
 
 }  // namespace explore_sites
diff --git a/chrome/browser/android/explore_sites/import_catalog_task.cc b/chrome/browser/android/explore_sites/import_catalog_task.cc
index 8b52936..6dc99f4 100644
--- a/chrome/browser/android/explore_sites/import_catalog_task.cc
+++ b/chrome/browser/android/explore_sites/import_catalog_task.cc
@@ -36,7 +36,7 @@
 bool ImportCatalogSync(std::string version_token,
                        std::unique_ptr<Catalog> catalog_proto,
                        sql::Database* db) {
-  if (!db || !catalog_proto)
+  if (!db || !catalog_proto || version_token.empty())
     return false;
 
   sql::Transaction transaction(db);
@@ -82,7 +82,8 @@
     category_statement.BindString(col++, version_token);
     category_statement.BindInt(col++, static_cast<int>(category.type()));
     category_statement.BindString(col++, category.localized_title());
-    category_statement.BindString(col++, category.icon());
+    category_statement.BindBlob(col++, category.icon().data(),
+                                category.icon().length());
 
     category_statement.Run();
 
@@ -96,7 +97,7 @@
       site_statement.BindString(col++, site.site_url());
       site_statement.BindInt64(col++, category_id);
       site_statement.BindString(col++, site.title());
-      site_statement.BindString(col++, site.icon());
+      site_statement.BindBlob(col++, site.icon().data(), site.icon().length());
 
       site_statement.Run();
     }
diff --git a/chrome/browser/android/explore_sites/import_catalog_task_unittest.cc b/chrome/browser/android/explore_sites/import_catalog_task_unittest.cc
index a8ae2ef..de34ed9 100644
--- a/chrome/browser/android/explore_sites/import_catalog_task_unittest.cc
+++ b/chrome/browser/android/explore_sites/import_catalog_task_unittest.cc
@@ -78,7 +78,7 @@
                      base::Unretained(this)));
   RunTask(&task);
 
-  // A null catalog should be completed but return with an error.
+  // A database failure should be completed but return with an error.
   EXPECT_TRUE(task.complete());
   EXPECT_FALSE(task.result());
 }
@@ -95,6 +95,19 @@
   EXPECT_FALSE(task.result());
 }
 
+TEST_F(ExploreSitesImportCatalogTaskTest, EmptyVersion) {
+  std::string empty_version_token;
+  ImportCatalogTask task(
+      store(), empty_version_token, std::make_unique<Catalog>(),
+      base::BindOnce(&ExploreSitesImportCatalogTaskTest::OnImportTaskDone,
+                     base::Unretained(this)));
+  RunTask(&task);
+
+  // A catalog with no version should be completed but return with an error.
+  EXPECT_TRUE(task.complete());
+  EXPECT_FALSE(task.result());
+}
+
 // This tests the behavior of the catalog task when there is already a catalog
 // with the current version_token in the database. This tests both the case
 // where it is the "current" catalog, and where it is the "downloading" catalog.
diff --git a/chrome/browser/android/feed/feed_lifecycle_bridge.cc b/chrome/browser/android/feed/feed_lifecycle_bridge.cc
new file mode 100644
index 0000000..752a1824
--- /dev/null
+++ b/chrome/browser/android/feed/feed_lifecycle_bridge.cc
@@ -0,0 +1,70 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/feed/feed_lifecycle_bridge.h"
+
+#include "base/android/jni_android.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_android.h"
+#include "components/feed/feed_feature_list.h"
+#include "components/history/core/browser/history_service.h"
+#include "jni/FeedLifecycleBridge_jni.h"
+
+using base::android::JavaRef;
+using base::android::JavaParamRef;
+
+namespace feed {
+
+static jlong JNI_FeedLifecycleBridge_Init(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& j_this,
+    const JavaParamRef<jobject>& j_profile) {
+  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
+  return reinterpret_cast<intptr_t>(new FeedLifecycleBridge(profile));
+}
+
+FeedLifecycleBridge::FeedLifecycleBridge(Profile* profile) : profile_(profile) {
+  history::HistoryService* history_service =
+      HistoryServiceFactory::GetForProfile(profile_,
+                                           ServiceAccessType::IMPLICIT_ACCESS);
+  if (history_service)
+    history_service->AddObserver(this);
+}
+
+FeedLifecycleBridge::~FeedLifecycleBridge() {
+  history::HistoryService* history_service =
+      HistoryServiceFactory::GetForProfile(profile_,
+                                           ServiceAccessType::IMPLICIT_ACCESS);
+  if (history_service)
+    history_service->RemoveObserver(this);
+}
+
+void FeedLifecycleBridge::Destroy(JNIEnv* env, const JavaRef<jobject>& j_this) {
+  delete this;
+}
+
+void FeedLifecycleBridge::OnURLsDeleted(
+    history::HistoryService* history_service,
+    const history::DeletionInfo& deletion_info) {
+  DCHECK(base::FeatureList::IsEnabled(feed::kInterestFeedContentSuggestions));
+  // We ignore expirations since they're not user-initiated.
+  if (deletion_info.is_from_expiration()) {
+    return;
+  }
+
+  if (deletion_info.IsAllHistory() || deletion_info.deleted_rows().size() > 0) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_FeedLifecycleBridge_onHistoryDeleted(env);
+  }
+}
+
+// static
+void FeedLifecycleBridge::ClearCachedData() {
+  DCHECK(base::FeatureList::IsEnabled(feed::kInterestFeedContentSuggestions));
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_FeedLifecycleBridge_onCachedDataCleared(env);
+}
+
+}  // namespace feed
diff --git a/chrome/browser/android/feed/feed_lifecycle_bridge.h b/chrome/browser/android/feed/feed_lifecycle_bridge.h
new file mode 100644
index 0000000..5b483ae
--- /dev/null
+++ b/chrome/browser/android/feed/feed_lifecycle_bridge.h
@@ -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.
+
+#ifndef CHROME_BROWSER_ANDROID_FEED_FEED_LIFECYCLE_BRIDGE_H_
+#define CHROME_BROWSER_ANDROID_FEED_FEED_LIFECYCLE_BRIDGE_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "components/history/core/browser/history_service_observer.h"
+
+namespace history {
+class HistoryService;
+}
+
+class Profile;
+
+namespace feed {
+
+// Native counterpart of FeedLifecycleBridge.java. Receives lifecycle events
+// that originate in native code (namely, history deletion and cached data
+// clearing) and forwards them to Java so that the Feed library can be notified.
+class FeedLifecycleBridge : public history::HistoryServiceObserver {
+ public:
+  explicit FeedLifecycleBridge(Profile* profile);
+  ~FeedLifecycleBridge() override;
+
+  void Destroy(JNIEnv* env, const base::android::JavaRef<jobject>& j_this);
+
+  // Overridden from history::HistoryServiceObserver.
+  void OnURLsDeleted(history::HistoryService* history_service,
+                     const history::DeletionInfo& deletion_info) override;
+
+  // Triggers clearing of all data cached by the Feed library. This should only
+  // be called if Feed is enabled.
+  static void ClearCachedData();
+
+ private:
+  // Reference to the Java half of this bridge. Always valid.
+  base::android::ScopedJavaGlobalRef<jobject> j_this_;
+
+  Profile* profile_;
+
+  DISALLOW_COPY_AND_ASSIGN(FeedLifecycleBridge);
+};
+
+}  // namespace feed
+
+#endif  // CHROME_BROWSER_ANDROID_FEED_FEED_LIFECYCLE_BRIDGE_H_
diff --git a/chrome/browser/badging/OWNERS b/chrome/browser/badging/OWNERS
new file mode 100644
index 0000000..bd22cdd6
--- /dev/null
+++ b/chrome/browser/badging/OWNERS
@@ -0,0 +1 @@
+file://third_party/blink/renderer/modules/badging/OWNERS
diff --git a/chrome/browser/badging/badge_service_impl.cc b/chrome/browser/badging/badge_service_impl.cc
new file mode 100644
index 0000000..b8c88c1
--- /dev/null
+++ b/chrome/browser/badging/badge_service_impl.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 "chrome/browser/badging/badge_service_impl.h"
+
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+BadgeServiceImpl::BadgeServiceImpl() = default;
+BadgeServiceImpl::~BadgeServiceImpl() = default;
+
+// static
+void BadgeServiceImpl::Create(blink::mojom::BadgeServiceRequest request) {
+  mojo::MakeStrongBinding(std::make_unique<BadgeServiceImpl>(),
+                          std::move(request));
+}
+
+void BadgeServiceImpl::SetBadge() {
+  NOTIMPLEMENTED();
+}
+
+void BadgeServiceImpl::ClearBadge() {
+  NOTIMPLEMENTED();
+}
diff --git a/chrome/browser/badging/badge_service_impl.h b/chrome/browser/badging/badge_service_impl.h
new file mode 100644
index 0000000..3770305
--- /dev/null
+++ b/chrome/browser/badging/badge_service_impl.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 CHROME_BROWSER_BADGING_BADGE_SERVICE_IMPL_H_
+#define CHROME_BROWSER_BADGING_BADGE_SERVICE_IMPL_H_
+
+#include "base/optional.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "third_party/blink/public/platform/modules/badging/badging.mojom.h"
+
+class BadgeServiceImpl : public blink::mojom::BadgeService {
+ public:
+  BadgeServiceImpl();
+  ~BadgeServiceImpl() override;
+
+  static void Create(mojo::InterfaceRequest<BadgeService> request);
+
+  // blink::mojom::BadgeService overrides.
+  void SetBadge() override;
+  void ClearBadge() override;
+};
+
+#endif  // CHROME_BROWSER_BADGING_BADGE_SERVICE_IMPL_H_
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index c26c31c..f5174a6 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -104,12 +104,14 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/customtabs/origin_verifier.h"
+#include "chrome/browser/android/feed/feed_lifecycle_bridge.h"
 #include "chrome/browser/android/oom_intervention/oom_intervention_decider.h"
 #include "chrome/browser/android/search_permissions/search_permissions_service.h"
 #include "chrome/browser/android/webapps/webapp_registry.h"
 #include "chrome/browser/media/android/cdm/media_drm_license_manager.h"
 #include "chrome/browser/offline_pages/offline_page_model_factory.h"
 #include "components/feed/buildflags.h"
+#include "components/feed/feed_feature_list.h"
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/offline_pages/core/offline_page_model.h"
 #include "sql/database.h"
@@ -897,6 +899,13 @@
     if (content_suggestions_service)
       content_suggestions_service->ClearAllCachedSuggestions();
 
+#if defined(OS_ANDROID)
+#if BUILDFLAG(ENABLE_FEED_IN_CHROME)
+    if (base::FeatureList::IsEnabled(feed::kInterestFeedContentSuggestions))
+      feed::FeedLifecycleBridge::ClearCachedData();
+#endif  // BUILDFLAG(ENABLE_FEED_IN_CHROME)
+#endif  // defined(OS_ANDROID)
+
     // |ui_nqe_service| may be null if |profile_| is not a regular profile.
     UINetworkQualityEstimatorService* ui_nqe_service =
         UINetworkQualityEstimatorServiceFactory::GetForProfile(profile_);
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 92039b7..b915c89 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -314,6 +314,7 @@
 #elif defined(OS_CHROMEOS)
 #include "ash/public/interfaces/constants.mojom.h"
 #include "chrome/browser/ash_service_registry.h"
+#include "chrome/browser/badging/badge_service_impl.h"
 #include "chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_content_file_system_backend_delegate.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_backend_delegate.h"
@@ -4247,6 +4248,11 @@
   frame_interfaces_->AddInterface(base::Bind(&ShareServiceImpl::Create));
 #endif
 
+#if defined(OS_CHROMEOS)
+  frame_interfaces_->AddInterface(
+      base::BindRepeating(&BadgeServiceImpl::Create));
+#endif
+
   frame_interfaces_parameterized_->AddInterface(
       base::BindRepeating(&NavigationPredictor::Create));
 }
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index 3ad35d2..2e5854f 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -81,6 +81,7 @@
         "renderer": [
           "autofill.mojom.AutofillDriver",
           "autofill.mojom.PasswordManagerDriver",
+          "blink.mojom.BadgeService",
           "blink.mojom.CredentialManager",
           "blink.mojom.InstalledAppProvider",
           "blink.mojom.MediaDownloadInProductHelp",
diff --git a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc
index 394c699..887868b3 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc
@@ -53,6 +53,11 @@
   locale_ = locale;
 }
 
+void FakeVoiceInteractionController::NotifyLaunchWithMicOpen(
+    bool launch_with_mic_open) {
+  launch_with_mic_open_ = launch_with_mic_open;
+}
+
 void FakeVoiceInteractionController::IsSettingEnabled(
     IsSettingEnabledCallback callback) {
   std::move(callback).Run(voice_interaction_settings_enabled_);
diff --git a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h
index 43dcd76..fc5bd0d8 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h
@@ -27,6 +27,7 @@
   void NotifyFeatureAllowed(ash::mojom::AssistantAllowedState state) override;
   void NotifyNotificationEnabled(bool enabled) override;
   void NotifyLocaleChanged(const std::string& locale) override;
+  void NotifyLaunchWithMicOpen(bool launch_with_mic_open) override;
   void IsSettingEnabled(IsSettingEnabledCallback callback) override;
   void IsSetupCompleted(IsSetupCompletedCallback callback) override;
   void IsContextEnabled(IsContextEnabledCallback callback) override;
@@ -54,8 +55,8 @@
   bool voice_interaction_notification_enabled() const {
     return voice_interaction_notification_enabled_;
   }
-
   const std::string& locale() const { return locale_; }
+  bool launch_with_mic_open() const { return launch_with_mic_open_; }
 
  private:
   ash::mojom::VoiceInteractionState voice_interaction_state_ =
@@ -68,6 +69,7 @@
   std::string locale_;
   ash::mojom::AssistantAllowedState assistant_allowed_state_ =
       ash::mojom::AssistantAllowedState::DISALLOWED_BY_INCOGNITO;
+  bool launch_with_mic_open_ = false;
 
   mojo::Binding<ash::mojom::VoiceInteractionController> binding_;
 
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
index ba33cde..92e70b30df 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
@@ -145,6 +145,14 @@
   voice_interaction_controller_->NotifyLocaleChanged(out_locale);
 }
 
+void VoiceInteractionControllerClient::NotifyLaunchWithMicOpen() {
+  DCHECK(profile_);
+  PrefService* prefs = profile_->GetPrefs();
+  bool voice_preferred =
+      prefs->GetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen);
+  voice_interaction_controller_->NotifyLaunchWithMicOpen(voice_preferred);
+}
+
 void VoiceInteractionControllerClient::ActiveUserChanged(
     const user_manager::User* active_user) {
   if (active_user && active_user->is_profile_created())
@@ -212,12 +220,18 @@
       base::BindRepeating(
           &VoiceInteractionControllerClient::NotifyNotificationEnabled,
           base::Unretained(this)));
+  pref_change_registrar_->Add(
+      prefs::kVoiceInteractionLaunchWithMicOpen,
+      base::BindRepeating(
+          &VoiceInteractionControllerClient::NotifyLaunchWithMicOpen,
+          base::Unretained(this)));
 
   NotifySetupCompleted();
   NotifySettingsEnabled();
   NotifyContextEnabled();
   NotifyLocaleChanged();
   NotifyNotificationEnabled();
+  NotifyLaunchWithMicOpen();
   if (prefs->GetBoolean(prefs::kVoiceInteractionEnabled))
     NotifyHotwordEnabled();
 }
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
index 025cb62..d7e7a1d 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
@@ -60,6 +60,7 @@
   void NotifyFeatureAllowed();
   void NotifyNotificationEnabled();
   void NotifyLocaleChanged();
+  void NotifyLaunchWithMicOpen();
 
   // user_manager::UserManager::UserSessionStateObserver overrides:
   void ActiveUserChanged(const user_manager::User* active_user) override;
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
index 47f7435..62a5f902 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
@@ -153,6 +153,13 @@
   ASSERT_EQ("en-CA", prefs->GetString(language::prefs::kApplicationLocale));
   voice_interaction_controller_client()->FlushMojoForTesting();
   EXPECT_EQ("en-CA", voice_interaction_controller()->locale());
+
+  ASSERT_EQ(false,
+            prefs->GetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen));
+  prefs->SetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen, true);
+  ASSERT_EQ(true, prefs->GetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen));
+  voice_interaction_controller_client()->FlushMojoForTesting();
+  EXPECT_EQ(true, voice_interaction_controller()->launch_with_mic_open());
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service.cc b/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service.cc
index db86685..4cedc885 100644
--- a/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service.cc
+++ b/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service.cc
@@ -233,6 +233,9 @@
 }
 
 void ChromeCryptAuthService::OnPrefsChanged() {
+  if (!identity_manager_->HasPrimaryAccountWithRefreshToken())
+    return;
+
   // Note: We only start the CryptAuth services if a feature was toggled on. In
   // the inverse case, we simply leave the services running until the user logs
   // off.
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 7211d982..48ee56e 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -8,6 +8,11 @@
 #include <sstream>
 #include <utility>
 
+#include "ash/public/interfaces/ash_message_center_controller.mojom.h"
+#include "ash/public/interfaces/constants.mojom.h"
+#include "ash/shell.h"
+#include "base/base64.h"
+#include "base/feature_list.h"
 #include "base/lazy_instance.h"
 #include "base/metrics/histogram_base.h"
 #include "base/metrics/histogram_samples.h"
@@ -16,11 +21,31 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/crostini/crostini_manager.h"
+#include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
+#include "chrome/browser/chromeos/crostini/crostini_util.h"
+#include "chrome/browser/chromeos/login/lock/screen_locker.h"
+#include "chrome/browser/chromeos/printing/cups_printers_manager.h"
+#include "chrome/browser/chromeos/system/input_device_settings.h"
 #include "chrome/browser/extensions/extension_action_manager.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/ash/login_screen_client.h"
+#include "chrome/browser/ui/views/crostini/crostini_installer_view.h"
+#include "chrome/browser/ui/views/crostini/crostini_uninstaller_view.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/extensions/api/autotest_private.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/session_manager_client.h"
+#include "chromeos/printing/printer_configuration.h"
+#include "chromeos/services/machine_learning/public/cpp/service_connection.h"
+#include "components/arc/arc_prefs.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/common/service_manager_connection.h"
 #include "extensions/browser/extension_function_registry.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
@@ -30,44 +55,23 @@
 #include "extensions/common/permissions/api_permission_set.h"
 #include "extensions/common/permissions/permission_set.h"
 #include "extensions/common/permissions/permissions_data.h"
-
-#if defined(OS_CHROMEOS)
-#include "ash/public/interfaces/ash_message_center_controller.mojom.h"
-#include "ash/public/interfaces/constants.mojom.h"
-#include "ash/shell.h"
-#include "base/base64.h"
-#include "base/feature_list.h"
-#include "chrome/browser/chromeos/arc/arc_util.h"
-#include "chrome/browser/chromeos/crostini/crostini_manager.h"
-#include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
-#include "chrome/browser/chromeos/crostini/crostini_util.h"
-#include "chrome/browser/chromeos/login/lock/screen_locker.h"
-#include "chrome/browser/chromeos/printing/cups_printers_manager.h"
-#include "chrome/browser/chromeos/system/input_device_settings.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
-#include "chrome/browser/ui/ash/login_screen_client.h"
-#include "chrome/browser/ui/views/crostini/crostini_installer_view.h"
-#include "chrome/browser/ui/views/crostini/crostini_uninstaller_view.h"
-#include "chrome/common/chrome_features.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/session_manager_client.h"
-#include "chromeos/printing/printer_configuration.h"
-#include "chromeos/services/machine_learning/public/cpp/service_connection.h"
-#include "components/arc/arc_prefs.h"
-#include "components/user_manager/user_manager.h"
-#include "content/public/common/service_manager_connection.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "net/base/filename_util.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/display/screen.h"
 #include "ui/message_center/public/cpp/notification.h"
-#endif
 
 namespace extensions {
 namespace {
 
+constexpr char kCrostiniNotAvailableForCurrentUserError[] =
+    "Crostini is not available for the current user";
+
+int AccessArray(const volatile int arr[], const volatile int* index) {
+  return arr[*index];
+}
+
 std::unique_ptr<base::ListValue> GetHostPermissions(const Extension* ext,
                                                     bool effective_perm) {
   const PermissionsData* permissions_data = ext->permissions_data();
@@ -99,7 +103,6 @@
   return AutotestPrivateAPI::GetFactoryInstance()->Get(context)->test_mode();
 }
 
-#if defined(OS_CHROMEOS)
 std::string ConvertToString(message_center::NotificationType type) {
   switch (type) {
     case message_center::NOTIFICATION_TYPE_SIMPLE:
@@ -130,17 +133,27 @@
   return result;
 }
 
-constexpr char kCrostiniNotAvailableForCurrentUserError[] =
-    "Crostini is not available for the current user";
-
-#else
-
-constexpr char kOnlyAvailableOnChromeOSError[] = "Only available on ChromeOS";
-
-#endif
+std::string GetPrinterType(chromeos::CupsPrintersManager::PrinterClass type) {
+  switch (type) {
+    case chromeos::CupsPrintersManager::PrinterClass::kConfigured:
+      return "configured";
+    case chromeos::CupsPrintersManager::PrinterClass::kEnterprise:
+      return "enterprise";
+    case chromeos::CupsPrintersManager::PrinterClass::kAutomatic:
+      return "automatic";
+    case chromeos::CupsPrintersManager::PrinterClass::kDiscovered:
+      return "discovered";
+    default:
+      return "unknown";
+  }
+}
 
 }  // namespace
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateLogoutFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateLogoutFunction::~AutotestPrivateLogoutFunction() = default;
 
 ExtensionFunction::ResponseAction AutotestPrivateLogoutFunction::Run() {
@@ -150,6 +163,10 @@
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateRestartFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateRestartFunction::~AutotestPrivateRestartFunction() = default;
 
 ExtensionFunction::ResponseAction AutotestPrivateRestartFunction::Run() {
@@ -159,13 +176,16 @@
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateShutdownFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateShutdownFunction::~AutotestPrivateShutdownFunction() = default;
 
 ExtensionFunction::ResponseAction AutotestPrivateShutdownFunction::Run() {
   std::unique_ptr<api::autotest_private::Shutdown::Params> params(
       api::autotest_private::Shutdown::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-
   DVLOG(1) << "AutotestPrivateShutdownFunction " << params->force;
 
   if (!IsTestMode(browser_context()))
@@ -173,22 +193,21 @@
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateLoginStatusFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateLoginStatusFunction::~AutotestPrivateLoginStatusFunction() =
     default;
 
 ExtensionFunction::ResponseAction AutotestPrivateLoginStatusFunction::Run() {
   DVLOG(1) << "AutotestPrivateLoginStatusFunction";
 
-#if defined(OS_CHROMEOS)
   LoginScreenClient::Get()->login_screen()->IsReadyForPassword(base::BindOnce(
       &AutotestPrivateLoginStatusFunction::OnIsReadyForPassword, this));
   return RespondLater();
-#else
-  return RespondNow(OneArgument(std::make_unique<base::DictionaryValue>()));
-#endif
 }
 
-#if defined(OS_CHROMEOS)
 void AutotestPrivateLoginStatusFunction::OnIsReadyForPassword(bool is_ready) {
   auto result = std::make_unique<base::DictionaryValue>();
   const user_manager::UserManager* user_manager =
@@ -234,21 +253,27 @@
   }
   Respond(OneArgument(std::move(result)));
 }
-#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateLockScreenFunction
+///////////////////////////////////////////////////////////////////////////////
 
 AutotestPrivateLockScreenFunction::~AutotestPrivateLockScreenFunction() =
     default;
 
 ExtensionFunction::ResponseAction AutotestPrivateLockScreenFunction::Run() {
   DVLOG(1) << "AutotestPrivateLockScreenFunction";
-#if defined(OS_CHROMEOS)
+
   chromeos::DBusThreadManager::Get()
       ->GetSessionManagerClient()
       ->RequestLockScreen();
-#endif
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateGetExtensionsInfoFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateGetExtensionsInfoFunction::
     ~AutotestPrivateGetExtensionsInfoFunction() = default;
 
@@ -311,9 +336,9 @@
   return RespondNow(OneArgument(std::move(return_value)));
 }
 
-static int AccessArray(const volatile int arr[], const volatile int* index) {
-  return arr[*index];
-}
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateSimulateAsanMemoryBugFunction
+///////////////////////////////////////////////////////////////////////////////
 
 AutotestPrivateSimulateAsanMemoryBugFunction::
     ~AutotestPrivateSimulateAsanMemoryBugFunction() = default;
@@ -321,6 +346,7 @@
 ExtensionFunction::ResponseAction
 AutotestPrivateSimulateAsanMemoryBugFunction::Run() {
   DVLOG(1) << "AutotestPrivateSimulateAsanMemoryBugFunction";
+
   if (!IsTestMode(browser_context())) {
     // This array is volatile not to let compiler optimize us out.
     volatile int testarray[3] = {0, 0, 0};
@@ -332,6 +358,10 @@
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateSetTouchpadSensitivityFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateSetTouchpadSensitivityFunction::
     ~AutotestPrivateSetTouchpadSensitivityFunction() = default;
 
@@ -340,16 +370,17 @@
   std::unique_ptr<api::autotest_private::SetTouchpadSensitivity::Params> params(
       api::autotest_private::SetTouchpadSensitivity::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-
   DVLOG(1) << "AutotestPrivateSetTouchpadSensitivityFunction " << params->value;
 
-#if defined(OS_CHROMEOS)
   chromeos::system::InputDeviceSettings::Get()->SetTouchpadSensitivity(
       params->value);
-#endif
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateSetTapToClickFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateSetTapToClickFunction::~AutotestPrivateSetTapToClickFunction() =
     default;
 
@@ -357,15 +388,16 @@
   std::unique_ptr<api::autotest_private::SetTapToClick::Params> params(
       api::autotest_private::SetTapToClick::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-
   DVLOG(1) << "AutotestPrivateSetTapToClickFunction " << params->enabled;
 
-#if defined(OS_CHROMEOS)
   chromeos::system::InputDeviceSettings::Get()->SetTapToClick(params->enabled);
-#endif
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateSetThreeFingerClickFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateSetThreeFingerClickFunction::
     ~AutotestPrivateSetThreeFingerClickFunction() = default;
 
@@ -374,16 +406,17 @@
   std::unique_ptr<api::autotest_private::SetThreeFingerClick::Params> params(
       api::autotest_private::SetThreeFingerClick::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-
   DVLOG(1) << "AutotestPrivateSetThreeFingerClickFunction " << params->enabled;
 
-#if defined(OS_CHROMEOS)
   chromeos::system::InputDeviceSettings::Get()->SetThreeFingerClick(
       params->enabled);
-#endif
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateSetTapDraggingFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateSetTapDraggingFunction::
     ~AutotestPrivateSetTapDraggingFunction() = default;
 
@@ -391,15 +424,16 @@
   std::unique_ptr<api::autotest_private::SetTapDragging::Params> params(
       api::autotest_private::SetTapDragging::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-
   DVLOG(1) << "AutotestPrivateSetTapDraggingFunction " << params->enabled;
 
-#if defined(OS_CHROMEOS)
   chromeos::system::InputDeviceSettings::Get()->SetTapDragging(params->enabled);
-#endif
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateSetNaturalScrollFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateSetNaturalScrollFunction::
     ~AutotestPrivateSetNaturalScrollFunction() = default;
 
@@ -408,16 +442,17 @@
   std::unique_ptr<api::autotest_private::SetNaturalScroll::Params> params(
       api::autotest_private::SetNaturalScroll::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-
   DVLOG(1) << "AutotestPrivateSetNaturalScrollFunction " << params->enabled;
 
-#if defined(OS_CHROMEOS)
   chromeos::system::InputDeviceSettings::Get()->SetNaturalScroll(
       params->enabled);
-#endif
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateSetMouseSensitivityFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateSetMouseSensitivityFunction::
     ~AutotestPrivateSetMouseSensitivityFunction() = default;
 
@@ -426,16 +461,17 @@
   std::unique_ptr<api::autotest_private::SetMouseSensitivity::Params> params(
       api::autotest_private::SetMouseSensitivity::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-
   DVLOG(1) << "AutotestPrivateSetMouseSensitivityFunction " << params->value;
 
-#if defined(OS_CHROMEOS)
   chromeos::system::InputDeviceSettings::Get()->SetMouseSensitivity(
       params->value);
-#endif
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateSetPrimaryButtonRightFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateSetPrimaryButtonRightFunction::
     ~AutotestPrivateSetPrimaryButtonRightFunction() = default;
 
@@ -444,16 +480,17 @@
   std::unique_ptr<api::autotest_private::SetPrimaryButtonRight::Params> params(
       api::autotest_private::SetPrimaryButtonRight::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-
   DVLOG(1) << "AutotestPrivateSetPrimaryButtonRightFunction " << params->right;
 
-#if defined(OS_CHROMEOS)
   chromeos::system::InputDeviceSettings::Get()->SetPrimaryButtonRight(
       params->right);
-#endif
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateSetMouseReverseScrollFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateSetMouseReverseScrollFunction::
     ~AutotestPrivateSetMouseReverseScrollFunction() = default;
 
@@ -462,17 +499,18 @@
   std::unique_ptr<api::autotest_private::SetMouseReverseScroll::Params> params(
       api::autotest_private::SetMouseReverseScroll::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-
   DVLOG(1) << "AutotestPrivateSetMouseReverseScrollFunction "
            << params->enabled;
 
-#if defined(OS_CHROMEOS)
   chromeos::system::InputDeviceSettings::Get()->SetMouseReverseScroll(
       params->enabled);
-#endif
   return RespondNow(NoArguments());
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateGetVisibleNotificationsFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateGetVisibleNotificationsFunction::
     AutotestPrivateGetVisibleNotificationsFunction() = default;
 AutotestPrivateGetVisibleNotificationsFunction::
@@ -481,7 +519,7 @@
 ExtensionFunction::ResponseAction
 AutotestPrivateGetVisibleNotificationsFunction::Run() {
   DVLOG(1) << "AutotestPrivateGetVisibleNotificationsFunction";
-#if defined(OS_CHROMEOS)
+
   auto* connection = content::ServiceManagerConnection::GetForProcess();
   connection->GetConnector()->BindInterface(ash::mojom::kServiceName,
                                             &controller_);
@@ -489,12 +527,8 @@
       &AutotestPrivateGetVisibleNotificationsFunction::OnGotNotifications,
       this));
   return RespondLater();
-#else
-  return RespondNow(OneArgument(std::make_unique<base::ListValue>()));
-#endif
 }
 
-#if defined(OS_CHROMEOS)
 void AutotestPrivateGetVisibleNotificationsFunction::OnGotNotifications(
     const std::vector<message_center::Notification>& notifications) {
   auto values = std::make_unique<base::ListValue>();
@@ -504,109 +538,9 @@
   Respond(OneArgument(std::move(values)));
 }
 
-// static
-std::string AutotestPrivateGetPrinterListFunction::GetPrinterType(
-    chromeos::CupsPrintersManager::PrinterClass type) {
-  switch (type) {
-    case chromeos::CupsPrintersManager::PrinterClass::kConfigured:
-      return "configured";
-    case chromeos::CupsPrintersManager::PrinterClass::kEnterprise:
-      return "enterprise";
-    case chromeos::CupsPrintersManager::PrinterClass::kAutomatic:
-      return "automatic";
-    case chromeos::CupsPrintersManager::PrinterClass::kDiscovered:
-      return "discovered";
-    default:
-      return "unknown";
-  }
-}
-#endif
-
-AutotestPrivateGetPrinterListFunction::
-    ~AutotestPrivateGetPrinterListFunction() = default;
-
-ExtensionFunction::ResponseAction AutotestPrivateGetPrinterListFunction::Run() {
-  DVLOG(1) << "AutotestPrivateGetPrinterListFunction";
-  auto values = std::make_unique<base::ListValue>();
-#if defined(OS_CHROMEOS)
-  Profile* profile = ProfileManager::GetActiveUserProfile();
-  std::unique_ptr<chromeos::CupsPrintersManager> printers_manager =
-      chromeos::CupsPrintersManager::Create(profile);
-  std::vector<chromeos::CupsPrintersManager::PrinterClass> printer_type = {
-      chromeos::CupsPrintersManager::PrinterClass::kConfigured,
-      chromeos::CupsPrintersManager::PrinterClass::kEnterprise,
-      chromeos::CupsPrintersManager::PrinterClass::kAutomatic};
-  for (const auto& type : printer_type) {
-    std::vector<chromeos::Printer> printer_list =
-        printers_manager->GetPrinters(type);
-    for (const auto& printer : printer_list) {
-      auto result = std::make_unique<base::DictionaryValue>();
-      result->SetString("printerName", printer.display_name());
-      result->SetString("printerId", printer.id());
-      result->SetString("printerType", GetPrinterType(type));
-      values->Append(std::move(result));
-    }
-  }
-#endif
-  return RespondNow(OneArgument(std::move(values)));
-}
-
-AutotestPrivateUpdatePrinterFunction::AutotestPrivateUpdatePrinterFunction() =
-    default;
-AutotestPrivateUpdatePrinterFunction::~AutotestPrivateUpdatePrinterFunction() =
-    default;
-
-ExtensionFunction::ResponseAction AutotestPrivateUpdatePrinterFunction::Run() {
-  std::unique_ptr<api::autotest_private::UpdatePrinter::Params> params(
-      api::autotest_private::UpdatePrinter::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params);
-  DVLOG(1) << "AutotestPrivateUpdatePrinterFunction";
-#if defined(OS_CHROMEOS)
-  const api::autotest_private::Printer& js_printer = params->printer;
-  chromeos::Printer printer(js_printer.printer_id ? *js_printer.printer_id
-                                                  : "");
-  printer.set_display_name(js_printer.printer_name);
-  if (js_printer.printer_desc)
-    printer.set_description(*js_printer.printer_desc);
-
-  if (js_printer.printer_make_and_model)
-    printer.set_make_and_model(*js_printer.printer_make_and_model);
-
-  if (js_printer.printer_uri)
-    printer.set_uri(*js_printer.printer_uri);
-
-  if (js_printer.printer_ppd) {
-    const GURL ppd =
-        net::FilePathToFileURL(base::FilePath(*js_printer.printer_ppd));
-    if (ppd.is_valid())
-      printer.mutable_ppd_reference()->user_supplied_ppd_url = ppd.spec();
-    else
-      LOG(ERROR) << "Invalid ppd path: " << *js_printer.printer_ppd;
-  }
-  auto printers_manager = chromeos::CupsPrintersManager::Create(
-      ProfileManager::GetActiveUserProfile());
-  printers_manager->UpdateConfiguredPrinter(printer);
-#endif
-  return RespondNow(NoArguments());
-}
-
-AutotestPrivateRemovePrinterFunction::AutotestPrivateRemovePrinterFunction() =
-    default;
-AutotestPrivateRemovePrinterFunction::~AutotestPrivateRemovePrinterFunction() =
-    default;
-
-ExtensionFunction::ResponseAction AutotestPrivateRemovePrinterFunction::Run() {
-  std::unique_ptr<api::autotest_private::RemovePrinter::Params> params(
-      api::autotest_private::RemovePrinter::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params);
-  DVLOG(1) << "AutotestPrivateRemovePrinterFunction";
-#if defined(OS_CHROMEOS)
-  auto printers_manager = chromeos::CupsPrintersManager::Create(
-      ProfileManager::GetActiveUserProfile());
-  printers_manager->RemoveConfiguredPrinter(params->printer_id);
-#endif
-  return RespondNow(NoArguments());
-}
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateGetPlayStoreStateFunction
+///////////////////////////////////////////////////////////////////////////////
 
 AutotestPrivateGetPlayStoreStateFunction::
     ~AutotestPrivateGetPlayStoreStateFunction() = default;
@@ -614,10 +548,10 @@
 ExtensionFunction::ResponseAction
 AutotestPrivateGetPlayStoreStateFunction::Run() {
   DVLOG(1) << "AutotestPrivateGetPlayStoreStateFunction";
+
   api::autotest_private::PlayStoreState play_store_state;
   play_store_state.allowed = false;
-#if defined(OS_CHROMEOS)
-  Profile* profile = ProfileManager::GetActiveUserProfile();
+  Profile* profile = Profile::FromBrowserContext(browser_context());
   if (arc::IsArcAllowedForProfile(profile)) {
     play_store_state.allowed = true;
     play_store_state.enabled =
@@ -625,21 +559,24 @@
     play_store_state.managed = std::make_unique<bool>(
         arc::IsArcPlayStoreEnabledPreferenceManagedForProfile(profile));
   }
-#endif
   return RespondNow(OneArgument(play_store_state.ToValue()));
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateSetPlayStoreEnabledFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateSetPlayStoreEnabledFunction::
     ~AutotestPrivateSetPlayStoreEnabledFunction() = default;
 
 ExtensionFunction::ResponseAction
 AutotestPrivateSetPlayStoreEnabledFunction::Run() {
-  DVLOG(1) << "AutotestPrivateSetPlayStoreEnabledFunction";
-#if defined(OS_CHROMEOS)
   std::unique_ptr<api::autotest_private::SetPlayStoreEnabled::Params> params(
       api::autotest_private::SetPlayStoreEnabled::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params);
-  Profile* profile = ProfileManager::GetActiveUserProfile();
+  DVLOG(1) << "AutotestPrivateSetPlayStoreEnabledFunction " << params->enabled;
+
+  Profile* profile = Profile::FromBrowserContext(browser_context());
   if (arc::IsArcAllowedForProfile(profile)) {
     if (!arc::SetArcPlayStoreEnabledForProfile(profile, params->enabled)) {
       return RespondNow(
@@ -651,19 +588,20 @@
   } else {
     return RespondNow(Error("ARC is not available for the current user"));
   }
-#else
-  return RespondNow(Error(kOnlyAvailableOnChromeOSError));
-#endif
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateGetHistogramFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateGetHistogramFunction::~AutotestPrivateGetHistogramFunction() =
     default;
 
 ExtensionFunction::ResponseAction AutotestPrivateGetHistogramFunction::Run() {
-  DVLOG(1) << "AutotestPrivateGetHistogramFunction";
   std::unique_ptr<api::autotest_private::GetHistogram::Params> params(
       api::autotest_private::GetHistogram::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params);
+  DVLOG(1) << "AutotestPrivateGetHistogramFunction " << params->name;
 
   const base::HistogramBase* histogram =
       base::StatisticsRecorder::FindHistogram(params->name);
@@ -693,15 +631,19 @@
   return RespondNow(OneArgument(result.ToValue()));
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateIsAppShownFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateIsAppShownFunction::~AutotestPrivateIsAppShownFunction() =
     default;
 
 ExtensionFunction::ResponseAction AutotestPrivateIsAppShownFunction::Run() {
-  DVLOG(1) << "AutotestPrivateIsAppShownFunction";
-#if defined(OS_CHROMEOS)
   std::unique_ptr<api::autotest_private::IsAppShown::Params> params(
       api::autotest_private::IsAppShown::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params);
+  DVLOG(1) << "AutotestPrivateIsAppShownFunction " << params->app_id;
+
   ChromeLauncherController* const controller =
       ChromeLauncherController::instance();
   if (!controller)
@@ -713,19 +655,20 @@
       item && item->status == ash::ShelfItemStatus::STATUS_RUNNING;
   return RespondNow(
       OneArgument(std::make_unique<base::Value>(window_attached)));
-#else
-  return RespondNow(Error(kOnlyAvailableOnChromeOSError));
-#endif
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateLaunchAppFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateLaunchAppFunction::~AutotestPrivateLaunchAppFunction() = default;
 
 ExtensionFunction::ResponseAction AutotestPrivateLaunchAppFunction::Run() {
-  DVLOG(1) << "AutotestPrivateLaunchAppFunction";
-#if defined(OS_CHROMEOS)
   std::unique_ptr<api::autotest_private::LaunchApp::Params> params(
       api::autotest_private::LaunchApp::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params);
+  DVLOG(1) << "AutotestPrivateLaunchAppFunction " << params->app_id;
+
   ChromeLauncherController* const controller =
       ChromeLauncherController::instance();
   if (!controller)
@@ -735,21 +678,22 @@
                         0, /* event_flags */
                         display::Screen::GetScreen()->GetPrimaryDisplay().id());
   return RespondNow(NoArguments());
-#else
-  return RespondNow(Error(kOnlyAvailableOnChromeOSError));
-#endif
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateSetCrostiniEnabledFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateSetCrostiniEnabledFunction::
     ~AutotestPrivateSetCrostiniEnabledFunction() = default;
 
 ExtensionFunction::ResponseAction
 AutotestPrivateSetCrostiniEnabledFunction::Run() {
-  DVLOG(1) << "AutotestPrivateSetCrostiniEnabledFunction";
-#if defined(OS_CHROMEOS)
   std::unique_ptr<api::autotest_private::SetCrostiniEnabled::Params> params(
       api::autotest_private::SetCrostiniEnabled::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params);
+  DVLOG(1) << "AutotestPrivateSetCrostiniEnabledFunction " << params->enabled;
+
   Profile* profile = Profile::FromBrowserContext(browser_context());
   if (!IsCrostiniUIAllowedForProfile(profile))
     return RespondNow(Error(kCrostiniNotAvailableForCurrentUserError));
@@ -763,18 +707,19 @@
       crostini::CrostiniManager::GetForProfile(profile);
   crostini_manager->set_skip_restart_for_testing();
   return RespondNow(NoArguments());
-#else
-  return RespondNow(Error(kOnlyAvailableOnChromeOSError));
-#endif
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateRunCrostiniInstallerFunction
+///////////////////////////////////////////////////////////////////////////////
+
 AutotestPrivateRunCrostiniInstallerFunction::
     ~AutotestPrivateRunCrostiniInstallerFunction() = default;
 
 ExtensionFunction::ResponseAction
 AutotestPrivateRunCrostiniInstallerFunction::Run() {
   DVLOG(1) << "AutotestPrivateInstallCrostiniFunction";
-#if defined(OS_CHROMEOS)
+
   Profile* profile = Profile::FromBrowserContext(browser_context());
   if (!IsCrostiniUIAllowedForProfile(profile))
     return RespondNow(Error(kCrostiniNotAvailableForCurrentUserError));
@@ -792,12 +737,8 @@
           this));
 
   return RespondLater();
-#else
-  return RespondNow(Error(kOnlyAvailableOnChromeOSError));
-#endif
 }
 
-#if defined(OS_CHROMEOS)
 void AutotestPrivateRunCrostiniInstallerFunction::CrostiniRestarted(
     crostini::ConciergeClientResult result) {
   if (result == crostini::ConciergeClientResult::SUCCESS) {
@@ -806,7 +747,10 @@
     Respond(Error("Error installing crostini"));
   }
 }
-#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateRunCrostiniUninstallerFunction
+///////////////////////////////////////////////////////////////////////////////
 
 AutotestPrivateRunCrostiniUninstallerFunction::
     ~AutotestPrivateRunCrostiniUninstallerFunction() = default;
@@ -814,7 +758,7 @@
 ExtensionFunction::ResponseAction
 AutotestPrivateRunCrostiniUninstallerFunction::Run() {
   DVLOG(1) << "AutotestPrivateRunCrostiniUninstallerFunction";
-#if defined(OS_CHROMEOS)
+
   Profile* profile = Profile::FromBrowserContext(browser_context());
   if (!IsCrostiniUIAllowedForProfile(profile))
     return RespondNow(Error(kCrostiniNotAvailableForCurrentUserError));
@@ -828,28 +772,25 @@
   CrostiniUninstallerView::Show(profile);
   CrostiniUninstallerView::GetActiveViewForTesting()->Accept();
   return RespondLater();
-#else
-  return RespondNow(Error(kOnlyAvailableOnChromeOSError));
-#endif
 }
 
-#if defined(OS_CHROMEOS)
 void AutotestPrivateRunCrostiniUninstallerFunction::CrostiniRemoved(
     crostini::ConciergeClientResult result) {
-  if (result == crostini::ConciergeClientResult::SUCCESS) {
+  if (result == crostini::ConciergeClientResult::SUCCESS)
     Respond(NoArguments());
-  } else {
+  else
     Respond(Error("Error uninstalling crostini"));
-  }
 }
-#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateTakeScreenshotFunction
+///////////////////////////////////////////////////////////////////////////////
 
 AutotestPrivateTakeScreenshotFunction::
     ~AutotestPrivateTakeScreenshotFunction() = default;
 
 ExtensionFunction::ResponseAction AutotestPrivateTakeScreenshotFunction::Run() {
   DVLOG(1) << "AutotestPrivateTakeScreenshotFunction";
-#if defined(OS_CHROMEOS)
   auto grabber = std::make_unique<ui::ScreenshotGrabber>();
   // TODO(mash): Fix for mash, http://crbug.com/557397
   aura::Window* primary_root = ash::Shell::GetPrimaryRootWindow();
@@ -861,12 +802,8 @@
       base::BindOnce(&AutotestPrivateTakeScreenshotFunction::ScreenshotTaken,
                      this, base::Passed(&grabber)));
   return RespondLater();
-#else
-  return RespondNow(Error(kOnlyAvailableOnChromeOSError));
-#endif
 }
 
-#if defined(OS_CHROMEOS)
 void AutotestPrivateTakeScreenshotFunction::ScreenshotTaken(
     std::unique_ptr<ui::ScreenshotGrabber> grabber,
     ui::ScreenshotResult screenshot_result,
@@ -883,18 +820,111 @@
          base::NumberToString(static_cast<int>(screenshot_result))})));
   }
 }
-#endif
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateGetPrinterListFunction
+///////////////////////////////////////////////////////////////////////////////
+
+AutotestPrivateGetPrinterListFunction::
+    ~AutotestPrivateGetPrinterListFunction() = default;
+
+ExtensionFunction::ResponseAction AutotestPrivateGetPrinterListFunction::Run() {
+  DVLOG(1) << "AutotestPrivateGetPrinterListFunction";
+
+  auto values = std::make_unique<base::ListValue>();
+  Profile* profile = Profile::FromBrowserContext(browser_context());
+  std::unique_ptr<chromeos::CupsPrintersManager> printers_manager =
+      chromeos::CupsPrintersManager::Create(profile);
+  std::vector<chromeos::CupsPrintersManager::PrinterClass> printer_type = {
+      chromeos::CupsPrintersManager::PrinterClass::kConfigured,
+      chromeos::CupsPrintersManager::PrinterClass::kEnterprise,
+      chromeos::CupsPrintersManager::PrinterClass::kAutomatic};
+  for (const auto& type : printer_type) {
+    std::vector<chromeos::Printer> printer_list =
+        printers_manager->GetPrinters(type);
+    for (const auto& printer : printer_list) {
+      auto result = std::make_unique<base::DictionaryValue>();
+      result->SetString("printerName", printer.display_name());
+      result->SetString("printerId", printer.id());
+      result->SetString("printerType", GetPrinterType(type));
+      values->Append(std::move(result));
+    }
+  }
+  return RespondNow(OneArgument(std::move(values)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateUpdatePrinterFunction
+///////////////////////////////////////////////////////////////////////////////
+
+AutotestPrivateUpdatePrinterFunction::~AutotestPrivateUpdatePrinterFunction() =
+    default;
+
+ExtensionFunction::ResponseAction AutotestPrivateUpdatePrinterFunction::Run() {
+  std::unique_ptr<api::autotest_private::UpdatePrinter::Params> params(
+      api::autotest_private::UpdatePrinter::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
+  DVLOG(1) << "AutotestPrivateUpdatePrinterFunction";
+
+  const api::autotest_private::Printer& js_printer = params->printer;
+  chromeos::Printer printer(js_printer.printer_id ? *js_printer.printer_id
+                                                  : "");
+  printer.set_display_name(js_printer.printer_name);
+  if (js_printer.printer_desc)
+    printer.set_description(*js_printer.printer_desc);
+
+  if (js_printer.printer_make_and_model)
+    printer.set_make_and_model(*js_printer.printer_make_and_model);
+
+  if (js_printer.printer_uri)
+    printer.set_uri(*js_printer.printer_uri);
+
+  if (js_printer.printer_ppd) {
+    const GURL ppd =
+        net::FilePathToFileURL(base::FilePath(*js_printer.printer_ppd));
+    if (ppd.is_valid())
+      printer.mutable_ppd_reference()->user_supplied_ppd_url = ppd.spec();
+    else
+      LOG(ERROR) << "Invalid ppd path: " << *js_printer.printer_ppd;
+  }
+  auto printers_manager = chromeos::CupsPrintersManager::Create(
+      Profile::FromBrowserContext(browser_context()));
+  printers_manager->UpdateConfiguredPrinter(printer);
+  return RespondNow(NoArguments());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateRemovePrinterFunction
+///////////////////////////////////////////////////////////////////////////////
+
+AutotestPrivateRemovePrinterFunction::~AutotestPrivateRemovePrinterFunction() =
+    default;
+
+ExtensionFunction::ResponseAction AutotestPrivateRemovePrinterFunction::Run() {
+  std::unique_ptr<api::autotest_private::RemovePrinter::Params> params(
+      api::autotest_private::RemovePrinter::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
+  DVLOG(1) << "AutotestPrivateRemovePrinterFunction " << params->printer_id;
+
+  auto printers_manager = chromeos::CupsPrintersManager::Create(
+      Profile::FromBrowserContext(browser_context()));
+  printers_manager->RemoveConfiguredPrinter(params->printer_id);
+  return RespondNow(NoArguments());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateBootstrapMachineLearningServiceFunction
+///////////////////////////////////////////////////////////////////////////////
+
+AutotestPrivateBootstrapMachineLearningServiceFunction::
+    AutotestPrivateBootstrapMachineLearningServiceFunction() = default;
 AutotestPrivateBootstrapMachineLearningServiceFunction::
     ~AutotestPrivateBootstrapMachineLearningServiceFunction() = default;
 
-AutotestPrivateBootstrapMachineLearningServiceFunction::
-    AutotestPrivateBootstrapMachineLearningServiceFunction() {}
-
 ExtensionFunction::ResponseAction
 AutotestPrivateBootstrapMachineLearningServiceFunction::Run() {
   DVLOG(1) << "AutotestPrivateBootstrapMachineLearningServiceFunction";
-#if defined(OS_CHROMEOS)
+
   // Load a model. This will first bootstrap the Mojo connection to ML Service.
   chromeos::machine_learning::ServiceConnection::GetInstance()->LoadModel(
       chromeos::machine_learning::mojom::ModelSpec::New(
@@ -907,12 +937,8 @@
       &AutotestPrivateBootstrapMachineLearningServiceFunction::ConnectionError,
       this));
   return RespondLater();
-#else
-  return RespondNow(Error(kOnlyAvailableOnChromeOSError));
-#endif
 }
 
-#if defined(OS_CHROMEOS)
 void AutotestPrivateBootstrapMachineLearningServiceFunction::ModelLoaded(
     chromeos::machine_learning::mojom::LoadModelResult result) {
   if (result == chromeos::machine_learning::mojom::LoadModelResult::OK) {
@@ -926,7 +952,10 @@
 void AutotestPrivateBootstrapMachineLearningServiceFunction::ConnectionError() {
   Respond(Error("ML Service connection error"));
 }
-#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateAPI
+///////////////////////////////////////////////////////////////////////////////
 
 static base::LazyInstance<BrowserContextKeyedAPIFactory<AutotestPrivateAPI>>::
     DestructorAtExit g_autotest_private_api_factory = LAZY_INSTANCE_INITIALIZER;
@@ -946,6 +975,6 @@
 
 AutotestPrivateAPI::AutotestPrivateAPI() : test_mode_(false) {}
 
-AutotestPrivateAPI::~AutotestPrivateAPI() {}
+AutotestPrivateAPI::~AutotestPrivateAPI() = default;
 
 }  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index f2acf42d..bedc06c 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -7,27 +7,22 @@
 
 #include <string>
 
+#include "ash/public/interfaces/ash_message_center_controller.mojom.h"
 #include "base/compiler_specific.h"
+#include "chrome/browser/chromeos/printing/cups_printers_manager.h"
 #include "chrome/browser/extensions/chrome_extension_function.h"
+#include "chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
 #include "ui/message_center/public/cpp/notification_types.h"
-
-#if defined(OS_CHROMEOS)
-#include "ash/public/interfaces/ash_message_center_controller.mojom.h"
-#include "chrome/browser/chromeos/printing/cups_printers_manager.h"
-#include "chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom.h"
 #include "ui/snapshot/screenshot_grabber.h"
-#endif
 
 namespace message_center {
 class Notification;
 }
 
-#if defined(OS_CHROMEOS)
 namespace crostini {
 enum class ConciergeClientResult;
 }
-#endif
 
 namespace extensions {
 
@@ -68,9 +63,7 @@
   ~AutotestPrivateLoginStatusFunction() override;
   ResponseAction Run() override;
 
-#if defined(OS_CHROMEOS)
   void OnIsReadyForPassword(bool is_ready);
-#endif
 };
 
 class AutotestPrivateLockScreenFunction : public UIThreadExtensionFunction {
@@ -194,21 +187,18 @@
 class AutotestPrivateGetVisibleNotificationsFunction
     : public UIThreadExtensionFunction {
  public:
+  AutotestPrivateGetVisibleNotificationsFunction();
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.getVisibleNotifications",
                              AUTOTESTPRIVATE_GETVISIBLENOTIFICATIONS)
 
-  AutotestPrivateGetVisibleNotificationsFunction();
-
  private:
   ~AutotestPrivateGetVisibleNotificationsFunction() override;
   ResponseAction Run() override;
 
-#if defined(OS_CHROMEOS)
   void OnGotNotifications(
       const std::vector<message_center::Notification>& notifications);
 
   ash::mojom::AshMessageCenterControllerPtr controller_;
-#endif
 };
 
 class AutotestPrivateGetPlayStoreStateFunction
@@ -266,109 +256,82 @@
 class AutotestPrivateSetCrostiniEnabledFunction
     : public UIThreadExtensionFunction {
  public:
-  AutotestPrivateSetCrostiniEnabledFunction() = default;
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.setCrostiniEnabled",
                              AUTOTESTPRIVATE_SETCROSTINIENABLED)
 
  private:
   ~AutotestPrivateSetCrostiniEnabledFunction() override;
   ResponseAction Run() override;
-
-  DISALLOW_COPY_AND_ASSIGN(AutotestPrivateSetCrostiniEnabledFunction);
 };
 
 class AutotestPrivateRunCrostiniInstallerFunction
     : public UIThreadExtensionFunction {
  public:
-  AutotestPrivateRunCrostiniInstallerFunction() = default;
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.runCrostiniInstaller",
                              AUTOTESTPRIVATE_RUNCROSTINIINSTALLER)
 
  private:
   ~AutotestPrivateRunCrostiniInstallerFunction() override;
   ResponseAction Run() override;
-#if defined(OS_CHROMEOS)
-  void CrostiniRestarted(crostini::ConciergeClientResult);
-#endif
 
-  DISALLOW_COPY_AND_ASSIGN(AutotestPrivateRunCrostiniInstallerFunction);
+  void CrostiniRestarted(crostini::ConciergeClientResult);
 };
 
 class AutotestPrivateRunCrostiniUninstallerFunction
     : public UIThreadExtensionFunction {
  public:
-  AutotestPrivateRunCrostiniUninstallerFunction() = default;
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.runCrostiniUninstaller",
                              AUTOTESTPRIVATE_RUNCROSTINIUNINSTALLER)
 
  private:
   ~AutotestPrivateRunCrostiniUninstallerFunction() override;
   ResponseAction Run() override;
-#if defined(OS_CHROMEOS)
-  void CrostiniRemoved(crostini::ConciergeClientResult);
-#endif
 
-  DISALLOW_COPY_AND_ASSIGN(AutotestPrivateRunCrostiniUninstallerFunction);
+  void CrostiniRemoved(crostini::ConciergeClientResult);
 };
 
 class AutotestPrivateTakeScreenshotFunction : public UIThreadExtensionFunction {
  public:
-  AutotestPrivateTakeScreenshotFunction() = default;
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.takeScreenshot",
                              AUTOTESTPRIVATE_TAKESCREENSHOT)
 
  private:
   ~AutotestPrivateTakeScreenshotFunction() override;
   ResponseAction Run() override;
-#if defined(OS_CHROMEOS)
+
   void ScreenshotTaken(std::unique_ptr<ui::ScreenshotGrabber> grabber,
                        ui::ScreenshotResult screenshot_result,
                        scoped_refptr<base::RefCountedMemory> png_data);
-#endif
-  DISALLOW_COPY_AND_ASSIGN(AutotestPrivateTakeScreenshotFunction);
 };
 
 class AutotestPrivateGetPrinterListFunction : public UIThreadExtensionFunction {
  public:
-  AutotestPrivateGetPrinterListFunction() = default;
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.getPrinterList",
                              AUTOTESTPRIVATE_GETPRINTERLIST)
 
  private:
-#if defined(OS_CHROMEOS)
-  static std::string GetPrinterType(
-      chromeos::CupsPrintersManager::PrinterClass type);
-#endif
   ~AutotestPrivateGetPrinterListFunction() override;
   ResponseAction Run() override;
-
-  DISALLOW_COPY_AND_ASSIGN(AutotestPrivateGetPrinterListFunction);
 };
 
 class AutotestPrivateUpdatePrinterFunction : public UIThreadExtensionFunction {
  public:
-  AutotestPrivateUpdatePrinterFunction();
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.updatePrinter",
                              AUTOTESTPRIVATE_UPDATEPRINTER)
 
  private:
   ~AutotestPrivateUpdatePrinterFunction() override;
   ResponseAction Run() override;
-
-  DISALLOW_COPY_AND_ASSIGN(AutotestPrivateUpdatePrinterFunction);
 };
 
 class AutotestPrivateRemovePrinterFunction : public UIThreadExtensionFunction {
  public:
-  AutotestPrivateRemovePrinterFunction();
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.removePrinter",
                              AUTOTESTPRIVATE_REMOVEPRINTER)
 
  private:
   ~AutotestPrivateRemovePrinterFunction() override;
   ResponseAction Run() override;
-
-  DISALLOW_COPY_AND_ASSIGN(AutotestPrivateRemovePrinterFunction);
 };
 
 class AutotestPrivateBootstrapMachineLearningServiceFunction
@@ -381,21 +344,14 @@
  private:
   ~AutotestPrivateBootstrapMachineLearningServiceFunction() override;
   ResponseAction Run() override;
-#if defined(OS_CHROMEOS)
+
   // Callbacks for a basic Mojo call to MachineLearningService.LoadModel.
   void ModelLoaded(chromeos::machine_learning::mojom::LoadModelResult result);
   void ConnectionError();
 
   chromeos::machine_learning::mojom::ModelPtr model_;
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(
-      AutotestPrivateBootstrapMachineLearningServiceFunction);
 };
 
-// Don't kill the browser when we're in a browser test.
-void SetAutotestPrivateTest();
-
 // The profile-keyed service that manages the autotestPrivate extension API.
 class AutotestPrivateAPI : public BrowserContextKeyedAPI {
  public:
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index a3bbd1e6..7eaeb8e 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -275,14 +275,9 @@
   return true;
 }
 
-// Return true if the feature flag for recommend app screen is on and the screen
-// is never triggered before.
+// Return true if the feature flag for recommend app screen is on.
 bool ShouldShowRecommendAppsScreen() {
-  return base::FeatureList::IsEnabled(features::kOobeRecommendAppsScreen) &&
-         (!ProfileManager::GetActiveUserProfile()->GetPrefs()->GetBoolean(
-              prefs::kOobeRecommendAppScreenFinished) ||
-          base::CommandLine::ForCurrentProcess()->HasSwitch(
-              chromeos::switches::kForceShowRecommendAppsScreenForTest));
+  return base::FeatureList::IsEnabled(features::kOobeRecommendAppsScreen);
 }
 
 chromeos::LoginDisplayHost* GetLoginDisplayHost() {
diff --git a/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl.cc b/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl.cc
index b914342..e2c718b 100644
--- a/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl.cc
+++ b/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "chrome/browser/chromeos/android_sms/android_sms_urls.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
@@ -17,6 +18,8 @@
 #include "chrome/browser/web_applications/components/pending_app_manager.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chromeos/components/proximity_auth/logging/logging.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_types.h"
 #include "extensions/common/constants.h"
 #include "ui/base/window_open_disposition.h"
 
@@ -29,11 +32,16 @@
     : pending_app_manager_(
           &web_app::WebAppProvider::Get(profile)->pending_app_manager()),
       profile_(profile),
+      host_content_settings_map_(
+          HostContentSettingsMapFactory::GetForProfile(profile)),
       weak_ptr_factory_(this) {}
 
 AndroidSmsAppHelperDelegateImpl::AndroidSmsAppHelperDelegateImpl(
-    web_app::PendingAppManager* pending_app_manager)
-    : pending_app_manager_(pending_app_manager), weak_ptr_factory_(this) {}
+    web_app::PendingAppManager* pending_app_manager,
+    HostContentSettingsMap* host_content_settings_map)
+    : pending_app_manager_(pending_app_manager),
+      host_content_settings_map_(host_content_settings_map),
+      weak_ptr_factory_(this) {}
 
 AndroidSmsAppHelperDelegateImpl::~AndroidSmsAppHelperDelegateImpl() = default;
 
@@ -88,6 +96,14 @@
     const GURL& app_url,
     web_app::InstallResultCode code) {
   if (code == web_app::InstallResultCode::kSuccess) {
+    // Pre-Grant notification permission for Messages.
+    host_content_settings_map_->SetWebsiteSettingDefaultScope(
+        chromeos::android_sms::GetAndroidMessagesURL(),
+        GURL() /* top_level_url */,
+        ContentSettingsType::CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+        content_settings::ResourceIdentifier(),
+        std::make_unique<base::Value>(ContentSetting::CONTENT_SETTING_ALLOW));
+
     PA_LOG(INFO) << "Messages app installed! URL: " << app_url;
     if (launch_on_install)
       LaunchAndroidSmsApp();
diff --git a/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl.h b/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl.h
index 3e2a0b0..2d96d68e 100644
--- a/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl.h
+++ b/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "url/gurl.h"
 
 class Profile;
@@ -34,8 +35,9 @@
   // built using this constructor will segfault on profile_ if
   // LaunchAndroidSmsApp is called. We'll need to fix this once tests for that
   // function are added. See https://crbug.com/876972.
-  explicit AndroidSmsAppHelperDelegateImpl(
-      web_app::PendingAppManager* pending_app_manager);
+  AndroidSmsAppHelperDelegateImpl(
+      web_app::PendingAppManager* pending_app_manager,
+      HostContentSettingsMap* host_content_settings_map);
   void OnAppInstalled(bool launch_on_install,
                       const GURL& app_url,
                       web_app::InstallResultCode code);
@@ -49,6 +51,7 @@
   static const char kMessagesWebAppUrl[];
   web_app::PendingAppManager* pending_app_manager_;
   Profile* profile_;
+  HostContentSettingsMap* host_content_settings_map_;
   base::WeakPtrFactory<AndroidSmsAppHelperDelegateImpl> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AndroidSmsAppHelperDelegateImpl);
diff --git a/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl_unittest.cc b/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl_unittest.cc
index 6de6c3b..3cc1db76 100644
--- a/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl_unittest.cc
+++ b/chrome/browser/chromeos/multidevice_setup/android_sms_app_helper_delegate_impl_unittest.cc
@@ -11,7 +11,11 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/chromeos/android_sms/android_sms_urls.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/web_applications/components/test_pending_app_manager.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
@@ -20,15 +24,21 @@
 
 class AndroidSmsAppHelperDelegateImplTest : public testing::Test {
  protected:
-  AndroidSmsAppHelperDelegateImplTest() = default;
+  AndroidSmsAppHelperDelegateImplTest()
+      : host_content_settings_map_(
+            HostContentSettingsMapFactory::GetForProfile(&profile_)) {}
+
   ~AndroidSmsAppHelperDelegateImplTest() override = default;
 
   // testing::Test:
   void SetUp() override {
+    host_content_settings_map_->ClearSettingsForOneType(
+        ContentSettingsType::CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
     test_pending_app_manager_ =
         std::make_unique<web_app::TestPendingAppManager>();
-    android_sms_app_helper_delegate_ = base::WrapUnique(
-        new AndroidSmsAppHelperDelegateImpl(test_pending_app_manager_.get()));
+    android_sms_app_helper_delegate_ =
+        base::WrapUnique(new AndroidSmsAppHelperDelegateImpl(
+            test_pending_app_manager_.get(), host_content_settings_map_));
   }
 
   web_app::TestPendingAppManager* test_pending_app_manager() {
@@ -43,7 +53,20 @@
     android_sms_app_helper_delegate_->InstallAndLaunchAndroidSmsApp();
   }
 
+  ContentSetting GetNotificationSetting() {
+    std::unique_ptr<base::Value> notification_settings_value =
+        host_content_settings_map_->GetWebsiteSetting(
+            chromeos::android_sms::GetAndroidMessagesURL(),
+            GURL() /* top_level_url */,
+            ContentSettingsType::CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+            content_settings::ResourceIdentifier(), nullptr);
+    return static_cast<ContentSetting>(notification_settings_value->GetInt());
+  }
+
  private:
+  content::TestBrowserThreadBundle thread_bundle_;
+  TestingProfile profile_;
+  HostContentSettingsMap* host_content_settings_map_;
   std::unique_ptr<web_app::TestPendingAppManager> test_pending_app_manager_;
   std::unique_ptr<AndroidSmsAppHelperDelegate> android_sms_app_helper_delegate_;
 
@@ -51,6 +74,7 @@
 };
 
 TEST_F(AndroidSmsAppHelperDelegateImplTest, TestInstallMessagesApp) {
+  EXPECT_NE(ContentSetting::CONTENT_SETTING_ALLOW, GetNotificationSetting());
   InstallApp();
 
   std::vector<web_app::PendingAppManager::AppInfo> expected_apps_to_install;
@@ -62,6 +86,7 @@
       true);  // override_previous_user_uninstall
   EXPECT_EQ(expected_apps_to_install,
             test_pending_app_manager()->install_requests());
+  EXPECT_EQ(ContentSetting::CONTENT_SETTING_ALLOW, GetNotificationSetting());
 }
 
 TEST_F(AndroidSmsAppHelperDelegateImplTest, TestInstallAndLaunchMessagesApp) {
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client.cc b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
index c1a95cd..a117e62 100644
--- a/chrome/browser/extensions/api/chrome_extensions_api_client.cc
+++ b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
@@ -101,6 +101,9 @@
     const std::string& header_name) const {
   // Gaia may send a OAUth2 authorization code in the Dice response header,
   // which could allow an extension to generate a refresh token for the account.
+  // TODO(crbug.com/890006): Determine if the code here can be cleaned up
+  // since browser initiated non-navigation requests are now hidden from
+  // extensions.
   return (
       (url.host_piece() == GaiaUrls::GetInstance()->gaia_url().host_piece()) &&
       (base::CompareCaseInsensitiveASCII(header_name,
@@ -111,6 +114,10 @@
     const WebRequestInfo& request) const {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
+  // TODO(crbug.com/890006): Determine if the code here can be cleaned up
+  // since browser initiated non-navigation requests are now hidden from
+  // extensions.
+
   // Exclude main frame navigation requests.
   bool is_browser_request = request.render_process_id == -1 &&
                             request.type != content::RESOURCE_TYPE_MAIN_FRAME;
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index 1340fd5..1836e3b 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -1607,67 +1607,6 @@
   verify_page_load(false /*expect_load*/);
 }
 
-// Tests that requests which can't be mapped to a render frame (e.g. non-
-// navigation browser requests) are not affected by the page allowing API.
-IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
-                       PageAllowingAPI_BrowserRequests) {
-  // Load an extension which blocks all requests.
-  set_has_background_script(true);
-  std::vector<TestRule> rules;
-  TestRule rule = CreateGenericRule();
-  rule.condition->url_filter = std::string("*");
-  rules.push_back(rule);
-  ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
-
-  // Allow requests served from "google.com/*" pages.
-  AddAllowedPages(last_loaded_extension_id(), {"http://google.com:*/*"});
-
-  const GURL url = embedded_test_server()->GetURL(
-      "google.com", "/pages_with_script/index.html");
-
-  // A navigation to |url| should not be blocked as google.com/* pages have been
-  // allowed. This will cause two network requests to index.html and
-  // script.js.
-  ui_test_utils::NavigateToURL(browser(), url);
-  EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
-  EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
-  std::set<GURL> requests_seen = GetAndResetRequestsToServer();
-
-  // The EmbeddedTestServer sees requests after the hostname has been resolved.
-  EXPECT_TRUE(base::ContainsKey(
-      requests_seen,
-      embedded_test_server()->GetURL("/pages_with_script/index.html")));
-  EXPECT_TRUE(base::ContainsKey(
-      requests_seen,
-      embedded_test_server()->GetURL("/pages_with_script/script.js")));
-
-  // But a non-navigation browser initiated resource request should still be
-  // blocked. This is because such a request can't be mapped to a top level
-  // frame and hence won't be considered for allowing by the page
-  // allowing API.
-  auto request = std::make_unique<network::ResourceRequest>();
-  request->url = embedded_test_server()->GetURL("google.com",
-                                                "/pages_with_script/script.js");
-  request->resource_type = content::ResourceType::RESOURCE_TYPE_SCRIPT;
-  request->render_frame_id = MSG_ROUTING_NONE;
-
-  auto loader = network::SimpleURLLoader::Create(std::move(request),
-                                                 TRAFFIC_ANNOTATION_FOR_TESTS);
-  content::SimpleURLLoaderTestHelper loader_helper;
-  loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      content::BrowserContext::GetDefaultStoragePartition(profile())
-          ->GetURLLoaderFactoryForBrowserProcess()
-          .get(),
-      loader_helper.GetCallback());
-  loader_helper.WaitForCallback();
-
-  EXPECT_FALSE(loader_helper.response_body());
-  EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, loader->NetError());
-  EXPECT_FALSE(base::ContainsKey(
-      GetAndResetRequestsToServer(),
-      embedded_test_server()->GetURL("/pages_with_script/script.js")));
-}
-
 // Ensures that any <img> elements blocked by the API are collapsed.
 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, ImageCollapsed) {
   // Loads a page with an image and returns whether the image was collapsed.
diff --git a/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
index 740ea3e..bad7fc3 100644
--- a/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
@@ -96,9 +96,12 @@
     return last_loaded_extension_.get();
   }
 
+  // Returns a renderer-initiated request to the given |url|.
   WebRequestInfo GetRequestForURL(base::StringPiece url) {
+    const int kRendererId = 1;
     WebRequestInfo info;
     info.url = GURL(url);
+    info.render_process_id = kRendererId;
     return info;
   }
 
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc
index c2cdf33f..fd60fff 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc
@@ -26,6 +26,7 @@
 #include "extensions/browser/api/web_request/web_request_info.h"
 #include "extensions/browser/info_map.h"
 #include "extensions/common/extension.h"
+#include "extensions/common/extensions_client.h"
 #include "net/base/request_priority.h"
 #include "net/http/http_response_headers.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
@@ -146,6 +147,7 @@
     const std::string& extension_id,
     const WebRequestActionSet* action_set,
     RequestStage stage) {
+  const int kRendererId = 2;
   std::unique_ptr<net::URLRequest> regular_request(
       context_.CreateRequest(GURL(url_string), net::DEFAULT_PRIORITY, NULL,
                              TRAFFIC_ANNOTATION_FOR_TESTS));
@@ -153,6 +155,7 @@
   scoped_refptr<net::HttpResponseHeaders> headers(
       new net::HttpResponseHeaders(""));
   WebRequestInfo request_info(regular_request.get());
+  request_info.render_process_id = kRendererId;
   WebRequestData request_data(&request_info, stage, headers.get());
   std::set<std::string> ignored_tags;
   WebRequestAction::ApplyInfo apply_info = { extension_info_map_.get(),
@@ -177,12 +180,13 @@
   EXPECT_TRUE(ActionWorksOnRequest(
       "http://test.com", extension_all_urls_->id(), action_set.get(), stage));
 
+  const std::string& webstore_url =
+      ExtensionsClient::Get()->GetWebstoreBaseURL().spec();
   // The protected URLs should not be touched at all.
-  EXPECT_FALSE(ActionWorksOnRequest(
-      "http://clients1.google.com", extension_->id(), action_set.get(), stage));
-  EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
-                                    extension_all_urls_->id(),
-                                    action_set.get(),
+  EXPECT_FALSE(ActionWorksOnRequest(webstore_url.c_str(), extension_->id(),
+                                    action_set.get(), stage));
+  EXPECT_FALSE(ActionWorksOnRequest(webstore_url.c_str(),
+                                    extension_all_urls_->id(), action_set.get(),
                                     stage));
 }
 
@@ -359,13 +363,12 @@
                                    ON_BEFORE_REQUEST));
 
   // The protected URLs should not be touched at all.
-  EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
-                                    extension_->id(),
-                                    action_set.get(),
-                                    ON_BEFORE_REQUEST));
-  EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
-                                    extension_all_urls_->id(),
-                                    action_set.get(),
+  const std::string& webstore_url =
+      ExtensionsClient::Get()->GetWebstoreBaseURL().spec();
+  EXPECT_FALSE(ActionWorksOnRequest(webstore_url.c_str(), extension_->id(),
+                                    action_set.get(), ON_BEFORE_REQUEST));
+  EXPECT_FALSE(ActionWorksOnRequest(webstore_url.c_str(),
+                                    extension_all_urls_->id(), action_set.get(),
                                     ON_BEFORE_REQUEST));
 }
 
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
index 9fd06229..8c01dd7 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
@@ -25,10 +25,6 @@
 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h"
 #include "extensions/browser/api/web_request/web_request_api_helpers.h"
 #include "extensions/browser/api/web_request/web_request_info.h"
-#include "net/base/request_priority.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest-message.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -39,6 +35,8 @@
 using testing::HasSubstr;
 using url_matcher::URLMatcher;
 
+namespace extensions {
+
 namespace {
 const char kExtensionId[] = "ext1";
 const char kExtensionId2[] = "ext2";
@@ -46,9 +44,18 @@
 const char kRuleId2[] = "rule2";
 const char kRuleId3[] = "rule3";
 const char kRuleId4[] = "rule4";
-}  // namespace
 
-namespace extensions {
+// Creates a main-frame navigation request to |url|.
+WebRequestInfo CreateRequest(const GURL& url) {
+  WebRequestInfo info;
+  info.url = url;
+  info.is_browser_side_navigation = true;
+  info.type = content::RESOURCE_TYPE_MAIN_FRAME;
+  info.web_request_type = WebRequestResourceType::MAIN_FRAME;
+  return info;
+}
+
+}  // namespace
 
 namespace helpers = extension_web_request_api_helpers;
 namespace keys = declarative_webrequest_constants;
@@ -280,10 +287,7 @@
   std::set<const WebRequestRule*> matches;
 
   GURL http_url("http://www.example.com");
-  net::TestURLRequestContext context;
-  std::unique_ptr<net::URLRequest> http_request(context.CreateRequest(
-      http_url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
-  WebRequestInfo http_request_info(http_request.get());
+  WebRequestInfo http_request_info = CreateRequest(http_url);
   WebRequestData request_data(&http_request_info, ON_BEFORE_REQUEST);
   matches = registry->GetMatches(request_data);
   EXPECT_EQ(2u, matches.size());
@@ -298,9 +302,7 @@
       base::ContainsKey(matches_ids, std::make_pair(kExtensionId, kRuleId2)));
 
   GURL foobar_url("http://www.foobar.com");
-  std::unique_ptr<net::URLRequest> foobar_request(context.CreateRequest(
-      foobar_url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
-  WebRequestInfo foobar_request_info(foobar_request.get());
+  WebRequestInfo foobar_request_info = CreateRequest(foobar_url);
   request_data.request = &foobar_request_info;
   matches = registry->GetMatches(request_data);
   EXPECT_EQ(1u, matches.size());
@@ -425,10 +427,7 @@
   EXPECT_EQ("", error);
 
   GURL url("http://www.google.com");
-  net::TestURLRequestContext context;
-  std::unique_ptr<net::URLRequest> request(context.CreateRequest(
-      url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
-  WebRequestInfo request_info(request.get());
+  WebRequestInfo request_info = CreateRequest(url);
   WebRequestData request_data(&request_info, ON_BEFORE_REQUEST);
   std::list<LinkedPtrEventResponseDelta> deltas =
       registry->CreateDeltas(NULL, request_data, false);
@@ -475,10 +474,7 @@
   EXPECT_EQ("", error);
 
   GURL url("http://www.google.com/index.html");
-  net::TestURLRequestContext context;
-  std::unique_ptr<net::URLRequest> request(context.CreateRequest(
-      url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
-  WebRequestInfo request_info(request.get());
+  WebRequestInfo request_info = CreateRequest(url);
   WebRequestData request_data(&request_info, ON_BEFORE_REQUEST);
   std::list<LinkedPtrEventResponseDelta> deltas =
       registry->CreateDeltas(nullptr, request_data, false);
@@ -550,10 +546,7 @@
   EXPECT_FALSE(registry->IsEmpty());
 
   GURL url("http://www.foo.com/test");
-  net::TestURLRequestContext context;
-  std::unique_ptr<net::URLRequest> request(context.CreateRequest(
-      url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
-  WebRequestInfo request_info(request.get());
+  WebRequestInfo request_info = CreateRequest(url);
   WebRequestData request_data(&request_info, ON_BEFORE_REQUEST);
   std::list<LinkedPtrEventResponseDelta> deltas =
       registry->CreateDeltas(NULL, request_data, false);
@@ -601,10 +594,7 @@
   std::set<const WebRequestRule*> matches;
 
   GURL http_url("http://www.example.com");
-  net::TestURLRequestContext context;
-  std::unique_ptr<net::URLRequest> http_request(context.CreateRequest(
-      http_url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
-  WebRequestInfo http_request_info(http_request.get());
+  WebRequestInfo http_request_info = CreateRequest(http_url);
   WebRequestData request_data(&http_request_info, ON_BEFORE_REQUEST);
   matches = registry->GetMatches(request_data);
   EXPECT_EQ(1u, matches.size());
@@ -658,14 +648,11 @@
   static_assert(arraysize(urls) == arraysize(matchingRuleIds),
                 "urls and matchingRuleIds must have the same number "
                 "of elements");
-  net::TestURLRequestContext context;
 
   for (size_t i = 0; i < arraysize(matchingRuleIds); ++i) {
     // Construct the inputs.
-    std::unique_ptr<net::URLRequest> http_request(context.CreateRequest(
-        urls[i], net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
-    http_request->set_site_for_cookies(firstPartyUrls[i]);
-    WebRequestInfo http_request_info(http_request.get());
+    WebRequestInfo http_request_info = CreateRequest(urls[i]);
+    http_request_info.site_for_cookies = firstPartyUrls[i];
     WebRequestData request_data(&http_request_info, ON_BEFORE_REQUEST);
     // Now run both rules on the input.
     matches = registry->GetMatches(request_data);
@@ -807,23 +794,18 @@
   std::string error = registry->AddRulesImpl(kExtensionId, rules);
   EXPECT_EQ("", error);
 
-  net::TestURLRequestContext context;
   std::list<LinkedPtrEventResponseDelta> deltas;
 
   // No match because match is in the query parameter.
   GURL url1("http://bar.com/index.html?foo.com");
-  std::unique_ptr<net::URLRequest> request1(context.CreateRequest(
-      url1, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
-  WebRequestInfo request1_info(request1.get());
+  WebRequestInfo request1_info = CreateRequest(url1);
   WebRequestData request_data1(&request1_info, ON_BEFORE_REQUEST);
   deltas = registry->CreateDeltas(NULL, request_data1, false);
   EXPECT_EQ(0u, deltas.size());
 
   // This is a correct match.
   GURL url2("http://foo.com/index.html");
-  std::unique_ptr<net::URLRequest> request2(context.CreateRequest(
-      url2, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
-  WebRequestInfo request2_info(request2.get());
+  WebRequestInfo request2_info = CreateRequest(url2);
   WebRequestData request_data2(&request2_info, ON_BEFORE_REQUEST);
   deltas = registry->CreateDeltas(NULL, request_data2, false);
   EXPECT_EQ(1u, deltas.size());
diff --git a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
index 566f108..5e25f25 100644
--- a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/extensions/event_router_forwarder.h"
 #include "chrome/browser/net/chrome_extensions_network_delegate.h"
 #include "chrome/browser/net/chrome_network_delegate.h"
+#include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
@@ -153,6 +154,26 @@
   return list;
 }
 
+// Returns a main-frame request to |url|.
+std::unique_ptr<net::URLRequest> CreateRequestHelper(
+    const GURL& url,
+    net::TestURLRequestContext* context,
+    net::TestDelegate* delegate) {
+  CHECK(context);
+  CHECK(delegate);
+
+  std::unique_ptr<net::URLRequest> request = context->CreateRequest(
+      url, net::DEFAULT_PRIORITY, delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
+  content::ResourceRequestInfo::AllocateForTesting(
+      request.get(), content::RESOURCE_TYPE_MAIN_FRAME, /*context*/ nullptr,
+      -1 /* render_process_id */, -1 /* render_view_id */,
+      -1 /* render_frame_id */, true /* is_main_frame */,
+      false /* allow_download */, false /* is_async */, content::PREVIEWS_OFF,
+      ChromeNavigationUIData::CreateForMainFrameNavigation(
+          nullptr /* web_contents */, WindowOpenDisposition::CURRENT_TAB));
+  return request;
+}
+
 }  // namespace
 
 // A mock event router that responds to events with a pre-arranged queue of
@@ -245,6 +266,11 @@
                               const std::vector<char>& bytes_1,
                               const std::vector<char>& bytes_2);
 
+  // Returns a main-frame request to |url|.
+  std::unique_ptr<net::URLRequest> CreateRequest(const GURL& url) {
+    return CreateRequestHelper(url, context_.get(), &delegate_);
+  }
+
   content::TestBrowserThreadBundle thread_bundle_;
   TestingProfile profile_;
   TestingProfileManager profile_manager_;
@@ -282,9 +308,7 @@
   GURL redirect_url("about:redirected");
   GURL not_chosen_redirect_url("about:not_chosen");
 
-  std::unique_ptr<net::URLRequest> request(
-      context_->CreateRequest(GURL("about:blank"), net::DEFAULT_PRIORITY,
-                              &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
+  std::unique_ptr<net::URLRequest> request = CreateRequest(GURL("about:blank"));
   {
     // onBeforeRequest will be dispatched twice initially. The second response -
     // the redirect - should win, since it has a later |install_time|. The
@@ -337,9 +361,8 @@
   }
 
   // Now test the same thing but the extensions answer in reverse order.
-  std::unique_ptr<net::URLRequest> request2(
-      context_->CreateRequest(GURL("about:blank"), net::DEFAULT_PRIORITY,
-                              &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
+  std::unique_ptr<net::URLRequest> request2 =
+      CreateRequest(GURL("about:blank"));
   {
     ExtensionWebRequestEventRouter::EventResponse* response = NULL;
 
@@ -415,9 +438,7 @@
       ipc_sender_factory.GetWeakPtr());
 
   GURL request_url("about:blank");
-  std::unique_ptr<net::URLRequest> request(
-      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
-                              TRAFFIC_ANNOTATION_FOR_TESTS));
+  std::unique_ptr<net::URLRequest> request = CreateRequest(request_url);
 
   // onBeforeRequest will be dispatched twice. The second response -
   // the redirect - would win, since it has a later |install_time|, but
@@ -487,9 +508,7 @@
       kEventName2 + "/1", filter, 0, 0, 0, ipc_sender_factory.GetWeakPtr());
 
   GURL request_url("about:blank");
-  std::unique_ptr<net::URLRequest> request(
-      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
-                              TRAFFIC_ANNOTATION_FOR_TESTS));
+  std::unique_ptr<net::URLRequest> request = CreateRequest(request_url);
 
   ExtensionWebRequestEventRouter::EventResponse* response = NULL;
 
@@ -556,9 +575,7 @@
     const std::vector<char>& bytes_2) {
   // The request URL can be arbitrary but must have an HTTP or HTTPS scheme.
   GURL request_url("http://www.example.com");
-  std::unique_ptr<net::URLRequest> request(
-      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
-                              TRAFFIC_ANNOTATION_FOR_TESTS));
+  std::unique_ptr<net::URLRequest> request = CreateRequest(request_url);
   request->set_method(method);
   if (content_type != NULL) {
     request->SetExtraRequestHeaderByName(net::HttpRequestHeaders::kContentType,
@@ -945,9 +962,7 @@
   const GURL request_url("http://www.example.com");
 
   for (size_t i = 0; i < arraysize(kMethods); ++i) {
-    std::unique_ptr<net::URLRequest> request(
-        context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
-                                TRAFFIC_ANNOTATION_FOR_TESTS));
+    std::unique_ptr<net::URLRequest> request = CreateRequest(request_url);
     request->set_method(kMethods[i]);
     ipc_sender_.PushTask(base::DoNothing());
     request->Start();
@@ -1042,9 +1057,7 @@
 
   // Send a request. It should block. Wait for the run loop to become idle.
   GURL request_url("about:blank");
-  std::unique_ptr<net::URLRequest> request(
-      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
-                              TRAFFIC_ANNOTATION_FOR_TESTS));
+  std::unique_ptr<net::URLRequest> request = CreateRequest(request_url);
   // Extension response for OnErrorOccurred: Terminate the message loop.
   {
     base::RunLoop run_loop;
@@ -1132,6 +1145,11 @@
     context_->Init();
   }
 
+  // Returns a main-frame request to |url|.
+  std::unique_ptr<net::URLRequest> CreateRequest(const GURL& url) {
+    return CreateRequestHelper(url, context_.get(), &delegate_);
+  }
+
   content::TestBrowserThreadBundle thread_bundle_;
   TestingProfile profile_;
   TestingProfileManager profile_manager_;
@@ -1170,9 +1188,7 @@
       ipc_sender_factory.GetWeakPtr());
 
   GURL request_url("http://doesnotexist/does_not_exist.html");
-  std::unique_ptr<net::URLRequest> request(
-      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
-                              TRAFFIC_ANNOTATION_FOR_TESTS));
+  std::unique_ptr<net::URLRequest> request = CreateRequest(request_url);
 
   // Initialize headers available before extensions are notified of the
   // onBeforeSendHeaders event.
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 5e3c5a6..94aece1 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -1104,6 +1104,7 @@
   auto make_browser_request = [this](const GURL& url) {
     auto request = std::make_unique<network::ResourceRequest>();
     request->url = url;
+    request->resource_type = content::RESOURCE_TYPE_SUB_RESOURCE;
 
     auto* url_loader_factory =
         content::BrowserContext::GetDefaultStoragePartition(profile())
@@ -1129,11 +1130,11 @@
       embedded_test_server()->GetURL("clients1.google.com", "/simple.html"));
   EXPECT_EQ(1, get_clients_google_request_count());
 
-  // Sanity check that other requests made by the browser can still be
-  // intercepted by the extension.
+  // Other non-navigation browser requests should also be hidden from
+  // extensions.
   make_browser_request(
       embedded_test_server()->GetURL("yahoo.com", "/simple.html"));
-  EXPECT_EQ(1, get_yahoo_request_count());
+  EXPECT_EQ(0, get_yahoo_request_count());
 }
 
 // Verify that requests for PAC scripts are protected properly.
@@ -1405,7 +1406,7 @@
   GURL google_url =
       embedded_test_server()->GetURL("google.com", "/extensions/body1.html");
 
-  // First, check normal requets (e.g., navigations) to verify the extension
+  // First, check normal requests (e.g., navigations) to verify the extension
   // is working correctly.
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
@@ -1483,10 +1484,11 @@
   }
 
   {
-    // example.com should fail.
+    // example.com should also succeed since non-navigation browser-initiated
+    // requests are hidden from extensions. See crbug.com/884932.
     SCOPED_TRACE("example.com with Profile's url loader");
-    make_browser_request(url_loader_factory, example_url, base::nullopt,
-                         net::ERR_BLOCKED_BY_CLIENT);
+    make_browser_request(url_loader_factory, example_url, kExampleFullContent,
+                         net::OK);
   }
 
   // Requests going through the system network context manager should always
diff --git a/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
index ab284e6..11395c83 100644
--- a/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
@@ -32,6 +32,7 @@
 using extensions::Extension;
 using extensions::Manifest;
 using extensions::PermissionsData;
+using extensions::WebRequestInfo;
 using extension_test_util::LoadManifestUnchecked;
 
 class ExtensionWebRequestHelpersTestWithThreadsTest : public testing::Test {
@@ -104,46 +105,115 @@
 
 TEST_F(ExtensionWebRequestHelpersTestWithThreadsTest, TestHideRequestForURL) {
   net::TestURLRequestContext context;
-  const char* const sensitive_urls[] = {
-      "http://clients2.google.com",
-      "http://clients22.google.com",
-      "https://clients2.google.com",
-      "http://clients2.google.com/service/update2/crx",
-      "https://clients.google.com",
-      "https://test.clients.google.com",
-      "https://clients2.google.com/service/update2/crx",
-      "http://www.gstatic.com/chrome/extensions/blacklist",
-      "https://www.gstatic.com/chrome/extensions/blacklist",
-      "notregisteredscheme://www.foobar.com",
-      "https://chrome.google.com/webstore/",
-      "https://chrome.google.com/webstore/"
-          "inlineinstall/detail/kcnhkahnjcbndmmehfkdnkjomaanaooo"
-  };
-  const char* const non_sensitive_urls[] = {
-      "http://www.google.com/"
+
+  // TODO(karandeepb): Merge some of this test with
+  // ExtensionWebRequestPermissions.IsSensitiveRequest.
+  enum HideRequestMask {
+    HIDE_NONE = 0,
+    HIDE_RENDERER_REQUEST = 1,
+    HIDE_SUB_FRAME_NAVIGATION = 2,
+    HIDE_MAIN_FRAME_NAVIGATION = 4,
+    HIDE_BROWSER_SUB_RESOURCE_REQUEST = 8,
+    HIDE_ALL = HIDE_RENDERER_REQUEST | HIDE_SUB_FRAME_NAVIGATION |
+               HIDE_MAIN_FRAME_NAVIGATION | HIDE_BROWSER_SUB_RESOURCE_REQUEST,
   };
 
-  // Check that requests are rejected based on the destination
-  for (size_t i = 0; i < arraysize(sensitive_urls); ++i) {
-    GURL sensitive_url(sensitive_urls[i]);
-    std::unique_ptr<net::URLRequest> request(
-        context.CreateRequest(sensitive_url, net::DEFAULT_PRIORITY, NULL,
-                              TRAFFIC_ANNOTATION_FOR_TESTS));
-    extensions::WebRequestInfo request_info(request.get());
-    EXPECT_TRUE(WebRequestPermissions::HideRequest(extension_info_map_.get(),
-                                                   request_info))
-        << sensitive_urls[i];
-  }
-  // Check that requests are accepted if they don't touch sensitive urls.
-  for (size_t i = 0; i < arraysize(non_sensitive_urls); ++i) {
-    GURL non_sensitive_url(non_sensitive_urls[i]);
-    std::unique_ptr<net::URLRequest> request(
-        context.CreateRequest(non_sensitive_url, net::DEFAULT_PRIORITY, NULL,
-                              TRAFFIC_ANNOTATION_FOR_TESTS));
-    extensions::WebRequestInfo request_info(request.get());
-    EXPECT_FALSE(WebRequestPermissions::HideRequest(extension_info_map_.get(),
-                                                    request_info))
-        << non_sensitive_urls[i];
+  struct {
+    std::string url;
+    int expected_hide_request_mask;
+  } test_cases[] = {
+      // clientX.google.com urls.
+      {"http://clients2.google.com",
+       HIDE_SUB_FRAME_NAVIGATION | HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"http://clients22.google.com",
+       HIDE_SUB_FRAME_NAVIGATION | HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://clients2.google.com",
+       HIDE_SUB_FRAME_NAVIGATION | HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://clients.google.com",
+       HIDE_SUB_FRAME_NAVIGATION | HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://test.clients.google.com",
+       HIDE_SUB_FRAME_NAVIGATION | HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+
+      // Sensitive urls.
+      {"http://clients2.google.com/service/update2/crx", HIDE_ALL},
+      {"https://clients2.google.com/service/update2/crx", HIDE_ALL},
+      {"http://www.gstatic.com/chrome/extensions/blacklist", HIDE_ALL},
+      {"https://www.gstatic.com/chrome/extensions/blacklist", HIDE_ALL},
+      {"https://chrome.google.com/webstore/", HIDE_ALL},
+      {"https://chrome.google.com/webstore/inlineinstall/detail/"
+       "kcnhkahnjcbndmmehfkdnkjomaanaooo",
+       HIDE_ALL},
+
+      // Scheme not allowed by web request api.
+      {"notregisteredscheme://www.foobar.com", HIDE_ALL},
+
+      // Non-sensitive url.
+      {"http://www.google.com/", HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+  };
+
+  const int kRendererProcessId = 2;
+  const int kBrowserProcessId = -1;
+
+  // Returns a WebRequestInfo instance constructed as per the given parameters.
+  auto create_request = [](const GURL& url, content::ResourceType type,
+                           int render_process_id) {
+    WebRequestInfo request;
+    request.url = url;
+    request.type = type;
+    request.render_process_id = render_process_id;
+
+    request.web_request_type = extensions::ToWebRequestResourceType(type);
+    request.is_browser_side_navigation =
+        type == content::RESOURCE_TYPE_MAIN_FRAME ||
+        type == content::RESOURCE_TYPE_SUB_FRAME;
+    return request;
+  };
+
+  for (const auto& test_case : test_cases) {
+    SCOPED_TRACE(test_case.url);
+
+    GURL request_url(test_case.url);
+    ASSERT_TRUE(request_url.is_valid());
+
+    {
+      SCOPED_TRACE("Renderer initiated sub-resource request");
+      WebRequestInfo request = create_request(
+          request_url, content::RESOURCE_TYPE_SUB_RESOURCE, kRendererProcessId);
+      bool expect_hidden =
+          test_case.expected_hide_request_mask & HIDE_RENDERER_REQUEST;
+      EXPECT_EQ(expect_hidden, WebRequestPermissions::HideRequest(
+                                   extension_info_map_.get(), request));
+    }
+
+    {
+      SCOPED_TRACE("Browser initiated sub-resource request");
+      WebRequestInfo request = create_request(
+          request_url, content::RESOURCE_TYPE_SUB_RESOURCE, kBrowserProcessId);
+      bool expect_hidden = test_case.expected_hide_request_mask &
+                           HIDE_BROWSER_SUB_RESOURCE_REQUEST;
+      EXPECT_EQ(expect_hidden, WebRequestPermissions::HideRequest(
+                                   extension_info_map_.get(), request));
+    }
+
+    {
+      SCOPED_TRACE("Main-frame navigation");
+      WebRequestInfo request = create_request(
+          request_url, content::RESOURCE_TYPE_MAIN_FRAME, kBrowserProcessId);
+      bool expect_hidden =
+          test_case.expected_hide_request_mask & HIDE_MAIN_FRAME_NAVIGATION;
+      EXPECT_EQ(expect_hidden, WebRequestPermissions::HideRequest(
+                                   extension_info_map_.get(), request));
+    }
+
+    {
+      SCOPED_TRACE("Sub-frame navigation");
+      WebRequestInfo request = create_request(
+          request_url, content::RESOURCE_TYPE_SUB_FRAME, kBrowserProcessId);
+      bool expect_hidden =
+          test_case.expected_hide_request_mask & HIDE_SUB_FRAME_NAVIGATION;
+      EXPECT_EQ(expect_hidden, WebRequestPermissions::HideRequest(
+                                   extension_info_map_.get(), request));
+    }
   }
 
   // Check protection of requests originating from the frame showing the Chrome
@@ -153,8 +223,8 @@
   std::unique_ptr<net::URLRequest> non_sensitive_request(
       context.CreateRequest(non_sensitive_url, net::DEFAULT_PRIORITY, NULL,
                             TRAFFIC_ANNOTATION_FOR_TESTS));
-  extensions::WebRequestInfo non_sensitive_request_info(
-      non_sensitive_request.get());
+  WebRequestInfo non_sensitive_request_info(non_sensitive_request.get());
+  non_sensitive_request_info.render_process_id = kRendererProcessId;
   EXPECT_FALSE(WebRequestPermissions::HideRequest(extension_info_map_.get(),
                                                   non_sensitive_request_info));
   // If the origin is labeled by the WebStoreAppId, it becomes protected.
@@ -174,7 +244,7 @@
         /*navigation_ui_data*/ nullptr);
     extension_info_map_->RegisterExtensionProcess(extensions::kWebStoreAppId,
                                                   process_id, site_instance_id);
-    extensions::WebRequestInfo sensitive_request_info(sensitive_request.get());
+    WebRequestInfo sensitive_request_info(sensitive_request.get());
     EXPECT_TRUE(WebRequestPermissions::HideRequest(extension_info_map_.get(),
                                                    sensitive_request_info));
   }
@@ -185,7 +255,7 @@
       context.CreateRequest(non_sensitive_url, net::DEFAULT_PRIORITY, NULL,
                             TRAFFIC_ANNOTATION_FOR_TESTS));
   request->set_is_pac_request(true);
-  extensions::WebRequestInfo request_info(request.get());
+  WebRequestInfo request_info(request.get());
   EXPECT_TRUE(WebRequestPermissions::HideRequest(extension_info_map_.get(),
                                                  request_info));
 }
diff --git a/chrome/browser/media/unified_autoplay_browsertest.cc b/chrome/browser/media/unified_autoplay_browsertest.cc
index 3fa0f35..14c6e8b 100644
--- a/chrome/browser/media/unified_autoplay_browsertest.cc
+++ b/chrome/browser/media/unified_autoplay_browsertest.cc
@@ -71,6 +71,17 @@
     return played;
   }
 
+  bool NavigateInRenderer(content::WebContents* web_contents, const GURL& url) {
+    content::TestNavigationObserver observer(web_contents);
+
+    bool result = content::ExecuteScriptWithoutUserGesture(
+        web_contents, "window.location = '" + url.spec() + "';");
+
+    if (result)
+      observer.Wait();
+    return result;
+  }
+
   void SetAutoplayForceAllowFlag(const GURL& url) {
     blink::mojom::AutoplayConfigurationClientAssociatedPtr client;
     GetWebContents()
@@ -253,6 +264,66 @@
   EXPECT_TRUE(AttemptPlay(GetWebContents()));
 }
 
+IN_PROC_BROWSER_TEST_F(UnifiedAutoplayBrowserTest,
+                       Redirect_SameOrigin_WithGesture) {
+  const GURL kRedirectPageUrl =
+      embedded_test_server()->GetURL(kFramedTestPagePath);
+  const GURL kTestPageUrl = embedded_test_server()->GetURL(kTestPagePath);
+
+  NavigateParams params(browser(), kRedirectPageUrl, ui::PAGE_TRANSITION_LINK);
+  params.was_activated = content::WasActivatedOption::kYes;
+  ui_test_utils::NavigateToURL(&params);
+
+  EXPECT_TRUE(NavigateInRenderer(GetWebContents(), kTestPageUrl));
+  EXPECT_EQ(kTestPageUrl, GetWebContents()->GetLastCommittedURL());
+  EXPECT_TRUE(AttemptPlay(GetWebContents()));
+}
+
+IN_PROC_BROWSER_TEST_F(UnifiedAutoplayBrowserTest,
+                       Redirect_SameOrigin_WithoutGesture) {
+  const GURL kRedirectPageUrl =
+      embedded_test_server()->GetURL(kFramedTestPagePath);
+  const GURL kTestPageUrl = embedded_test_server()->GetURL(kTestPagePath);
+
+  NavigateParams params(browser(), kRedirectPageUrl, ui::PAGE_TRANSITION_LINK);
+  ui_test_utils::NavigateToURL(&params);
+
+  EXPECT_TRUE(NavigateInRenderer(GetWebContents(), kTestPageUrl));
+  EXPECT_EQ(kTestPageUrl, GetWebContents()->GetLastCommittedURL());
+  EXPECT_FALSE(AttemptPlay(GetWebContents()));
+}
+
+IN_PROC_BROWSER_TEST_F(UnifiedAutoplayBrowserTest,
+                       Redirect_CrossOrigin_WithGesture) {
+  const GURL kRedirectPageUrl =
+      embedded_test_server()->GetURL(kFramedTestPagePath);
+  const GURL kTestPageUrl =
+      embedded_test_server()->GetURL("foo.example.com", kTestPagePath);
+
+  NavigateParams params(browser(), kRedirectPageUrl, ui::PAGE_TRANSITION_LINK);
+  params.was_activated = content::WasActivatedOption::kYes;
+  ui_test_utils::NavigateToURL(&params);
+
+  EXPECT_TRUE(NavigateInRenderer(GetWebContents(), kTestPageUrl));
+  EXPECT_EQ(kTestPageUrl, GetWebContents()->GetLastCommittedURL());
+  EXPECT_FALSE(AttemptPlay(GetWebContents()));
+}
+
+IN_PROC_BROWSER_TEST_F(UnifiedAutoplayBrowserTest,
+                       Redirect_CrossOrigin_WithoutGesture) {
+  const GURL kRedirectPageUrl =
+      embedded_test_server()->GetURL(kFramedTestPagePath);
+  const GURL kTestPageUrl =
+      embedded_test_server()->GetURL("foo.example.com", kTestPagePath);
+
+  NavigateParams params(browser(), kRedirectPageUrl, ui::PAGE_TRANSITION_LINK);
+  ui_test_utils::NavigateToURL(&params);
+
+  EXPECT_TRUE(NavigateInRenderer(GetWebContents(), kTestPageUrl));
+  EXPECT_EQ(kTestPageUrl, GetWebContents()->GetLastCommittedURL());
+  EXPECT_FALSE(AttemptPlay(GetWebContents()));
+}
+
 // Integration tests for the new unified autoplay sound settings UI.
 
 class UnifiedAutoplaySettingBrowserTest : public UnifiedAutoplayBrowserTest {
diff --git a/chrome/browser/net/proxy_config_monitor.cc b/chrome/browser/net/proxy_config_monitor.cc
index 6793074..2d08d23e 100644
--- a/chrome/browser/net/proxy_config_monitor.cc
+++ b/chrome/browser/net/proxy_config_monitor.cc
@@ -21,6 +21,8 @@
 #include "chrome/browser/extensions/api/proxy/proxy_api.h"
 #endif
 
+using content::BrowserThread;
+
 ProxyConfigMonitor::ProxyConfigMonitor(Profile* profile) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(profile);
@@ -51,12 +53,13 @@
   proxy_config_service_->AddObserver(this);
 }
 
-ProxyConfigMonitor::ProxyConfigMonitor() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ProxyConfigMonitor::ProxyConfigMonitor(PrefService* local_state) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+         !BrowserThread::IsThreadInitialized(BrowserThread::UI));
 
   pref_proxy_config_tracker_.reset(
       ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState(
-          g_browser_process->local_state()));
+          local_state));
 
   proxy_config_service_ = ProxyServiceFactory::CreateProxyConfigService(
       pref_proxy_config_tracker_.get());
@@ -65,7 +68,8 @@
 }
 
 ProxyConfigMonitor::~ProxyConfigMonitor() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+         !BrowserThread::IsThreadInitialized(BrowserThread::UI));
   proxy_config_service_->RemoveObserver(this);
   pref_proxy_config_tracker_->DetachFromPrefService();
 }
@@ -100,7 +104,8 @@
 void ProxyConfigMonitor::OnProxyConfigChanged(
     const net::ProxyConfigWithAnnotation& config,
     net::ProxyConfigService::ConfigAvailability availability) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+         !BrowserThread::IsThreadInitialized(BrowserThread::UI));
   proxy_config_client_set_.ForAllPtrs(
       [config,
        availability](network::mojom::ProxyConfigClient* proxy_config_client) {
@@ -134,7 +139,8 @@
 
 void ProxyConfigMonitor::OnRequestMaybeFailedDueToProxySettings(
     int32_t net_error) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+         !BrowserThread::IsThreadInitialized(BrowserThread::UI));
 
   if (net_error >= 0) {
     // If the error is obviously wrong, don't dispatch it to extensions. If the
diff --git a/chrome/browser/net/proxy_config_monitor.h b/chrome/browser/net/proxy_config_monitor.h
index 5383985..172e71b0 100644
--- a/chrome/browser/net/proxy_config_monitor.h
+++ b/chrome/browser/net/proxy_config_monitor.h
@@ -22,6 +22,7 @@
 
 class Profile;
 class PrefProxyConfigTracker;
+class PrefService;
 
 // Tracks the ProxyConfig to use, and passes any updates to a NetworkContext's
 // ProxyConfigClient. This also responds to errors related to proxy settings
@@ -42,10 +43,9 @@
   explicit ProxyConfigMonitor(Profile* profile);
 
   // Creates a ProxyConfigMonitor that gets proxy settings from the
-  // BrowserProcess's |local_state_|, for use with NetworkContexts not
-  // assocaited with a profile. Must be destroyed before the BrowserProcess's
-  // |local_state_|.
-  ProxyConfigMonitor();
+  // |local_state|, for use with NetworkContexts not
+  // associated with a profile. Must be destroyed before |local_state|.
+  explicit ProxyConfigMonitor(PrefService* local_state);
 
   ~ProxyConfigMonitor() override;
 
diff --git a/chrome/browser/net/proxy_service_factory.cc b/chrome/browser/net/proxy_service_factory.cc
index 2b5838c..47c9f2094 100644
--- a/chrome/browser/net/proxy_service_factory.cc
+++ b/chrome/browser/net/proxy_service_factory.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/net/proxy_service_factory.h"
 
 #include "base/task/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -22,8 +23,10 @@
 std::unique_ptr<net::ProxyConfigService>
 ProxyServiceFactory::CreateProxyConfigService(PrefProxyConfigTracker* tracker) {
   // The linux gsettings-based proxy settings getter relies on being initialized
-  // from the UI thread.
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // from the UI thread. The system proxy config service could also get created
+  // without full browser process by launching service manager alone.
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+         !BrowserThread::IsThreadInitialized(BrowserThread::UI));
 
   std::unique_ptr<net::ProxyConfigService> base_service;
 
@@ -37,7 +40,7 @@
   // include command line and configuration policy).
 
   base_service = net::ProxyResolutionService::CreateSystemProxyConfigService(
-      base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}));
+      base::ThreadTaskRunnerHandle::Get());
 #endif  // !defined(OS_CHROMEOS)
 
   return tracker->CreateTrackingProxyConfigService(std::move(base_service));
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index fcb74ff..06f3774 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -346,7 +346,8 @@
     PrefService* local_state)
     : local_state_(local_state),
       ssl_config_service_manager_(
-          SSLConfigServiceManager::CreateDefaultManager(local_state_)) {
+          SSLConfigServiceManager::CreateDefaultManager(local_state_)),
+      proxy_config_monitor_(local_state_) {
 #if !defined(OS_ANDROID)
   // QuicAllowed was not part of Android policy.
   const base::Value* value =
@@ -682,7 +683,8 @@
 
   network_context_params->primary_network_context = true;
 
-  proxy_config_monitor_.AddToNetworkContextParams(network_context_params.get());
+  proxy_config_monitor_.AddToNetworkContextParams(
+      network_context_params.get());
 
   return network_context_params;
 }
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index bfa92f3d..3e4160cd 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -46,6 +46,7 @@
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/prefs/origin_trial_prefs.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
+#include "chrome/browser/previews/previews_lite_page_decider.h"
 #include "chrome/browser/profiles/chrome_version_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_impl.h"
@@ -580,6 +581,7 @@
   policy::URLBlacklistManager::RegisterProfilePrefs(registry);
   PrefProxyConfigTrackerImpl::RegisterProfilePrefs(registry);
   PrefsTabHelper::RegisterProfilePrefs(registry);
+  PreviewsLitePageDecider::RegisterProfilePrefs(registry);
   Profile::RegisterProfilePrefs(registry);
   ProfileImpl::RegisterProfilePrefs(registry);
   ProfileNetworkContextService::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/prefs/incognito_mode_prefs.cc b/chrome/browser/prefs/incognito_mode_prefs.cc
index 8ae5e753..6f3bb9c 100644
--- a/chrome/browser/prefs/incognito_mode_prefs.cc
+++ b/chrome/browser/prefs/incognito_mode_prefs.cc
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
-#include "base/threading/thread_restrictions.h"
+#include "base/threading/scoped_blocking_call.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
@@ -73,9 +73,6 @@
   // feature is available on Windows 7 and beyond. This function should be
   // called on a COM Initialized thread and is potentially blocking.
   static bool IsParentalControlActivityLoggingOn() {
-    // Since we can potentially block, make sure the thread is okay with this.
-    base::AssertBlockingAllowed();
-
     ThreadType thread_type = ThreadType::BLOCKING;
     if (BrowserThread::IsThreadInitialized(BrowserThread::UI) &&
         content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
@@ -97,6 +94,9 @@
   // Does the work of determining if Windows Parental control activity logging
   // is enabled.
   static bool IsParentalControlActivityLoggingOnImpl() {
+    // Since we can potentially block, make sure the thread is okay with this.
+    base::ScopedBlockingCall scoped_blocking_call(
+        base::BlockingType::MAY_BLOCK);
     Microsoft::WRL::ComPtr<IWindowsParentalControlsCore> parent_controls;
     HRESULT hr = ::CoCreateInstance(__uuidof(WindowsParentalControls), nullptr,
                                     CLSCTX_ALL, IID_PPV_ARGS(&parent_controls));
diff --git a/chrome/browser/previews/previews_lite_page_decider.cc b/chrome/browser/previews/previews_lite_page_decider.cc
index cec4c558..784f863 100644
--- a/chrome/browser/previews/previews_lite_page_decider.cc
+++ b/chrome/browser/previews/previews_lite_page_decider.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/previews/previews_lite_page_decider.h"
 
+#include "base/callback.h"
 #include "base/memory/ptr_util.h"
 #include "base/rand_util.h"
 #include "base/time/default_tick_clock.h"
@@ -13,31 +14,103 @@
 #include "chrome/browser/previews/previews_service.h"
 #include "chrome/browser/previews/previews_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
 #include "components/previews/core/previews_experiments.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "net/base/net_errors.h"
+
+namespace {
+const char kUserNeedsNotification[] =
+    "previews.litepage.user-needs-notification";
+}
+
+// This WebContentsObserver watches the rest of the current navigation shows a
+// notification to the user that this preview now exists and will be used on
+// future eligible page loads. This is only done if the navigations finishes on
+// the same URL as the one when it began. After finishing the navigation, |this|
+// will be removed as an observer.
+class UserNotificationWebContentsObserver
+    : public content::WebContentsObserver,
+      public content::WebContentsUserData<UserNotificationWebContentsObserver> {
+ public:
+  void SetUIShownCallback(base::OnceClosure callback) {
+    ui_shown_callback_ = std::move(callback);
+  }
+
+ private:
+  friend class content::WebContentsUserData<
+      UserNotificationWebContentsObserver>;
+
+  explicit UserNotificationWebContentsObserver(
+      content::WebContents* web_contents)
+      : content::WebContentsObserver(web_contents) {}
+
+  void DestroySelf() {
+    content::WebContents* old_web_contents = web_contents();
+    Observe(nullptr);
+    old_web_contents->RemoveUserData(UserDataKey());
+    // DO NOT add code past this point. |this| is destroyed.
+  }
+
+  void DidRedirectNavigation(
+      content::NavigationHandle* navigation_handle) override {
+    DestroySelf();
+    // DO NOT add code past this point. |this| is destroyed.
+  }
+
+  void DidFinishNavigation(content::NavigationHandle* handle) override {
+    if (ui_shown_callback_ && handle->GetNetErrorCode() == net::OK) {
+      // TODO(robertogden): Call PreviewsLitePageInfobarDelegate::Create and
+      // pass it the callback instead.
+      std::move(ui_shown_callback_).Run();
+    }
+    DestroySelf();
+    // DO NOT add code past this point. |this| is destroyed.
+  }
+
+  base::OnceClosure ui_shown_callback_;
+};
 
 PreviewsLitePageDecider::PreviewsLitePageDecider(
     content::BrowserContext* browser_context)
     : clock_(base::DefaultTickClock::GetInstance()),
       page_id_(base::RandUint64()),
-      drp_settings_(nullptr) {
-  if (browser_context && !browser_context->IsOffTheRecord()) {
-    DataReductionProxyChromeSettings* drp_settings =
-        DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
-            browser_context);
-    DCHECK(drp_settings);
+      drp_settings_(nullptr),
+      pref_service_(nullptr) {
+  if (!browser_context)
+    return;
 
-    drp_settings_ = drp_settings;
-    drp_settings_->AddProxyRequestHeadersObserver(this);
-  }
+  DataReductionProxyChromeSettings* drp_settings =
+      DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
+          browser_context);
+  if (!drp_settings)
+    return;
+
+  DCHECK(!browser_context->IsOffTheRecord());
+
+  drp_settings_ = drp_settings;
+  drp_settings_->AddDataReductionProxySettingsObserver(this);
+
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  pref_service_ = profile->GetPrefs();
+  DCHECK(pref_service_);
 }
 
 PreviewsLitePageDecider::~PreviewsLitePageDecider() = default;
 
 // static
+void PreviewsLitePageDecider::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  registry->RegisterBooleanPref(kUserNeedsNotification, true);
+}
+
+// static
 std::unique_ptr<content::NavigationThrottle>
 PreviewsLitePageDecider::MaybeCreateThrottleFor(
     content::NavigationHandle* handle) {
@@ -78,19 +151,45 @@
   page_id_ = base::RandUint64();
 }
 
+void PreviewsLitePageDecider::OnSettingsInitialized() {
+  // The notification only needs to be shown if the user has never seen it
+  // before, and is an existing Data Saver user.
+  if (!pref_service_->GetBoolean(kUserNeedsNotification)) {
+    need_to_show_notification_ = false;
+  } else if (drp_settings_->IsDataReductionProxyEnabled()) {
+    need_to_show_notification_ = true;
+  } else {
+    need_to_show_notification_ = false;
+    pref_service_->SetBoolean(kUserNeedsNotification, false);
+  }
+}
+
 void PreviewsLitePageDecider::Shutdown() {
   if (drp_settings_)
-    drp_settings_->RemoveProxyRequestHeadersObserver(this);
+    drp_settings_->RemoveDataReductionProxySettingsObserver(this);
 }
 
 void PreviewsLitePageDecider::SetClockForTesting(const base::TickClock* clock) {
   clock_ = clock;
 }
 
+void PreviewsLitePageDecider::SetDRPSettingsForTesting(
+    data_reduction_proxy::DataReductionProxySettings* drp_settings) {
+  drp_settings_ = drp_settings;
+  drp_settings_->AddDataReductionProxySettingsObserver(this);
+}
+
 void PreviewsLitePageDecider::ClearSingleBypassForTesting() {
   single_bypass_.clear();
 }
 
+void PreviewsLitePageDecider::SetUserHasSeenUINotification() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(pref_service_);
+  need_to_show_notification_ = false;
+  pref_service_->SetBoolean(kUserNeedsNotification, false);
+}
+
 void PreviewsLitePageDecider::SetServerUnavailableFor(
     base::TimeDelta retry_after) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -146,3 +245,23 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return ++page_id_;
 }
+
+bool PreviewsLitePageDecider::NeedsToNotifyUser() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return need_to_show_notification_;
+}
+
+void PreviewsLitePageDecider::NotifyUser(content::WebContents* web_contents) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(need_to_show_notification_);
+  DCHECK(!UserNotificationWebContentsObserver::FromWebContents(web_contents));
+
+  UserNotificationWebContentsObserver::CreateForWebContents(web_contents);
+  UserNotificationWebContentsObserver* observer =
+      UserNotificationWebContentsObserver::FromWebContents(web_contents);
+
+  // base::Unretained is safe here because |this| outlives |web_contents|.
+  observer->SetUIShownCallback(
+      base::BindOnce(&PreviewsLitePageDecider::SetUserHasSeenUINotification,
+                     base::Unretained(this)));
+}
diff --git a/chrome/browser/previews/previews_lite_page_decider.h b/chrome/browser/previews/previews_lite_page_decider.h
index feca5277..83a6107d 100644
--- a/chrome/browser/previews/previews_lite_page_decider.h
+++ b/chrome/browser/previews/previews_lite_page_decider.h
@@ -18,37 +18,53 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "net/http/http_request_headers.h"
 
+class PrefService;
+
 namespace content {
 class BrowserContext;
 class NavigationHandle;
 class NavigationThrottle;
 }  // namespace content
 
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
 // This class ensures that the feature is enabled and the
 // current Profile is not incognito before handing off the real legwork of the
 // triggering decision to |PreviewsLitePageNavigationThrottle|.
 class PreviewsLitePageDecider
     : public PreviewsLitePageNavigationThrottleManager,
-      public data_reduction_proxy::ProxyRequestHeadersObserver {
+      public data_reduction_proxy::DataReductionProxySettingsObserver {
  public:
   explicit PreviewsLitePageDecider(content::BrowserContext* browser_context);
   virtual ~PreviewsLitePageDecider();
 
+  // Registers the prefs used in this class.
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
   // Checks if the feature is enabled and if so, returns a
   // |PreviewsLitePageNavigationThrottle| that handles the rest of the decision
   // making.
   static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
       content::NavigationHandle* handle);
 
-  // Removes |this| as a ProxyRequestHeadersObserver.
+  // Removes |this| as a DataReductionProxySettingsObserver.
   void Shutdown();
 
   // Sets the internal clock for testing.
   void SetClockForTesting(const base::TickClock* clock);
 
+  // Sets |drp_settings_| for testing and registers |this| as an observer.
+  void SetDRPSettingsForTesting(
+      data_reduction_proxy::DataReductionProxySettings* drp_settings);
+
   // Clears all single bypasses for testing.
   void ClearSingleBypassForTesting();
 
+  // Sets that the user has seen the UI notification.
+  void SetUserHasSeenUINotification();
+
  private:
   FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageDeciderTest, TestServerUnavailable);
   FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageDeciderTest, TestSingleBypass);
@@ -59,10 +75,13 @@
   void AddSingleBypass(std::string url) override;
   bool CheckSingleBypass(std::string url) override;
   uint64_t GeneratePageID() override;
+  bool NeedsToNotifyUser() override;
+  void NotifyUser(content::WebContents* web_contents) override;
 
-  // data_reduction_proxy::ProxyRequestHeadersObserver:
+  // data_reduction_proxy::DataReductionProxySettingsObserver:
   void OnProxyRequestHeadersChanged(
       const net::HttpRequestHeaders& headers) override;
+  void OnSettingsInitialized() override;
 
   // The time after which it is ok to send the server more preview requests.
   base::Optional<base::TimeTicks> retry_at_;
@@ -82,6 +101,13 @@
   // observer on |Shutdown|. Not owned.
   data_reduction_proxy::DataReductionProxySettings* drp_settings_;
 
+  // A reference to the profile's |PrefService|.
+  PrefService* pref_service_;
+
+  // Whether the notification infobar needs to be shown to the user in order to
+  // use this preview.
+  bool need_to_show_notification_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(PreviewsLitePageDecider);
diff --git a/chrome/browser/previews/previews_lite_page_decider_unittest.cc b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
index 0fff471..a6779d9f 100644
--- a/chrome/browser/previews/previews_lite_page_decider_unittest.cc
+++ b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
@@ -8,8 +8,24 @@
 
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_tick_clock.h"
+#include "chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/web_contents_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
+const char kTestUrl[] = "http://www.test.com/";
+}
+
 class PreviewsLitePageDeciderTest : public testing::Test {
  protected:
   PreviewsLitePageDeciderTest()
@@ -94,3 +110,76 @@
               test_case.want_check);
   }
 }
+
+class PreviewsLitePageDeciderPrefTest : public ChromeRenderViewHostTestHarness {
+ protected:
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
+    drp_test_context_ =
+        data_reduction_proxy::DataReductionProxyTestContext::Builder()
+            .WithMockConfig()
+            .SkipSettingsInitialization()
+            .Build();
+  }
+
+  void TearDown() override {
+    drp_test_context_->DestroySettings();
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
+  PreviewsLitePageNavigationThrottleManager* GetManagerWithDRPEnabled(
+      bool enabled) {
+    drp_test_context_->SetDataReductionProxyEnabled(enabled);
+
+    decider_ = std::make_unique<PreviewsLitePageDecider>(
+        web_contents()->GetBrowserContext());
+    decider_->SetDRPSettingsForTesting(drp_test_context_->settings());
+
+    drp_test_context_->InitSettings();
+
+    return decider_.get();
+  }
+
+ private:
+  std::unique_ptr<data_reduction_proxy::DataReductionProxyTestContext>
+      drp_test_context_;
+  std::unique_ptr<PreviewsLitePageDecider> decider_;
+};
+
+TEST_F(PreviewsLitePageDeciderPrefTest, TestDRPDisabled) {
+  PreviewsLitePageNavigationThrottleManager* manager =
+      GetManagerWithDRPEnabled(false);
+  EXPECT_FALSE(manager->NeedsToNotifyUser());
+
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(GURL(kTestUrl));
+
+  // Should still be false after a navigation
+  EXPECT_FALSE(manager->NeedsToNotifyUser());
+}
+
+TEST_F(PreviewsLitePageDeciderPrefTest, TestDRPEnabled) {
+  PreviewsLitePageNavigationThrottleManager* manager =
+      GetManagerWithDRPEnabled(true);
+  EXPECT_TRUE(manager->NeedsToNotifyUser());
+
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(GURL(kTestUrl));
+
+  // Should still be true after a navigation
+  EXPECT_TRUE(manager->NeedsToNotifyUser());
+}
+
+TEST_F(PreviewsLitePageDeciderPrefTest, TestDRPEnabledThenNotify) {
+  PreviewsLitePageNavigationThrottleManager* manager =
+      GetManagerWithDRPEnabled(true);
+  EXPECT_TRUE(manager->NeedsToNotifyUser());
+
+  manager->NotifyUser(web_contents());
+
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(GURL(kTestUrl));
+
+  EXPECT_FALSE(manager->NeedsToNotifyUser());
+}
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h b/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
index 799f8379..d36687e 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
@@ -9,6 +9,10 @@
 
 #include "base/time/time.h"
 
+namespace content {
+class WebContents;
+}
+
 // This interface specifies the interaction that a
 // |PreviewsLitePageNavigationThrottle| has with it's state manager. This class
 // tracks the state of the Navigation Throttle since a single instance of the
@@ -33,6 +37,17 @@
 
   // Generates a new page id for a request to the previews server.
   virtual uint64_t GeneratePageID() = 0;
+
+  // Note: |NeedsToToNotify| is intentionally separate from |NotifyUser| for
+  // ease of testing and metrics collection without changing the notification
+  // state.
+
+  // Returns true if the UI notification needs to be shown to the user before
+  // this preview can be shown.
+  virtual bool NeedsToNotifyUser() = 0;
+
+  // Prompts |this| to display the required UI notifications to the user.
+  virtual void NotifyUser(content::WebContents* web_contents) = 0;
 };
 
 #endif  // CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_MANAGER_H_
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 730a4ef..b37dde1 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -422,9 +422,6 @@
   registry->RegisterIntegerPref(prefs::kPrintingColorDefault, 0);
   registry->RegisterIntegerPref(prefs::kPrintingDuplexDefault, 0);
   registry->RegisterDictionaryPref(prefs::kPrintingSizeDefault);
-  registry->RegisterBooleanPref(
-      prefs::kOobeRecommendAppScreenFinished, false,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
 #endif  // defined(OS_CHROMEOS)
 #endif  // BUILDFLAG(ENABLE_PRINTING)
   registry->RegisterBooleanPref(prefs::kPrintPreviewDisabled, false);
diff --git a/chrome/browser/resources/chromeos/login/recommend_apps.html b/chrome/browser/resources/chromeos/login/recommend_apps.html
index 6645c12..b581001 100644
--- a/chrome/browser/resources/chromeos/login/recommend_apps.html
+++ b/chrome/browser/resources/chromeos/login/recommend_apps.html
@@ -33,11 +33,11 @@
           <div>[[i18nDynamic(locale, 'recommendAppsSkip')]]</div>
         </oobe-text-button>
         <oobe-next-button id="recommend-apps-install-button"
-          on-tap="onInstall_" class="focus-on-show" inverse>
+            on-tap="onInstall_" inverse>
           <div>[[i18nDynamic(locale, 'recommendAppsInstall')]]</div>
         </oobe-next-button>
         <oobe-text-button id="recommend-apps-retry-button" border
-                          on-tap="onRetry_">
+            on-tap="onRetry_">
           <div>[[i18nDynamic(locale, 'recommendAppsRetry')]]</div>
         </oobe-text-button>
       </div>
diff --git a/chrome/browser/resources/chromeos/login/screen_recommend_apps.js b/chrome/browser/resources/chromeos/login/screen_recommend_apps.js
index 5e0c2a8..0004ca2 100644
--- a/chrome/browser/resources/chromeos/login/screen_recommend_apps.js
+++ b/chrome/browser/resources/chromeos/login/screen_recommend_apps.js
@@ -117,6 +117,7 @@
       this.removeClass_('recommend-apps-loading');
       this.removeClass_('error');
       this.addClass_('recommend-apps-loaded');
+      this.getElement_('recommend-apps-install-button').focus();
     },
 
     /**
diff --git a/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js b/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
index 660e1f5..73f06aaf 100644
--- a/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
+++ b/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
@@ -101,6 +101,7 @@
   this.prefsManager_ = new PrefsManager();
   this.prefsManager_.initPreferences();
 
+  this.runContentScripts_();
   this.setUpEventListeners_();
 };
 
@@ -399,6 +400,31 @@
   },
 
   /**
+   * Runs content scripts that allow Select-to-Speak access to
+   * Google Docs content without a11y mode enabled, in every open
+   * tab. Should be run when Select-to-Speak starts up so that any
+   * tabs already opened will be checked.
+   * This should be kept in sync with the "content_scripts" section in
+   * the Select-to-Speak manifest.
+   * @private
+   */
+  runContentScripts_: function() {
+    chrome.tabs.query(
+        {
+          url: [
+            'https://docs.google.com/document*',
+            'https://docs.sandbox.google.com/*'
+          ]
+        },
+        (tabs) => {
+          tabs.forEach((tab) => {
+            chrome.tabs.executeScript(
+                tab.id, {file: 'select_to_speak_gdocs_script.js'});
+          });
+        });
+  },
+
+  /**
    * Set up event listeners user input.
    */
   setUpEventListeners_: function() {
diff --git a/chrome/browser/resources/local_ntp/custom_links_edit.css b/chrome/browser/resources/local_ntp/custom_links_edit.css
index 8dd2471..aad5dec 100644
--- a/chrome/browser/resources/local_ntp/custom_links_edit.css
+++ b/chrome/browser/resources/local_ntp/custom_links_edit.css
@@ -192,6 +192,10 @@
       0 3px 6px 2px rgba(60, 64, 67, 0.15);
 }
 
-#cancel {
+html:not([dir=rtl]) #cancel {
   margin-right: 8px;
 }
+
+html[dir=rtl] #cancel {
+  margin-left: 8px;
+}
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
index 3177ae7d7..62ca596f 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -103,14 +103,47 @@
   THUMBNAIL_FAILED: 8,
 };
 
+
 /**
- * Number of tiles per row for Material Design.
+ * Timeout delay for the window.onresize event throttle. Set to 15 frame per
+ * second.
+ * @const {number}
+ */
+const RESIZE_TIMEOUT_DELAY = 66;
+
+
+/**
+ * Maximum number of tiles if custom links is enabled.
+ * @const {number}
+ */
+const MD_MAX_NUM_CUSTOM_LINK_TILES = 10;
+
+
+/**
+ * Maximum number of tiles per row for Material Design.
  * @const {number}
  */
 const MD_MAX_TILES_PER_ROW = 5;
 
 
 /**
+ * Width of a tile for Material Design. Keep in sync with
+ * most_visited_single.css.
+ * @const {number}
+ */
+const MD_TILE_WIDTH = 112;
+
+
+/**
+ * Number of tiles that will always be visible for Material Design. Calculated
+ * by dividing minimum |--content-width| (see local_ntp.css) by |MD_TILE_WIDTH|
+ * and multiplying by 2 rows.
+ * @const {number}
+ */
+const MD_NUM_TILES_ALWAYS_VISIBLE = 6;
+
+
+/**
  * Number of lines to display in titles.
  * @type {number}
  */
@@ -118,6 +151,13 @@
 
 
 /**
+ * Largest minimum font size in settings.
+ * @const {number}
+ */
+const LARGEST_MINIMUM_FONT_SIZE = 24;
+
+
+/**
  * The origin of this request, i.e. 'https://www.google.TLD' for the remote NTP,
  * or 'chrome-search://local-ntp' for the local NTP.
  * @const {string}
@@ -400,6 +440,9 @@
       cur.style.maxWidth =
           'calc(var(--md-tile-width) * ' + Math.ceil(cur.childNodes.length / 2);
     }
+
+    // Prevent keyboard navigation to tiles that are not visible.
+    updateTileVisibility();
   }
 
   // getComputedStyle causes the initial style (opacity 0) to be applied, so
@@ -414,6 +457,25 @@
   tiles = document.createElement('div');
 };
 
+
+/**
+ * Explicitly hide tiles that are not visible in order to prevent keyboard
+ * navigation.
+ */
+function updateTileVisibility() {
+  const allTiles =
+      document.querySelectorAll('#mv-tiles .' + CLASSES.MD_TILE_CONTAINER);
+  if (allTiles.length === 0)
+    return;
+
+  // Get the current number of tiles per row. Hide any tile after the first two
+  // rows.
+  const tilesPerRow = Math.trunc(document.body.offsetWidth / MD_TILE_WIDTH);
+  for (let i = MD_NUM_TILES_ALWAYS_VISIBLE; i < allTiles.length; i++)
+    allTiles[i].style.display = (i < tilesPerRow * 2) ? 'block' : 'none';
+}
+
+
 /**
  * Truncates titles that are longer than one line and appends an ellipsis. Text
  * overflow in CSS ("text-overflow: ellipsis") requires "overflow: hidden",
@@ -425,7 +487,8 @@
     let el = titles[i];
     const originalTitle = el.innerText;
     let truncatedTitle = el.innerText;
-    while (el.scrollHeight > el.offsetHeight && truncatedTitle.length > 0) {
+    while (el.scrollHeight > LARGEST_MINIMUM_FONT_SIZE
+    && truncatedTitle.length > 0) {
       el.innerText = (truncatedTitle = truncatedTitle.slice(0, -1)) + '\u2026';
     }
     if (truncatedTitle.length === 0) {
@@ -434,6 +497,7 @@
   }
 }
 
+
 /**
  * Handler for the 'show' message from the host page, called when it wants to
  * add a suggestion tile.
@@ -922,9 +986,20 @@
 
   // Set the maximum number of tiles to show.
   if (isCustomLinksEnabled) {
-    maxNumTiles = 10;
+    maxNumTiles = MD_MAX_NUM_CUSTOM_LINK_TILES;
   }
 
+  // Throttle the resize event.
+  let resizeTimeout;
+  window.onresize = () => {
+    if (resizeTimeout)
+      return;
+    resizeTimeout = window.setTimeout(() => {
+      resizeTimeout = null;
+      updateTileVisibility();
+    }, RESIZE_TIMEOUT_DURATION);
+  };
+
   window.addEventListener('message', handlePostMessage);
 };
 
diff --git a/chrome/browser/resources/omnibox/omnibox.js b/chrome/browser/resources/omnibox/omnibox.js
index c9ab027..b6e4e6f 100644
--- a/chrome/browser/resources/omnibox/omnibox.js
+++ b/chrome/browser/resources/omnibox/omnibox.js
@@ -25,13 +25,6 @@
   let showAllProviders;
 
   /**
-   * @type {!Array<mojom.OmniboxResult>} an array of all autocomplete results we've seen
-   *     for this query.  We append to this list once for every call to
-   *     handleNewAutocompleteResult.  See omnibox.mojom for details..
-   */
-  let progressiveAutocompleteResults = [];
-
-  /**
    * @type {number} the value for cursor position we sent with the most
    *     recent request.  We need to remember this in order to display it
    *     in the output; otherwise it's hard or impossible to determine
@@ -42,12 +35,14 @@
   /**
    * Register our event handlers.
    */
-  function initialize() {
+  function initializeEventHandlers() {
     omniboxDebugText = $('omnibox-debug-text');
     showIncompleteResults = $('show-incomplete-results');
     showDetails = $('show-details');
     showAllProviders = $('show-all-providers');
 
+    let startOmniboxQuery = () => browserProxy.makeRequest();
+
     $('input-text').addEventListener('input', startOmniboxQuery);
     $('prevent-inline-autocomplete')
         .addEventListener('change', startOmniboxQuery);
@@ -59,29 +54,6 @@
   }
 
   /**
-   * Extracts the input text from the text field and sends it to the
-   * C++ portion of chrome to handle.  The C++ code will iteratively
-   * call handleNewAutocompleteResult as results come in.
-   */
-  function startOmniboxQuery(event) {
-    // First, clear the results of past calls (if any).
-    clearOutput();
-    progressiveAutocompleteResults = [];
-    // Then, call chrome with a five-element list:
-    // - first element: the value in the text box
-    // - second element: the location of the cursor in the text box
-    // - third element: the value of prevent-inline-autocomplete
-    // - forth element: the value of prefer-keyword
-    // - fifth element: the value of page-classification
-    let inputTextElement = $('input-text');
-    cursorPositionUsed = inputTextElement.selectionEnd;
-    browserProxy.startOmniboxQuery(
-        inputTextElement.value, cursorPositionUsed,
-        $('prevent-inline-autocomplete').checked, $('prefer-keyword').checked,
-        parseInt($('page-classification').value, 10));
-  }
-
-  /**
    * Returns a simple object with information about how to display an
    * autocomplete result data field.
    */
@@ -128,8 +100,8 @@
     new PresentationInfoRecord(
         'Can Be Default', '', 'allowedToBeDefaultMatch', false,
         'A green checkmark indicates that the result can be the default ' +
-        'match (i.e., can be the match that pressing enter in the omnibox ' +
-        'navigates to).'),
+            'match (i.e., can be the match that pressing enter in the ' +
+            'omnibox navigates to).'),
     new PresentationInfoRecord(
         'Starred', '', 'starred', false,
         'A green checkmark indicates that the result has been bookmarked.'),
@@ -137,7 +109,8 @@
         'Has tab match', '', 'hasTabMatch', false,
         'A green checkmark indicates that the result URL matches an open tab.'),
     new PresentationInfoRecord(
-        'Description', '', 'description', false, 'The page title of the result.'),
+        'Description', '', 'description', false,
+        'The page title of the result.'),
     new PresentationInfoRecord(
         'URL', '', 'destinationUrl', true, 'The URL for the result.'),
     new PresentationInfoRecord(
@@ -146,32 +119,32 @@
     new PresentationInfoRecord(
         'Inline Autocompletion', '', 'inlineAutocompletion', false,
         'The text shown in the omnibox as a blue highlight selection ' +
-        'following the cursor, if this match is shown inline.'),
+            'following the cursor, if this match is shown inline.'),
     new PresentationInfoRecord(
         'Del', '', 'deletable', false,
         'A green checkmark indicates that the result can be deleted from ' +
-        'the visit history.'),
+            'the visit history.'),
     new PresentationInfoRecord('Prev', '', 'fromPrevious', false, ''),
     new PresentationInfoRecord(
         'Tran',
         'https://cs.chromium.org/chromium/src/ui/base/page_transition_types.h' +
-        '?q=page_transition_types.h&sq=package:chromium&dr=CSs&l=14',
+            '?q=page_transition_types.h&sq=package:chromium&dr=CSs&l=14',
         'transition', false, 'How the user got to the result.'),
     new PresentationInfoRecord(
         'Done', '', 'providerDone', false,
         'A green checkmark indicates that the provider is done looking for ' +
-        'more results.'),
+            'more results.'),
     new PresentationInfoRecord(
         'Associated Keyword', '', 'associatedKeyword', false,
         'If non-empty, a "press tab to search" hint will be shown and will ' +
-        'engage this keyword.'),
+            'engage this keyword.'),
     new PresentationInfoRecord(
         'Keyword', '', 'keyword', false,
         'The keyword of the search engine to be used.'),
     new PresentationInfoRecord(
         'Duplicates', '', 'duplicates', false,
         'The number of matches that have been marked as duplicates of this ' +
-        'match.'),
+            'match.'),
     new PresentationInfoRecord(
         'Additional Info', '', 'additionalInfo', false,
         'Provider-specific information about the result.'),
@@ -400,9 +373,11 @@
   function refreshAllResults() {
     clearOutput();
     if (showIncompleteResults.checked)
-      progressiveAutocompleteResults.forEach(addResultToOutput);
-    else if (progressiveAutocompleteResults.length)
-      addResultToOutput(progressiveAutocompleteResults[progressiveAutocompleteResults.length - 1]);
+      browserProxy.progressiveAutocompleteResults.forEach(addResultToOutput);
+    else if (browserProxy.progressiveAutocompleteResults.length)
+      addResultToOutput(
+          browserProxy.progressiveAutocompleteResults
+              [browserProxy.progressiveAutocompleteResults.length - 1]);
   }
 
   /*
@@ -416,7 +391,9 @@
   function refreshNewResult() {
     if (!showIncompleteResults.checked)
       clearOutput();
-    addResultToOutput(progressiveAutocompleteResults[progressiveAutocompleteResults.length - 1]);
+    addResultToOutput(
+        browserProxy.progressiveAutocompleteResults
+            [browserProxy.progressiveAutocompleteResults.length - 1]);
   }
 
   /*
@@ -427,35 +404,62 @@
       omniboxDebugText.removeChild(omniboxDebugText.firstChild);
   }
 
-// NOTE: Need to keep a global reference to the |pageImpl| such that it is not
-// garbage collected, which causes the pipe to close and future calls from C++
-// to JS to get dropped.
-  let pageImpl = null;
-  let browserProxy = null;
+  class BrowserProxy {
+    constructor() {
+      /** @private {!mojom.OmniboxPageHandlerPtr} */
+      this.pagehandlePtr_ = new mojom.OmniboxPageHandlerPtr;
+      Mojo.bindInterface(
+          mojom.OmniboxPageHandler.name,
+          mojo.makeRequest(this.pagehandlePtr_).handle);
+      let client = new mojom.OmniboxPagePtr;
+      // NOTE: Need to keep a global reference to the |binding_| such that it is
+      // not garbage collected, which causes the pipe to close and future calls
+      // from C++ to JS to get dropped.
+      /** @private {!mojo.Binding} */
+      this.binding_ =
+          new mojo.Binding(mojom.OmniboxPage, this, mojo.makeRequest(client));
+      this.pagehandlePtr_.setClientPage(client);
 
-  function initializeProxies() {
-    browserProxy = new mojom.OmniboxPageHandlerPtr;
-    Mojo.bindInterface(
-        mojom.OmniboxPageHandler.name, mojo.makeRequest(browserProxy).handle);
-
-    class OmniboxPageImpl {
-      constructor(request) {
-        this.binding_ = new mojo.Binding(mojom.OmniboxPage, this, request);
-      }
-
-      handleNewAutocompleteResult(result) {
-        progressiveAutocompleteResults.push(result);
-        refreshNewResult();
-      }
+      /**
+       * @type {!Array<mojom.OmniboxResult>} an array of all autocomplete
+       *     results we've seen for this query.  We append to this list once for
+       *     every call to handleNewAutocompleteResult.  See omnibox.mojom for
+       *     details..
+       */
+      this.progressiveAutocompleteResults = [];
     }
 
-    let client = new mojom.OmniboxPagePtr;
-    pageImpl = new OmniboxPageImpl(mojo.makeRequest(client));
-    browserProxy.setClientPage(client);
+    /**
+     * Extracts the input text from the text field and sends it to the
+     * C++ portion of chrome to handle.  The C++ code will iteratively
+     * call handleNewAutocompleteResult as results come in.
+     */
+    makeRequest() {
+      this.progressiveAutocompleteResults = [];
+      // Then, call chrome with a five-element list:
+      // - first element: the value in the text box
+      // - second element: the location of the cursor in the text box
+      // - third element: the value of prevent-inline-autocomplete
+      // - forth element: the value of prefer-keyword
+      // - fifth element: the value of page-classification
+      let inputTextElement = $('input-text');
+      cursorPositionUsed = inputTextElement.selectionEnd;
+      this.pagehandlePtr_.startOmniboxQuery(
+          inputTextElement.value, cursorPositionUsed,
+          $('prevent-inline-autocomplete').checked, $('prefer-keyword').checked,
+          parseInt($('page-classification').value, 10));
+    }
+
+    handleNewAutocompleteResult(result) {
+      this.progressiveAutocompleteResults.push(result);
+      refreshNewResult();
+    }
   }
 
+  let browserProxy;
+
   document.addEventListener('DOMContentLoaded', () => {
-    initializeProxies();
-    initialize();
+    browserProxy = new BrowserProxy();
+    initializeEventHandlers();
   });
 })();
diff --git a/chrome/browser/resources/print_preview/new/app.html b/chrome/browser/resources/print_preview/new/app.html
index 77d84b9..8236c723c 100644
--- a/chrome/browser/resources/print_preview/new/app.html
+++ b/chrome/browser/resources/print_preview/new/app.html
@@ -69,6 +69,20 @@
         padding-bottom: 16px;
       }
 
+      #container > *,
+      #container iron-collapse > * {
+        display: block;
+        margin-bottom: 16px;
+        margin-top: 16px;
+      }
+
+      #container print-preview-more-settings,
+      #container iron-collapse,
+      #container print-preview-link-container {
+        margin-bottom: 0;
+        margin-top: 0;
+      }
+
       #preview-area-container {
         align-items: center;
         background-color: var(--google-grey-200);
diff --git a/chrome/browser/resources/print_preview/new/link_container.html b/chrome/browser/resources/print_preview/new/link_container.html
index 710b1c0..db63ae11 100644
--- a/chrome/browser/resources/print_preview/new/link_container.html
+++ b/chrome/browser/resources/print_preview/new/link_container.html
@@ -9,7 +9,7 @@
 <dom-module id="print-preview-link-container">
   <template>
     <style include="print-preview-shared throbber cr-hidden-style">
-      :host([disabled]) {
+      .link:not([actionable]) {
         pointer-events: none;
       }
 
@@ -35,18 +35,19 @@
         flex: 1;
       }
 
-      :host([disabled]) .label {
+      .link:not([actionable]) .label {
         color: var(--paper-grey-600);
         opacity: .65;
       }
     </style>
-    <div class="link" id="systemDialogLink" actionable$="[[!disabled]]"
+    <div class="link" id="systemDialogLink"
+         actionable$="[[!systemDialogLinkDisabled_]]"
          hidden$="[[!shouldShowSystemDialogLink_]]"
          on-click="onSystemDialogClick_">
       <div class="label">$i18n{systemDialogOption}</div>
       <paper-icon-button-light actionable class="icon-external"
           hidden$="[[openingSystemDialog_]]">
-        <button disabled="[[disabled]]"
+        <button disabled="[[systemDialogLinkDisabled_]]"
             aria-label="$i18n{systemDialogOption}">
         </button>
       </paper-icon-button-light>
diff --git a/chrome/browser/resources/print_preview/new/link_container.js b/chrome/browser/resources/print_preview/new/link_container.js
index 6b9dec2..db32458a7 100644
--- a/chrome/browser/resources/print_preview/new/link_container.js
+++ b/chrome/browser/resources/print_preview/new/link_container.js
@@ -11,10 +11,7 @@
     /** @type {?print_preview.Destination} */
     destination: Object,
 
-    disabled: {
-      type: Boolean,
-      reflectToAttribute: true,
-    },
+    disabled: Boolean,
 
     /** @private {boolean} */
     shouldShowSystemDialogLink_: {
@@ -23,6 +20,12 @@
     },
 
     /** @private {boolean} */
+    systemDialogLinkDisabled_: {
+      type: Boolean,
+      computed: 'computeSystemDialogLinkDisabled_(disabled)',
+    },
+
+    /** @private {boolean} */
     openingSystemDialog_: {
       type: Boolean,
       value: false,
@@ -50,6 +53,14 @@
         print_preview.Destination.GooglePromotedId.SAVE_AS_PDF;
   },
 
+  /**
+   * @return {boolean} Whether the system dialog link should be disabled
+   * @private
+   */
+  computeSystemDialogLinkDisabled_: function() {
+    return cr.isWindows && this.disabled;
+  },
+
   /** @private */
   onSystemDialogClick_: function() {
     if (!this.shouldShowSystemDialogLink_)
diff --git a/chrome/browser/resources/print_preview/new/other_options_settings.html b/chrome/browser/resources/print_preview/new/other_options_settings.html
index 7d5b963..b40a600d 100644
--- a/chrome/browser/resources/print_preview/new/other_options_settings.html
+++ b/chrome/browser/resources/print_preview/new/other_options_settings.html
@@ -10,19 +10,6 @@
 <dom-module id="print-preview-other-options-settings">
   <template>
     <style include="print-preview-shared cr-hidden-style">
-      print-preview-settings-section {
-        margin-bottom: 0;
-        margin-top: 0;
-      }
-
-      print-preview-settings-section.last-visible {
-        margin-bottom: 8px;
-      }
-
-      print-preview-settings-section.first-visible {
-        margin-top: 8px;
-      }
-
       print-preview-settings-section:not(.first-visible) .title {
         display: none;
       }
@@ -30,7 +17,7 @@
     <template is="dom-repeat" items="[[options_]]">
       <print-preview-settings-section managed="[[item.managed]]"
           show-policy-on-end hidden$="[[!item.available]]"
-          class$="[[getClass_(index, firstIndex_, lastIndex_)]]">
+          class$="[[getClass_(index, firstIndex_)]]">
         <div slot="title">
           <span class="title">$i18n{optionsLabel}</span>
         </div>
diff --git a/chrome/browser/resources/print_preview/new/other_options_settings.js b/chrome/browser/resources/print_preview/new/other_options_settings.js
index b6dbcec..cf69ee5 100644
--- a/chrome/browser/resources/print_preview/new/other_options_settings.js
+++ b/chrome/browser/resources/print_preview/new/other_options_settings.js
@@ -38,15 +38,6 @@
       type: Number,
       value: 0,
     },
-
-    /**
-     * The index of the last visible checkbox.
-     * @private {number}
-     */
-    lastIndex_: {
-      type: Number,
-      value: 0,
-    },
   },
 
   observers: [
@@ -94,13 +85,10 @@
     this.set(`options_.${index}.value`, setting.value);
     this.set(`options_.${index}.managed`, setting.setByPolicy);
 
-    // Update last/first
+    // Update first index
     const availableOptions = this.options_.filter(option => !!option.available);
-    if (availableOptions.length > 0) {
+    if (availableOptions.length > 0)
       this.firstIndex_ = this.options_.indexOf(availableOptions[0]);
-      this.lastIndex_ =
-          this.options_.indexOf(availableOptions[availableOptions.length - 1]);
-    }
   },
 
   /**
@@ -149,16 +137,11 @@
 
   /**
    * @param {number} index The index of the settings section.
-   * @return {string} Class string containing 'first-visible' and/or
-   *     'last-visible' if the settings section is the first or last visible.
+   * @return {string} Class string containing 'first-visible' if the settings
+   *     section is the first visible.
    * @private
    */
   getClass_: function(index) {
-    let classString = '';
-    if (index === this.firstIndex_)
-      classString += 'first-visible';
-    if (index === this.lastIndex_)
-      classString += ' last-visible';
-    return classString;
+    return index === this.firstIndex_ ? 'first-visible' : '';
   },
 });
diff --git a/chrome/browser/resources/print_preview/new/pages_settings.html b/chrome/browser/resources/print_preview/new/pages_settings.html
index 22e39988..9d59c9e 100644
--- a/chrome/browser/resources/print_preview/new/pages_settings.html
+++ b/chrome/browser/resources/print_preview/new/pages_settings.html
@@ -29,11 +29,6 @@
         };
       }
 
-      /* Margin = standard margin (16px) - error field margin (8px) */
-      :host(:not([error-state_='0'])) print-preview-settings-section {
-        margin-bottom: 8px;
-      }
-
       #pageSettingsCustomInput {
         cursor: default;
         height: 100%;
diff --git a/chrome/browser/resources/print_preview/new/scaling_settings.html b/chrome/browser/resources/print_preview/new/scaling_settings.html
index 642a9d28..bd6c505 100644
--- a/chrome/browser/resources/print_preview/new/scaling_settings.html
+++ b/chrome/browser/resources/print_preview/new/scaling_settings.html
@@ -10,15 +10,12 @@
 <dom-module id="print-preview-scaling-settings">
   <template>
     <style include="print-preview-shared">
-      print-preview-settings-section {
-        margin-bottom: 0;
-      }
     </style>
     <print-preview-settings-section hidden$="[[!settings.fitToPage.available]]">
       <span slot="title">$i18n{scalingLabel}</span>
       <div slot="controls" class="checkbox">
         <cr-checkbox id="fit-to-page-checkbox" tabindex="1"
-            disabled$="[[getDisabled_(disabled, inputValid_)]]"
+            disabled$="[[getDisabled_(disabled, currentState_)]]"
             on-change="onFitToPageChange_" aria-live="polite">
           $i18n{optionFitToPage}
         </cr-checkbox>
diff --git a/chrome/browser/resources/print_preview/new/scaling_settings.js b/chrome/browser/resources/print_preview/new/scaling_settings.js
index fe71d7e..adc3c76 100644
--- a/chrome/browser/resources/print_preview/new/scaling_settings.js
+++ b/chrome/browser/resources/print_preview/new/scaling_settings.js
@@ -150,7 +150,9 @@
     if (this.ignoreValue_)
       return;
 
-    this.setSettingValid('scaling', this.inputValid_);
+    if (this.currentValue_ !== '')
+      this.setSettingValid('scaling', this.inputValid_);
+
     if (this.currentValue_ !== '' && this.inputValid_)
       this.setSetting('scaling', this.currentValue_);
   },
@@ -173,6 +175,8 @@
 
       if (newValue == false)
         this.currentValue_ = this.lastValidScaling_;
+      else
+        this.currentState_ = print_preview_new.ScalingState.FIT_TO_PAGE;
 
       // For tests only
       this.fire('update-checkbox-setting', 'fitToPage');
@@ -184,7 +188,8 @@
    * @private
    */
   getDisabled_: function() {
-    return this.disabled && this.inputValid_;
+    return this.disabled &&
+        this.currentState_ !== print_preview_new.ScalingState.INVALID;
   },
 
   /**
@@ -197,7 +202,8 @@
       this.ignoreFtp_ = true;
       this.$$('#fit-to-page-checkbox').checked = false;
       this.lastFitToPageValue_ = false;
-      this.setSetting('fitToPage', false);
+      if (current == print_preview_new.ScalingState.VALID)
+        this.setSetting('fitToPage', false);
       this.ignoreFtp_ = false;
     }
     if (current == print_preview_new.ScalingState.FIT_TO_PAGE) {
@@ -211,6 +217,12 @@
       this.currentValue_ = this.getFitToPageScalingDisplayValue_();
       this.ignoreValue_ = false;
     }
+    if (current == print_preview_new.ScalingState.VALID &&
+        previous == print_preview_new.ScalingState.INVALID &&
+        this.getSetting('fitToPage').available) {
+      this.setSetting('fitToPage', false);
+    }
+
   },
 
   /**
diff --git a/chrome/browser/resources/print_preview/new/settings_section.html b/chrome/browser/resources/print_preview/new/settings_section.html
index 90b09f8c..475ce1b 100644
--- a/chrome/browser/resources/print_preview/new/settings_section.html
+++ b/chrome/browser/resources/print_preview/new/settings_section.html
@@ -11,8 +11,6 @@
     <style include="print-preview-shared cr-shared-style">
       :host {
         display: flex;
-        margin-bottom: 16px;
-        margin-top: 16px;
         padding: 0 20px;
         --policy-icon-padding: 4px;
         --policy-icon-size: 20px;
diff --git a/chrome/browser/resources/settings/site_settings/site_details_permission.html b/chrome/browser/resources/settings/site_settings/site_details_permission.html
index 9da49da8..55250bf 100644
--- a/chrome/browser/resources/settings/site_settings/site_details_permission.html
+++ b/chrome/browser/resources/settings/site_settings/site_details_permission.html
@@ -32,8 +32,8 @@
                 site.source,
                 category,
                 site.setting,
-                '$i18nPolymer{siteSettingsSourceAdsBlacklist}',
-                '$i18nPolymer{siteSettingsAdsBlockSingular}',
+                '$i18nPolymer{siteSettingsAdsBlockBlacklistedSingular}',
+                '$i18nPolymer{siteSettingsAdsBlockNotBlacklistedSingular}',
                 '$i18nPolymer{siteSettingsSourceEmbargo}',
                 '$i18nPolymer{siteSettingsSourceInsecureOrigin}',
                 '$i18nPolymer{siteSettingsSourceKillSwitch}',
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index 1d56c8c..1c59d78 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -544,8 +544,10 @@
 SafeBrowsingService::CreateNetworkContextParams() {
   auto params = g_browser_process->system_network_context_manager()
                     ->CreateDefaultNetworkContextParams();
-  if (!proxy_config_monitor_)
-    proxy_config_monitor_ = std::make_unique<ProxyConfigMonitor>();
+  if (!proxy_config_monitor_) {
+    proxy_config_monitor_ =
+        std::make_unique<ProxyConfigMonitor>(g_browser_process->local_state());
+  }
   proxy_config_monitor_->AddToNetworkContextParams(params.get());
   return params;
 }
diff --git a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc
index de9794b..dffb2b1 100644
--- a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc
+++ b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc
@@ -491,14 +491,6 @@
   }
 
   load_credentials_state_ = LOAD_CREDENTIALS_IN_PROGRESS;
-  if (primary_account_id.empty() &&
-      (account_consistency_ ==
-           signin::AccountConsistencyMethod::kDiceFixAuthErrors ||
-       account_consistency_ == signin::AccountConsistencyMethod::kDisabled)) {
-    load_credentials_state_ = LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS;
-    FinishLoadingCredentials();
-    return;
-  }
 
   if (!primary_account_id.empty())
     ValidateAccountId(primary_account_id);
@@ -515,15 +507,13 @@
     return;
   }
 
-  if (!primary_account_id.empty()) {
-    // If the account_id is an email address, then canonicalize it.  This
-    // is to support legacy account_ids, and will not be needed after
-    // switching to gaia-ids.
-    if (primary_account_id.find('@') != std::string::npos) {
-      loading_primary_account_id_ = gaia::CanonicalizeEmail(primary_account_id);
-    } else {
-      loading_primary_account_id_ = primary_account_id;
-    }
+  // If |account_id| is an email address, then canonicalize it. This is needed
+  // to support legacy account IDs, and will not be needed after switching to
+  // gaia IDs.
+  if (primary_account_id.find('@') != std::string::npos) {
+    loading_primary_account_id_ = gaia::CanonicalizeEmail(primary_account_id);
+  } else {
+    loading_primary_account_id_ = primary_account_id;
   }
 
   web_data_service_request_ = token_web_data_->GetAllTokens(this);
@@ -553,11 +543,6 @@
   // Make sure that we have an entry for |loading_primary_account_id_| in the
   // map.  The entry could be missing if there is a corruption in the token DB
   // while this profile is connected to an account.
-  DCHECK(!loading_primary_account_id_.empty() ||
-         account_consistency_ == signin::AccountConsistencyMethod::kMirror ||
-         signin::DiceMethodGreaterOrEqual(
-             account_consistency_,
-             signin::AccountConsistencyMethod::kDiceMigration));
   if (!loading_primary_account_id_.empty() &&
       refresh_tokens_.count(loading_primary_account_id_) == 0) {
     if (load_credentials_state_ == LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS) {
diff --git a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h
index 82da56e9..529742e 100644
--- a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h
+++ b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h
@@ -123,6 +123,9 @@
   FRIEND_TEST_ALL_PREFIXES(
       MutableProfileOAuth2TokenServiceDelegateTest,
       PersistenceLoadCredentialsEmptyPrimaryAccountId_DiceEnabled);
+  FRIEND_TEST_ALL_PREFIXES(
+      MutableProfileOAuth2TokenServiceDelegateTest,
+      LoadCredentialsClearsTokenDBWhenNoPrimaryAccount_DiceDisabled);
   FRIEND_TEST_ALL_PREFIXES(MutableProfileOAuth2TokenServiceDelegateTest,
                            PersistenceLoadCredentials);
   FRIEND_TEST_ALL_PREFIXES(MutableProfileOAuth2TokenServiceDelegateTest,
diff --git a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc
index 656edd5..5fb72e2 100644
--- a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc
+++ b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc
@@ -78,7 +78,8 @@
 class MutableProfileOAuth2TokenServiceDelegateTest
     : public testing::Test,
       public OAuth2AccessTokenConsumer,
-      public OAuth2TokenService::Observer {
+      public OAuth2TokenService::Observer,
+      public WebDataServiceConsumer {
  public:
   MutableProfileOAuth2TokenServiceDelegateTest()
       : signin_error_controller_(
@@ -157,6 +158,16 @@
       token_web_data_->SetTokenForService(service, value);
   }
 
+  // WebDataServiceConsumer implementation
+  void OnWebDataServiceRequestDone(
+      WebDataServiceBase::Handle h,
+      std::unique_ptr<WDTypedResult> result) override {
+    DCHECK(!token_web_data_result_);
+    DCHECK_EQ(TOKEN_RESULT, result->GetType());
+    token_web_data_result_.reset(
+        static_cast<WDResult<TokenResult>*>(result.release()));
+  }
+
   // OAuth2AccessTokenConusmer implementation
   void OnGetTokenSuccess(
       const OAuth2AccessTokenConsumer::TokenResponse& token_response) override {
@@ -234,6 +245,7 @@
   sync_preferences::TestingPrefServiceSyncable pref_service_;
   AccountTrackerService account_tracker_service_;
   scoped_refptr<TokenWebData> token_web_data_;
+  std::unique_ptr<WDResult<TokenResult>> token_web_data_result_;
   int access_token_success_count_;
   int access_token_failure_count_;
   GoogleServiceAuthError access_token_failure_;
@@ -246,6 +258,32 @@
   bool revoke_all_tokens_on_load_;
 };
 
+TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
+       LoadCredentialsClearsTokenDBWhenNoPrimaryAccount_DiceDisabled) {
+  // Populate DB with 2 valid tokens.
+  AddAuthTokenManually("AccountId-12345", "refresh_token");
+  AddAuthTokenManually("AccountId-67890", "refresh_token");
+
+  CreateOAuth2ServiceDelegate(
+      signin::AccountConsistencyMethod::kDiceFixAuthErrors);
+  oauth2_service_delegate_->LoadCredentials(/*primary_account_id=*/"");
+  base::RunLoop().RunUntilIdle();
+
+  // No tokens were loaded.
+  EXPECT_EQ(1, tokens_loaded_count_);
+  EXPECT_EQ(1, start_batch_changes_);
+  EXPECT_EQ(0, token_available_count_);
+  EXPECT_EQ(2, token_revoked_count_);
+  EXPECT_EQ(1, end_batch_changes_);
+  EXPECT_EQ(0U, oauth2_service_delegate_->refresh_tokens_.size());
+
+  // Handle to the request reading tokens from database.
+  token_web_data_->GetAllTokens(this);
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(token_web_data_result_.get());
+  ASSERT_EQ(0u, token_web_data_result_->GetValue().tokens.size());
+}
+
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceDBUpgrade) {
   CreateOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kMirror);
   std::string main_account_id("account_id");
@@ -364,6 +402,7 @@
   EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_NOT_STARTED,
             oauth2_service_delegate_->GetLoadCredentialsState());
   oauth2_service_delegate_->LoadCredentials("");
+  base::RunLoop().RunUntilIdle();
   EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS,
             oauth2_service_delegate_->GetLoadCredentialsState());
 }
diff --git a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
index 2a050b9..3946f88 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
@@ -139,8 +139,8 @@
 // blink/renderer/core/loader/subresource_filter.cc
 constexpr const char kBlinkDisallowSubframeConsoleMessageFormat[] =
     "Chrome blocked resource %s on this site because this site tends to show "
-    "ads that interrupt, distract, or prevent user control. Learn more at "
-    "https://www.chromestatus.com/feature/5738264052891648";
+    "ads that interrupt, distract, mislead, or prevent user control. Learn "
+    "more at https://www.chromestatus.com/feature/5738264052891648";
 
 }  // namespace
 
diff --git a/chrome/browser/sync/sync_ui_util.h b/chrome/browser/sync/sync_ui_util.h
index 1fc4636..b91f4dc 100644
--- a/chrome/browser/sync/sync_ui_util.h
+++ b/chrome/browser/sync/sync_ui_util.h
@@ -23,8 +23,6 @@
   PRE_SYNCED,  // User has not set up sync.
   SYNCED,      // We are synced and authenticated to a gmail account.
   SYNC_ERROR,  // A sync error (such as invalid credentials) has occurred.
-  SYNC_PROMO,  // A situation has occurred which should be brought to the user's
-               // attention, but not as an error.
 };
 
 // The action associated with the sync status.
diff --git a/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc b/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
index 0412945..976589c 100644
--- a/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
@@ -337,21 +337,55 @@
 
   // Make the two clients have the same profile.
   AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
-  EXPECT_TRUE(AutofillProfileChecker(0, 1).Wait());
-  EXPECT_EQ(1U, GetAllAutoFillProfiles(0).size());
+  ASSERT_TRUE(AutofillProfileChecker(0, 1).Wait());
+  ASSERT_EQ(1U, GetAllAutoFillProfiles(0).size());
 
   RemoveProfile(0, GetAllAutoFillProfiles(0)[0]->guid());
-  UpdateProfile(1,
-                GetAllAutoFillProfiles(1)[0]->guid(),
-                AutofillType(autofill::NAME_FIRST),
-                base::ASCIIToUTF16("Bart"));
+  UpdateProfile(1, GetAllAutoFillProfiles(1)[0]->guid(),
+                AutofillType(autofill::NAME_FIRST), base::ASCIIToUTF16("Bart"));
 
   EXPECT_TRUE(AutofillProfileChecker(0, 1).Wait());
-  // TODO(crbug.com/870333): The result should be deterministic for USS (either
-  // update or delete, but always do the same).
+  // The exact result is non-deterministic without a strong consistency model
+  // server-side, but both clients should converge (either update or delete).
   EXPECT_EQ(GetAllAutoFillProfiles(0).size(), GetAllAutoFillProfiles(1).size());
 }
 
+// Tests that modifying a profile at the same time on two clients while
+// syncing results in a conflict where the update wins. This only works with
+// a server that supports a strong consistency model and is hence capable of
+// detecting conflicts server-side.
+IN_PROC_BROWSER_TEST_P(TwoClientAutofillProfileSyncTest,
+                       DeleteAndUpdateWithStrongConsistency) {
+  if (GetParam() == false) {
+    // TODO(crbug.com/890746): There seems to be a bug in directory code that
+    // resolves conflicts in a way that local deletion wins over a remote
+    // update, which makes this test non-deterministic, because the logic is
+    // asymmetric (so the outcome depends on which client commits first).
+    // For now, we "disable" the test.
+    return;
+  }
+
+  ASSERT_TRUE(SetupSync());
+  GetFakeServer()->EnableStrongConsistencyWithConflictDetectionModel();
+
+  // Make the two clients have the same profile.
+  AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+  ASSERT_TRUE(AutofillProfileChecker(0, 1).Wait());
+  ASSERT_EQ(1U, GetAllAutoFillProfiles(0).size());
+
+  RemoveProfile(0, GetAllAutoFillProfiles(0)[0]->guid());
+  UpdateProfile(1, GetAllAutoFillProfiles(1)[0]->guid(),
+                AutofillType(autofill::NAME_FIRST), base::ASCIIToUTF16("Bart"));
+
+  // One of the two clients (the second one committing) will be requested by the
+  // server to resolve the conflict and recommit. The conflict resolution should
+  // be undeletion wins, which can mean local wins or remote wins, depending on
+  // which client is involved.
+  EXPECT_TRUE(AutofillProfileChecker(0, 1).Wait());
+  EXPECT_EQ(1U, GetAllAutoFillProfiles(0).size());
+  EXPECT_EQ(1U, GetAllAutoFillProfiles(1).size());
+}
+
 IN_PROC_BROWSER_TEST_P(TwoClientAutofillProfileSyncTest, MaxLength) {
   ASSERT_TRUE(SetupSync());
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 3fb0956..5e86d16f 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -120,8 +120,6 @@
       "cocoa/single_web_contents_dialog_manager_cocoa.mm",
       "cocoa/ssl_client_certificate_selector_cocoa.h",
       "cocoa/ssl_client_certificate_selector_cocoa.mm",
-      "cocoa/tab_contents/favicon_util_mac.h",
-      "cocoa/tab_contents/favicon_util_mac.mm",
       "cocoa/tab_contents/overlayable_contents_controller.h",
       "cocoa/tab_contents/overlayable_contents_controller.mm",
       "cocoa/tab_contents/tab_contents_controller.h",
@@ -1496,6 +1494,8 @@
       "ash/network/network_state_notifier.h",
       "ash/network/tether_notification_presenter.cc",
       "ash/network/tether_notification_presenter.h",
+      "ash/screen_orientation_delegate_chromeos.cc",
+      "ash/screen_orientation_delegate_chromeos.h",
       "ash/session_controller_client.cc",
       "ash/session_controller_client.h",
       "ash/session_util.cc",
diff --git a/chrome/browser/ui/ash/DEPS b/chrome/browser/ui/ash/DEPS
index 359629c..9086047 100644
--- a/chrome/browser/ui/ash/DEPS
+++ b/chrome/browser/ui/ash/DEPS
@@ -49,6 +49,10 @@
     # https://crbug.com/665064
     "+ash/shell_delegate.h",
   ],
+  "screen_orientation_delegate_chromeos.cc": [
+    "+ash/display/screen_orientation_controller.h",
+    "+ash/shell.h",
+  ],
   # For ash::Shell::GetContainer (!mash)
   "system_tray_client\.cc": [
     "+ash/shell.h",
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 3e2961c..be613f7 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -35,6 +35,7 @@
 #include "chrome/browser/ui/ash/network/data_promo_notification.h"
 #include "chrome/browser/ui/ash/network/network_connect_delegate_chromeos.h"
 #include "chrome/browser/ui/ash/network/network_portal_notification_controller.h"
+#include "chrome/browser/ui/ash/screen_orientation_delegate_chromeos.h"
 #include "chrome/browser/ui/ash/session_controller_client.h"
 #include "chrome/browser/ui/ash/system_tray_client.h"
 #include "chrome/browser/ui/ash/tab_scrubber.h"
@@ -205,6 +206,13 @@
         std::move(user_activity_monitor), user_activity_detector_.get());
   }
 
+  // TODO(estade): implement ScreenOrientationDelegateChromeos for Mash and
+  // remove this condition.
+  if (!features::IsUsingWindowService()) {
+    screen_orientation_delegate_ =
+        std::make_unique<ScreenOrientationDelegateChromeos>();
+  }
+
   app_list_client_ = std::make_unique<AppListClientImpl>();
 
   // Must be available at login screen, so initialize before profile.
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
index eb24d1a..8574037 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
@@ -44,6 +44,7 @@
 class MediaClient;
 class NetworkConnectDelegateChromeOS;
 class NightLightClient;
+class ScreenOrientationDelegateChromeos;
 class SessionControllerClient;
 class SystemTrayClient;
 class TabletModeClient;
@@ -97,6 +98,8 @@
   std::unique_ptr<AppListClientImpl> app_list_client_;
   std::unique_ptr<ChromeNewWindowClient> chrome_new_window_client_;
   std::unique_ptr<ImeControllerClient> ime_controller_client_;
+  std::unique_ptr<ScreenOrientationDelegateChromeos>
+      screen_orientation_delegate_;
   std::unique_ptr<SessionControllerClient> session_controller_client_;
   std::unique_ptr<SystemTrayClient> system_tray_client_;
   std::unique_ptr<TabletModeClient> tablet_mode_client_;
diff --git a/ash/content/screen_orientation_delegate_chromeos.cc b/chrome/browser/ui/ash/screen_orientation_delegate_chromeos.cc
similarity index 61%
rename from ash/content/screen_orientation_delegate_chromeos.cc
rename to chrome/browser/ui/ash/screen_orientation_delegate_chromeos.cc
index 6dd0d68..716400b 100644
--- a/ash/content/screen_orientation_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/screen_orientation_delegate_chromeos.cc
@@ -2,37 +2,37 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/content/screen_orientation_delegate_chromeos.h"
+#include "chrome/browser/ui/ash/screen_orientation_delegate_chromeos.h"
 
-#include "ash/display/screen_orientation_controller.h"
-#include "ash/shell.h"
+#include "ash/display/screen_orientation_controller.h"  // mash-ok
+#include "ash/shell.h"                                  // mash-ok
+#include "chrome/browser/ui/ash/tablet_mode_client.h"
 #include "content/public/browser/web_contents.h"
 
-namespace ash {
 namespace {
 
-OrientationLockType ToAshOrientationLockType(
+ash::OrientationLockType ToAshOrientationLockType(
     blink::WebScreenOrientationLockType blink_orientation_lock) {
   switch (blink_orientation_lock) {
     case blink::kWebScreenOrientationLockDefault:
     case blink::kWebScreenOrientationLockAny:
-      return OrientationLockType::kAny;
+      return ash::OrientationLockType::kAny;
     case blink::kWebScreenOrientationLockPortrait:
-      return OrientationLockType::kPortrait;
+      return ash::OrientationLockType::kPortrait;
     case blink::kWebScreenOrientationLockPortraitPrimary:
-      return OrientationLockType::kPortraitPrimary;
+      return ash::OrientationLockType::kPortraitPrimary;
     case blink::kWebScreenOrientationLockPortraitSecondary:
-      return OrientationLockType::kPortraitSecondary;
+      return ash::OrientationLockType::kPortraitSecondary;
     case blink::kWebScreenOrientationLockLandscape:
-      return OrientationLockType::kLandscape;
+      return ash::OrientationLockType::kLandscape;
     case blink::kWebScreenOrientationLockLandscapePrimary:
-      return OrientationLockType::kLandscapePrimary;
+      return ash::OrientationLockType::kLandscapePrimary;
     case blink::kWebScreenOrientationLockLandscapeSecondary:
-      return OrientationLockType::kLandscapeSecondary;
+      return ash::OrientationLockType::kLandscapeSecondary;
     case blink::kWebScreenOrientationLockNatural:
-      return OrientationLockType::kNatural;
+      return ash::OrientationLockType::kNatural;
   }
-  return OrientationLockType::kAny;
+  return ash::OrientationLockType::kAny;
 }
 
 }  // namespace
@@ -53,21 +53,19 @@
 void ScreenOrientationDelegateChromeos::Lock(
     content::WebContents* web_contents,
     blink::WebScreenOrientationLockType orientation_lock) {
-  Shell::Get()->screen_orientation_controller()->LockOrientationForWindow(
+  ash::Shell::Get()->screen_orientation_controller()->LockOrientationForWindow(
       web_contents->GetNativeView(),
       ToAshOrientationLockType(orientation_lock));
 }
 
 bool ScreenOrientationDelegateChromeos::ScreenOrientationProviderSupported() {
-  return Shell::Get()
-      ->screen_orientation_controller()
-      ->ScreenOrientationProviderSupported();
+  return TabletModeClient::Get() &&
+         TabletModeClient::Get()->tablet_mode_enabled();
 }
 
 void ScreenOrientationDelegateChromeos::Unlock(
     content::WebContents* web_contents) {
-  Shell::Get()->screen_orientation_controller()->UnlockOrientationForWindow(
-      web_contents->GetNativeView());
+  ash::Shell::Get()
+      ->screen_orientation_controller()
+      ->UnlockOrientationForWindow(web_contents->GetNativeView());
 }
-
-}  // namespace ash
diff --git a/ash/content/screen_orientation_delegate_chromeos.h b/chrome/browser/ui/ash/screen_orientation_delegate_chromeos.h
similarity index 69%
rename from ash/content/screen_orientation_delegate_chromeos.h
rename to chrome/browser/ui/ash/screen_orientation_delegate_chromeos.h
index 67f40fe..5bba1789e 100644
--- a/ash/content/screen_orientation_delegate_chromeos.h
+++ b/chrome/browser/ui/ash/screen_orientation_delegate_chromeos.h
@@ -2,16 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_CONTENT_SCREEN_ORIENTATION_DELEGATE_CHROMEOS_H_
-#define ASH_CONTENT_SCREEN_ORIENTATION_DELEGATE_CHROMEOS_H_
+#ifndef CHROME_BROWSER_UI_ASH_SCREEN_ORIENTATION_DELEGATE_CHROMEOS_H_
+#define CHROME_BROWSER_UI_ASH_SCREEN_ORIENTATION_DELEGATE_CHROMEOS_H_
 
 #include "content/public/browser/screen_orientation_delegate.h"
 
-#include "ash/content/ash_with_content_export.h"
-
-namespace ash {
-
-class ASH_WITH_CONTENT_EXPORT ScreenOrientationDelegateChromeos
+// Chrome OS implementation for screen orientation JS api. TODO(estade):
+// implement for Mash.
+class ScreenOrientationDelegateChromeos
     : public content::ScreenOrientationDelegate {
  public:
   ScreenOrientationDelegateChromeos();
@@ -28,6 +26,4 @@
   DISALLOW_COPY_AND_ASSIGN(ScreenOrientationDelegateChromeos);
 };
 
-}  // namespace ash
-
-#endif  // ASH_CONTENT_SCREEN_ORIENTATION_DELEGATE_CHROMEOS_H_
+#endif  // CHROME_BROWSER_UI_ASH_SCREEN_ORIENTATION_DELEGATE_CHROMEOS_H_
diff --git a/chrome/browser/ui/cocoa/tab_contents/favicon_util_mac.h b/chrome/browser/ui/cocoa/tab_contents/favicon_util_mac.h
deleted file mode 100644
index 275cb77..0000000
--- a/chrome/browser/ui/cocoa/tab_contents/favicon_util_mac.h
+++ /dev/null
@@ -1,27 +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_TAB_CONTENTS_FAVICON_UTIL_MAC_H_
-#define CHROME_BROWSER_UI_COCOA_TAB_CONTENTS_FAVICON_UTIL_MAC_H_
-
-#include "third_party/skia/include/core/SkColor.h"
-
-@class NSImage;
-
-namespace content {
-class WebContents;
-}
-
-namespace mac {
-
-// Returns an autoreleased favicon for a given WebContents. If |contents|
-// is NULL or there's no favicon for the NavigationEntry, this will return the
-// default image. The color parameter is only used for the default vector image
-// in Material design.
-NSImage* FaviconForWebContents(content::WebContents* contents,
-                               SkColor color = SK_ColorBLACK);
-
-}  // namespace mac
-
-#endif  // CHROME_BROWSER_UI_COCOA_TAB_CONTENTS_FAVICON_UTIL_MAC_H_
diff --git a/chrome/browser/ui/cocoa/tab_contents/favicon_util_mac.mm b/chrome/browser/ui/cocoa/tab_contents/favicon_util_mac.mm
deleted file mode 100644
index ace75e3..0000000
--- a/chrome/browser/ui/cocoa/tab_contents/favicon_util_mac.mm
+++ /dev/null
@@ -1,56 +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.
-
-#include "chrome/browser/ui/cocoa/tab_contents/favicon_util_mac.h"
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/mac/scoped_nsobject.h"
-#include "chrome/app/vector_icons/vector_icons.h"
-#include "chrome/browser/favicon/favicon_utils.h"
-#include "skia/ext/skia_utils_mac.h"
-#include "ui/base/material_design/material_design_controller.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/image/image_skia_util_mac.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/resources/grit/ui_resources.h"
-
-namespace {
-
-const CGFloat kVectorIconSize = 16;
-
-bool HasDefaultFavicon(content::WebContents* contents) {
-  gfx::Image favicon = favicon::TabFaviconFromWebContents(contents);
-  if (favicon.IsEmpty())
-    return false;
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  const gfx::ImageSkia* default_favicon =
-      rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON);
-
-  return favicon.ToImageSkia()->BackedBySameObjectAs(*default_favicon);
-}
-
-}  // namespace
-
-namespace mac {
-
-NSImage* FaviconForWebContents(content::WebContents* contents, SkColor color) {
-  // If |contents| is using the default favicon, it needs to be drawn in
-  // |color|, which means this function can't necessarily reuse the existing
-  // favicon.
-  if (contents && !HasDefaultFavicon(contents)) {
-    NSImage* image = favicon::TabFaviconFromWebContents(contents).AsNSImage();
-
-    // The |image| could be nil if the bitmap is null. In that case, fallback
-    // to the default image.
-    if (image)
-      return image;
-  }
-
-  return NSImageFromImageSkia(
-      gfx::CreateVectorIcon(kDefaultFaviconIcon, kVectorIconSize, color));
-}
-
-}  // namespace mac
diff --git a/chrome/browser/ui/tab_ui_helper.cc b/chrome/browser/ui/tab_ui_helper.cc
index 365ca24..4df739c 100644
--- a/chrome/browser/ui/tab_ui_helper.cc
+++ b/chrome/browser/ui/tab_ui_helper.cc
@@ -18,10 +18,6 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/resources/grit/ui_resources.h"
 
-#if defined(OS_MACOSX)
-#include "chrome/browser/ui/views_mode_controller.h"
-#endif
-
 namespace {
 
 base::string16 FormatUrlToSubdomain(const GURL& url) {
@@ -60,15 +56,6 @@
 gfx::Image TabUIHelper::GetFavicon() const {
   if (ShouldUseFaviconFromHistory() && tab_ui_data_)
     return tab_ui_data_->favicon;
-
-#if defined(OS_MACOSX)
-  if (views_mode_controller::IsViewsBrowserCocoa())
-    return gfx::Image();
-// For views browser windows on Mac, it will fall through to be handled
-// in the following function. If default favicon needs to be drawn more
-// visible on dark theme, consider porting code from
-// mac::FaviconForWebContents().
-#endif
   return favicon::TabFaviconFromWebContents(web_contents());
 }
 
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
index fedefea..0abc5c66 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
+++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
@@ -33,6 +33,7 @@
       bubble_view_(nullptr) {
   DCHECK(delegate_);
   SetUpForInOutAnimation();
+  image()->EnableCanvasFlippingForRTLUI(true);
 }
 
 ContentSettingImageView::~ContentSettingImageView() {
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
index 48ec85b..3ebc0bd 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -508,7 +508,6 @@
 void IconLabelBubbleView::SetUpForInOutAnimation() {
   SetInkDropMode(InkDropMode::ON);
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
-  image_->EnableCanvasFlippingForRTLUI(true);
   label_->SetElideBehavior(gfx::NO_ELIDE);
   label_->SetVisible(false);
   slide_animation_.SetSlideDuration(GetSlideDurationTime());
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
index c6e9984..dfe8240 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
@@ -218,7 +218,7 @@
   void ShowAnimation();
 
   // Disables highlights and calls Hide on the slide animation, should not be
-  // called directly, use AnimateIn() instead, which handles label visibility.
+  // called directly, use AnimateOut() instead, which handles label visibility.
   void HideAnimation();
 
   // The contents of the bubble.
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.cc b/chrome/browser/ui/views/tabs/new_tab_button.cc
index 3eaaa6b..05df702 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.cc
+++ b/chrome/browser/ui/views/tabs/new_tab_button.cc
@@ -34,11 +34,8 @@
 #include "chrome/browser/ui/views/feature_promos/new_tab_promo_bubble_view.h"
 #endif
 
-namespace {
-
-constexpr gfx::Size kButtonSize(28, 28);
-
-}  // namespace
+// static
+const gfx::Size NewTabButton::kButtonSize{28, 28};
 
 NewTabButton::NewTabButton(TabStrip* tab_strip, views::ButtonListener* listener)
     : views::ImageButton(listener), tab_strip_(tab_strip) {
@@ -57,14 +54,6 @@
 
   SetFocusPainter(nullptr);
   SetInstallFocusRingOnFocus(true);
-
-  // The button is placed vertically exactly in the center of the tabstrip.
-  const int extra_vertical_space = GetLayoutConstant(TAB_HEIGHT) -
-                                   GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP) -
-                                   kButtonSize.height();
-  constexpr int kHorizontalInset = 8;
-  SetBorder(views::CreateEmptyBorder(gfx::Insets(
-      extra_vertical_space / 2, kHorizontalInset, 0, kHorizontalInset)));
 }
 
 NewTabButton::~NewTabButton() {
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.h b/chrome/browser/ui/views/tabs/new_tab_button.h
index 8358f702..9c729b9b 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.h
+++ b/chrome/browser/ui/views/tabs/new_tab_button.h
@@ -25,6 +25,8 @@
                      public views::MaskedTargeterDelegate,
                      public views::WidgetObserver {
  public:
+  static const gfx::Size kButtonSize;
+
   NewTabButton(TabStrip* tab_strip, views::ButtonListener* listener);
   ~NewTabButton() override;
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index f815caf..2610875 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -266,6 +266,7 @@
     : controller_(std::move(controller)) {
   Init();
   SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
+  md_observer_.Add(ui::MaterialDesignController::GetInstance());
 }
 
 TabStrip::~TabStrip() {
@@ -1464,6 +1465,7 @@
       std::make_unique<views::ViewTargeter>(new_tab_button_));
   AddChildView(new_tab_button_);
 
+  UpdateNewTabButtonBorder();
   new_tab_button_bounds_.set_size(new_tab_button_->GetPreferredSize());
 
   if (g_drop_indicator_width == 0) {
@@ -2433,6 +2435,16 @@
     RemoveMessageLoopObserver();
 }
 
+void TabStrip::UpdateNewTabButtonBorder() {
+  // The button is placed vertically exactly in the center of the tabstrip.
+  const int extra_vertical_space = GetLayoutConstant(TAB_HEIGHT) -
+                                   GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP) -
+                                   NewTabButton::kButtonSize.height();
+  constexpr int kHorizontalInset = 8;
+  new_tab_button_->SetBorder(views::CreateEmptyBorder(gfx::Insets(
+      extra_vertical_space / 2, kHorizontalInset, 0, kHorizontalInset)));
+}
+
 void TabStrip::SingleTabModeChanged() {
   const int active_tab_index = controller_->GetActiveIndex();
   if (IsValidModelIndex(active_tab_index))
@@ -2597,3 +2609,10 @@
   }
   return this;
 }
+
+void TabStrip::OnMdModeChanged() {
+  UpdateNewTabButtonBorder();
+  new_tab_button_bounds_.set_size(new_tab_button_->GetPreferredSize());
+  new_tab_button_->SetBoundsRect(new_tab_button_bounds_);
+  StopAnimating(true);
+}
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 5bdf393..b237f4d 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -13,12 +13,14 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
+#include "base/scoped_observer.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/ui/tabs/tab_utils.h"
 #include "chrome/browser/ui/views/frame/browser_root_view.h"
 #include "chrome/browser/ui/views/tabs/tab.h"
 #include "chrome/browser/ui/views/tabs/tab_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "ui/base/material_design/material_design_controller_observer.h"
 #include "ui/gfx/animation/animation_container.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/point.h"
@@ -63,7 +65,8 @@
                  public views::MouseWatcherListener,
                  public views::ViewTargeterDelegate,
                  public TabController,
-                 public BrowserRootView::DropTarget {
+                 public BrowserRootView::DropTarget,
+                 public ui::MaterialDesignControllerObserver {
  public:
   explicit TabStrip(std::unique_ptr<TabStripController> controller);
   ~TabStrip() override;
@@ -559,10 +562,14 @@
   // layout is reset.
   void SetResetToShrinkOnExit(bool value);
 
-  // views::ButtonListener implementation:
+  // Updates the border padding for |new_tab_button_|.  This should be called
+  // whenever any input of the computation of the border's sizing changes.
+  void UpdateNewTabButtonBorder();
+
+  // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
-  // View overrides.
+  // views::View:
   const views::View* GetViewByID(int id) const override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   bool OnMouseDragged(const ui::MouseEvent& event) override;
@@ -571,12 +578,15 @@
   void OnMouseMoved(const ui::MouseEvent& event) override;
   void OnMouseEntered(const ui::MouseEvent& event) override;
 
-  // ui::EventHandler overrides.
+  // ui::EventHandler:
   void OnGestureEvent(ui::GestureEvent* event) override;
 
   // views::ViewTargeterDelegate:
   views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override;
 
+  // ui::MaterialDesignControllerObserver:
+  void OnMdModeChanged() override;
+
   // -- Member Variables ------------------------------------------------------
 
   base::ObserverList<TabStripObserver>::Unchecked observers_;
@@ -688,6 +698,10 @@
 
   SkColor separator_color_ = gfx::kPlaceholderColor;
 
+  ScopedObserver<ui::MaterialDesignController,
+                 ui::MaterialDesignControllerObserver>
+      md_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(TabStrip);
 };
 
diff --git a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc
index 1d61a9a1..df77482 100644
--- a/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/app_list/arc/arc_fast_app_reinstall_starter.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/component_extension_resources.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/arc/arc_prefs.h"
@@ -125,8 +124,6 @@
 
   Profile* profile = ProfileManager::GetActiveUserProfile();
   pref_service_ = profile->GetPrefs();
-
-  profile->GetPrefs()->SetBoolean(prefs::kOobeRecommendAppScreenFinished, true);
 }
 
 void RecommendAppsScreenHandler::Hide() {}
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 1dcaf52..b002a3f3 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -2430,9 +2430,10 @@
     {"siteSettingsSourcePolicyBlock",
      IDS_PAGE_INFO_PERMISSION_BLOCKED_BY_POLICY},
     {"siteSettingsSourcePolicyAsk", IDS_PAGE_INFO_PERMISSION_ASK_BY_POLICY},
-    {"siteSettingsAdsBlockSingular",
-     IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_SINGULAR},
-    {"siteSettingsSourceAdsBlacklist", IDS_PAGE_INFO_PERMISSION_ADS_SUBTITLE},
+    {"siteSettingsAdsBlockNotBlacklistedSingular",
+     IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_NOT_BLACKLISTED_SINGULAR},
+    {"siteSettingsAdsBlockBlacklistedSingular",
+     IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_BLACKLISTED_SINGULAR},
     {"siteSettingsSourceDrmDisabled",
      IDS_SETTINGS_SITE_SETTINGS_SOURCE_DRM_DISABLED},
     {"siteSettingsSourceEmbargo",
diff --git a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
index 5495017..88ecd47 100644
--- a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
@@ -18,8 +18,11 @@
 #include "chrome/browser/browsing_data/counters/browsing_data_counter_factory.h"
 #include "chrome/browser/browsing_data/counters/browsing_data_counter_utils.h"
 #include "chrome/browser/history/web_history_service_factory.h"
+#include "chrome/browser/signin/account_reconcilor_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/sync/sync_ui_util.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
@@ -248,15 +251,27 @@
         checked_other_types);
   }
 
+  // If Sync is running, prevent it from being paused during the operation.
+  // However, if Sync is in error, clearing cookies should pause it.
+  std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion>
+      scoped_data_deletion;
+  sync_ui_util::MessageType sync_status = sync_ui_util::GetStatus(
+      profile_, sync_service_, *SigninManagerFactory::GetForProfile(profile_));
+  if (sync_status == sync_ui_util::SYNCED) {
+    scoped_data_deletion = AccountReconcilorFactory::GetForProfile(profile_)
+                               ->GetScopedSyncDataDeletion();
+  }
+
   int period_selected;
   CHECK(args->GetInteger(2, &period_selected));
 
   content::BrowsingDataRemover* remover =
       content::BrowserContext::GetBrowsingDataRemover(profile_);
 
-  base::OnceClosure callback = base::BindOnce(
-      &ClearBrowsingDataHandler::OnClearingTaskFinished,
-      weak_ptr_factory_.GetWeakPtr(), webui_callback_id, std::move(data_types));
+  base::OnceClosure callback =
+      base::BindOnce(&ClearBrowsingDataHandler::OnClearingTaskFinished,
+                     weak_ptr_factory_.GetWeakPtr(), webui_callback_id,
+                     std::move(data_types), std::move(scoped_data_deletion));
   browsing_data::TimePeriod time_period =
       static_cast<browsing_data::TimePeriod>(period_selected);
 
@@ -275,7 +290,8 @@
 
 void ClearBrowsingDataHandler::OnClearingTaskFinished(
     const std::string& webui_callback_id,
-    const base::flat_set<BrowsingDataType>& data_types) {
+    const base::flat_set<BrowsingDataType>& data_types,
+    std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion> deletion) {
   PrefService* prefs = profile_->GetPrefs();
   int notice_shown_times = prefs->GetInteger(
       browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes);
diff --git a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
index 40fcfa0..96e23993 100644
--- a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
+++ b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
@@ -17,6 +17,7 @@
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/browsing_data/core/browsing_data_utils.h"
 #include "components/browsing_data/core/counters/browsing_data_counter.h"
+#include "components/signin/core/browser/account_reconcilor.h"
 
 namespace base {
 class ListValue;
@@ -47,12 +48,14 @@
   // Clears browsing data, called by Javascript.
   void HandleClearBrowsingData(const base::ListValue* value);
 
-
   // Called when a clearing task finished. |webui_callback_id| is provided
   // by the WebUI action that initiated it.
+  // The ScopedSyncedDataDeletion is passed here to ensure that the Sync token
+  // is not invalidated before this function is run.
   void OnClearingTaskFinished(
       const std::string& webui_callback_id,
-      const base::flat_set<browsing_data::BrowsingDataType>& data_types);
+      const base::flat_set<browsing_data::BrowsingDataType>& data_types,
+      std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion> deletion);
 
   // Initializes the dialog UI. Called by JavaScript when the DOM is ready.
   void HandleInitialize(const base::ListValue* args);
diff --git a/chrome/common/extensions/api/browser_action.json b/chrome/common/extensions/api/browser_action.json
index 77dd3e6..8b2c59f 100644
--- a/chrome/common/extensions/api/browser_action.json
+++ b/chrome/common/extensions/api/browser_action.json
@@ -5,7 +5,7 @@
 [
   {
     "namespace": "browserAction",
-    "description": "Use browser actions to put icons in the main Google Chrome toolbar, to the right of the address bar. In addition to its <a href='browserAction#icon'>icon</a>, a browser action can also have a <a href='browserAction#tooltip'>tooltip</a>, a <a href='browserAction#badge'>badge</a>, and a <a href='browserAction#popups'>popup</a>.",
+    "description": "Use browser actions to put icons in the main Google Chrome toolbar, to the right of the address bar. In addition to its <a href='browserAction#icon'>icon</a>, a browser action can have a <a href='browserAction#tooltip'>tooltip</a>, a <a href='browserAction#badge'>badge</a>, and a <a href='browserAction#popups'>popup</a>.",
     "types": [
       {
         "id": "ColorArray",
@@ -23,14 +23,14 @@
         "type": "object",
         "isInstanceOf": "ImageData",
         "additionalProperties": { "type": "any" },
-        "description": "Pixel data for an image. Must be an ImageData object (for example, from a <code>canvas</code> element)."
+        "description": "Pixel data for an image. Must be an ImageData object; for example, from a <code>canvas</code> element."
       }
     ],
     "functions": [
       {
         "name": "setTitle",
         "type": "function",
-        "description": "Sets the title of the browser action. This shows up in the tooltip.",
+        "description": "Sets the title of the browser action. This title appears in the tooltip.",
         "parameters": [
           {
             "name": "details",
@@ -62,7 +62,7 @@
               "tabId": {
                 "type": "integer",
                 "optional": true,
-                "description": "Specify the tab to get the title from. If no tab is specified, the non-tab-specific title is returned."
+                "description": "The tab to get the title from. If no tab is specified, the default title is returned."
               }
             }
           },
@@ -81,7 +81,7 @@
       {
         "name": "setIcon",
         "type": "function",
-        "description": "Sets the icon for the browser action. The icon can be specified either as the path to an image file or as the pixel data from a canvas element, or as dictionary of either one of those. Either the <b>path</b> or the <b>imageData</b> property must be specified.",
+        "description": "Sets the icon for the browser action. The icon can be specified as the path to an image file, as the pixel data from a canvas element, or as a dictionary of one of those. Either the <code>path</code> or the <code>imageData</code> property must be specified.",
         "parameters": [
           {
             "name": "details",
@@ -96,7 +96,7 @@
                   }
                 ],
                 "optional": true,
-                "description": "Either an ImageData object or a dictionary {size -> ImageData} representing icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * n will be selected, where n is the size of the icon in the UI. At least one image must be specified. Note that 'details.imageData = foo' is equivalent to 'details.imageData = {'16': foo}'"
+                "description": "Either an ImageData object or a dictionary {size -> ImageData} representing an icon to be set. If the icon is specified as a dictionary, the image used is chosen depending on the screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then an image with size <code>scale</code> * n is selected, where <i>n</i> is the size of the icon in the UI. At least one image must be specified. Note that 'details.imageData = foo' is equivalent to 'details.imageData = {'16': foo}'"
               },
               "path": {
                 "choices": [
@@ -107,7 +107,7 @@
                   }
                 ],
                 "optional": true,
-                "description": "Either a relative image path or a dictionary {size -> relative image path} pointing to icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * n will be selected, where n is the size of the icon in the UI. At least one image must be specified. Note that 'details.path = foo' is equivalent to 'details.path = {'16': foo}'"
+                "description": "Either a relative image path or a dictionary {size -> relative image path} pointing to an icon to be set. If the icon is specified as a dictionary, the image used is chosen depending on the screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then an image with size <code>scale</code> * n is selected, where <i>n</i> is the size of the icon in the UI. At least one image must be specified. Note that 'details.path = foo' is equivalent to 'details.path = {'16': foo}'"
               },
               "tabId": {
                 "type": "integer",
@@ -127,7 +127,7 @@
       {
         "name": "setPopup",
         "type": "function",
-        "description": "Sets the html document to be opened as a popup when the user clicks on the browser action's icon.",
+        "description": "Sets the HTML document to be opened as a popup when the user clicks the browser action icon.",
         "parameters": [
           {
             "name": "details",
@@ -141,7 +141,7 @@
               },
               "popup": {
                 "type": "string",
-                "description": "The html file to show in a popup.  If set to the empty string (''), no popup is shown."
+                "description": "The HTML file to show in a popup. If set to the empty string (''), no popup is shown."
               }
             }
           },
@@ -151,7 +151,7 @@
       {
         "name": "getPopup",
         "type": "function",
-        "description": "Gets the html document set as the popup for this browser action.",
+        "description": "Gets the HTML document that is set as the popup for this browser action.",
         "parameters": [
           {
             "name": "details",
@@ -160,7 +160,7 @@
               "tabId": {
                 "type": "integer",
                 "optional": true,
-                "description": "Specify the tab to get the popup from. If no tab is specified, the non-tab-specific popup is returned."
+                "description": "The tab to get the popup from. If no tab is specified, the non-tab-specific popup is returned."
               }
             }
           },
@@ -187,7 +187,7 @@
             "properties": {
               "text": {
                 "type": "string",
-                "description": "Any number of characters can be passed, but only about four can fit in the space."
+                "description": "Any number of characters can be passed, but only about four can fit into the space."
               },
               "tabId": {
                 "type": "integer",
@@ -211,7 +211,7 @@
               "tabId": {
                 "type": "integer",
                 "optional": true,
-                "description": "Specify the tab to get the badge text from. If no tab is specified, the non-tab-specific badge text is returned."
+                "description": "The tab to get the badge text from. If no tab is specified, the non-tab-specific badge text is returned."
               }
             }
           },
@@ -237,7 +237,7 @@
             "type": "object",
             "properties": {
               "color": {
-                "description": "An array of four integers in the range [0,255] that make up the RGBA color of the badge. Can also be a string with a CSS value, red being <code>#FF0000</code> or <code>#F00</code>. Renders colors at full opacity.",
+                "description": "An array of four integers in the range 0-255 that make up the RGBA color of the badge. Can also be a string with a CSS hex color value; for example, <code>#FF0000</code> or <code>#F00</code> (red). Renders colors at full opacity.",
                 "choices": [
                   {"type": "string"},
                   {"$ref": "ColorArray"}
@@ -265,7 +265,7 @@
               "tabId": {
                 "type": "integer",
                 "optional": true,
-                "description": "Specify the tab to get the badge background color from. If no tab is specified, the non-tab-specific badge background color is returned."
+                "description": "The tab to get the badge background color from. If no tab is specified, the non-tab-specific badge background color is returned."
               }
             }
           },
@@ -284,14 +284,14 @@
       {
         "name": "enable",
         "type": "function",
-        "description": "Enables the browser action for a tab. By default, browser actions are enabled.",
+        "description": "Enables the browser action for a tab. Defaults to enabled.",
         "parameters": [
           {
             "type": "integer",
             "optional": true,
             "name": "tabId",
             "minimum": 0,
-            "description": "The id of the tab for which you want to modify the browser action."
+            "description": "The ID of the tab for which to modify the browser action."
           },
           {"type": "function", "name": "callback", "parameters": [], "optional": true}
         ]
@@ -306,7 +306,7 @@
             "optional": true,
             "name": "tabId",
             "minimum": 0,
-            "description": "The id of the tab for which you want to modify the browser action."
+            "description": "The ID of the tab for which to modify the browser action."
           },
           {"type": "function", "name": "callback", "parameters": [], "optional": true}
         ]
@@ -337,7 +337,7 @@
       {
         "name": "onClicked",
         "type": "function",
-        "description": "Fired when a browser action icon is clicked.  This event will not fire if the browser action has a popup.",
+        "description": "Fired when a browser action icon is clicked. Does not fire if the browser action has a popup.",
         "parameters": [
           {
             "name": "tab",
diff --git a/chrome/common/extensions/api/windows.json b/chrome/common/extensions/api/windows.json
index 6d2adadc..3d3df03 100644
--- a/chrome/common/extensions/api/windows.json
+++ b/chrome/common/extensions/api/windows.json
@@ -13,13 +13,13 @@
       {
         "id": "WindowType",
         "type": "string",
-        "description": "The type of browser window this is. Under some circumstances a Window may not be assigned type property, for example when querying closed windows from the $(ref:sessions) API.",
+        "description": "The type of browser window this is. In some circumstances a window may not be assigned a <code>type</code> property; for example, when querying closed windows from the $(ref:sessions) API.",
         "enum": [{
           "name": "normal",
           "description": "A normal browser window."
         }, {
           "name": "popup",
-          "description": "A browser popup window."
+          "description": "A browser popup."
         }, {
           "name": "panel",
           "description": "<i>Deprecated in this API.</i> A Chrome App panel-style window. Extensions can only see their own panel windows."
@@ -28,13 +28,13 @@
           "description": "<i>Deprecated in this API.</i> A Chrome App window. Extensions can only see their app own windows."
         }, {
           "name": "devtools",
-          "description": "A devtools window."
+          "description": "A Developer Tools window."
         }]
       },
       {
         "id": "WindowState",
         "type": "string",
-        "description": "The state of this browser window. Under some circumstances a Window may not be assigned state property, for example when querying closed windows from the $(ref:sessions) API.",
+        "description": "The state of this browser window. In some circumstances a window may not be assigned a <code>state</code> property; for example, when querying closed windows from the $(ref:sessions) API.",
         // WARNING: These values are written to logs. New enum values can be
         // added, but existing enums must never be renumbered or deleted and
         // reused. If something needs to be removed, make sure to remove the
@@ -42,7 +42,7 @@
         // valid, and make sure to mark it as obsolete in histograms.xml.
         "enum": [{
           "name": "normal",
-          "description": "Normal window state (i.e. not minimized, maximized, or fullscreen)."
+          "description": "Normal window state (not minimized, maximized, or fullscreen)."
         }, {
           "name": "minimized",
           "description": "Minimized window state."
@@ -58,19 +58,19 @@
         }, {
           "name": "locked-fullscreen",
           "nodoc": true,
-          "description": "Locked fullscreen window state. This fullscreen state cannot be exited by user action. It is available only to whitelisted extensions on Chrome OS."
+          "description": "Locked fullscreen window state. This fullscreen state cannot be exited by user action and is available only to allowlisted extensions on Chrome OS."
         }]
       },
       {
         "id": "Window",
         "type": "object",
         "properties": {
-          "id": {"type": "integer", "optional": true, "minimum": 0, "description": "The ID of the window. Window IDs are unique within a browser session. Under some circumstances a Window may not be assigned an ID, for example when querying windows using the $(ref:sessions) API, in which case a session ID may be present."},
+          "id": {"type": "integer", "optional": true, "minimum": 0, "description": "The ID of the window. Window IDs are unique within a browser session. In some circumstances a window may not be assigned an <code>ID</code> property; for example, when querying windows using the $(ref:sessions) API, in which case a session ID may be present."},
           "focused": {"type": "boolean", "description": "Whether the window is currently the focused window."},
-          "top": {"type": "integer", "optional": true, "description": "The offset of the window from the top edge of the screen in pixels. Under some circumstances a Window may not be assigned top property, for example when querying closed windows from the $(ref:sessions) API."},
-          "left": {"type": "integer", "optional": true, "description": "The offset of the window from the left edge of the screen in pixels. Under some circumstances a Window may not be assigned left property, for example when querying closed windows from the $(ref:sessions) API."},
-          "width": {"type": "integer", "optional": true, "description": "The width of the window, including the frame, in pixels. Under some circumstances a Window may not be assigned width property, for example when querying closed windows from the $(ref:sessions) API."},
-          "height": {"type": "integer", "optional": true, "description": "The height of the window, including the frame, in pixels. Under some circumstances a Window may not be assigned height property, for example when querying closed windows from the $(ref:sessions) API."},
+          "top": {"type": "integer", "optional": true, "description": "The offset of the window from the top edge of the screen in pixels. In some circumstances a window may not be assigned a <code>top</code> property; for example, when querying closed windows from the $(ref:sessions) API."},
+          "left": {"type": "integer", "optional": true, "description": "The offset of the window from the left edge of the screen in pixels. In some circumstances a window may not be assigned a <code>left</code> property; for example, when querying closed windows from the $(ref:sessions) API."},
+          "width": {"type": "integer", "optional": true, "description": "The width of the window, including the frame, in pixels. In some circumstances a window may not be assigned a <code>width</code> property; for example, when querying closed windows from the $(ref:sessions) API."},
+          "height": {"type": "integer", "optional": true, "description": "The height of the window, including the frame, in pixels. In some circumstances a window may not be assigned a <code>height</code> property; for example, when querying closed windows from the $(ref:sessions) API."},
           "tabs": {"type": "array", "items": { "$ref": "tabs.Tab" }, "optional": true, "description": "Array of $(ref:tabs.Tab) objects representing the current tabs in the window."},
           "incognito": {"type": "boolean", "description": "Whether the window is incognito."},
           "type": {
@@ -84,13 +84,13 @@
             "description": "The state of this browser window."
           },
           "alwaysOnTop": {"type": "boolean", "description": "Whether the window is set to be always on top."},
-          "sessionId": {"type": "string", "optional": true, "description": "The session ID used to uniquely identify a Window obtained from the $(ref:sessions) API."}
+          "sessionId": {"type": "string", "optional": true, "description": "The session ID used to uniquely identify a window, obtained from the $(ref:sessions) API."}
         }
       },
       {
         "id": "CreateType",
         "type": "string",
-        "description": "Specifies what type of browser window to create. 'panel' is deprecated and only available to existing whitelisted extensions on Chrome OS.",
+        "description": "Specifies what type of browser window to create. 'panel' is deprecated and is available only to existing whitelisted extensions on Chrome OS.",
         "enum": ["normal", "popup", "panel"]
       }
     ],
@@ -117,8 +117,8 @@
             "optional": true,
             "description": "",
             "properties": {
-              "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
-              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['normal', 'popup']</code>."}
+              "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object has a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code>, and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned is filtered based on its type. If unset, the default filter is set to <code>['normal', 'popup']</code>."}
             }
           },
           {
@@ -143,8 +143,8 @@
             "optional": true,
             "description": "",
             "properties": {
-              "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
-              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['normal', 'popup']</code>."}
+              "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object has a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code>, and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned is filtered based on its type. If unset, the default filter is set to <code>['normal', 'popup']</code>."}
             }
           },
           {
@@ -169,8 +169,8 @@
             "optional": true,
             "description": "",
             "properties": {
-              "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
-              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['normal', 'popup']</code>."}
+              "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object has a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code>, and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned is filtered based on its type. If unset, the default filter is set to <code>['normal', 'popup']</code>."}
             }
           },
           {
@@ -195,8 +195,8 @@
             "optional": true,
             "description": "",
             "properties": {
-              "populate": {"type": "boolean", "optional": true, "description": "If true, each $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects for that window. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
-              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['normal', 'popup']</code>."}
+              "populate": {"type": "boolean", "optional": true, "description": "If true, each $(ref:windows.Window) object has a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects for that window. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code>, and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned is filtered based on its type. If unset, the default filter is set to <code>['normal', 'popup']</code>."}
             }
           },
           {
@@ -213,26 +213,26 @@
       {
         "name": "create",
         "type": "function",
-        "description": "Creates (opens) a new browser with any optional sizing, position or default URL provided.",
+        "description": "Creates (opens) a new browser window with any optional sizing, position, or default URL provided.",
         "parameters": [
           {
             "type": "object",
             "name": "createData",
             "properties": {
               "url": {
-                "description": "A URL or array of URLs to open as tabs in the window. Fully-qualified URLs must include a scheme (i.e. 'http://www.google.com', not 'www.google.com'). Relative URLs will be relative to the current page within the extension. Defaults to the New Tab Page.",
+                "description": "A URL or array of URLs to open as tabs in the window. Fully-qualified URLs must include a scheme, e.g., 'http://www.google.com', not 'www.google.com'. Non-fully-qualified URLs are considered relative within the extension. Defaults to the New Tab Page.",
                 "optional": true,
                 "choices": [
                   {"type": "string"},
                   {"type": "array", "items": {"type": "string"}}
                 ]
               },
-              "tabId": {"type": "integer", "minimum": 0, "optional": true, "description": "The id of the tab for which you want to adopt to the new window."},
+              "tabId": {"type": "integer", "minimum": 0, "optional": true, "description": "The ID of the tab to add to the new window."},
               "left": {"type": "integer", "optional": true, "description": "The number of pixels to position the new window from the left edge of the screen. If not specified, the new window is offset naturally from the last focused window. This value is ignored for panels."},
               "top": {"type": "integer", "optional": true, "description": "The number of pixels to position the new window from the top edge of the screen. If not specified, the new window is offset naturally from the last focused window. This value is ignored for panels."},
-              "width": {"type": "integer", "minimum": 0, "optional": true, "description": "The width in pixels of the new window, including the frame. If not specified defaults to a natural width."},
-              "height": {"type": "integer", "minimum": 0, "optional": true, "description": "The height in pixels of the new window, including the frame. If not specified defaults to a natural height."},
-              "focused": {"type": "boolean", "optional": true, "description": "If true, opens an active window. If false, opens an inactive window."},
+              "width": {"type": "integer", "minimum": 0, "optional": true, "description": "The width in pixels of the new window, including the frame. If not specified, defaults to a natural width."},
+              "height": {"type": "integer", "minimum": 0, "optional": true, "description": "The height in pixels of the new window, including the frame. If not specified, defaults to a natural height."},
+              "focused": {"type": "boolean", "optional": true, "description": "If <code>true</code>, opens an active window. If <code>false</code>, opens an inactive window."},
               "incognito": {"type": "boolean", "optional": true, "description": "Whether the new window should be an incognito window."},
               "type": {
                 "$ref": "CreateType",
@@ -242,12 +242,12 @@
               "state": {
                 "$ref": "WindowState",
                 "optional": true,
-                "description": "The initial state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'."
+                "description": "The initial state of the window. The <code>minimized</code>, <code>maximized</code>, and <code>fullscreen</code> states cannot be combined with <code>left</code>, <code>top</code>, <code>width</code>, or <code>height</code>."
               },
               "setSelfAsOpener": {
                 "type": "boolean",
                 "optional": true,
-                "description": "If 'setSelfAsOpener' is true, then the newly created window will have its 'window.opener' set to the caller and will be in the same <a href=\"https://www.w3.org/TR/html51/browsers.html#unit-of-related-browsing-contexts\">unit of related browsing contexts</a> as the caller."
+                "description": "If <code>true</code>, the newly-created window's 'window.opener' is set to the caller and is in the same <a href=\"https://www.w3.org/TR/html51/browsers.html#unit-of-related-browsing-contexts\">unit of related browsing contexts</a> as the caller."
               }
             },
             "optional": true
@@ -268,7 +268,7 @@
       {
         "name": "update",
         "type": "function",
-        "description": "Updates the properties of a window. Specify only the properties that you want to change; unspecified properties will be left unchanged.",
+        "description": "Updates the properties of a window. Specify only the properties that to be changed; unspecified properties are unchanged.",
         "parameters": [
           {"type": "integer", "name": "windowId", "minimum": -2},
           {
@@ -279,12 +279,12 @@
               "top": {"type": "integer", "optional": true, "description": "The offset from the top edge of the screen to move the window to in pixels. This value is ignored for panels."},
               "width": {"type": "integer", "minimum": 0, "optional": true, "description": "The width to resize the window to in pixels. This value is ignored for panels."},
               "height": {"type": "integer", "minimum": 0, "optional": true, "description": "The height to resize the window to in pixels. This value is ignored for panels."},
-              "focused": {"type": "boolean", "optional": true, "description": "If true, brings the window to the front and cannot be combined with the state 'minimized'. If false, brings the next window in the z-order to the front and cannot be combined with the state 'fullscreen' or 'maximized'."},
-              "drawAttention": {"type": "boolean", "optional": true, "description": "If true, causes the window to be displayed in a manner that draws the user's attention to the window, without changing the focused window. The effect lasts until the user changes focus to the window. This option has no effect if the window already has focus. Set to false to cancel a previous draw attention request."},
+              "focused": {"type": "boolean", "optional": true, "description": "If <code>true</code>, brings the window to the front; cannot be combined with the state 'minimized'. If <code>false</code>, brings the next window in the z-order to the front; cannot be combined with the state 'fullscreen' or 'maximized'."},
+              "drawAttention": {"type": "boolean", "optional": true, "description": "If <code>true</code>, causes the window to be displayed in a manner that draws the user's attention to the window, without changing the focused window. The effect lasts until the user changes focus to the window. This option has no effect if the window already has focus. Set to <code>false</code> to cancel a previous <code>drawAttention</code> request."},
               "state": {
                 "$ref": "WindowState",
                 "optional": true,
-                "description": "The new state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'."
+                "description": "The new state of the window. The 'minimized', 'maximized', and 'fullscreen' states cannot be combined with 'left', 'top', 'width', or 'height'."
               }
             }
           },
@@ -303,7 +303,7 @@
       {
         "name": "remove",
         "type": "function",
-        "description": "Removes (closes) a window, and all the tabs inside it.",
+        "description": "Removes (closes) a window and all the tabs inside it.",
         "parameters": [
           {"type": "integer", "name": "windowId", "minimum": 0},
           {"type": "function", "name": "callback", "optional": true, "parameters": []}
@@ -320,14 +320,14 @@
             "name": "windowTypes",
             "type": "array",
             "items": { "$ref": "WindowType" },
-            "description": "Conditions that the window's type being created must satisfy. By default it will satisfy <code>['normal', 'popup']</code>."
+            "description": "Conditions that the window's type being created must satisfy. By default it satisfies <code>['normal', 'popup']</code>."
           }
         ],
         "parameters": [
           {
             "$ref": "Window",
             "name": "window",
-            "description": "Details of the window that was created."
+            "description": "Details of the created window."
           }
         ]
       },
@@ -340,7 +340,7 @@
             "name": "windowTypes",
             "type": "array",
             "items": { "$ref": "WindowType" },
-            "description": "Conditions that the window's type being removed must satisfy. By default it will satisfy <code>['normal', 'popup']</code>."
+            "description": "Conditions that the window's type being removed must satisfy. By default it satisfies <code>['normal', 'popup']</code>."
           }
         ],
         "parameters": [
@@ -350,17 +350,17 @@
       {
         "name": "onFocusChanged",
         "type": "function",
-        "description": "Fired when the currently focused window changes. Will be chrome.windows.WINDOW_ID_NONE if all chrome windows have lost focus. Note: On some Linux window managers, WINDOW_ID_NONE will always be sent immediately preceding a switch from one chrome window to another.",
+        "description": "Fired when the currently focused window changes. Returns <code>chrome.windows.WINDOW_ID_NONE</code> if all Chrome windows have lost focus. <b>Note:</b> On some Linux window managers, <code>WINDOW_ID_NONE</code> is always sent immediately preceding a switch from one Chrome window to another.",
         "filters": [
           {
             "name": "windowTypes",
             "type": "array",
             "items": { "$ref": "WindowType" },
-            "description": "Conditions that the window's type being removed must satisfy. By default it will satisfy <code>['normal', 'popup']</code>."
+            "description": "Conditions that the window's type being removed must satisfy. By default it satisfies <code>['normal', 'popup']</code>."
           }
         ],
         "parameters": [
-          {"type": "integer", "name": "windowId", "minimum": -1, "description": "ID of the newly focused window."}
+          {"type": "integer", "name": "windowId", "minimum": -1, "description": "ID of the newly-focused window."}
         ]
       }
     ]
diff --git a/chrome/common/extensions/docs/templates/intros/webRequest.html b/chrome/common/extensions/docs/templates/intros/webRequest.html
index 516a082..9c0617d 100644
--- a/chrome/common/extensions/docs/templates/intros/webRequest.html
+++ b/chrome/common/extensions/docs/templates/intros/webRequest.html
@@ -130,14 +130,14 @@
 <code>wss://</code> (<span class="availability">since Chrome 58</span>), or
 <code>chrome-extension://</code>.
 In addition, even certain requests with URLs using one of the above schemes
-are hidden, e.g.,
+are hidden. These include
 <code>chrome-extension://other_extension_id</code> where
 <code>other_extension_id</code> is not the ID of the extension to handle
 the request,
 <code>https://www.google.com/chrome</code>,
-and others (this list is not complete). Also synchronous XMLHttpRequests from
-your extension are hidden from blocking event handlers in order to prevent
-deadlocks.
+and other sensitive requests core to browser functionality. Also synchronous
+XMLHttpRequests from your extension are hidden from blocking event handlers in
+order to prevent deadlocks.
 Note that for some of the supported schemes the set of available events might be
 limited due to the nature of the corresponding protocol.
 For example, for the <q>file:</q> scheme, only <code>onBeforeRequest</code>,
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index d4f9778..6cd80827 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1833,10 +1833,6 @@
 // and get enrolled into a domain automatically.
 const char kOobeControllerDetected[] = "OobeControllerDetected";
 
-// A boolean pref to indicate if the Recommend App screen in OOBE is finished
-// for the user.
-const char kOobeRecommendAppScreenFinished[] = "OobeRecommendAppScreenFinished";
-
 // A boolean pref for whether the Goodies promotion webpage has been displayed,
 // or otherwise disqualified for auto-display, on this device.
 const char kCanShowOobeGoodiesPage[] = "CanShowOobeGoodiesPage";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 89c4a33..d724b117 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -617,7 +617,6 @@
 extern const char kOobeComplete[];
 extern const char kOobeScreenPending[];
 extern const char kOobeControllerDetected[];
-extern const char kOobeRecommendAppScreenFinished[];
 extern const char kCanShowOobeGoodiesPage[];
 extern const char kDeviceRegistered[];
 extern const char kEnrollmentRecoveryRequired[];
diff --git a/chrome/services/media_gallery_util/BUILD.gn b/chrome/services/media_gallery_util/BUILD.gn
index 2d2043d..4a9bc23 100644
--- a/chrome/services/media_gallery_util/BUILD.gn
+++ b/chrome/services/media_gallery_util/BUILD.gn
@@ -55,6 +55,8 @@
 
     data = [
       "//media/test/data/bear.mp4",
+      "//media/test/data/bear-vp8-webvtt.webm",
+      "//media/test/data/bear-vp8a.webm",
       "//media/test/data/sfx.mp3",
     ]
 
diff --git a/chrome/services/media_gallery_util/media_parser_android.cc b/chrome/services/media_gallery_util/media_parser_android.cc
index c65308ff..7aa662c 100644
--- a/chrome/services/media_gallery_util/media_parser_android.cc
+++ b/chrome/services/media_gallery_util/media_parser_android.cc
@@ -8,25 +8,85 @@
 #include "base/task/task_traits.h"
 #include "chrome/services/media_gallery_util/ipc_data_source.h"
 #include "media/base/bind_to_current_loop.h"
+#include "media/base/video_codecs.h"
+#include "media/base/video_thumbnail_decoder.h"
 #include "media/filters/android/video_frame_extractor.h"
+#include "media/filters/vpx_video_decoder.h"
+#include "media/media_buildflags.h"
+#include "media/mojo/common/mojo_shared_buffer_video_frame.h"
 
 namespace {
 
-void OnThumbnailGenerated(
+// Return the video frame back to browser process. A valid |config| is
+// needed for deserialization.
+void OnSoftwareVideoFrameDecoded(
+    std::unique_ptr<media::VideoThumbnailDecoder>,
+    MediaParser::ExtractVideoFrameCallback video_frame_callback,
+    const media::VideoDecoderConfig& config,
+    scoped_refptr<media::VideoFrame> frame) {
+  DCHECK(video_frame_callback);
+
+  if (!frame) {
+    std::move(video_frame_callback)
+        .Run(false, chrome::mojom::VideoFrameData::New(), config);
+    return;
+  }
+
+  std::move(video_frame_callback)
+      .Run(true,
+           chrome::mojom::VideoFrameData::NewDecodedFrame(
+               media::MojoSharedBufferVideoFrame::CreateFromYUVFrame(*frame)),
+           config);
+}
+
+void OnEncodedVideoFrameExtracted(
     std::unique_ptr<media::VideoFrameExtractor> video_frame_extractor,
-    MediaParser::ExtractVideoFrameCallback extract_frame_cb,
+    MediaParser::ExtractVideoFrameCallback video_frame_callback,
     bool success,
-    const std::vector<uint8_t>& data,
+    std::vector<uint8_t> data,
     const media::VideoDecoderConfig& config) {
-  std::move(extract_frame_cb).Run(success, data, config);
+  if (!success || data.empty()) {
+    std::move(video_frame_callback)
+        .Run(false, chrome::mojom::VideoFrameData::New(), config);
+    return;
+  }
+
+#if BUILDFLAG(USE_PROPRIETARY_CODECS) && \
+    !BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
+  // H264 currently needs to be decoded in GPU process when no software decoder
+  // is provided.
+  if (config.codec() == media::VideoCodec::kCodecH264) {
+    std::move(video_frame_callback)
+        .Run(success,
+             chrome::mojom::VideoFrameData::NewEncodedData(std::move(data)),
+             config);
+    return;
+  }
+#endif
+
+  if (config.codec() != media::VideoCodec::kCodecVP8 &&
+      config.codec() != media::VideoCodec::kCodecVP9) {
+    std::move(video_frame_callback)
+        .Run(false, chrome::mojom::VideoFrameData::New(), config);
+    return;
+  }
+
+  // Decode with libvpx for vp8, vp9.
+  auto thumbnail_decoder = std::make_unique<media::VideoThumbnailDecoder>(
+      std::make_unique<media::VpxVideoDecoder>(), config, std::move(data));
+
+  thumbnail_decoder->Start(
+      base::BindOnce(&OnSoftwareVideoFrameDecoded, std::move(thumbnail_decoder),
+                     std::move(video_frame_callback), config));
 }
 
 void ExtractVideoFrameOnMediaThread(
     media::DataSource* data_source,
-    MediaParser::ExtractVideoFrameCallback extract_frame_callback) {
+    MediaParser::ExtractVideoFrameCallback video_frame_callback) {
   auto extractor = std::make_unique<media::VideoFrameExtractor>(data_source);
-  extractor->Start(base::BindOnce(&OnThumbnailGenerated, std::move(extractor),
-                                  std::move(extract_frame_callback)));
+  extractor->Start(base::BindOnce(&OnEncodedVideoFrameExtracted,
+                                  std::move(extractor),
+                                  std::move(video_frame_callback)));
 }
 
 }  // namespace
@@ -43,7 +103,7 @@
     const std::string& mime_type,
     uint32_t total_size,
     chrome::mojom::MediaDataSourcePtr media_data_source,
-    MediaParser::ExtractVideoFrameCallback extract_frame_callback) {
+    MediaParser::ExtractVideoFrameCallback video_frame_callback) {
   data_source_ = std::make_unique<IPCDataSource>(
       std::move(media_data_source), static_cast<int64_t>(total_size));
 
@@ -51,5 +111,5 @@
       FROM_HERE,
       base::BindOnce(
           &ExtractVideoFrameOnMediaThread, data_source_.get(),
-          media::BindToCurrentLoop(std::move(extract_frame_callback))));
+          media::BindToCurrentLoop(std::move(video_frame_callback))));
 }
diff --git a/chrome/services/media_gallery_util/media_parser_android.h b/chrome/services/media_gallery_util/media_parser_android.h
index 702fd40..2eb33d4 100644
--- a/chrome/services/media_gallery_util/media_parser_android.h
+++ b/chrome/services/media_gallery_util/media_parser_android.h
@@ -22,10 +22,11 @@
   ~MediaParserAndroid() override;
 
   // MediaParser implementation.
-  void ExtractVideoFrame(const std::string& mime_type,
-                         uint32_t total_size,
-                         chrome::mojom::MediaDataSourcePtr media_data_source,
-                         ExtractVideoFrameCallback callback) override;
+  void ExtractVideoFrame(
+      const std::string& mime_type,
+      uint32_t total_size,
+      chrome::mojom::MediaDataSourcePtr media_data_source,
+      ExtractVideoFrameCallback video_frame_callback) override;
 
  private:
   // The task runner to do blocking IO. The utility thread cannot be blocked.
diff --git a/chrome/services/media_gallery_util/media_parser_android_unittest.cc b/chrome/services/media_gallery_util/media_parser_android_unittest.cc
index e6e783a5..14b0a43 100644
--- a/chrome/services/media_gallery_util/media_parser_android_unittest.cc
+++ b/chrome/services/media_gallery_util/media_parser_android_unittest.cc
@@ -15,12 +15,41 @@
 #include "base/test/scoped_task_environment.h"
 #include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
 #include "media/base/test_data_util.h"
+#include "media/media_buildflags.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/service_manager/public/cpp/service_context_ref.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
+struct ExtractVideoFrameResult {
+  bool success = false;
+  chrome::mojom::VideoFrameDataPtr video_frame_data;
+  media::VideoDecoderConfig config;
+};
+
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+// Returns if the first 3 or 4 bytes of H264 encoded |data| is the start code,
+// 0x000001 or 0x00000001.
+bool HasH264StartCode(const std::vector<uint8_t>& data) {
+  if (data.size() < 4 || (data[0] != 0u || data[1] != 0u))
+    return false;
+  return data[2] == 0x01 || (data[2] == 0u && data[3] == 0x01);
+}
+#endif
+
+// Returns if the first few bytes in the YUV frame are not all zero. This is a
+// rough method to verify the frame is not empty.
+bool HasValidYUVData(const scoped_refptr<media::VideoFrame>& frame) {
+  bool valid = false;
+  for (size_t i = 0; i < 8; ++i) {
+    valid |= *(frame->data(media::VideoFrame::kYPlane) + i);
+    if (valid)
+      break;
+  }
+  return valid;
+}
+
 // Used in test that do blocking reads from a local file.
 class TestMediaDataSource : public chrome::mojom::MediaDataSource {
  public:
@@ -65,6 +94,33 @@
  protected:
   MediaParserAndroid* parser() { return parser_.get(); }
 
+  ExtractVideoFrameResult ExtractFrame(const std::string& file_name,
+                                       const std::string& mime_type) {
+    auto file_path = media::GetTestDataFilePath(file_name);
+    int64_t size = 0;
+    EXPECT_TRUE(base::GetFileSize(file_path, &size));
+
+    chrome::mojom::MediaDataSourcePtr data_source_ptr;
+    TestMediaDataSource test_data_source(&data_source_ptr, file_path);
+
+    ExtractVideoFrameResult result;
+    base::RunLoop run_loop;
+    parser()->ExtractVideoFrame(
+        mime_type, size, std::move(data_source_ptr),
+        base::BindLambdaForTesting(
+            [&](bool success, chrome::mojom::VideoFrameDataPtr video_frame_data,
+                const media::VideoDecoderConfig& config) {
+              result.success = success;
+              result.video_frame_data = std::move(video_frame_data);
+              result.config = config;
+
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+
+    return result;
+  }
+
  private:
   std::unique_ptr<MediaParserAndroid> parser_;
 
@@ -74,26 +130,50 @@
   DISALLOW_COPY_AND_ASSIGN(MediaParserAndroidTest);
 };
 
-// Test to verify encoded video frame can be extracted.
-TEST_F(MediaParserAndroidTest, VideoFrameExtraction) {
-  auto file_path = media::GetTestDataFilePath("bear.mp4");
-  int64_t size = 0;
-  ASSERT_TRUE(base::GetFileSize(file_path, &size));
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+// Test to verify an encoded video frame can be extracted for h264 codec video
+// file. Decoding needs to happen in other process.
+TEST_F(MediaParserAndroidTest, VideoFrameExtractionH264) {
+  auto result = ExtractFrame("bear.mp4", "video/mp4");
+  EXPECT_TRUE(result.success);
+  EXPECT_EQ(result.video_frame_data->which(),
+            chrome::mojom::VideoFrameData::Tag::ENCODED_DATA);
+  EXPECT_FALSE(result.video_frame_data->get_encoded_data().empty());
+  EXPECT_TRUE(HasH264StartCode(result.video_frame_data->get_encoded_data()));
+}
+#endif
 
-  chrome::mojom::MediaDataSourcePtr data_source_ptr;
-  TestMediaDataSource test_data_source(&data_source_ptr, file_path);
+// Test to verify a decoded video frame can be extracted for vp8 codec video
+// file with YUV420 color format.
+TEST_F(MediaParserAndroidTest, VideoFrameExtractionVp8) {
+  auto result = ExtractFrame("bear-vp8-webvtt.webm", "video/webm");
+  EXPECT_TRUE(result.success);
+  EXPECT_EQ(result.video_frame_data->which(),
+            chrome::mojom::VideoFrameData::Tag::DECODED_FRAME);
+  const auto& frame = result.video_frame_data->get_decoded_frame();
+  EXPECT_TRUE(frame);
+  EXPECT_TRUE(HasValidYUVData(frame));
+  EXPECT_TRUE(frame->IsMappable());
+  EXPECT_FALSE(frame->HasTextures());
+  EXPECT_EQ(frame->storage_type(),
+            media::VideoFrame::StorageType::STORAGE_MOJO_SHARED_BUFFER);
+}
 
-  bool result = false;
-  base::RunLoop run_loop;
-  parser()->ExtractVideoFrame(
-      "video/mp4", size, std::move(data_source_ptr),
-      base::BindLambdaForTesting([&](bool success, const std::vector<uint8_t>&,
-                                     const media::VideoDecoderConfig&) {
-        result = success;
-        run_loop.Quit();
-      }));
-  run_loop.Run();
-  EXPECT_TRUE(result);
+// Test to verify a decoded video frame can be extracted for vp8 codec with
+// alpha plane.
+TEST_F(MediaParserAndroidTest, VideoFrameExtractionVp8WithAlphaPlane) {
+  auto result = ExtractFrame("bear-vp8a.webm", "video/webm");
+  EXPECT_TRUE(result.success);
+
+  EXPECT_EQ(result.video_frame_data->which(),
+            chrome::mojom::VideoFrameData::Tag::DECODED_FRAME);
+  const auto& frame = result.video_frame_data->get_decoded_frame();
+  EXPECT_TRUE(frame);
+  EXPECT_TRUE(HasValidYUVData(frame));
+  EXPECT_TRUE(frame->IsMappable());
+  EXPECT_FALSE(frame->HasTextures());
+  EXPECT_EQ(frame->storage_type(),
+            media::VideoFrame::StorageType::STORAGE_MOJO_SHARED_BUFFER);
 }
 
 }  // namespace
diff --git a/chrome/services/media_gallery_util/public/mojom/media_parser.mojom b/chrome/services/media_gallery_util/public/mojom/media_parser.mojom
index 97e40c43..cb381b6 100644
--- a/chrome/services/media_gallery_util/public/mojom/media_parser.mojom
+++ b/chrome/services/media_gallery_util/public/mojom/media_parser.mojom
@@ -9,6 +9,13 @@
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/values.mojom";
 
+// Contains either encoded video frame in |encoded_data|, or decoded video
+// frame in |decoded_frame|.
+union VideoFrameData {
+  array<uint8> encoded_data;
+  media.mojom.VideoFrame? decoded_frame;
+};
+
 interface MediaParser {
   // Extracts metadata from media data with |mime_type|, |total_size| and
   // available from |media_data_source|. If there are images referred to in the
@@ -22,14 +29,15 @@
           MediaMetadata metadata,
           array<AttachedImage> attached_images);
 
-  // Extracts one video key frame from |media_data_source|. The frame is still
-  // in encoded format.
+  // Extracts one video key frame from |media_data_source|. Returns the decoded
+  // frame if decoding can be done in utility process, or an encoded frame if
+  // decoding needs to happen in other process.
   [EnableIf=is_android]
   ExtractVideoFrame(string mime_type,
                     uint32 total_size,
                     MediaDataSource media_data_source)
       => (bool success,
-          array<uint8> data,
+          VideoFrameData frame_data,
           media.mojom.VideoDecoderConfig config);
 
   // Validates the passed media file with sanity checks, and file decoding
diff --git a/chrome/test/chromedriver/chrome/automation_extension.cc b/chrome/test/chromedriver/chrome/automation_extension.cc
index a368764..b18f328 100644
--- a/chrome/test/chromedriver/chrome/automation_extension.cc
+++ b/chrome/test/chromedriver/chrome/automation_extension.cc
@@ -32,89 +32,6 @@
   return Status(kOk);
 }
 
-Status AutomationExtension::GetWindowPosition(int* x, int* y) {
-  int temp_width, temp_height;
-  return GetWindowInfo(x, y, &temp_width, &temp_height);
-}
-
-Status AutomationExtension::SetWindowPosition(int x, int y) {
-  base::DictionaryValue update_info;
-  update_info.SetInteger("left", x);
-  update_info.SetInteger("top", y);
-  update_info.SetString("state", "normal");
-  return UpdateWindow(update_info);
-}
-
-Status AutomationExtension::GetWindowSize(int* width, int* height) {
-  int temp_x, temp_y;
-  return GetWindowInfo(&temp_x, &temp_y, width, height);
-}
-
-Status AutomationExtension::SetWindowSize(int width, int height) {
-  base::DictionaryValue update_info;
-  update_info.SetInteger("width", width);
-  update_info.SetInteger("height", height);
-  update_info.SetString("state", "normal");
-  return UpdateWindow(update_info);
-}
-
-Status AutomationExtension::MaximizeWindow() {
-  base::DictionaryValue update_info;
-  update_info.SetString("state", "maximized");
-  return UpdateWindow(update_info);
-}
-
-Status AutomationExtension::FullScreenWindow() {
-  base::DictionaryValue update_info;
-  update_info.SetString("state", "fullscreen");
-  return UpdateWindow(update_info);
-}
-
-Status AutomationExtension::GetWindowInfo(int* x,
-                                          int* y,
-                                          int* width,
-                                          int* height) {
-  base::ListValue args;
-  std::unique_ptr<base::Value> result;
-  Status status = web_view_->CallAsyncFunction(std::string(),
-                                               "getWindowInfo",
-                                               args,
-                                               base::TimeDelta::FromSeconds(10),
-                                               &result);
-  if (status.IsError())
-    return status;
-
-  base::DictionaryValue* dict;
-  int temp_x = 0;
-  int temp_y = 0;
-  int temp_width = 0;
-  int temp_height = 0;
-  if (!result->GetAsDictionary(&dict) ||
-      !dict->GetInteger("left", &temp_x) ||
-      !dict->GetInteger("top", &temp_y) ||
-      !dict->GetInteger("width", &temp_width) ||
-      !dict->GetInteger("height", &temp_height)) {
-    return Status(kUnknownError, "received invalid window info");
-  }
-  *x = temp_x;
-  *y = temp_y;
-  *width = temp_width;
-  *height = temp_height;
-  return Status(kOk);
-}
-
-Status AutomationExtension::UpdateWindow(
-    const base::DictionaryValue& update_info) {
-  base::ListValue args;
-  args.Append(update_info.CreateDeepCopy());
-  std::unique_ptr<base::Value> result;
-  return web_view_->CallAsyncFunction(std::string(),
-                                      "updateWindow",
-                                      args,
-                                      base::TimeDelta::FromSeconds(10),
-                                      &result);
-}
-
 Status AutomationExtension::LaunchApp(const std::string& id) {
   base::ListValue args;
   args.AppendString(id);
diff --git a/chrome/test/chromedriver/chrome/automation_extension.h b/chrome/test/chromedriver/chrome/automation_extension.h
index d4e10ec..fea9cd1 100644
--- a/chrome/test/chromedriver/chrome/automation_extension.h
+++ b/chrome/test/chromedriver/chrome/automation_extension.h
@@ -30,28 +30,7 @@
   // Launches an app with the specified id.
   Status LaunchApp(const std::string& id);
 
-  // Gets the position of the current window.
-  Status GetWindowPosition(int* x, int* y);
-
-  // Sets the position of the current window.
-  Status SetWindowPosition(int x, int y);
-
-  // Gets the size of the current window.
-  Status GetWindowSize(int* width, int* height);
-
-  // Sets the size of the current window.
-  Status SetWindowSize(int width, int height);
-
-  // Maximizes the current window.
-  Status MaximizeWindow();
-
-  // Sets the current window to fullscreen state.
-  Status FullScreenWindow();
-
  private:
-  Status GetWindowInfo(int* x, int* y, int* width, int* height);
-  Status UpdateWindow(const base::DictionaryValue& update_info);
-
   std::unique_ptr<WebView> web_view_;
 
   DISALLOW_COPY_AND_ASSIGN(AutomationExtension);
diff --git a/chrome/test/chromedriver/extension/background.js b/chrome/test/chromedriver/extension/background.js
index 9655ffa7..686adb2 100644
--- a/chrome/test/chromedriver/extension/background.js
+++ b/chrome/test/chromedriver/extension/background.js
@@ -41,38 +41,6 @@
 }
 
 /**
- * Gets info about the current window.
- *
- * @param {function(*)} callback The callback to invoke with the window info.
- * @param {function(!Error)} errCallback The callback to invoke for error
- *     reporting.
- */
-function getWindowInfo(callback, errCallback) {
-  chrome.windows.getCurrent({populate: true}, function(window) {
-    checkForExtensionError(errCallback);
-    callback(window);
-  });
-}
-
-/**
- * Updates the properties of the current window.
- *
- * @param {Object} updateInfo Update info to pass to chrome.windows.update.
- * @param {function()} callback Invoked when the updating is complete.
- * @param {function(!Error)} errCallback The callback to invoke for error
- *     reporting.
- */
-function updateWindow(updateInfo, callback, errCallback) {
-  chrome.windows.getCurrent({}, function(window) {
-    checkForExtensionError(errCallback);
-    chrome.windows.update(window.id, updateInfo, function(window) {
-      checkForExtensionError(errCallback);
-      callback();
-    });
-  });
-}
-
-/**
  * Launches an app with the specified id.
  *
  * @param {string} id The ID of the app to launch.
diff --git a/chrome/test/chromedriver/session_commands.cc b/chrome/test/chromedriver/session_commands.cc
index ae63c03..3cef335 100644
--- a/chrome/test/chromedriver/session_commands.cc
+++ b/chrome/test/chromedriver/session_commands.cc
@@ -40,10 +40,6 @@
 
 namespace {
 
-// The minimium chrome build no that supports window management devtools
-// commands.
-const int kBrowserWindowDevtoolsBuildNo = 3076;
-
 const int kWifiMask = 0x2;
 const int k4GMask = 0x8;
 const int k3GMask = 0x10;
@@ -874,17 +870,8 @@
   if (status.IsError())
     return status;
 
-  if (desktop->GetBrowserInfo()->build_no >= kBrowserWindowDevtoolsBuildNo) {
-    return desktop->SetWindowPosition(session->window, static_cast<int>(x),
+  return desktop->SetWindowPosition(session->window, static_cast<int>(x),
                                       static_cast<int>(y));
-  }
-
-  AutomationExtension* extension = NULL;
-  status = desktop->GetAutomationExtension(&extension, session->w3c_compliant);
-  if (status.IsError())
-    return status;
-
-  return extension->SetWindowPosition(static_cast<int>(x), static_cast<int>(y));
 }
 
 Status ExecuteGetWindowSize(Session* session,
@@ -952,18 +939,8 @@
   if (status.IsError())
     return status;
 
-  if (desktop->GetBrowserInfo()->build_no >= kBrowserWindowDevtoolsBuildNo) {
-    return desktop->SetWindowSize(session->window, static_cast<int>(width),
+  return desktop->SetWindowSize(session->window, static_cast<int>(width),
                                   static_cast<int>(height));
-  }
-
-  AutomationExtension* extension = NULL;
-  status = desktop->GetAutomationExtension(&extension, session->w3c_compliant);
-  if (status.IsError())
-    return status;
-
-  return extension->SetWindowSize(
-      static_cast<int>(width), static_cast<int>(height));
 }
 
 Status ExecuteMaximizeWindow(Session* session,
diff --git a/chrome/test/data/webui/print_preview/link_container_test.js b/chrome/test/data/webui/print_preview/link_container_test.js
index 6acbf6f5..0d4f1649 100644
--- a/chrome/test/data/webui/print_preview/link_container_test.js
+++ b/chrome/test/data/webui/print_preview/link_container_test.js
@@ -7,6 +7,7 @@
   const TestNames = {
     HideInAppKioskMode: 'hide in app kiosk mode',
     SystemDialogLinkClick: 'system dialog link click',
+    InvalidState: 'invalid state',
     OpenInPreviewLinkClick: 'open in preview link click',
   };
 
@@ -60,6 +61,34 @@
     });
 
     /**
+     * Test that if settings are invalid, the open in preview link is disabled
+     * (if it exists), and that the system dialog link is disabled on Windows
+     * and enabled on other platforms.
+     */
+    test(assert(TestNames.InvalidState), function() {
+      const systemDialogLink = linkContainer.$.systemDialogLink;
+      const openInPreviewLink =
+          cr.isMac ? linkContainer.$.openPdfInPreviewLink : null;
+
+      const validateLinkState = (link, disabled) => {
+        assertFalse(link.hidden);
+        assertEquals(!disabled, link.hasAttribute('actionable'));
+        assertEquals(disabled, link.querySelector('button').disabled);
+      };
+
+      validateLinkState(systemDialogLink, false);
+      if (cr.isMac)
+        validateLinkState(openInPreviewLink, false);
+
+      // Set disabled to true, indicating that there is a validation error or
+      // printer error.
+      linkContainer.disabled = true;
+      validateLinkState(systemDialogLink, cr.isWindows);
+      if (cr.isMac)
+        validateLinkState(openInPreviewLink, true);
+    });
+
+    /**
      * Test that clicking the open in preview link correctly results in a
      * property change and that the throbber appears. Mac only.
      */
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index 26c4850..89c4527 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -492,6 +492,10 @@
 TEST_F('PrintPreviewLinkContainerTest', 'SystemDialogLinkClick', function() {
   this.runMochaTest(link_container_test.TestNames.SystemDialogLinkClick);
 });
+
+TEST_F('PrintPreviewLinkContainerTest', 'InvalidState', function() {
+  this.runMochaTest(link_container_test.TestNames.InvalidState);
+});
 GEN('#endif');  // !defined(OS_CHROMEOS)
 
 GEN('#if defined(OS_MACOSX)');
diff --git a/chrome/test/data/webui/print_preview/settings_section_test.js b/chrome/test/data/webui/print_preview/settings_section_test.js
index 4670b74..dc8332c4 100644
--- a/chrome/test/data/webui/print_preview/settings_section_test.js
+++ b/chrome/test/data/webui/print_preview/settings_section_test.js
@@ -860,33 +860,34 @@
               .$.userValue.inputElement;
       const fitToPageCheckbox = scalingElement.$$('#fit-to-page-checkbox');
 
-      const validateScalingState = (scalingValue, scalingValid, fitToPage) => {
-        // Invalid scalings are always set directly in the input, so no need to
-        // verify that the input matches them.
-        if (scalingValid) {
-          const scalingDisplay = fitToPage ?
-              page.documentInfo_.fitToPageScaling.toString() :
-              scalingValue;
-          assertEquals(scalingDisplay, scalingInput.value);
-        }
-        assertEquals(scalingValue, page.settings.scaling.value);
-        assertEquals(scalingValid, page.settings.scaling.valid);
-        assertEquals(fitToPage, fitToPageCheckbox.checked);
-        assertEquals(fitToPage, page.settings.fitToPage.value);
-      };
+      const validateScalingState =
+          (scalingValue, scalingValid, fitToPage, fitToPageDisplay) => {
+            // Invalid scalings are always set directly in the input, so no need
+            // to verify that the input matches them.
+            if (scalingValid) {
+              const scalingDisplay = fitToPage ?
+                  page.documentInfo_.fitToPageScaling.toString() :
+                  scalingValue;
+              assertEquals(scalingDisplay, scalingInput.value);
+            }
+            assertEquals(scalingValue, page.settings.scaling.value);
+            assertEquals(scalingValid, page.settings.scaling.valid);
+            assertEquals(fitToPageDisplay, fitToPageCheckbox.checked);
+            assertEquals(fitToPage, page.settings.fitToPage.value);
+          };
 
       // Set PDF so both scaling and fit to page are active.
       initDocumentInfo(true, false);
       assertFalse(scalingElement.hidden);
 
       // Default is 100
-      validateScalingState('100', true, false);
+      validateScalingState('100', true, false, false);
 
       // Change to 105
       triggerInputEvent(scalingInput, '105');
       return test_util.eventToPromise('input-change', scalingElement)
           .then(function() {
-            validateScalingState('105', true, false);
+            validateScalingState('105', true, false, false);
 
             // Change to fit to page. Should display fit to page scaling but not
             // alter the scaling setting.
@@ -897,7 +898,7 @@
           })
           .then(function(event) {
             assertEquals('fitToPage', event.detail);
-            validateScalingState('105', true, true);
+            validateScalingState('105', true, true, true);
 
             // Set scaling. Should uncheck fit to page and set the settings for
             // scaling and fit to page.
@@ -905,7 +906,7 @@
             return test_util.eventToPromise('input-change', scalingElement);
           })
           .then(function() {
-            validateScalingState('95', true, false);
+            validateScalingState('95', true, false, false);
 
             // Set scaling to something invalid. Should change setting validity
             // but not value.
@@ -913,7 +914,7 @@
             return test_util.eventToPromise('input-change', scalingElement);
           })
           .then(function() {
-            validateScalingState('95', false, false);
+            validateScalingState('95', false, false, false);
 
             // Check fit to page. Should set scaling valid.
             fitToPageCheckbox.checked = true;
@@ -923,7 +924,7 @@
           })
           .then(function(event) {
             assertEquals('fitToPage', event.detail);
-            validateScalingState('95', true, true);
+            validateScalingState('95', true, true, true);
 
             // Uncheck fit to page. Should reset scaling to last valid.
             fitToPageCheckbox.checked = false;
@@ -933,7 +934,45 @@
           })
           .then(function(event) {
             assertEquals('fitToPage', event.detail);
-            validateScalingState('95', true, false);
+            validateScalingState('95', true, false, false);
+
+            // Change to fit to page. Should display fit to page scaling but not
+            // alter the scaling setting.
+            fitToPageCheckbox.checked = true;
+            fitToPageCheckbox.dispatchEvent(new CustomEvent('change'));
+            return test_util.eventToPromise(
+                'update-checkbox-setting', scalingElement);
+          })
+          .then(function(event) {
+            assertEquals('fitToPage', event.detail);
+            validateScalingState('95', true, true, true);
+
+            // Enter something invalid in the scaling field. This should not
+            // change the stored value of scaling or fit to page, to avoid an
+            // unnecessary preview regeneration, but should display fit to page
+            // as unchecked.
+            triggerInputEvent(scalingInput, '9');
+            return test_util.eventToPromise('input-change', scalingElement);
+          })
+          .then(function() {
+            validateScalingState('95', false, true, false);
+
+            // Enter a blank value in the scaling field. This should not
+            // change the stored value of scaling or fit to page, to avoid an
+            // unnecessary preview regeneration.
+            triggerInputEvent(scalingInput, '');
+            return test_util.eventToPromise('input-change', scalingElement);
+          })
+          .then(function() {
+            validateScalingState('95', false, true, false);
+
+            // Entering something valid unsets fit to page and sets scaling
+            // valid to true.
+            triggerInputEvent(scalingInput, '90');
+            return test_util.eventToPromise('input-change', scalingElement);
+          })
+          .then(function() {
+            validateScalingState('90', true, false, false);
           });
     });
 
diff --git a/chrome/test/data/webui/settings/site_details_permission_tests.js b/chrome/test/data/webui/settings/site_details_permission_tests.js
index 8549b26..32a47e90 100644
--- a/chrome/test/data/webui/settings/site_details_permission_tests.js
+++ b/chrome/test/data/webui/settings/site_details_permission_tests.js
@@ -278,7 +278,7 @@
       source: settings.SiteSettingSource.ADS_FILTER_BLACKLIST,
     };
     assertEquals(
-        'Site tends to show intrusive ads' +
+        'Site shows intrusive or misleading ads' +
             '\nAllow\nBlock\nAsk',
         testElement.$.permissionItem.innerText.trim());
     assertTrue(testElement.$.permissionItem.classList.contains('two-line'));
@@ -292,7 +292,7 @@
       source: settings.SiteSettingSource.PREFERENCE,
     };
     assertEquals(
-        'Block if site tends to show intrusive ads' +
+        'Block if site shows intrusive or misleading ads' +
             '\nAllow\nBlock\nAsk',
         testElement.$.permissionItem.innerText.trim());
     assertTrue(testElement.$.permissionItem.classList.contains('two-line'));
@@ -306,7 +306,7 @@
       source: settings.SiteSettingSource.DEFAULT,
     };
     assertEquals(
-        'Block if site tends to show intrusive ads' +
+        'Block if site shows intrusive or misleading ads' +
             '\nBlock (default)\nAllow\nBlock\nAsk',
         testElement.$.permissionItem.innerText.trim());
     assertTrue(testElement.$.permissionItem.classList.contains('two-line'));
diff --git a/chromecast/browser/metrics/external_metrics.cc b/chromecast/browser/metrics/external_metrics.cc
index cbfe2570f..0bcf2a2 100644
--- a/chromecast/browser/metrics/external_metrics.cc
+++ b/chromecast/browser/metrics/external_metrics.cc
@@ -19,7 +19,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
-#include "base/threading/thread_restrictions.h"
+#include "base/threading/scoped_blocking_call.h"
 #include "chromecast/base/metrics/cast_histograms.h"
 #include "chromecast/base/metrics/cast_metrics_helper.h"
 #include "chromecast/browser/metrics/cast_stability_metrics_provider.h"
@@ -119,7 +119,7 @@
 
 int ExternalMetrics::CollectEvents() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  base::AssertBlockingAllowed();
+  base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::WILL_BLOCK);
 
   std::vector<std::unique_ptr<::metrics::MetricSample>> samples;
   ::metrics::SerializationUtils::ReadAndTruncateMetricsFromFile(
diff --git a/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.cc b/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.cc
index 17252760..b745942 100644
--- a/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.cc
+++ b/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.cc
@@ -8,133 +8,44 @@
 
 #include "chromecast/browser/ui/aura/accessibility/automation_manager_aura.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/accessibility/ax_action_data.h"
-#include "ui/views/accessibility/ax_aura_obj_cache.h"
 #include "ui/views/accessibility/ax_aura_obj_wrapper.h"
 #include "ui/views/accessibility/ax_view_obj_wrapper.h"
 
-#if defined(TOOLKIT_VIEWS)
-#include "ui/views/controls/webview/webview.h"  // nogncheck
-#endif
+AXTreeSourceAura::AXTreeSourceAura()
+    : desktop_root_(std::make_unique<AXRootObjWrapper>(
+          AutomationManagerAura::GetInstance())) {}
 
-using views::AXAuraObjCache;
-using views::AXAuraObjWrapper;
-
-AXTreeSourceAura::AXTreeSourceAura() {
-  root_.reset(new AXRootObjWrapper(AutomationManagerAura::GetInstance()));
-}
-
-AXTreeSourceAura::~AXTreeSourceAura() {
-  root_.reset();
-}
-
-bool AXTreeSourceAura::HandleAccessibleAction(const ui::AXActionData& action) {
-  int id = action.target_node_id;
-
-  // In Views, we only support setting the selection within a single node,
-  // not across multiple nodes like on the web.
-  if (action.action == ax::mojom::Action::kSetSelection) {
-    if (action.anchor_node_id != action.focus_node_id) {
-      NOTREACHED();
-      return false;
-    }
-    id = action.anchor_node_id;
-  }
-
-  AXAuraObjWrapper* obj = AXAuraObjCache::GetInstance()->Get(id);
-  if (obj)
-    return obj->HandleAccessibleAction(action);
-
-  return false;
-}
+AXTreeSourceAura::~AXTreeSourceAura() = default;
 
 bool AXTreeSourceAura::GetTreeData(ui::AXTreeData* tree_data) const {
   tree_data->tree_id = ui::DesktopAXTreeID();
-  tree_data->loaded = true;
-  tree_data->loading_progress = 1.0;
-  AXAuraObjWrapper* focus = AXAuraObjCache::GetInstance()->GetFocus();
+  AXTreeSourceViews::GetTreeData(tree_data);
 
   // TODO(b/111911092): AXTreeData::focus_id represents the node within the
   // tree with 'keyboard focus'.  We have no keyboard focus on chromecast so
   // this is being left as -1. This prevents getFocus calls from the chromevox
   // background page from finding any window in focus and interferes with
   // gesture event processing.  Since we only ever have one top level window
-  // and one ax tree, temporarily returning 1 here to indicate the root node is
-  // always the focused window. A better solution would be to fix the focus
+  // and one ax tree, temporarily returning 1 here to indicate the root node
+  // is always the focused window. A better solution would be to fix the focus
   // issues on chromecast which relies on a) the root window to be focused via
   // Focus() and 2) a native widget being registered with the root window so
   // the above GetFocus call will work.  When this code is re-unified with
   // chrome, this will need to be a special case for chromecast unless the
   // better solution described above is implemented.
-  if (focus)
-    tree_data->focus_id = focus->GetUniqueId().Get();
-  else
-    tree_data->focus_id = 1;  // root node
-
+  tree_data->focus_id = 1;
   return true;
 }
 
-AXAuraObjWrapper* AXTreeSourceAura::GetRoot() const {
-  return root_.get();
+views::AXAuraObjWrapper* AXTreeSourceAura::GetRoot() const {
+  return desktop_root_.get();
 }
 
-AXAuraObjWrapper* AXTreeSourceAura::GetFromId(int32_t id) const {
-  if (id == root_->GetUniqueId().Get())
-    return root_.get();
-  return AXAuraObjCache::GetInstance()->Get(id);
-}
-
-int32_t AXTreeSourceAura::GetId(AXAuraObjWrapper* node) const {
-  return node->GetUniqueId().Get();
-}
-
-void AXTreeSourceAura::GetChildren(
-    AXAuraObjWrapper* node,
-    std::vector<AXAuraObjWrapper*>* out_children) const {
-  node->GetChildren(out_children);
-}
-
-AXAuraObjWrapper* AXTreeSourceAura::GetParent(AXAuraObjWrapper* node) const {
-  AXAuraObjWrapper* parent = node->GetParent();
-  if (!parent && node->GetUniqueId() != root_->GetUniqueId())
-    parent = root_.get();
-  return parent;
-}
-
-bool AXTreeSourceAura::IsValid(AXAuraObjWrapper* node) const {
-  return node != nullptr;
-}
-
-bool AXTreeSourceAura::IsEqual(AXAuraObjWrapper* node1,
-                               AXAuraObjWrapper* node2) const {
-  if (!node1 || !node2)
-    return false;
-
-  return node1->GetUniqueId() == node2->GetUniqueId();
-}
-
-AXAuraObjWrapper* AXTreeSourceAura::GetNull() const {
-  return NULL;
-}
-
-void AXTreeSourceAura::SerializeNode(AXAuraObjWrapper* node,
+void AXTreeSourceAura::SerializeNode(views::AXAuraObjWrapper* node,
                                      ui::AXNodeData* out_data) const {
-  node->Serialize(out_data);
-
-  // Convert the global coordinates reported by each AXAuraObjWrapper
-  // into parent-relative coordinates to be used in the accessibility
-  // tree. That way when any Window, Widget, or View moves (and fires
-  // a location changed event), its descendants all move relative to
-  // it by default.
-  AXAuraObjWrapper* parent = node->GetParent();
-  if (parent) {
-    ui::AXNodeData parent_data;
-    parent->Serialize(&parent_data);
-    out_data->location.Offset(-parent_data.location.OffsetFromOrigin());
-    out_data->offset_container_id = parent->GetUniqueId().Get();
-  }
+  AXTreeSourceViews::SerializeNode(node, out_data);
 
   if (out_data->role == ax::mojom::Role::kWebView) {
     // TODO(rmrossi) : Figure out whether this will ever be required
@@ -147,18 +58,3 @@
   }
 }
 
-std::string AXTreeSourceAura::ToString(AXAuraObjWrapper* root,
-                                       std::string prefix) {
-  ui::AXNodeData data;
-  root->Serialize(&data);
-  std::string output = prefix + data.ToString() + '\n';
-
-  std::vector<AXAuraObjWrapper*> children;
-  root->GetChildren(&children);
-
-  prefix += prefix[0];
-  for (size_t i = 0; i < children.size(); ++i)
-    output += ToString(children[i], prefix);
-
-  return output;
-}
diff --git a/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.h b/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.h
index b170f1c8..be51f12 100644
--- a/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.h
+++ b/chromecast/browser/ui/aura/accessibility/ax_tree_source_aura.h
@@ -5,60 +5,28 @@
 #ifndef CHROMECAST_BROWSER_UI_AURA_ACCESSIBILITY_AX_TREE_SOURCE_AURA_H_
 #define CHROMECAST_BROWSER_UI_AURA_ACCESSIBILITY_AX_TREE_SOURCE_AURA_H_
 
-#include <stdint.h>
-
-#include <map>
 #include <memory>
-#include <string>
-#include <vector>
 
 #include "base/macros.h"
-#include "ui/accessibility/ax_tree_data.h"
-#include "ui/accessibility/ax_tree_source.h"
 #include "ui/views/accessibility/ax_root_obj_wrapper.h"
-
-namespace ui {
-struct AXActionData;
-}  // namespace ui
-
-namespace views {
-class AXAuraObjWrapper;
-}  // namespace views
+#include "ui/views/accessibility/ax_tree_source_views.h"
 
 // This class exposes the views hierarchy as an accessibility tree permitting
 // use with other accessibility classes.
-class AXTreeSourceAura : public ui::AXTreeSource<views::AXAuraObjWrapper*,
-                                                 ui::AXNodeData,
-                                                 ui::AXTreeData> {
+class AXTreeSourceAura : public views::AXTreeSourceViews {
  public:
   AXTreeSourceAura();
   ~AXTreeSourceAura() override;
 
-  // Invoke actions on an Aura view.
-  bool HandleAccessibleAction(const ui::AXActionData& action);
-
-  // AXTreeSource implementation.
+  // AXTreeSource:
   bool GetTreeData(ui::AXTreeData* data) const override;
   views::AXAuraObjWrapper* GetRoot() const override;
-  views::AXAuraObjWrapper* GetFromId(int32_t id) const override;
-  int32_t GetId(views::AXAuraObjWrapper* node) const override;
-  void GetChildren(
-      views::AXAuraObjWrapper* node,
-      std::vector<views::AXAuraObjWrapper*>* out_children) const override;
-  views::AXAuraObjWrapper* GetParent(
-      views::AXAuraObjWrapper* node) const override;
-  bool IsValid(views::AXAuraObjWrapper* node) const override;
-  bool IsEqual(views::AXAuraObjWrapper* node1,
-               views::AXAuraObjWrapper* node2) const override;
-  views::AXAuraObjWrapper* GetNull() const override;
   void SerializeNode(views::AXAuraObjWrapper* node,
                      ui::AXNodeData* out_data) const override;
 
-  // Useful for debugging.
-  std::string ToString(views::AXAuraObjWrapper* root, std::string prefix);
-
  private:
-  std::unique_ptr<AXRootObjWrapper> root_;
+  // A root object representing the entire desktop.
+  std::unique_ptr<AXRootObjWrapper> desktop_root_;
 
   DISALLOW_COPY_AND_ASSIGN(AXTreeSourceAura);
 };
diff --git a/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.cc b/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.cc
index b9034cfa..e42bd8fa 100644
--- a/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.cc
+++ b/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.cc
@@ -28,9 +28,9 @@
 // MixerOutputStreamFuchsia::Write().
 constexpr int kMaxOutputBufferSizeFrames = 4096;
 
-// Current AudioOut implementation allows only one buffer with id=0.
+// Current AudioRenderer implementation allows only one buffer with id=0.
 // TODO(sergeyu): Replace with an incrementing buffer id once AddPayloadBuffer()
-// and RemovePayloadBuffer() are implemented properly in AudioOut.
+// and RemovePayloadBuffer() are implemented properly in AudioRenderer.
 const uint32_t kBufferId = 0;
 
 // static
@@ -42,7 +42,7 @@
 MixerOutputStreamFuchsia::~MixerOutputStreamFuchsia() = default;
 
 bool MixerOutputStreamFuchsia::Start(int requested_sample_rate, int channels) {
-  DCHECK(!audio_out_);
+  DCHECK(!audio_renderer_);
   DCHECK(reference_time_.is_null());
 
   sample_rate_ = requested_sample_rate;
@@ -50,12 +50,12 @@
   target_packet_size_ = ::media::AudioTimestampHelper::TimeToFrames(
       kTargetWritePeriod, sample_rate_);
 
-  // Connect |audio_out_|.
+  // Connect |audio_renderer_|.
   fuchsia::media::AudioPtr audio_server =
       base::fuchsia::ComponentContext::GetDefault()
           ->ConnectToService<fuchsia::media::Audio>();
-  audio_server->CreateAudioOut(audio_out_.NewRequest());
-  audio_out_.set_error_handler(
+  audio_server->CreateAudioRenderer(audio_renderer_.NewRequest());
+  audio_renderer_.set_error_handler(
       fit::bind_member(this, &MixerOutputStreamFuchsia::OnRendererError));
 
   // Configure the renderer.
@@ -63,13 +63,13 @@
   format.sample_format = fuchsia::media::AudioSampleFormat::FLOAT;
   format.channels = channels_;
   format.frames_per_second = sample_rate_;
-  audio_out_->SetPcmStreamType(std::move(format));
+  audio_renderer_->SetPcmStreamType(std::move(format));
 
   // Use number of samples to specify media position.
-  audio_out_->SetPtsUnits(sample_rate_, 1);
+  audio_renderer_->SetPtsUnits(sample_rate_, 1);
 
-  audio_out_->EnableMinLeadTimeEvents(true);
-  audio_out_.events().OnMinLeadTimeChanged =
+  audio_renderer_->EnableMinLeadTimeEvents(true);
+  audio_renderer_.events().OnMinLeadTimeChanged =
       fit::bind_member(this, &MixerOutputStreamFuchsia::OnMinLeadTimeChanged);
 
   return true;
@@ -98,7 +98,7 @@
 bool MixerOutputStreamFuchsia::Write(const float* data,
                                      int data_size,
                                      bool* out_playback_interrupted) {
-  if (!audio_out_)
+  if (!audio_renderer_)
     return false;
 
   DCHECK_EQ(data_size % channels_, 0);
@@ -136,7 +136,7 @@
   packet.payload_offset = payload_buffer_pos_;
   packet.payload_size = packet_size;
   packet.flags = 0;
-  audio_out_->SendPacketNoReply(std::move(packet));
+  audio_renderer_->SendPacketNoReply(std::move(packet));
 
   // Update stream position.
   int frames = data_size / channels_;
@@ -145,8 +145,8 @@
 
   if (reference_time_.is_null()) {
     reference_time_ = now + min_lead_time_;
-    audio_out_->PlayNoReply(reference_time_.ToZxTime(),
-                            stream_position_samples_ - frames);
+    audio_renderer_->PlayNoReply(reference_time_.ToZxTime(),
+                                 stream_position_samples_ - frames);
   } else {
     // Block the thread to limit amount of buffered data. Currently
     // MixerOutputStreamAlsa uses blocking Write() and StreamMixer relies on
@@ -168,7 +168,7 @@
 
 void MixerOutputStreamFuchsia::Stop() {
   reference_time_ = base::TimeTicks();
-  audio_out_.Unbind();
+  audio_renderer_.Unbind();
 }
 
 size_t MixerOutputStreamFuchsia::GetMinBufferSize() {
@@ -190,7 +190,7 @@
   }
 
   payload_buffer_pos_ = 0;
-  audio_out_->AddPayloadBuffer(
+  audio_renderer_->AddPayloadBuffer(
       kBufferId, zx::vmo(payload_buffer_.handle().Duplicate().GetHandle()));
 
   return true;
diff --git a/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h b/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h
index b82e1aa..fe65894d 100644
--- a/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h
+++ b/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h
@@ -37,7 +37,7 @@
 
   base::TimeTicks GetCurrentStreamTime();
 
-  // Event handlers for |audio_out_|.
+  // Event handlers for |audio_renderer_|.
   void OnRendererError();
   void OnMinLeadTimeChanged(int64_t min_lead_time);
 
@@ -48,7 +48,7 @@
   int target_packet_size_ = 0;
 
   // Audio renderer connection.
-  fuchsia::media::AudioOutPtr audio_out_;
+  fuchsia::media::AudioRendererPtr audio_renderer_;
 
   base::SharedMemory payload_buffer_;
   size_t payload_buffer_pos_ = 0;
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 8660ec4..b3dd06bd 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -437,11 +437,6 @@
 // tests can change how it's brought up. This flag disables that.
 const char kForceLoginManagerInTests[] = "force-login-manager-in-tests";
 
-// Recommend-apps screen is only shown to first-time Chromebook users. This flag
-// skips the check so that testers can reuse the same accounts.
-const char kForceShowRecommendAppsScreenForTest[] =
-    "force-show-recommend-apps-screen-for-test";
-
 // Force system compositor mode when set.
 const char kForceSystemCompositorMode[] = "force-system-compositor-mode";
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 6f51a85..8fb8236 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -124,7 +124,6 @@
 CHROMEOS_EXPORT extern const char kForceFirstRunUI[];
 CHROMEOS_EXPORT extern const char kForceHappinessTrackingSystem[];
 CHROMEOS_EXPORT extern const char kForceLoginManagerInTests[];
-CHROMEOS_EXPORT extern const char kForceShowRecommendAppsScreenForTest[];
 CHROMEOS_EXPORT extern const char kForceSystemCompositorMode[];
 CHROMEOS_EXPORT extern const char kGoldenScreenshotsDir[];
 CHROMEOS_EXPORT extern const char kGuestSession[];
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h
index e301abb..fc11533e 100644
--- a/components/autofill/core/browser/personal_data_manager.h
+++ b/components/autofill/core/browser/personal_data_manager.h
@@ -148,7 +148,7 @@
   // Returns the profile with the specified |guid|, or nullptr if there is no
   // profile with the specified |guid|. Both web and auxiliary profiles may
   // be returned.
-  AutofillProfile* GetProfileByGUID(const std::string& guid);
+  virtual AutofillProfile* GetProfileByGUID(const std::string& guid);
 
   // Returns the profile with the specified |guid| from the given |profiles|, or
   // nullptr if there is no profile with the specified |guid|.
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index bd52c55d..5533593 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -13,8 +13,13 @@
 class GURL;
 
 namespace autofill {
-class AutofillProfile;
-}
+class CreditCard;
+class PersonalDataManager;
+}  // namespace autofill
+
+namespace content {
+class WebContents;
+}  // namespace content
 
 namespace autofill_assistant {
 class ClientMemory;
@@ -54,9 +59,10 @@
   virtual void ChooseCard(
       base::OnceCallback<void(const std::string&)> callback) = 0;
 
-  // Fill the card form given by |selectors| with the given card |guid| in
-  // personal data manager.
-  virtual void FillCardForm(const std::string& guid,
+  // Fill the card form given by |selectors| with the given |card| and its
+  // |cvc|. Return result asynchronously through |callback|.
+  virtual void FillCardForm(std::unique_ptr<autofill::CreditCard> card,
+                            const base::string16& cvc,
                             const std::vector<std::string>& selectors,
                             base::OnceCallback<void(bool)> callback) = 0;
 
@@ -82,10 +88,6 @@
                              const std::string& value,
                              base::OnceCallback<void(bool)> callback) = 0;
 
-  // Get the AutofillProfile with ID |guid|, or nullptr if it doesn't exist.
-  virtual const autofill::AutofillProfile* GetAutofillProfile(
-      const std::string& guid) = 0;
-
   // Given an element |selectors| on the page as the root element, build a node
   // tree using the output parameter |node_tree_out| as a starting node.
   virtual void BuildNodeTree(const std::vector<std::string>& selectors,
@@ -106,6 +108,12 @@
   // Return the current ClientMemory.
   virtual ClientMemory* GetClientMemory() = 0;
 
+  // Get current personal data manager.
+  virtual autofill::PersonalDataManager* GetPersonalDataManager() = 0;
+
+  // Get associated web contents.
+  virtual content::WebContents* GetWebContents() = 0;
+
  protected:
   ActionDelegate() = default;
 };
diff --git a/components/autofill_assistant/browser/actions/autofill_action.cc b/components/autofill_assistant/browser/actions/autofill_action.cc
index e46ed1e..e1af5799 100644
--- a/components/autofill_assistant/browser/actions/autofill_action.cc
+++ b/components/autofill_assistant/browser/actions/autofill_action.cc
@@ -10,13 +10,83 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/autofill/content/browser/content_autofill_driver.h"
+#include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/payments/full_card_request.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill_assistant/browser/actions/action_delegate.h"
 #include "components/autofill_assistant/browser/client_memory.h"
+#include "content/public/browser/web_contents.h"
 
 namespace autofill_assistant {
 
+namespace {
+// Self-deleting requester of full card details, including full PAN and the CVC
+// number.
+class SelfDeleteFullCardRequester
+    : public autofill::payments::FullCardRequest::ResultDelegate {
+ public:
+  SelfDeleteFullCardRequester() : weak_ptr_factory_(this) {}
+
+  using GetFullCardCallback =
+      base::OnceCallback<void(std::unique_ptr<autofill::CreditCard>,
+                              const base::string16& cvc)>;
+  void GetFullCard(content::WebContents* web_contents,
+                   autofill::CreditCard* card,
+                   GetFullCardCallback callback) {
+    DCHECK(card);
+    callback_ = std::move(callback);
+
+    autofill::ContentAutofillDriverFactory* factory =
+        autofill::ContentAutofillDriverFactory::FromWebContents(web_contents);
+    if (!factory) {
+      OnFullCardRequestFailed();
+      return;
+    }
+
+    autofill::ContentAutofillDriver* driver =
+        factory->DriverForFrame(web_contents->GetMainFrame());
+    if (!driver) {
+      OnFullCardRequestFailed();
+      return;
+    }
+
+    driver->autofill_manager()->GetOrCreateFullCardRequest()->GetFullCard(
+        *card, autofill::AutofillClient::UNMASK_FOR_AUTOFILL,
+        weak_ptr_factory_.GetWeakPtr(),
+        driver->autofill_manager()->GetAsFullCardRequestUIDelegate());
+  }
+
+ private:
+  ~SelfDeleteFullCardRequester() override {}
+
+  // payments::FullCardRequest::ResultDelegate:
+  void OnFullCardRequestSucceeded(
+      const autofill::payments::FullCardRequest& /* full_card_request */,
+      const autofill::CreditCard& card,
+      const base::string16& cvc) override {
+    std::move(callback_).Run(std::make_unique<autofill::CreditCard>(card), cvc);
+    delete this;
+  }
+
+  // payments::FullCardRequest::ResultDelegate:
+  void OnFullCardRequestFailed() override {
+    // Failed might because of cancel, so return nullptr to notice caller.
+    std::move(callback_).Run(nullptr, base::string16());
+    delete this;
+  }
+
+  GetFullCardCallback callback_;
+
+  base::WeakPtrFactory<SelfDeleteFullCardRequester> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(SelfDeleteFullCardRequester);
+};
+
+}  // namespace
+
 AutofillAction::AutofillAction(const ActionProto& proto)
     : Action(proto), pending_set_field_value_(0), weak_ptr_factory_(this) {
   if (proto.has_use_address()) {
@@ -129,10 +199,15 @@
                                       ActionDelegate* delegate) {
   DCHECK(!selectors_.empty());
   if (is_autofill_card_) {
-    delegate->FillCardForm(
-        guid, selectors_,
-        base::BindOnce(&AutofillAction::OnFormFilled,
-                       weak_ptr_factory_.GetWeakPtr(), guid, delegate));
+    autofill::CreditCard* card =
+        delegate->GetPersonalDataManager()->GetCreditCardByGUID(guid);
+    DCHECK(card);
+    // TODO(crbug.com/806868): Consider refactoring SelfDeleteFullCardRequester
+    // so as to unit test it.
+    (new SelfDeleteFullCardRequester())
+        ->GetFullCard(delegate->GetWebContents(), card,
+                      base::BindOnce(&AutofillAction::OnGetFullCard,
+                                     weak_ptr_factory_.GetWeakPtr(), delegate));
     return;
   }
 
@@ -142,6 +217,24 @@
                      weak_ptr_factory_.GetWeakPtr(), guid, delegate));
 }
 
+void AutofillAction::OnGetFullCard(ActionDelegate* delegate,
+                                   std::unique_ptr<autofill::CreditCard> card,
+                                   const base::string16& cvc) {
+  if (!card) {
+    // TODO(crbug.com/806868): The failure might because of cancel, then ask to
+    // choose a card again.
+    UpdateProcessedAction(false);
+    std::move(process_action_callback_).Run(std::move(processed_action_proto_));
+    return;
+  }
+
+  std::string guid = card->guid();
+  delegate->FillCardForm(
+      std::move(card), cvc, selectors_,
+      base::BindOnce(&AutofillAction::OnFormFilled,
+                     weak_ptr_factory_.GetWeakPtr(), guid, delegate));
+}
+
 void AutofillAction::OnFormFilled(const std::string& guid,
                                   ActionDelegate* delegate,
                                   bool successful) {
@@ -208,7 +301,8 @@
     }
   }
 
-  const autofill::AutofillProfile* profile = delegate->GetAutofillProfile(guid);
+  const autofill::AutofillProfile* profile =
+      delegate->GetPersonalDataManager()->GetProfileByGUID(guid);
   DCHECK(profile);
 
   // We process all fields with an empty value in order to perform the fallback
diff --git a/components/autofill_assistant/browser/actions/autofill_action.h b/components/autofill_assistant/browser/actions/autofill_action.h
index 8fd8658..4107d52 100644
--- a/components/autofill_assistant/browser/actions/autofill_action.h
+++ b/components/autofill_assistant/browser/actions/autofill_action.h
@@ -16,6 +16,7 @@
 
 namespace autofill {
 class AutofillProfile;
+class CreditCard;
 }
 
 namespace autofill_assistant {
@@ -40,6 +41,11 @@
   // or not through |callback|.
   void FillFormWithData(const std::string& guid, ActionDelegate* delegate);
 
+  // Called after getting full credit card with its cvc.
+  void OnGetFullCard(ActionDelegate* delegate,
+                     std::unique_ptr<autofill::CreditCard> card,
+                     const base::string16& cvc);
+
   // Called when the form has been filled.
   void OnFormFilled(const std::string& guid,
                     ActionDelegate* delegate,
diff --git a/components/autofill_assistant/browser/actions/autofill_action_unittest.cc b/components/autofill_assistant/browser/actions/autofill_action_unittest.cc
index a68986a..722af2c 100644
--- a/components/autofill_assistant/browser/actions/autofill_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/autofill_action_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/guid.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/autofill_assistant/browser/actions/mock_action_delegate.h"
 #include "components/autofill_assistant/browser/mock_client_memory.h"
 #include "components/autofill_assistant/browser/mock_run_once_callback.h"
@@ -23,6 +24,41 @@
 using ::testing::Return;
 using ::testing::StrNe;
 
+class MockPersonalDataManager : public autofill::PersonalDataManager {
+ public:
+  MockPersonalDataManager() : PersonalDataManager("en-US") {}
+  ~MockPersonalDataManager() override{};
+
+  // PersonalDataManager:
+  std::string SaveImportedProfile(
+      const autofill::AutofillProfile& profile) override {
+    std::vector<autofill::AutofillProfile> profiles;
+    std::string merged_guid =
+        MergeProfile(profile, &profiles_, "en-US", &profiles);
+    if (merged_guid == profile.guid())
+      profiles_.push_back(std::make_unique<autofill::AutofillProfile>(profile));
+    return merged_guid;
+  }
+
+  autofill::AutofillProfile* GetProfileByGUID(
+      const std::string& guid) override {
+    autofill::AutofillProfile* result = nullptr;
+    for (const auto& profile : profiles_) {
+      if (profile->guid() != guid)
+        continue;
+      result = profile.get();
+      break;
+    }
+
+    return result;
+  }
+
+ private:
+  std::vector<std::unique_ptr<autofill::AutofillProfile>> profiles_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockPersonalDataManager);
+};
+
 // A callback that expects to be called immediately.
 //
 // This relies on mocked methods calling their callbacks immediately (which is
@@ -59,12 +95,14 @@
                                       autofill::test::kEmptyOrigin);
     autofill::test::SetProfileInfo(&profile, kFirstName, "", kLastName, kEmail,
                                    "", "", "", "", "", "", "", "");
-    autofill_profile_ = std::move(profile);
+    autofill_profile_guid_ = profile.guid();
+    personal_data_manager_ = std::make_unique<MockPersonalDataManager>();
+    personal_data_manager_->SaveImportedProfile(profile);
 
     ON_CALL(mock_action_delegate_, GetClientMemory)
         .WillByDefault(Return(&mock_client_memory_));
-    ON_CALL(mock_action_delegate_, GetAutofillProfile(autofill_profile_.guid()))
-        .WillByDefault(Return(&autofill_profile_));
+    ON_CALL(mock_action_delegate_, GetPersonalDataManager)
+        .WillByDefault(Return(personal_data_manager_.get()));
   }
 
  protected:
@@ -102,7 +140,8 @@
 
   MockActionDelegate mock_action_delegate_;
   MockClientMemory mock_client_memory_;
-  autofill::AutofillProfile autofill_profile_;
+  std::string autofill_profile_guid_;
+  std::unique_ptr<autofill::PersonalDataManager> personal_data_manager_;
 };
 
 TEST_F(AutofillActionTest, FillManually) {
@@ -128,41 +167,6 @@
   EXPECT_FALSE(ProcessAction(action_proto));
 }
 
-TEST_F(AutofillActionTest, FillCardFormFails) {
-  InSequence seq;
-
-  ActionProto action_proto = CreateUseCardAction();
-
-  // Return a fake selected card.
-  EXPECT_CALL(mock_client_memory_, selected_card())
-      .WillOnce(Return(autofill_profile_.guid()));
-
-  // Autofill fails.
-  EXPECT_CALL(mock_action_delegate_,
-              OnFillCardForm(autofill_profile_.guid(), _, _))
-      .WillOnce(RunOnceCallback<2>(false));
-
-  EXPECT_FALSE(ProcessAction(action_proto));
-}
-
-TEST_F(AutofillActionTest, FillCardFormSucceeds) {
-  InSequence seq;
-
-  ActionProto action_proto = CreateUseCardAction();
-
-  // Return a fake selected card.
-  EXPECT_CALL(mock_client_memory_, selected_card())
-      .WillOnce(Return(autofill_profile_.guid()));
-
-  // Autofill succeeds.
-  EXPECT_CALL(
-      mock_action_delegate_,
-      OnFillCardForm(autofill_profile_.guid(), ElementsAre(kFakeSelector), _))
-      .WillOnce(RunOnceCallback<2>(true));
-
-  EXPECT_TRUE(ProcessAction(action_proto));
-}
-
 TEST_F(AutofillActionTest, ValidationSucceeds) {
   InSequence seq;
 
@@ -178,12 +182,12 @@
 
   // Return a fake selected address.
   EXPECT_CALL(mock_client_memory_, selected_address(kAddressName))
-      .WillOnce(Return(autofill_profile_.guid()));
+      .WillOnce(Return(autofill_profile_guid_));
 
   // Autofill succeeds.
-  EXPECT_CALL(mock_action_delegate_,
-              OnFillAddressForm(autofill_profile_.guid(),
-                                ElementsAre(kFakeSelector), _))
+  EXPECT_CALL(
+      mock_action_delegate_,
+      OnFillAddressForm(autofill_profile_guid_, ElementsAre(kFakeSelector), _))
       .WillOnce(RunOnceCallback<2>(true));
 
   // Validation succeeds.
@@ -213,12 +217,12 @@
 
   // Return a fake selected address.
   EXPECT_CALL(mock_client_memory_, selected_address(kAddressName))
-      .WillOnce(Return(autofill_profile_.guid()));
+      .WillOnce(Return(autofill_profile_guid_));
 
   // Autofill succeeds.
-  EXPECT_CALL(mock_action_delegate_,
-              OnFillAddressForm(autofill_profile_.guid(),
-                                ElementsAre(kFakeSelector), _))
+  EXPECT_CALL(
+      mock_action_delegate_,
+      OnFillAddressForm(autofill_profile_guid_, ElementsAre(kFakeSelector), _))
       .WillOnce(RunOnceCallback<2>(true));
 
   // Validation fails when getting FIRST_NAME.
@@ -254,12 +258,12 @@
 
   // Return a fake selected address.
   EXPECT_CALL(mock_client_memory_, selected_address(kAddressName))
-      .WillOnce(Return(autofill_profile_.guid()));
+      .WillOnce(Return(autofill_profile_guid_));
 
   // Autofill succeeds.
-  EXPECT_CALL(mock_action_delegate_,
-              OnFillAddressForm(autofill_profile_.guid(),
-                                ElementsAre(kFakeSelector), _))
+  EXPECT_CALL(
+      mock_action_delegate_,
+      OnFillAddressForm(autofill_profile_guid_, ElementsAre(kFakeSelector), _))
       .WillOnce(RunOnceCallback<2>(true));
 
   // Validation fails when getting FIRST_NAME.
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 2c6bee0..3d9edeea 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill_assistant/browser/actions/action_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -54,10 +55,11 @@
   MOCK_METHOD1(OnChooseCard,
                void(base::OnceCallback<void(const std::string&)>& callback));
 
-  void FillCardForm(const std::string& guid,
+  void FillCardForm(std::unique_ptr<autofill::CreditCard> card,
+                    const base::string16& cvc,
                     const std::vector<std::string>& selectors,
                     base::OnceCallback<void(bool)> callback) override {
-    OnFillCardForm(guid, selectors, callback);
+    OnFillCardForm(card->guid(), selectors, callback);
   }
 
   MOCK_METHOD3(OnFillCardForm,
@@ -92,8 +94,6 @@
                void(const std::vector<std::string>& selectors,
                     const std::string& value,
                     base::OnceCallback<void(bool)>& callback));
-  MOCK_METHOD1(GetAutofillProfile,
-               const autofill::AutofillProfile*(const std::string& guid));
   MOCK_METHOD3(BuildNodeTree,
                void(const std::vector<std::string>& selectors,
                     NodeProto* node_tree_out,
@@ -102,6 +102,8 @@
   MOCK_METHOD0(Shutdown, void());
   MOCK_METHOD0(Restart, void());
   MOCK_METHOD0(GetClientMemory, ClientMemory*());
+  MOCK_METHOD0(GetPersonalDataManager, autofill::PersonalDataManager*());
+  MOCK_METHOD0(GetWebContents, content::WebContents*());
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/client.h b/components/autofill_assistant/browser/client.h
index 6e1ab50e..4b95cfa 100644
--- a/components/autofill_assistant/browser/client.h
+++ b/components/autofill_assistant/browser/client.h
@@ -7,6 +7,10 @@
 
 #include <string>
 
+namespace autofill {
+class PersonalDataManager;
+}  // namespace autofill
+
 namespace autofill_assistant {
 class UiController;
 
@@ -19,6 +23,9 @@
   // Returns the API key to be used for requests to the backend.
   virtual std::string GetApiKey() = 0;
 
+  // Returns the current active personal data manager.
+  virtual autofill::PersonalDataManager* GetPersonalDataManager() = 0;
+
   // Returns the server URL to be used for requests to the backend.
   virtual std::string GetServerUrl() = 0;
 
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 1e554b0c..c032a0ba5 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -49,6 +49,14 @@
   return *parameters_;
 }
 
+autofill::PersonalDataManager* Controller::GetPersonalDataManager() {
+  return client_->GetPersonalDataManager();
+}
+
+content::WebContents* Controller::GetWebContents() {
+  return web_contents();
+}
+
 Controller::Controller(
     content::WebContents* web_contents,
     std::unique_ptr<Client> client,
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index f9b93c4..6a1d02d 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -48,6 +48,8 @@
   WebController* GetWebController() override;
   ClientMemory* GetClientMemory() override;
   const std::map<std::string, std::string>& GetParameters() override;
+  autofill::PersonalDataManager* GetPersonalDataManager() override;
+  content::WebContents* GetWebContents() override;
 
  private:
   friend ControllerTest;
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 77359f4f..dacf7fe 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -40,6 +40,9 @@
 
   // Implements Client
   std::string GetApiKey() override { return ""; }
+  autofill::PersonalDataManager* GetPersonalDataManager() override {
+    return nullptr;
+  }
   std::string GetServerUrl() override { return ""; }
   UiController* GetUiController() override { return ui_controller_.get(); }
 
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 9e3f510..37e0398 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -11,7 +11,7 @@
 #include "base/callback.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill_assistant/browser/protocol_utils.h"
 #include "components/autofill_assistant/browser/service.h"
 #include "components/autofill_assistant/browser/ui_controller.h"
@@ -70,10 +70,11 @@
   delegate_->GetUiController()->ChooseCard(std::move(callback));
 }
 
-void ScriptExecutor::FillCardForm(const std::string& guid,
+void ScriptExecutor::FillCardForm(std::unique_ptr<autofill::CreditCard> card,
+                                  const base::string16& cvc,
                                   const std::vector<std::string>& selectors,
                                   base::OnceCallback<void(bool)> callback) {
-  delegate_->GetWebController()->FillCardForm(guid, selectors,
+  delegate_->GetWebController()->FillCardForm(std::move(card), cvc, selectors,
                                               std::move(callback));
 }
 
@@ -102,12 +103,6 @@
                                                std::move(callback));
 }
 
-const autofill::AutofillProfile* ScriptExecutor::GetAutofillProfile(
-    const std::string& guid) {
-  // TODO(crbug.com/806868): Implement GetAutofillProfile.
-  return nullptr;
-}
-
 void ScriptExecutor::BuildNodeTree(const std::vector<std::string>& selectors,
                                    NodeProto* node_tree_out,
                                    base::OnceCallback<void(bool)> callback) {
@@ -131,6 +126,14 @@
   return delegate_->GetClientMemory();
 }
 
+autofill::PersonalDataManager* ScriptExecutor::GetPersonalDataManager() {
+  return delegate_->GetPersonalDataManager();
+}
+
+content::WebContents* ScriptExecutor::GetWebContents() {
+  return delegate_->GetWebContents();
+}
+
 void ScriptExecutor::OnGetActions(bool result, const std::string& response) {
   if (!result) {
     RunCallback(false);
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index 2bfb5cb5..14a966f5 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -61,7 +61,8 @@
                        base::OnceCallback<void(bool)> callback) override;
   void ChooseCard(
       base::OnceCallback<void(const std::string&)> callback) override;
-  void FillCardForm(const std::string& guid,
+  void FillCardForm(std::unique_ptr<autofill::CreditCard> card,
+                    const base::string16& cvc,
                     const std::vector<std::string>& selectors,
                     base::OnceCallback<void(bool)> callback) override;
   void SelectOption(const std::vector<std::string>& selectors,
@@ -75,8 +76,6 @@
   void SetFieldValue(const std::vector<std::string>& selectors,
                      const std::string& value,
                      base::OnceCallback<void(bool)> callback) override;
-  const autofill::AutofillProfile* GetAutofillProfile(
-      const std::string& guid) override;
   void BuildNodeTree(const std::vector<std::string>& selectors,
                      NodeProto* node_tree_out,
                      base::OnceCallback<void(bool)> callback) override;
@@ -84,6 +83,8 @@
   void Shutdown() override;
   void Restart() override;
   ClientMemory* GetClientMemory() override;
+  autofill::PersonalDataManager* GetPersonalDataManager() override;
+  content::WebContents* GetWebContents() override;
 
  private:
   void OnGetActions(bool result, const std::string& response);
diff --git a/components/autofill_assistant/browser/script_executor_delegate.h b/components/autofill_assistant/browser/script_executor_delegate.h
index 3c5c63270..e73e37d 100644
--- a/components/autofill_assistant/browser/script_executor_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_delegate.h
@@ -8,6 +8,14 @@
 #include <map>
 #include <string>
 
+namespace autofill {
+class PersonalDataManager;
+}  // namespace autofill
+
+namespace content {
+class WebContents;
+}  // namespace content
+
 namespace autofill_assistant {
 
 class Service;
@@ -27,6 +35,10 @@
 
   virtual const std::map<std::string, std::string>& GetParameters() = 0;
 
+  virtual autofill::PersonalDataManager* GetPersonalDataManager() = 0;
+
+  virtual content::WebContents* GetWebContents() = 0;
+
  protected:
   virtual ~ScriptExecutorDelegate() {}
 };
diff --git a/components/autofill_assistant/browser/script_executor_unittest.cc b/components/autofill_assistant/browser/script_executor_unittest.cc
index 8aa3cbb..fd008540 100644
--- a/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -59,6 +59,12 @@
     return parameters_;
   }
 
+  autofill::PersonalDataManager* GetPersonalDataManager() override {
+    return nullptr;
+  }
+
+  content::WebContents* GetWebContents() override { return nullptr; }
+
   std::string Serialize(const google::protobuf::MessageLite& message) {
     std::string output;
     message.SerializeToString(&output);
diff --git a/components/autofill_assistant/browser/script_tracker_unittest.cc b/components/autofill_assistant/browser/script_tracker_unittest.cc
index 9acb108..d3cbed5 100644
--- a/components/autofill_assistant/browser/script_tracker_unittest.cc
+++ b/components/autofill_assistant/browser/script_tracker_unittest.cc
@@ -61,6 +61,12 @@
     return parameters_;
   }
 
+  autofill::PersonalDataManager* GetPersonalDataManager() override {
+    return nullptr;
+  }
+
+  content::WebContents* GetWebContents() override { return nullptr; }
+
   // Overrides ScriptTracker::Listener
   void OnRunnableScriptsChanged(
       const std::vector<ScriptHandle>& runnable_scripts) override {
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index 3dfe1f9..1562ca6 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -10,6 +10,8 @@
 #include "base/logging.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/common/autofill_constants.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "content/public/browser/render_frame_host.h"
@@ -81,6 +83,10 @@
 
 WebController::~WebController() {}
 
+WebController::FillFormInputData::FillFormInputData() {}
+
+WebController::FillFormInputData::~FillFormInputData() {}
+
 const GURL& WebController::GetUrl() {
   return web_contents_->GetLastCommittedURL();
 }
@@ -450,14 +456,17 @@
 void WebController::FillAddressForm(const std::string& guid,
                                     const std::vector<std::string>& selectors,
                                     base::OnceCallback<void(bool)> callback) {
+  auto data_to_autofill = std::make_unique<FillFormInputData>();
+  data_to_autofill->autofill_data_guid = guid;
   FindElement(selectors,
               base::BindOnce(&WebController::OnFindElementForFillingForm,
-                             weak_ptr_factory_.GetWeakPtr(), guid, selectors,
+                             weak_ptr_factory_.GetWeakPtr(),
+                             std::move(data_to_autofill), selectors,
                              std::move(callback)));
 }
 
 void WebController::OnFindElementForFillingForm(
-    const std::string& autofill_data_guid,
+    std::unique_ptr<FillFormInputData> data_to_autofill,
     const std::vector<std::string>& selectors,
     base::OnceCallback<void(bool)> callback,
     std::unique_ptr<FindElementResult> element_result) {
@@ -469,13 +478,13 @@
 
   ClickObject(element_result->object_id,
               base::BindOnce(&WebController::OnClickObjectForFillingForm,
-                             weak_ptr_factory_.GetWeakPtr(), autofill_data_guid,
-                             selectors, std::move(callback),
-                             std::move(element_result)));
+                             weak_ptr_factory_.GetWeakPtr(),
+                             std::move(data_to_autofill), selectors,
+                             std::move(callback), std::move(element_result)));
 }
 
 void WebController::OnClickObjectForFillingForm(
-    const std::string& autofill_data_guid,
+    std::unique_ptr<FillFormInputData> data_to_autofill,
     const std::vector<std::string>& selectors,
     base::OnceCallback<void(bool)> callback,
     std::unique_ptr<FindElementResult> element_result,
@@ -496,13 +505,13 @@
   driver->GetAutofillAgent()->GetElementFormAndFieldData(
       element_selectors,
       base::BindOnce(&WebController::OnGetFormAndFieldDataForFillingForm,
-                     weak_ptr_factory_.GetWeakPtr(), autofill_data_guid,
-                     std::move(callback),
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(data_to_autofill), std::move(callback),
                      element_result->container_frame_host));
 }
 
 void WebController::OnGetFormAndFieldDataForFillingForm(
-    const std::string& autofill_data_guid,
+    std::unique_ptr<FillFormInputData> data_to_autofill,
     base::OnceCallback<void(bool)> callback,
     content::RenderFrameHost* container_frame_host,
     const autofill::FormData& form_data,
@@ -520,16 +529,31 @@
     OnResult(false, std::move(callback));
     return;
   }
-  driver->autofill_manager()->FillProfileForm(autofill_data_guid, form_data,
-                                              form_field);
+
+  if (data_to_autofill->card) {
+    driver->autofill_manager()->FillCreditCardForm(
+        autofill::kNoQueryId, form_data, form_field, *data_to_autofill->card,
+        data_to_autofill->cvc);
+  } else {
+    driver->autofill_manager()->FillProfileForm(
+        data_to_autofill->autofill_data_guid, form_data, form_field);
+  }
+
   OnResult(true, std::move(callback));
 }
 
-void WebController::FillCardForm(const std::string& guid,
+void WebController::FillCardForm(std::unique_ptr<autofill::CreditCard> card,
+                                 const base::string16& cvc,
                                  const std::vector<std::string>& selectors,
                                  base::OnceCallback<void(bool)> callback) {
-  // TODO(crbug.com/806868): Implement fill card form operation.
-  std::move(callback).Run(true);
+  auto data_to_autofill = std::make_unique<FillFormInputData>();
+  data_to_autofill->card = std::move(card);
+  data_to_autofill->cvc = cvc;
+  FindElement(selectors,
+              base::BindOnce(&WebController::OnFindElementForFillingForm,
+                             weak_ptr_factory_.GetWeakPtr(),
+                             std::move(data_to_autofill), selectors,
+                             std::move(callback)));
 }
 
 void WebController::SelectOption(const std::vector<std::string>& selectors,
diff --git a/components/autofill_assistant/browser/web_controller.h b/components/autofill_assistant/browser/web_controller.h
index 278798b3..c975563 100644
--- a/components/autofill_assistant/browser/web_controller.h
+++ b/components/autofill_assistant/browser/web_controller.h
@@ -17,6 +17,10 @@
 #include "components/autofill_assistant/browser/devtools/devtools/domains/types_runtime.h"
 #include "components/autofill_assistant/browser/devtools/devtools_client.h"
 
+namespace autofill {
+class CreditCard;
+}  // namespace autofill
+
 namespace content {
 class WebContents;
 class RenderFrameHost;
@@ -66,9 +70,10 @@
                                const std::vector<std::string>& selectors,
                                base::OnceCallback<void(bool)> callback);
 
-  // Fill the card form given by |selectors| with the given card |guid| in
-  // personal data manager.
-  virtual void FillCardForm(const std::string& guid,
+  // Fill the card form given by |selectors| with the given |card| and its
+  // |cvc|.
+  virtual void FillCardForm(std::unique_ptr<autofill::CreditCard> card,
+                            const base::string16& cvc,
                             const std::vector<std::string>& selectors,
                             base::OnceCallback<void(bool)> callback);
 
@@ -122,6 +127,18 @@
   using FindElementCallback =
       base::OnceCallback<void(std::unique_ptr<FindElementResult>)>;
 
+  struct FillFormInputData {
+    FillFormInputData();
+    ~FillFormInputData();
+
+    // Data for filling address form.
+    std::string autofill_data_guid;
+
+    // Data for filling card form.
+    std::unique_ptr<autofill::CreditCard> card;
+    base::string16 cvc;
+  };
+
   void OnFindElementForClick(base::OnceCallback<void(bool)> callback,
                              std::unique_ptr<FindElementResult> result);
   void ClickObject(const std::string& object_id,
@@ -178,18 +195,18 @@
   void OnResult(const std::string& result,
                 base::OnceCallback<void(const std::string&)> callback);
   void OnFindElementForFillingForm(
-      const std::string& autofill_data_guid,
+      std::unique_ptr<FillFormInputData> data_to_autofill,
       const std::vector<std::string>& selectors,
       base::OnceCallback<void(bool)> callback,
       std::unique_ptr<FindElementResult> element_result);
   void OnClickObjectForFillingForm(
-      const std::string& autofill_data_guid,
+      std::unique_ptr<FillFormInputData> data_to_autofill,
       const std::vector<std::string>& selectors,
       base::OnceCallback<void(bool)> callback,
       std::unique_ptr<FindElementResult> element_result,
       bool click_result);
   void OnGetFormAndFieldDataForFillingForm(
-      const std::string& autofill_data_guid,
+      std::unique_ptr<FillFormInputData> data_to_autofill,
       base::OnceCallback<void(bool)> callback,
       content::RenderFrameHost* container_frame_host,
       const autofill::FormData& form_data,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
index 17388f8..ace43150 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
@@ -96,6 +96,9 @@
         ->SetDataUsageReportingEnabled(true);
   }
 #endif  // defined(OS_ANDROID)
+
+  for (auto& observer : observers_)
+    observer.OnSettingsInitialized();
 }
 
 void DataReductionProxySettings::OnServiceInitialized() {
@@ -286,20 +289,20 @@
     const net::HttpRequestHeaders& headers) {
   DCHECK(thread_checker_.CalledOnValidThread());
   proxy_request_headers_ = headers;
-  for (auto& observer : proxy_request_headers_observers_)
+  for (auto& observer : observers_)
     observer.OnProxyRequestHeadersChanged(headers);
 }
 
-void DataReductionProxySettings::AddProxyRequestHeadersObserver(
-    ProxyRequestHeadersObserver* observer) {
+void DataReductionProxySettings::AddDataReductionProxySettingsObserver(
+    DataReductionProxySettingsObserver* observer) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  proxy_request_headers_observers_.AddObserver(observer);
+  observers_.AddObserver(observer);
 }
 
-void DataReductionProxySettings::RemoveProxyRequestHeadersObserver(
-    ProxyRequestHeadersObserver* observer) {
+void DataReductionProxySettings::RemoveDataReductionProxySettingsObserver(
+    DataReductionProxySettingsObserver* observer) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  proxy_request_headers_observers_.RemoveObserver(observer);
+  observers_.RemoveObserver(observer);
 }
 
 DataReductionProxyEventStore* DataReductionProxySettings::GetEventStore()
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
index 9c46e552..feafcc4 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
@@ -56,13 +56,18 @@
   DATA_REDUCTION_SETTINGS_ACTION_BOUNDARY,
 };
 
-// Classes may derive from |ProxyRequestHeadersObserver| and register as an
-// observer of |DataReductionProxySettings| to get notified when the proxy
-// request headers change.
-class ProxyRequestHeadersObserver {
+// Classes may derive from |DataReductionProxySettingsObserver| and register as
+// an observer of |DataReductionProxySettings| to get notified when the proxy
+// request headers change or when the DRPSettings class is initialized.
+class DataReductionProxySettingsObserver {
  public:
+  // Notifies when the proxy server request header change.
   virtual void OnProxyRequestHeadersChanged(
       const net::HttpRequestHeaders& headers) = 0;
+
+  // Notifies when |DataReductionProxySettings::InitDataReductionProxySettings|
+  // is finished.
+  virtual void OnSettingsInitialized() = 0;
 };
 
 // Central point for configuring the data reduction proxy.
@@ -152,11 +157,13 @@
 
   // Adds an observer that is notified every time the proxy request headers
   // change.
-  void AddProxyRequestHeadersObserver(ProxyRequestHeadersObserver* observer);
+  void AddDataReductionProxySettingsObserver(
+      DataReductionProxySettingsObserver* observer);
 
   // Removes an observer that is notified every time the proxy request headers
   // change.
-  void RemoveProxyRequestHeadersObserver(ProxyRequestHeadersObserver* observer);
+  void RemoveDataReductionProxySettingsObserver(
+      DataReductionProxySettingsObserver* observer);
 
   // Returns the event store being used. May be null if
   // InitDataReductionProxySettings has not been called.
@@ -298,9 +305,9 @@
   // Should not be null.
   base::Clock* clock_;
 
-  // Observers to notify when the proxy request headers change.
-  base::ObserverList<ProxyRequestHeadersObserver>::Unchecked
-      proxy_request_headers_observers_;
+  // Observers to notify when the proxy request headers change or |this| is
+  // initialized.
+  base::ObserverList<DataReductionProxySettingsObserver>::Unchecked observers_;
 
   // The headers to use for requests to the proxy server.
   net::HttpRequestHeaders proxy_request_headers_;
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index 31d127b..dd3e5d00 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -1141,11 +1141,12 @@
 
 HistoryCountResult HistoryBackend::GetHistoryCount(const Time& begin_time,
                                                    const Time& end_time) {
-  HistoryCountResult result;
-  result.count = 0;
-  result.success =
-      db_ && db_->GetHistoryCount(begin_time, end_time, &result.count);
-  return result;
+  int count = 0;
+  return {db_ && db_->GetHistoryCount(begin_time, end_time, &count), count};
+}
+
+HistoryCountResult HistoryBackend::CountUniqueHostsVisitedLastMonth() {
+  return {!!db_, db_ ? db_->CountUniqueHostsVisitedLastMonth() : 0};
 }
 
 // Keyword visits --------------------------------------------------------------
diff --git a/components/history/core/browser/history_backend.h b/components/history/core/browser/history_backend.h
index 6104491..3e9de09 100644
--- a/components/history/core/browser/history_backend.h
+++ b/components/history/core/browser/history_backend.h
@@ -288,6 +288,9 @@
   HistoryCountResult GetHistoryCount(const base::Time& begin_time,
                                      const base::Time& end_time);
 
+  // Returns the number of hosts visited in the last month.
+  HistoryCountResult CountUniqueHostsVisitedLastMonth();
+
   // Favicon -------------------------------------------------------------------
 
   void GetFavicon(
diff --git a/components/history/core/browser/history_database.cc b/components/history/core/browser/history_database.cc
index 272e32f..c393b4a 100644
--- a/components/history/core/browser/history_database.cc
+++ b/components/history/core/browser/history_database.cc
@@ -229,6 +229,29 @@
   }
 }
 
+int HistoryDatabase::CountUniqueHostsVisitedLastMonth() {
+  base::TimeTicks start_time = base::TimeTicks::Now();
+  // Collect all URLs visited within the last month.
+  base::Time one_month_ago = base::Time::Now() - base::TimeDelta::FromDays(30);
+
+  sql::Statement url_sql(
+      db_.GetUniqueStatement("SELECT url FROM urls "
+                             "WHERE last_visit_time > ? "
+                             "AND hidden = 0 "
+                             "AND visit_count > 0"));
+  url_sql.BindInt64(0, one_month_ago.ToInternalValue());
+
+  std::set<std::string> hosts;
+  while (url_sql.Step()) {
+    GURL url(url_sql.ColumnString(0));
+    hosts.insert(url.host());
+  }
+
+  UMA_HISTOGRAM_TIMES("History.DatabaseMonthlyHostCountTime",
+                      base::TimeTicks::Now() - start_time);
+  return hosts.size();
+}
+
 TopHostsList HistoryDatabase::TopHosts(size_t num_hosts) {
   base::Time one_month_ago =
       std::max(base::Time::Now() - base::TimeDelta::FromDays(30), base::Time());
diff --git a/components/history/core/browser/history_database.h b/components/history/core/browser/history_database.h
index 219b033..2c83c06 100644
--- a/components/history/core/browser/history_database.h
+++ b/components/history/core/browser/history_database.h
@@ -87,6 +87,9 @@
   // called once and only upon successful Init.
   void ComputeDatabaseMetrics(const base::FilePath& filename);
 
+  // Counts the number of unique Hosts visited in the last month.
+  int CountUniqueHostsVisitedLastMonth();
+
   // Computes the |num_hosts| most-visited hostnames in the past 30 days. See
   // history_service.h for details.
   TopHostsList TopHosts(size_t num_hosts);
diff --git a/components/history/core/browser/history_service.cc b/components/history/core/browser/history_service.cc
index 04a1b2b..4174e55 100644
--- a/components/history/core/browser/history_service.cc
+++ b/components/history/core/browser/history_service.cc
@@ -848,6 +848,19 @@
       callback);
 }
 
+void HistoryService::CountUniqueHostsVisitedLastMonth(
+    const GetHistoryCountCallback& callback,
+    base::CancelableTaskTracker* tracker) {
+  DCHECK(backend_task_runner_) << "History service being called after cleanup";
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  tracker->PostTaskAndReplyWithResult(
+      backend_task_runner_.get(), FROM_HERE,
+      base::BindRepeating(&HistoryBackend::CountUniqueHostsVisitedLastMonth,
+                          history_backend_),
+      callback);
+}
+
 // Downloads -------------------------------------------------------------------
 
 // Handle creation of a download by creating an entry in the history service's
diff --git a/components/history/core/browser/history_service.h b/components/history/core/browser/history_service.h
index 9d44a0867..2612a4d0 100644
--- a/components/history/core/browser/history_service.h
+++ b/components/history/core/browser/history_service.h
@@ -342,6 +342,10 @@
       const GetHistoryCountCallback& callback,
       base::CancelableTaskTracker* tracker);
 
+  // Returns, via a callback, the number of Hosts visited in the last month.
+  void CountUniqueHostsVisitedLastMonth(const GetHistoryCountCallback& callback,
+                                        base::CancelableTaskTracker* tracker);
+
   // Database management operations --------------------------------------------
 
   // Delete all the information related to a single url.
diff --git a/components/history/core/browser/history_service_unittest.cc b/components/history/core/browser/history_service_unittest.cc
index b5cfe96b6..6790a486 100644
--- a/components/history/core/browser/history_service_unittest.cc
+++ b/components/history/core/browser/history_service_unittest.cc
@@ -28,6 +28,8 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/history/core/browser/history_database_params.h"
@@ -878,4 +880,56 @@
   EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
 }
 
+// Helper to add a page with specified days back in the past.
+void AddPageInThePast(HistoryService* history,
+                      const std::string& url_spec,
+                      int days_back) {
+  const GURL url(url_spec);
+  base::Time time_in_the_past =
+      base::Time::Now() - base::TimeDelta::FromDays(days_back);
+  history->AddPage(url, time_in_the_past, nullptr, 0, GURL(),
+                   history::RedirectList(), ui::PAGE_TRANSITION_LINK,
+                   history::SOURCE_BROWSED, false);
+}
+
+// Helper to contain a callback and run loop logic.
+int GetMonthlyHostCountHelper(HistoryService* history,
+                              base::CancelableTaskTracker* tracker) {
+  base::RunLoop run_loop;
+  int count = 0;
+  history->CountUniqueHostsVisitedLastMonth(
+      base::BindLambdaForTesting([&](HistoryCountResult result) {
+        count = result.count;
+        run_loop.Quit();
+      }),
+      tracker);
+  run_loop.Run();
+  return count;
+}
+
+// Counts hosts visited in the last month.
+TEST_F(HistoryServiceTest, CountMonthlyVisitedHosts) {
+  base::HistogramTester histogram_tester;
+  HistoryService* history = history_service_.get();
+  ASSERT_TRUE(history);
+
+  AddPageInThePast(history, "http://www.google.com/", 0);
+  EXPECT_EQ(1, GetMonthlyHostCountHelper(history, &tracker_));
+
+  AddPageInThePast(history, "http://www.google.com/foo", 1);
+  AddPageInThePast(history, "https://www.google.com/foo", 5);
+  AddPageInThePast(history, "https://www.gmail.com/foo", 10);
+  // Expect 2 because only host part of URL counts.
+  EXPECT_EQ(2, GetMonthlyHostCountHelper(history, &tracker_));
+
+  AddPageInThePast(history, "https://www.gmail.com/foo", 31);
+  // Count should not change since URL added is older than a month.
+  EXPECT_EQ(2, GetMonthlyHostCountHelper(history, &tracker_));
+
+  AddPageInThePast(history, "https://www.yahoo.com/foo", 29);
+  EXPECT_EQ(3, GetMonthlyHostCountHelper(history, &tracker_));
+
+  // The time required to compute host count is reported on each computation.
+  histogram_tester.ExpectTotalCount("History.DatabaseMonthlyHostCountTime", 4);
+}
 }  // namespace history
diff --git a/components/history/core/browser/history_types.h b/components/history/core/browser/history_types.h
index e661601..c3c4b04 100644
--- a/components/history/core/browser/history_types.h
+++ b/components/history/core/browser/history_types.h
@@ -478,10 +478,11 @@
 // Statistics -----------------------------------------------------------------
 
 // HistoryCountResult encapsulates the result of a call to
-// HistoryBackend::GetHistoryCount.
+// HistoryBackend::GetHistoryCount or
+// HistoryBackend::CountUniqueHostsVisitedLastMonth.
 struct HistoryCountResult {
-  // Indicates whether the call to HistoryBackend::GetHistoryCount was
-  // successful or not. If false, then |count| is undefined.
+  // Indicates whether the call was successful or not. If false, then |count|
+  // is undefined.
   bool success = false;
   int count = 0;
 };
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp
index e327470..5bee972 100644
--- a/components/page_info_strings.grdp
+++ b/components/page_info_strings.grdp
@@ -362,8 +362,8 @@
     <message name="IDS_PAGE_INFO_PERMISSION_AUTOMATICALLY_BLOCKED" desc="The label used underneath a permission listed in the Page Info bubble if the permission was blocked by Chrome on behalf of the user.">
       Automatically blocked
     </message>
-    <message name="IDS_PAGE_INFO_PERMISSION_ADS_SUBTITLE" desc="The label used underneath the ads permission in the Page Info UI if the site is being considered for ad blocking. Used on both desktop and Android platforms" formatter_data="android_java">
-      Site tends to show intrusive ads
+    <message name="IDS_PAGE_INFO_PERMISSION_ADS_SUBTITLE" desc="A subtitle shown under the ‘Ads’ setting indicating that a site has, in the past, shown intrusive or misleading ads. Shown when user clicks/taps the lock/'Danger' icon in the address bar. Used on both desktop and Android platforms" formatter_data="android_java">
+      Site shows intrusive or misleading ads
     </message>
 
     <!-- Permission change infobar. -->
diff --git a/components/previews/core/previews_user_data.cc b/components/previews/core/previews_user_data.cc
index 0e06452..670b97f6 100644
--- a/components/previews/core/previews_user_data.cc
+++ b/components/previews/core/previews_user_data.cc
@@ -15,13 +15,12 @@
 
 PreviewsUserData::~PreviewsUserData() {}
 
+PreviewsUserData::PreviewsUserData(const PreviewsUserData& previews_user_data) =
+    default;
+
 std::unique_ptr<PreviewsUserData> PreviewsUserData::DeepCopy() const {
-  std::unique_ptr<PreviewsUserData> copy(new PreviewsUserData(page_id_));
-  copy->data_savings_inflation_percent_ = data_savings_inflation_percent_;
-  copy->cache_control_no_transform_directive_ =
-      cache_control_no_transform_directive_;
-  copy->SetCommittedPreviewsType(committed_previews_type_);
-  return copy;
+  // Raw new to avoid friending std::make_unique.
+  return base::WrapUnique(new PreviewsUserData(*this));
 }
 
 PreviewsUserData* PreviewsUserData::GetData(const net::URLRequest& request) {
diff --git a/components/previews/core/previews_user_data.h b/components/previews/core/previews_user_data.h
index df90bc1..baa9eb1 100644
--- a/components/previews/core/previews_user_data.h
+++ b/components/previews/core/previews_user_data.h
@@ -92,6 +92,8 @@
   bool offline_preview_used() { return offline_preview_used_; }
 
  private:
+  PreviewsUserData(const PreviewsUserData& previews_user_data);
+
   // A session unique ID related to this navigation.
   const uint64_t page_id_;
   // A previews data savings inflation percent for the navigation if not 0.
@@ -109,7 +111,7 @@
   // The committed previews type, if any.
   previews::PreviewsType committed_previews_type_ = PreviewsType::NONE;
 
-  DISALLOW_COPY_AND_ASSIGN(PreviewsUserData);
+  DISALLOW_ASSIGN(PreviewsUserData);
 };
 
 }  // namespace previews
diff --git a/components/previews/core/previews_user_data_unittest.cc b/components/previews/core/previews_user_data_unittest.cc
index 61d4c24..8714a342 100644
--- a/components/previews/core/previews_user_data_unittest.cc
+++ b/components/previews/core/previews_user_data_unittest.cc
@@ -59,10 +59,14 @@
   EXPECT_EQ(0, data->data_savings_inflation_percent());
   EXPECT_FALSE(data->cache_control_no_transform_directive());
   EXPECT_EQ(previews::PreviewsType::NONE, data->committed_previews_type());
+  EXPECT_FALSE(data->black_listed_for_lite_page());
+  EXPECT_FALSE(data->offline_preview_used());
 
   data->SetDataSavingsInflationPercent(123);
   data->SetCacheControlNoTransformDirective();
   data->SetCommittedPreviewsType(previews::PreviewsType::NOSCRIPT);
+  data->set_offline_preview_used(true);
+  data->set_black_listed_for_lite_page(true);
 
   std::unique_ptr<PreviewsUserData> deep_copy = data->DeepCopy();
   EXPECT_EQ(id, deep_copy->page_id());
@@ -70,6 +74,8 @@
   EXPECT_TRUE(deep_copy->cache_control_no_transform_directive());
   EXPECT_EQ(previews::PreviewsType::NOSCRIPT,
             deep_copy->committed_previews_type());
+  EXPECT_TRUE(data->black_listed_for_lite_page());
+  EXPECT_TRUE(data->offline_preview_used());
 }
 
 }  // namespace
diff --git a/components/signin/core/browser/account_reconcilor.cc b/components/signin/core/browser/account_reconcilor.cc
index cfeddd1..ce153368 100644
--- a/components/signin/core/browser/account_reconcilor.cc
+++ b/components/signin/core/browser/account_reconcilor.cc
@@ -14,6 +14,7 @@
 #include "base/bind_helpers.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -122,6 +123,18 @@
   reconcilor_->DecrementLockCount();
 }
 
+AccountReconcilor::ScopedSyncedDataDeletion::ScopedSyncedDataDeletion(
+    AccountReconcilor* reconcilor)
+    : reconcilor_(reconcilor) {
+  DCHECK(reconcilor_);
+  ++reconcilor_->synced_data_deletion_in_progress_count_;
+}
+
+AccountReconcilor::ScopedSyncedDataDeletion::~ScopedSyncedDataDeletion() {
+  DCHECK_GT(reconcilor_->synced_data_deletion_in_progress_count_, 0);
+  --reconcilor_->synced_data_deletion_in_progress_count_;
+}
+
 AccountReconcilor::AccountReconcilor(
     ProfileOAuth2TokenService* token_service,
     SigninManagerBase* signin_manager,
@@ -265,6 +278,11 @@
   return signin_metrics::ACCOUNT_RECONCILOR_RUNNING;
 }
 
+std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion>
+AccountReconcilor::GetScopedSyncDataDeletion() {
+  return base::WrapUnique(new ScopedSyncedDataDeletion(this));
+}
+
 void AccountReconcilor::AddObserver(Observer* observer) {
   observer_list_.AddObserver(observer);
 }
@@ -490,6 +508,26 @@
   }
 }
 
+void AccountReconcilor::OnGaiaCookieDeletedByUserAction() {
+  if (!delegate_->ShouldRevokeTokensOnCookieDeleted())
+    return;
+
+  const std::string& primary_account =
+      signin_manager_->GetAuthenticatedAccountId();
+  // Revoke secondary tokens.
+  RevokeAllSecondaryTokens(
+      token_service_, AccountReconcilorDelegate::RevokeTokenOption::kRevoke,
+      primary_account, /*account_consistency_enforced=*/true);
+  if (primary_account.empty())
+    return;
+  if (token_service_->RefreshTokenHasError(primary_account) ||
+      synced_data_deletion_in_progress_count_ == 0) {
+    // Invalidate the primary token, but do not revoke it.
+    token_service_->UpdateCredentials(
+        primary_account, OAuth2TokenServiceDelegate::kInvalidRefreshToken);
+  }
+}
+
 std::vector<std::string> AccountReconcilor::LoadValidAccountsFromTokenService()
     const {
   std::vector<std::string> chrome_accounts = token_service_->GetAccounts();
diff --git a/components/signin/core/browser/account_reconcilor.h b/components/signin/core/browser/account_reconcilor.h
index af197e7..6e8cd30 100644
--- a/components/signin/core/browser/account_reconcilor.h
+++ b/components/signin/core/browser/account_reconcilor.h
@@ -57,6 +57,19 @@
     DISALLOW_COPY_AND_ASSIGN(Lock);
   };
 
+  // Helper class to indicate that synced data is being deleted. The object
+  // must be destroyed when the data deletion is complete.
+  class ScopedSyncedDataDeletion {
+   public:
+    ~ScopedSyncedDataDeletion();
+
+   private:
+    friend class AccountReconcilor;
+    explicit ScopedSyncedDataDeletion(AccountReconcilor* reconcilor);
+    AccountReconcilor* reconcilor_;
+    DISALLOW_COPY_AND_ASSIGN(ScopedSyncedDataDeletion);
+  };
+
   class Observer {
    public:
     virtual ~Observer() {}
@@ -111,8 +124,12 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+  // ScopedSyncedDataDeletion can be created when synced data is being removed
+  // and destroyed when the deletion is complete. It prevents the Sync account
+  // from being invalidated during the deletion.
+  std::unique_ptr<ScopedSyncedDataDeletion> GetScopedSyncDataDeletion();
+
  private:
-  friend class Lock;
   friend class AccountReconcilorTest;
   friend class DiceBrowserTestBase;
   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorEndpointParamTest,
@@ -139,6 +156,7 @@
   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest,
                            MigrationClearSecondaryTokens);
   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, MigrationClearAllTokens);
+  FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, DiceDeleteCookie);
   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorEndpointParamTest,
                            TokensNotLoaded);
   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorEndpointParamTest,
@@ -249,6 +267,7 @@
         const std::vector<gaia::ListedAccount>& accounts,
         const std::vector<gaia::ListedAccount>& signed_out_accounts,
         const GoogleServiceAuthError& error) override;
+  void OnGaiaCookieDeletedByUserAction() override;
 
   // Overriden from OAuth2TokenService::Observer.
   void OnEndBatchChanges() override;
@@ -334,6 +353,10 @@
   std::unique_ptr<base::OneShotTimer> timer_;
   base::TimeDelta timeout_;
 
+  // Greater than 0 when synced data is being deleted, and it is important to
+  // not invalidate the primary token while this is happening.
+  int synced_data_deletion_in_progress_count_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(AccountReconcilor);
 };
 
diff --git a/components/signin/core/browser/account_reconcilor_delegate.cc b/components/signin/core/browser/account_reconcilor_delegate.cc
index 334667a..5542649 100644
--- a/components/signin/core/browser/account_reconcilor_delegate.cc
+++ b/components/signin/core/browser/account_reconcilor_delegate.cc
@@ -50,6 +50,10 @@
   return RevokeTokenOption::kDoNotRevoke;
 }
 
+bool AccountReconcilorDelegate::ShouldRevokeTokensOnCookieDeleted() {
+  return false;
+}
+
 base::TimeDelta AccountReconcilorDelegate::GetReconcileTimeout() const {
   return base::TimeDelta::Max();
 }
diff --git a/components/signin/core/browser/account_reconcilor_delegate.h b/components/signin/core/browser/account_reconcilor_delegate.h
index 1e4d994..cbec21d6 100644
--- a/components/signin/core/browser/account_reconcilor_delegate.h
+++ b/components/signin/core/browser/account_reconcilor_delegate.h
@@ -71,11 +71,18 @@
       const std::vector<gaia::ListedAccount>& gaia_accounts,
       std::vector<std::string>* accounts_to_send) const;
 
-  // Returns whether secondary accounts should be cleared at the beginning of
+  // Returns whether secondary accounts should be revoked at the beginning of
   // the reconcile.
   virtual RevokeTokenOption ShouldRevokeSecondaryTokensBeforeReconcile(
       const std::vector<gaia::ListedAccount>& gaia_accounts);
 
+  // Returns whether tokens should be revoked when the Gaia cookie has been
+  // explicitly deleted by the user.
+  // If this returns false, tokens will not be revoked. If this returns true,
+  // secondary tokens will be deleted ; and the primary token will be
+  // invalidated unless it has to be kept for critical Sync operations.
+  virtual bool ShouldRevokeTokensOnCookieDeleted();
+
   // Called when reconcile is finished.
   // |OnReconcileFinished| is always called at the end of reconciliation, even
   // when there is an error (except in cases where reconciliation times out
diff --git a/components/signin/core/browser/account_reconcilor_unittest.cc b/components/signin/core/browser/account_reconcilor_unittest.cc
index 91ec30c..d88aed1 100644
--- a/components/signin/core/browser/account_reconcilor_unittest.cc
+++ b/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -1298,6 +1298,68 @@
   EXPECT_TRUE(test_signin_client()->is_ready_for_dice_migration());
 }
 
+TEST_F(AccountReconcilorTest, DiceDeleteCookie) {
+  SetAccountConsistency(signin::AccountConsistencyMethod::kDice);
+
+  const std::string primary_account_id =
+      account_tracker()->SeedAccountInfo("12345", "user@gmail.com");
+  token_service()->UpdateCredentials(primary_account_id, "refresh_token");
+  signin_manager()->SignIn("12345", "user@gmail.com", "password");
+  const std::string secondary_account_id =
+      PickAccountIdForAccount("67890", "other@gmail.com");
+  token_service()->UpdateCredentials(secondary_account_id, "refresh_token");
+
+  ASSERT_TRUE(token_service()->RefreshTokenIsAvailable(primary_account_id));
+  ASSERT_FALSE(token_service()->RefreshTokenHasError(primary_account_id));
+  ASSERT_TRUE(token_service()->RefreshTokenIsAvailable(secondary_account_id));
+  ASSERT_FALSE(token_service()->RefreshTokenHasError(secondary_account_id));
+
+  AccountReconcilor* reconcilor = GetMockReconcilor();
+
+  // With scoped deletion, only secondary tokens are revoked.
+  {
+    std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion> deletion =
+        reconcilor->GetScopedSyncDataDeletion();
+    reconcilor->OnGaiaCookieDeletedByUserAction();
+    EXPECT_TRUE(token_service()->RefreshTokenIsAvailable(primary_account_id));
+    EXPECT_FALSE(token_service()->RefreshTokenHasError(primary_account_id));
+    EXPECT_FALSE(
+        token_service()->RefreshTokenIsAvailable(secondary_account_id));
+  }
+
+  token_service()->UpdateCredentials(secondary_account_id, "refresh_token");
+  reconcilor->OnGaiaCookieDeletedByUserAction();
+
+  // Without scoped deletion, the primary token is also invalidated.
+  EXPECT_TRUE(token_service()->RefreshTokenIsAvailable(primary_account_id));
+  EXPECT_TRUE(token_service()->RefreshTokenHasError(primary_account_id));
+  EXPECT_EQ(GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+                CREDENTIALS_REJECTED_BY_CLIENT,
+            token_service()
+                ->GetAuthError(primary_account_id)
+                .GetInvalidGaiaCredentialsReason());
+  EXPECT_FALSE(token_service()->RefreshTokenIsAvailable(secondary_account_id));
+
+  // If the primary account has an error, always revoke it.
+  token_service()->UpdateCredentials(primary_account_id, "refresh_token");
+  ASSERT_FALSE(token_service()->RefreshTokenHasError(primary_account_id));
+  token_service()->UpdateAuthErrorForTesting(
+      primary_account_id,
+      GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+          GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+              CREDENTIALS_REJECTED_BY_SERVER));
+  {
+    std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion> deletion =
+        reconcilor->GetScopedSyncDataDeletion();
+    reconcilor->OnGaiaCookieDeletedByUserAction();
+    EXPECT_EQ(GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+                  CREDENTIALS_REJECTED_BY_CLIENT,
+              token_service()
+                  ->GetAuthError(primary_account_id)
+                  .GetInvalidGaiaCredentialsReason());
+  }
+}
+
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 // clang-format off
@@ -2390,4 +2452,4 @@
   EXPECT_EQ(0, spy_delegate->num_reconcile_timeout_calls_);
   EXPECT_EQ(1, spy_delegate->num_reconcile_finished_calls_);
   EXPECT_FALSE(reconcilor->is_reconcile_started_);
-}
\ No newline at end of file
+}
diff --git a/components/signin/core/browser/dice_account_reconcilor_delegate.cc b/components/signin/core/browser/dice_account_reconcilor_delegate.cc
index 4191a21..7d606542 100644
--- a/components/signin/core/browser/dice_account_reconcilor_delegate.cc
+++ b/components/signin/core/browser/dice_account_reconcilor_delegate.cc
@@ -125,6 +125,10 @@
              : RevokeTokenOption::kDoNotRevoke;
 }
 
+bool DiceAccountReconcilorDelegate::ShouldRevokeTokensOnCookieDeleted() {
+  return account_consistency_ == AccountConsistencyMethod::kDice;
+}
+
 void DiceAccountReconcilorDelegate::OnReconcileFinished(
     const std::string& first_account,
     bool reconcile_is_noop) {
diff --git a/components/signin/core/browser/dice_account_reconcilor_delegate.h b/components/signin/core/browser/dice_account_reconcilor_delegate.h
index e6f628c0..a8e1559 100644
--- a/components/signin/core/browser/dice_account_reconcilor_delegate.h
+++ b/components/signin/core/browser/dice_account_reconcilor_delegate.h
@@ -36,6 +36,7 @@
       const std::vector<gaia::ListedAccount>& gaia_accounts) override;
   void OnReconcileFinished(const std::string& first_account,
                            bool reconcile_is_noop) override;
+  bool ShouldRevokeTokensOnCookieDeleted() override;
 
  private:
   SigninClient* signin_client_;
diff --git a/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc b/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc
index 2786ad1..366b8e4 100644
--- a/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc
+++ b/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc
@@ -105,4 +105,21 @@
   }
 }
 
+TEST(DiceAccountReconcilorDelegateTest, ShouldRevokeTokensOnCookieDeleted) {
+  sync_preferences::TestingPrefServiceSyncable pref_service;
+  TestSigninClient client(&pref_service);
+  {
+    // Dice is enabled, revoke tokens when Gaia cookie is deleted.
+    DiceAccountReconcilorDelegate delegate(&client,
+                                           AccountConsistencyMethod::kDice);
+    EXPECT_EQ(true, delegate.ShouldRevokeTokensOnCookieDeleted());
+  }
+  {
+    // Dice is not enabled, do not revoke tokens when Gaia cookie is deleted.
+    DiceAccountReconcilorDelegate delegate(
+        &client, AccountConsistencyMethod::kDiceMigration);
+    EXPECT_EQ(false, delegate.ShouldRevokeTokensOnCookieDeleted());
+  }
+}
+
 }  // namespace signin
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.cc b/components/signin/core/browser/gaia_cookie_manager_service.cc
index 134b601d..c2d2b53 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -26,6 +26,7 @@
 #include "google_apis/gaia/oauth2_token_service.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "net/base/load_flags.h"
+#include "net/cookies/cookie_change_dispatcher.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/resource_request.h"
@@ -625,6 +626,14 @@
   DCHECK_EQ(kGaiaCookieName, cookie.Name());
   DCHECK(cookie.IsDomainMatch(GaiaUrls::GetInstance()->google_url().host()));
   list_accounts_stale_ = true;
+
+  if (cause == network::mojom::CookieChangeCause::EXPLICIT) {
+    DCHECK(net::CookieChangeCauseIsDeletion(net::CookieChangeCause::EXPLICIT));
+    for (auto& observer : observer_list_) {
+      observer.OnGaiaCookieDeletedByUserAction();
+    }
+  }
+
   // Ignore changes to the cookie while requests are pending.  These changes
   // are caused by the service itself as it adds accounts.  A side effects is
   // that any changes to the gaia cookie outside of this class, while requests
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.h b/components/signin/core/browser/gaia_cookie_manager_service.h
index 6820afd..9f7339f 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.h
+++ b/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -128,6 +128,10 @@
         const std::vector<gaia::ListedAccount>& signed_out_accounts,
         const GoogleServiceAuthError& error) {}
 
+    // Called when the Gaia cookie has been deleted explicitly by a user action,
+    // e.g. from the settings or by an extension.
+    virtual void OnGaiaCookieDeletedByUserAction() {}
+
    protected:
     virtual ~Observer() {}
   };
diff --git a/components/signin/core/browser/signin_manager.cc b/components/signin/core/browser/signin_manager.cc
index 945a0d7..8b250a8 100644
--- a/components/signin/core/browser/signin_manager.cc
+++ b/components/signin/core/browser/signin_manager.cc
@@ -284,10 +284,17 @@
   std::string user = account_id.empty() ? std::string() :
       account_tracker_service()->GetAccountInfo(account_id).email;
   if ((!account_id.empty() && !IsAllowedUsername(user)) || !IsSigninAllowed()) {
-    // User is signed in, but the username is invalid - the administrator must
-    // have changed the policy since the last signin, so sign out the user.
-    SignOut(signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN,
-            signin_metrics::SignoutDelete::IGNORE_METRIC);
+    // User is signed in, but the username is invalid or signin is no longer
+    // allowed, so the user must be sign out.
+    //
+    // This may happen in the following cases:
+    //   a. The user has toggled off signin allowed in settings.
+    //   b. The administrator changed the policy since the last signin.
+    //
+    // Note: The token service has not yet loaded its credentials, so accounts
+    // cannot be revoked here.
+    SignOutAndKeepAllAccounts(signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN,
+                              signin_metrics::SignoutDelete::IGNORE_METRIC);
   }
 
   if (account_tracker_service()->GetMigrationState() ==
diff --git a/components/subresource_filter/core/browser/subresource_filter_constants.h b/components/subresource_filter/core/browser/subresource_filter_constants.h
index c99c719e..8a71877 100644
--- a/components/subresource_filter/core/browser/subresource_filter_constants.h
+++ b/components/subresource_filter/core/browser/subresource_filter_constants.h
@@ -50,22 +50,23 @@
 // Console message to be displayed on activation.
 constexpr char kActivationConsoleMessage[] =
     "Chrome is blocking ads on this site because this site tends to show ads "
-    "that interrupt, distract, or prevent user control. You should fix the "
-    "issues as soon as possible and submit your site for another review. Learn "
-    "more at https://www.chromestatus.com/feature/5738264052891648";
+    "that interrupt, distract, mislead, or prevent user control. You should "
+    "fix the issues as soon as possible and submit your site for another "
+    "review. Learn more at "
+    "https://www.chromestatus.com/feature/5738264052891648";
 
 constexpr char kActivationWarningConsoleMessage[] =
     "Chrome might start blocking ads on this site in the future because this "
-    "site tends to show ads that interrupt, distract, or prevent user control. "
-    "You should fix the issues as soon as possible and submit your site for "
-    "another review. Learn more more at "
+    "site tends to show ads that interrupt, distract, mislead, or prevent user "
+    "control. You should fix the issues as soon as possible and submit your "
+    "site for another review. Learn more more at "
     "https://www.chromestatus.com/feature/5738264052891648";
 
 // Console message to be displayed on disallowing subframe.
 constexpr char kDisallowSubframeConsoleMessageFormat[] =
     "Chrome blocked resource %s on this site because this site tends to show "
-    "ads that interrupt, distract, or prevent user control. Learn more at "
-    "https://www.chromestatus.com/feature/5738264052891648";
+    "ads that interrupt, distract, mislead, or prevent user control. Learn "
+    "more at https://www.chromestatus.com/feature/5738264052891648";
 
 constexpr char kLearnMoreLink[] =
     "https://support.google.com/chrome/?p=blocked_ads";
diff --git a/components/sync/engine_impl/conflict_resolver.cc b/components/sync/engine_impl/conflict_resolver.cc
index fabcbcc..a38f001 100644
--- a/components/sync/engine_impl/conflict_resolver.cc
+++ b/components/sync/engine_impl/conflict_resolver.cc
@@ -173,6 +173,9 @@
                << "for: " << entry;
       UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", OVERWRITE_SERVER,
                                 CONFLICT_RESOLUTION_SIZE);
+      // TODO(crbug.com/890746): It seems like local deletion can override a
+      // remote update, which goes against the usual spirit of undeletion-wins,
+      // and differs from the USS logic.
     } else {
       DVLOG(1) << "Resolving simple conflict, ignoring local changes for: "
                << entry;
diff --git a/components/viz/common/hit_test/hit_test_region_list.h b/components/viz/common/hit_test/hit_test_region_list.h
index 1ef8c4c..9c0d73c 100644
--- a/components/viz/common/hit_test/hit_test_region_list.h
+++ b/components/viz/common/hit_test/hit_test_region_list.h
@@ -14,6 +14,8 @@
 
 namespace viz {
 
+// New flags must be added to GetFlagNames in hit_test_query.cc in order to be
+// displayed in hit-test debug logging.
 enum HitTestRegionFlags : uint32_t {
   // Region maps to this surface (me).
   kHitTestMine = 0x01,
diff --git a/components/viz/host/hit_test/hit_test_query.cc b/components/viz/host/hit_test/hit_test_query.cc
index 892cbb5..3991ca79 100644
--- a/components/viz/host/hit_test/hit_test_query.cc
+++ b/components/viz/host/hit_test/hit_test_query.cc
@@ -4,6 +4,7 @@
 
 #include "components/viz/host/hit_test/hit_test_query.h"
 
+#include "base/containers/stack.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/timer/elapsed_timer.h"
 #include "components/viz/common/hit_test/hit_test_region_list.h"
@@ -29,6 +30,35 @@
          (static_cast<size_t>(child_count) < child_count_max);
 }
 
+const std::string GetFlagNames(uint32_t flag) {
+  std::string names = "";
+  uint32_t mask = 1;
+
+#define CASE_TYPE(t)                       \
+  case kHitTest##t:                        \
+    names += names.empty() ? #t : ", " #t; \
+    break;
+
+  while (flag) {
+    switch (flag & mask) {
+      CASE_TYPE(Mine);
+      CASE_TYPE(Ignore);
+      CASE_TYPE(ChildSurface);
+      CASE_TYPE(Ask);
+      CASE_TYPE(Mouse);
+      CASE_TYPE(Touch);
+      CASE_TYPE(NotActive);
+      case 0:
+        break;
+    }
+
+    flag &= ~mask;
+    mask <<= 1;
+  }
+#undef CASE_TYPE
+  return names;
+}
+
 }  // namespace
 
 HitTestQuery::HitTestQuery(base::RepeatingClosure bad_message_gpu_callback)
@@ -259,4 +289,47 @@
     bad_message_gpu_callback_.Run();
 }
 
+std::string HitTestQuery::PrintHitTestData() const {
+  std::ostringstream oss;
+  base::stack<uint32_t> parents;
+  std::string tabs = "";
+
+  for (uint32_t i = 0; i < hit_test_data_.size(); ++i) {
+    const AggregatedHitTestRegion& htr = hit_test_data_[i];
+
+    oss << tabs << "Index: " << i << '\n';
+    oss << tabs << "Children: " << htr.child_count << '\n';
+    oss << tabs << "Flags: " << GetFlagNames(htr.flags) << '\n';
+    oss << tabs << "Frame Sink Id: " << htr.frame_sink_id.ToString() << '\n';
+    oss << tabs << "Rect: " << htr.rect.ToString() << '\n';
+    oss << tabs << "Transform:" << '\n';
+
+    // gfx::Transform::ToString spans multiple lines, so we use an additional
+    // stringstream.
+    {
+      std::string s;
+      std::stringstream transform_ss;
+
+      transform_ss << htr.transform().ToString() << '\n';
+
+      while (getline(transform_ss, s)) {
+        oss << tabs << s << '\n';
+      }
+    }
+
+    tabs += "\t\t";
+    parents.push(i);
+
+    while (!parents.empty() &&
+           parents.top() + hit_test_data_[parents.top()].child_count <= i) {
+      tabs.pop_back();
+      tabs.pop_back();
+
+      parents.pop();
+    }
+  }
+
+  return oss.str();
+}
+
 }  // namespace viz
diff --git a/components/viz/host/hit_test/hit_test_query.h b/components/viz/host/hit_test/hit_test_query.h
index c5c43ce..cef04e9 100644
--- a/components/viz/host/hit_test/hit_test_query.h
+++ b/components/viz/host/hit_test/hit_test_query.h
@@ -101,6 +101,9 @@
   // data for |frame_sink_id|.
   bool ContainsActiveFrameSinkId(const FrameSinkId& frame_sink_id) const;
 
+  // Returns hit-test data, using indentation to visualize the tree structure.
+  std::string PrintHitTestData() const;
+
  private:
   friend class content::HitTestRegionObserver;
   // Helper function to find |target| for |location_in_parent| in the
diff --git a/components/viz/host/host_frame_sink_manager.cc b/components/viz/host/host_frame_sink_manager.cc
index 8efd26c..b0428e1 100644
--- a/components/viz/host/host_frame_sink_manager.cc
+++ b/components/viz/host/host_frame_sink_manager.cc
@@ -245,6 +245,23 @@
          base::ContainsValue(iter->second.children, child_frame_sink_id);
 }
 
+base::Optional<FrameSinkId> HostFrameSinkManager::FindRootFrameSinkId(
+    const FrameSinkId& start) const {
+  auto iter = frame_sink_data_map_.find(start);
+  if (iter == frame_sink_data_map_.end())
+    return base::nullopt;
+
+  if (iter->second.is_root)
+    return start;
+
+  for (const FrameSinkId& parent_id : iter->second.parents) {
+    base::Optional<FrameSinkId> root = FindRootFrameSinkId(parent_id);
+    if (root)
+      return root;
+  }
+  return base::nullopt;
+}
+
 void HostFrameSinkManager::DropTemporaryReference(const SurfaceId& surface_id) {
   frame_sink_manager_->DropTemporaryReference(surface_id);
 }
diff --git a/components/viz/host/host_frame_sink_manager.h b/components/viz/host/host_frame_sink_manager.h
index 71fc9e6..d49a698c 100644
--- a/components/viz/host/host_frame_sink_manager.h
+++ b/components/viz/host/host_frame_sink_manager.h
@@ -142,6 +142,10 @@
       const FrameSinkId& parent_frame_sink_id,
       const FrameSinkId& child_frame_sink_id) const;
 
+  // Returns the first ancestor of |start| (including |start|) that is a root.
+  base::Optional<FrameSinkId> FindRootFrameSinkId(
+      const FrameSinkId& start) const;
+
   void DropTemporaryReference(const SurfaceId& surface_id);
 
   // These two functions should only be used by WindowServer.
diff --git a/components/viz/host/host_frame_sink_manager_unittest.cc b/components/viz/host/host_frame_sink_manager_unittest.cc
index 0f91e106..48627f9f 100644
--- a/components/viz/host/host_frame_sink_manager_unittest.cc
+++ b/components/viz/host/host_frame_sink_manager_unittest.cc
@@ -547,6 +547,37 @@
   EXPECT_FALSE(DisplayHitTestQueryExists(kFrameSinkChild1));
 }
 
+TEST_F(HostFrameSinkManagerRemoteTest, FindRootFrameSinkId) {
+  FakeHostFrameSinkClient host_client;
+
+  EXPECT_FALSE(host().FindRootFrameSinkId(kFrameSinkParent1));
+  EXPECT_FALSE(host().FindRootFrameSinkId(kFrameSinkChild1));
+
+  // Register two FrameSinkIds, hierarchy between them and create a
+  // CompositorFrameSink for one.
+  host().RegisterFrameSinkId(kFrameSinkParent1, &host_client);
+  host().RegisterFrameSinkId(kFrameSinkChild1, &host_client);
+  host().RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1);
+
+  EXPECT_FALSE(host().FindRootFrameSinkId(kFrameSinkParent1));
+  EXPECT_FALSE(host().FindRootFrameSinkId(kFrameSinkChild1));
+
+  RootCompositorFrameSinkData root_data;
+  host().CreateRootCompositorFrameSink(
+      root_data.BuildParams(kFrameSinkParent1));
+
+  MockCompositorFrameSinkClient compositor_frame_sink_client;
+  mojom::CompositorFrameSinkPtr compositor_frame_sink;
+  host().CreateCompositorFrameSink(
+      kFrameSinkChild1, MakeRequest(&compositor_frame_sink),
+      compositor_frame_sink_client.BindInterfacePtr());
+
+  EXPECT_EQ(base::Optional<FrameSinkId>(kFrameSinkParent1),
+            host().FindRootFrameSinkId(kFrameSinkParent1));
+  EXPECT_EQ(base::Optional<FrameSinkId>(kFrameSinkParent1),
+            host().FindRootFrameSinkId(kFrameSinkChild1));
+}
+
 // Verify that HostFrameSinkManager can handle restarting after a GPU crash.
 TEST_F(HostFrameSinkManagerRemoteTest, RestartOnGpuCrash) {
   FakeHostFrameSinkClient host_client;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 17b01f1..c9814fd 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -627,6 +627,8 @@
     "devtools/devtools_network_transaction_factory.cc",
     "devtools/devtools_pipe_handler.cc",
     "devtools/devtools_pipe_handler.h",
+    "devtools/devtools_renderer_channel.cc",
+    "devtools/devtools_renderer_channel.h",
     "devtools/devtools_session.cc",
     "devtools/devtools_session.h",
     "devtools/devtools_stream_blob.cc",
@@ -1303,6 +1305,8 @@
     "renderer_host/frame_sink_provider_impl.h",
     "renderer_host/frame_token_message_queue.cc",
     "renderer_host/frame_token_message_queue.h",
+    "renderer_host/hit_test_debug_key_event_observer.cc",
+    "renderer_host/hit_test_debug_key_event_observer.h",
     "renderer_host/input/fling_controller.cc",
     "renderer_host/input/fling_controller.h",
     "renderer_host/input/fling_scheduler.cc",
diff --git a/content/browser/devtools/devtools_agent_host_impl.cc b/content/browser/devtools/devtools_agent_host_impl.cc
index ff4d73a..3d105841 100644
--- a/content/browser/devtools/devtools_agent_host_impl.cc
+++ b/content/browser/devtools/devtools_agent_host_impl.cc
@@ -115,7 +115,8 @@
   return result;
 }
 
-DevToolsAgentHostImpl::DevToolsAgentHostImpl(const std::string& id) : id_(id) {
+DevToolsAgentHostImpl::DevToolsAgentHostImpl(const std::string& id)
+    : id_(id), renderer_channel_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
@@ -179,15 +180,12 @@
 bool DevToolsAgentHostImpl::InnerAttachClient(DevToolsAgentHostClient* client,
                                               TargetRegistry* registry) {
   scoped_refptr<DevToolsAgentHostImpl> protect(this);
-  DevToolsSession* session = new DevToolsSession(this, client);
-  sessions_.insert(session);
-  session_by_client_[client].reset(session);
-  if (!AttachSession(session, registry)) {
-    sessions_.erase(session);
-    session_by_client_.erase(client);
+  auto session = std::make_unique<DevToolsSession>(this, client);
+  if (!AttachSession(session.get(), registry))
     return false;
-  }
-
+  renderer_channel_.AttachSession(session.get());
+  sessions_.insert(session.get());
+  session_by_client_[client] = std::move(session);
   if (sessions_.size() == 1)
     NotifyAttached();
   DevToolsManager* manager = DevToolsManager::GetInstance();
@@ -341,6 +339,8 @@
 
 void DevToolsAgentHostImpl::DetachSession(DevToolsSession* session) {}
 
+void DevToolsAgentHostImpl::UpdateRendererChannel(bool force) {}
+
 // static
 void DevToolsAgentHost::DetachAllClients() {
   if (!g_devtools_instances.IsCreated())
diff --git a/content/browser/devtools/devtools_agent_host_impl.h b/content/browser/devtools/devtools_agent_host_impl.h
index 76f1e31..957fcf1 100644
--- a/content/browser/devtools/devtools_agent_host_impl.h
+++ b/content/browser/devtools/devtools_agent_host_impl.h
@@ -14,6 +14,7 @@
 #include "base/containers/flat_set.h"
 #include "base/process/kill.h"
 #include "content/browser/devtools/devtools_io_context.h"
+#include "content/browser/devtools/devtools_renderer_channel.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/certificate_request_result_type.h"
 #include "content/public/browser/devtools_agent_host.h"
@@ -68,11 +69,11 @@
   virtual bool AttachSession(DevToolsSession* session,
                              TargetRegistry* registry);
   virtual void DetachSession(DevToolsSession* session);
-
   virtual bool DispatchProtocolMessage(
       DevToolsAgentHostClient* client,
       const std::string& message,
       std::unique_ptr<base::DictionaryValue> parsed_message);
+  virtual void UpdateRendererChannel(bool force);
 
   void NotifyCreated();
   void NotifyNavigated();
@@ -81,6 +82,7 @@
   void ForceDetachRestrictedSessions(
       const std::vector<DevToolsSession*>& restricted_sessions);
   DevToolsIOContext* GetIOContext() { return &io_context_; }
+  DevToolsRendererChannel* GetRendererChannel() { return &renderer_channel_; }
 
   base::flat_set<DevToolsSession*>& sessions() { return sessions_; }
 
@@ -88,6 +90,7 @@
   friend class DevToolsAgentHost;  // for static methods
   friend class DevToolsSession;
   friend class TargetRegistry;  // for subtarget management
+  friend class DevToolsRendererChannel;
 
   bool InnerAttachClient(DevToolsAgentHostClient* client,
                          TargetRegistry* registry);
@@ -106,6 +109,7 @@
   base::flat_map<DevToolsAgentHostClient*, std::unique_ptr<DevToolsSession>>
       session_by_client_;
   DevToolsIOContext io_context_;
+  DevToolsRendererChannel renderer_channel_;
   static int s_force_creation_count_;
 };
 
diff --git a/content/browser/devtools/devtools_renderer_channel.cc b/content/browser/devtools/devtools_renderer_channel.cc
new file mode 100644
index 0000000..0793d766
--- /dev/null
+++ b/content/browser/devtools/devtools_renderer_channel.cc
@@ -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.
+
+#include "content/browser/devtools/devtools_renderer_channel.h"
+
+#include "content/browser/devtools/devtools_agent_host_impl.h"
+#include "content/browser/devtools/devtools_session.h"
+#include "content/public/common/child_process_host.h"
+#include "ui/gfx/geometry/point.h"
+
+namespace content {
+
+DevToolsRendererChannel::DevToolsRendererChannel(DevToolsAgentHostImpl* owner)
+    : owner_(owner), process_id_(ChildProcessHost::kInvalidUniqueID) {}
+
+DevToolsRendererChannel::~DevToolsRendererChannel() = default;
+
+void DevToolsRendererChannel::SetRenderer(
+    blink::mojom::DevToolsAgentAssociatedPtr agent_ptr,
+    int process_id,
+    RenderFrameHostImpl* frame_host) {
+  agent_ptr_ = std::move(agent_ptr);
+  process_id_ = process_id;
+  frame_host_ = frame_host;
+  for (DevToolsSession* session : owner_->sessions())
+    session->AttachToAgent(agent_ptr_.get(), process_id_, frame_host_);
+}
+
+void DevToolsRendererChannel::AttachSession(DevToolsSession* session) {
+  if (!agent_ptr_)
+    owner_->UpdateRendererChannel(true /* force */);
+  session->AttachToAgent(agent_ptr_.get(), process_id_, frame_host_);
+}
+
+void DevToolsRendererChannel::InspectElement(const gfx::Point& point) {
+  if (!agent_ptr_)
+    owner_->UpdateRendererChannel(true /* force */);
+  // Previous call might update |agent_ptr_| via SetRenderer(),
+  // so we should check it again.
+  if (agent_ptr_)
+    agent_ptr_->InspectElement(point);
+}
+
+}  // namespace content
diff --git a/content/browser/devtools/devtools_renderer_channel.h b/content/browser/devtools/devtools_renderer_channel.h
new file mode 100644
index 0000000..ecfbf9f7f
--- /dev/null
+++ b/content/browser/devtools/devtools_renderer_channel.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 CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_RENDERER_CHANNEL_H_
+#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_RENDERER_CHANNEL_H_
+
+#include "base/macros.h"
+#include "content/common/content_export.h"
+#include "third_party/blink/public/web/devtools_agent.mojom.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace content {
+
+class DevToolsAgentHostImpl;
+class DevToolsSession;
+class RenderFrameHostImpl;
+
+// This class encapsulates a connection to blink::mojom::DevToolsAgent
+// in the renderer.
+// When the renderer changes, different DevToolsAgentHostImpl subclasses
+// retrieve a new blink::mojom::DevToolsAgent pointer, and this channel
+// starts using it for all existing and future sessions.
+class CONTENT_EXPORT DevToolsRendererChannel {
+ public:
+  explicit DevToolsRendererChannel(DevToolsAgentHostImpl* owner);
+  ~DevToolsRendererChannel();
+
+  void SetRenderer(blink::mojom::DevToolsAgentAssociatedPtr agent_ptr,
+                   int process_id,
+                   RenderFrameHostImpl* frame_host);
+  void AttachSession(DevToolsSession* session);
+  void InspectElement(const gfx::Point& point);
+
+ private:
+  DevToolsAgentHostImpl* owner_;
+  blink::mojom::DevToolsAgentAssociatedPtr agent_ptr_;
+  int process_id_;
+  RenderFrameHostImpl* frame_host_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(DevToolsRendererChannel);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_RENDERER_CHANNEL_H_
diff --git a/content/browser/devtools/devtools_session.cc b/content/browser/devtools/devtools_session.cc
index 73acd14..c306b8b 100644
--- a/content/browser/devtools/devtools_session.cc
+++ b/content/browser/devtools/devtools_session.cc
@@ -58,24 +58,28 @@
 void DevToolsSession::AddHandler(
     std::unique_ptr<protocol::DevToolsDomainHandler> handler) {
   handler->Wire(dispatcher_.get());
-  handler->InitRenderer(process_host_id_, host_);
   handlers_[handler->name()] = std::move(handler);
 }
 
-void DevToolsSession::SetRenderer(int process_host_id,
-                                  RenderFrameHostImpl* frame_host) {
-  process_host_id_ = process_host_id;
-  host_ = frame_host;
-  for (auto& pair : handlers_)
-    pair.second->UpdateRenderer(process_host_id_, host_);
-}
-
 void DevToolsSession::SetBrowserOnly(bool browser_only) {
   browser_only_ = browser_only;
 }
 
-void DevToolsSession::AttachToAgent(
-    const blink::mojom::DevToolsAgentAssociatedPtr& agent) {
+void DevToolsSession::AttachToAgent(blink::mojom::DevToolsAgent* agent,
+                                    int process_host_id,
+                                    RenderFrameHostImpl* frame_host) {
+  process_host_id_ = process_host_id;
+  host_ = frame_host;
+  for (auto& pair : handlers_)
+    pair.second->SetRenderer(process_host_id_, host_);
+
+  if (!agent) {
+    binding_.Close();
+    session_ptr_.reset();
+    io_session_ptr_.reset();
+    return;
+  }
+
   blink::mojom::DevToolsSessionHostAssociatedPtrInfo host_ptr_info;
   binding_.Bind(mojo::MakeRequest(&host_ptr_info));
   agent->AttachDevToolsSession(
diff --git a/content/browser/devtools/devtools_session.h b/content/browser/devtools/devtools_session.h
index 9eb95c6..43d3a71ab 100644
--- a/content/browser/devtools/devtools_session.h
+++ b/content/browser/devtools/devtools_session.h
@@ -37,10 +37,10 @@
   void SetBrowserOnly(bool browser_only);
 
   void AddHandler(std::unique_ptr<protocol::DevToolsDomainHandler> handler);
-  // TODO(dgozman): maybe combine this with AttachToAgent?
-  void SetRenderer(int process_host_id, RenderFrameHostImpl* frame_host);
 
-  void AttachToAgent(const blink::mojom::DevToolsAgentAssociatedPtr& agent);
+  void AttachToAgent(blink::mojom::DevToolsAgent* agent,
+                     int process_host_id,
+                     RenderFrameHostImpl* frame_host);
   void DispatchProtocolMessage(
       const std::string& message,
       std::unique_ptr<base::DictionaryValue> parsed_message);
diff --git a/content/browser/devtools/protocol/devtools_domain_handler.cc b/content/browser/devtools/protocol/devtools_domain_handler.cc
index 70099aca..5290245 100644
--- a/content/browser/devtools/protocol/devtools_domain_handler.cc
+++ b/content/browser/devtools/protocol/devtools_domain_handler.cc
@@ -17,16 +17,6 @@
 DevToolsDomainHandler::~DevToolsDomainHandler() {
 }
 
-void DevToolsDomainHandler::InitRenderer(int process_host_id,
-                                         RenderFrameHostImpl* frame_host) {
-  SetRenderer(process_host_id, frame_host);
-}
-
-void DevToolsDomainHandler::UpdateRenderer(int process_host_id,
-                                           RenderFrameHostImpl* frame_host) {
-  SetRenderer(process_host_id, frame_host);
-}
-
 void DevToolsDomainHandler::SetRenderer(int process_host_id,
                                         RenderFrameHostImpl* frame_host) {}
 
diff --git a/content/browser/devtools/protocol/devtools_domain_handler.h b/content/browser/devtools/protocol/devtools_domain_handler.h
index 84e4118..ea48934a 100644
--- a/content/browser/devtools/protocol/devtools_domain_handler.h
+++ b/content/browser/devtools/protocol/devtools_domain_handler.h
@@ -18,19 +18,13 @@
   explicit DevToolsDomainHandler(const std::string& name);
   virtual ~DevToolsDomainHandler();
 
-  virtual void InitRenderer(int process_host_id,
-                            RenderFrameHostImpl* frame_host);
-  virtual void UpdateRenderer(int process_host_id,
-                              RenderFrameHostImpl* frame_host);
+  virtual void SetRenderer(int process_host_id,
+                           RenderFrameHostImpl* frame_host);
   virtual void Wire(UberDispatcher* dispatcher);
   virtual Response Disable();
 
   const std::string& name() const { return name_; }
 
- protected:
-  virtual void SetRenderer(int process_host_id,
-                           RenderFrameHostImpl* frame_host);
-
  private:
   std::string name_;
 
diff --git a/content/browser/devtools/protocol/emulation_handler.cc b/content/browser/devtools/protocol/emulation_handler.cc
index 31c29c6..9a2cc5f 100644
--- a/content/browser/devtools/protocol/emulation_handler.cc
+++ b/content/browser/devtools/protocol/emulation_handler.cc
@@ -73,19 +73,15 @@
       host, Emulation::Metainfo::domainName);
 }
 
-void EmulationHandler::InitRenderer(int process_host_id,
-                                    RenderFrameHostImpl* frame_host) {
+void EmulationHandler::SetRenderer(int process_host_id,
+                                   RenderFrameHostImpl* frame_host) {
+  if (host_ == frame_host)
+    return;
   host_ = frame_host;
   if (touch_emulation_enabled_)
     UpdateTouchEventEmulationState();
-}
-
-void EmulationHandler::UpdateRenderer(int process_host_id,
-                                      RenderFrameHostImpl* frame_host) {
-  if (host_ == frame_host)
-    return;
-  InitRenderer(process_host_id, frame_host);
-  UpdateDeviceEmulationState();
+  if (device_emulation_enabled_)
+    UpdateDeviceEmulationState();
 }
 
 void EmulationHandler::Wire(UberDispatcher* dispatcher) {
@@ -98,8 +94,10 @@
     UpdateTouchEventEmulationState();
   }
   user_agent_ = std::string();
-  device_emulation_enabled_ = false;
-  UpdateDeviceEmulationState();
+  if (device_emulation_enabled_) {
+    device_emulation_enabled_ = false;
+    UpdateDeviceEmulationState();
+  }
   return Response::OK();
 }
 
diff --git a/content/browser/devtools/protocol/emulation_handler.h b/content/browser/devtools/protocol/emulation_handler.h
index 9dc8392..5890b67 100644
--- a/content/browser/devtools/protocol/emulation_handler.h
+++ b/content/browser/devtools/protocol/emulation_handler.h
@@ -32,10 +32,8 @@
       DevToolsAgentHostImpl* host);
 
   void Wire(UberDispatcher* dispatcher) override;
-  void InitRenderer(int process_host_id,
-                    RenderFrameHostImpl* frame_host) override;
-  void UpdateRenderer(int process_host_id,
-                      RenderFrameHostImpl* frame_host) override;
+  void SetRenderer(int process_host_id,
+                   RenderFrameHostImpl* frame_host) override;
 
   Response Disable() override;
 
diff --git a/content/browser/devtools/protocol/target_handler.cc b/content/browser/devtools/protocol/target_handler.cc
index c2a5cd6..9d63cfa 100644
--- a/content/browser/devtools/protocol/target_handler.cc
+++ b/content/browser/devtools/protocol/target_handler.cc
@@ -549,6 +549,10 @@
 Response TargetHandler::SetAutoAttach(bool auto_attach,
                                       bool wait_for_debugger_on_start,
                                       Maybe<bool> flatten) {
+  if (flatten.fromMaybe(false) && !target_registry_) {
+    return Response::InvalidParams(
+        "Will only provide flatten access for browser endpoint");
+  }
   flatten_auto_attach_ = flatten.fromMaybe(false);
   auto_attacher_.SetAutoAttach(auto_attach, wait_for_debugger_on_start);
   if (!auto_attacher_.ShouldThrottleFramesNavigation())
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 6c62f65..435f38d 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -17,6 +17,7 @@
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/devtools/devtools_frame_trace_recorder.h"
 #include "content/browser/devtools/devtools_manager.h"
+#include "content/browser/devtools/devtools_renderer_channel.h"
 #include "content/browser/devtools/devtools_session.h"
 #include "content/browser/devtools/protocol/browser_handler.h"
 #include "content/browser/devtools/protocol/dom_handler.h"
@@ -58,6 +59,7 @@
 #include "net/ssl/ssl_info.h"
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/web/devtools_agent.mojom.h"
 
 #if defined(OS_ANDROID)
 #include "content/browser/renderer_host/compositor_impl_android.h"
@@ -429,13 +431,9 @@
 
 bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session,
                                                  TargetRegistry* registry) {
-  if (!ShouldAllowSession(session, frame_host_))
+  if (!ShouldAllowSession(session))
     return false;
 
-  session->SetRenderer(frame_host_ ? frame_host_->GetProcess()->GetID()
-                                   : ChildProcessHost::kInvalidUniqueID,
-                       frame_host_);
-
   protocol::EmulationHandler* emulation_handler =
       new protocol::EmulationHandler();
   session->AddHandler(base::WrapUnique(new protocol::BrowserHandler()));
@@ -467,10 +465,7 @@
         new protocol::TracingHandler(frame_tree_node_, GetIOContext())));
   }
 
-  if (EnsureAgent())
-    session->AttachToAgent(agent_ptr_);
-
-  if (sessions().size() == 1) {
+  if (sessions().empty()) {
     bool use_video_capture_api = true;
 #ifdef OS_ANDROID
     // Video capture API cannot be used on Android WebView.
@@ -518,9 +513,7 @@
           view->TransformRootPointToViewCoordSpace(gfx::PointF(point)));
     }
   }
-
-  if (host->EnsureAgent())
-    host->agent_ptr_->InspectElement(point);
+  host->GetRendererChannel()->InspectElement(point);
 }
 
 RenderFrameDevToolsAgentHost::~RenderFrameDevToolsAgentHost() {
@@ -577,7 +570,7 @@
       render_frame_alive_ = true;
       for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
         inspector->TargetReloadedAfterCrash();
-      MaybeReattachToRenderFrame();
+      UpdateRendererChannel(IsAttached());
     }
     return;
   }
@@ -592,11 +585,10 @@
     RevokePolicy();
 
   frame_host_ = frame_host;
-  agent_ptr_.reset();
 
   std::vector<DevToolsSession*> restricted_sessions;
   for (DevToolsSession* session : sessions()) {
-    if (!ShouldAllowSession(session, frame_host))
+    if (!ShouldAllowSession(session))
       restricted_sessions.push_back(session);
   }
   if (!restricted_sessions.empty())
@@ -608,21 +600,9 @@
       inspector->TargetReloadedAfterCrash();
   }
 
-  if (IsAttached()) {
+  if (IsAttached())
     GrantPolicy();
-    for (DevToolsSession* session : sessions()) {
-      session->SetRenderer(frame_host ? frame_host->GetProcess()->GetID() : -1,
-                           frame_host);
-    }
-    MaybeReattachToRenderFrame();
-  }
-}
-
-void RenderFrameDevToolsAgentHost::MaybeReattachToRenderFrame() {
-  if (!EnsureAgent())
-    return;
-  for (DevToolsSession* session : sessions())
-    session->AttachToAgent(agent_ptr_);
+  UpdateRendererChannel(IsAttached());
 }
 
 void RenderFrameDevToolsAgentHost::GrantPolicy() {
@@ -696,7 +676,7 @@
 void RenderFrameDevToolsAgentHost::RenderFrameDeleted(RenderFrameHost* rfh) {
   if (rfh == frame_host_) {
     render_frame_alive_ = false;
-    agent_ptr_.reset();
+    UpdateRendererChannel(IsAttached());
   }
 }
 
@@ -706,7 +686,7 @@
     RevokePolicy();
   ForceDetachAllSessions();
   frame_host_ = nullptr;
-  agent_ptr_.reset();
+  UpdateRendererChannel(IsAttached());
   SetFrameTreeNode(nullptr);
   Release();
 }
@@ -939,12 +919,14 @@
   }
 }
 
-bool RenderFrameDevToolsAgentHost::EnsureAgent() {
-  if (!frame_host_ || !render_frame_alive_)
-    return false;
-  if (!agent_ptr_)
-    frame_host_->GetRemoteAssociatedInterfaces()->GetInterface(&agent_ptr_);
-  return true;
+void RenderFrameDevToolsAgentHost::UpdateRendererChannel(bool force) {
+  blink::mojom::DevToolsAgentAssociatedPtr agent_ptr;
+  if (frame_host_ && render_frame_alive_ && force)
+    frame_host_->GetRemoteAssociatedInterfaces()->GetInterface(&agent_ptr);
+  int process_id = frame_host_ ? frame_host_->GetProcess()->GetID()
+                               : ChildProcessHost::kInvalidUniqueID;
+  GetRendererChannel()->SetRenderer(std::move(agent_ptr), process_id,
+                                    frame_host_);
 }
 
 bool RenderFrameDevToolsAgentHost::IsChildFrame() {
@@ -952,16 +934,15 @@
 }
 
 bool RenderFrameDevToolsAgentHost::ShouldAllowSession(
-    DevToolsSession* session,
-    RenderFrameHostImpl* frame_host) {
+    DevToolsSession* session) {
   DevToolsManager* manager = DevToolsManager::GetInstance();
-  if (manager->delegate() && frame_host) {
-    if (!manager->delegate()->AllowInspectingRenderFrameHost(frame_host))
+  if (manager->delegate() && frame_host_) {
+    if (!manager->delegate()->AllowInspectingRenderFrameHost(frame_host_))
       return false;
   }
   const bool is_webui =
-      frame_host && (frame_host->web_ui() || frame_host->pending_web_ui());
-  if (!session->client()->MayAttachToRenderer(frame_host, is_webui))
+      frame_host_ && (frame_host_->web_ui() || frame_host_->pending_web_ui());
+  if (!session->client()->MayAttachToRenderer(frame_host_, is_webui))
     return false;
   return true;
 }
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index 37f0c8d1..42907088 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -21,7 +21,6 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "net/base/net_errors.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
-#include "third_party/blink/public/web/devtools_agent.mojom.h"
 
 #if defined(OS_ANDROID)
 #include "services/device/public/mojom/wake_lock.mojom.h"
@@ -165,6 +164,7 @@
                      TargetRegistry* registry) override;
   void DetachSession(DevToolsSession* session) override;
   void InspectElement(RenderFrameHost* frame_host, int x, int y) override;
+  void UpdateRendererChannel(bool force) override;
 
   // WebContentsObserver overrides.
   void DidStartNavigation(NavigationHandle* navigation_handle) override;
@@ -185,13 +185,11 @@
   void OnSwapCompositorFrame(const IPC::Message& message);
   void DestroyOnRenderFrameGone();
   void UpdateFrameHost(RenderFrameHostImpl* frame_host);
-  void MaybeReattachToRenderFrame();
   void GrantPolicy();
   void RevokePolicy();
   void SetFrameTreeNode(FrameTreeNode* frame_tree_node);
 
-  bool ShouldAllowSession(DevToolsSession* session,
-                          RenderFrameHostImpl* frame_host);
+  bool ShouldAllowSession(DevToolsSession* session);
 
 #if defined(OS_ANDROID)
   device::mojom::WakeLock* GetWakeLock();
@@ -199,7 +197,6 @@
 
   void SynchronousSwapCompositorFrame(
       viz::CompositorFrameMetadata frame_metadata);
-  bool EnsureAgent();
 
   std::unique_ptr<DevToolsFrameTraceRecorder> frame_trace_recorder_;
 #if defined(OS_ANDROID)
@@ -208,7 +205,6 @@
 
   // The active host we are talking to.
   RenderFrameHostImpl* frame_host_ = nullptr;
-  blink::mojom::DevToolsAgentAssociatedPtr agent_ptr_;
   base::flat_set<NavigationHandleImpl*> navigation_handles_;
   bool render_frame_alive_ = false;
 
diff --git a/content/browser/devtools/service_worker_devtools_agent_host.cc b/content/browser/devtools/service_worker_devtools_agent_host.cc
index 71048cc1..53af8c9 100644
--- a/content/browser/devtools/service_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/service_worker_devtools_agent_host.cc
@@ -6,6 +6,7 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
+#include "content/browser/devtools/devtools_renderer_channel.h"
 #include "content/browser/devtools/devtools_session.h"
 #include "content/browser/devtools/protocol/inspector_handler.h"
 #include "content/browser/devtools/protocol/network_handler.h"
@@ -119,47 +120,31 @@
 
 bool ServiceWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session,
                                                    TargetRegistry* registry) {
-  if (state_ == WORKER_READY) {
-    if (sessions().size() == 1) {
-      base::PostTaskWithTraits(
-          FROM_HERE, {BrowserThread::IO},
-          base::BindOnce(&SetDevToolsAttachedOnIO, context_weak_, version_id_,
-                         true));
-    }
-    session->SetRenderer(worker_process_id_, nullptr);
-    session->AttachToAgent(agent_ptr_);
-  }
   session->AddHandler(base::WrapUnique(new protocol::InspectorHandler()));
   session->AddHandler(base::WrapUnique(new protocol::NetworkHandler(
       GetId(), devtools_worker_token_, GetIOContext())));
   session->AddHandler(base::WrapUnique(new protocol::SchemaHandler()));
+  if (state_ == WORKER_READY && sessions().empty())
+    UpdateIsAttached(true);
   return true;
 }
 
 void ServiceWorkerDevToolsAgentHost::DetachSession(DevToolsSession* session) {
   // Destroying session automatically detaches in renderer.
-  if (state_ == WORKER_READY && sessions().empty()) {
-    base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO},
-                             base::BindOnce(&SetDevToolsAttachedOnIO,
-                                            context_weak_, version_id_, false));
-  }
+  if (state_ == WORKER_READY && sessions().empty())
+    UpdateIsAttached(false);
 }
 
 void ServiceWorkerDevToolsAgentHost::WorkerReadyForInspection(
     blink::mojom::DevToolsAgentAssociatedPtrInfo devtools_agent_ptr_info) {
   DCHECK_EQ(WORKER_NOT_READY, state_);
   state_ = WORKER_READY;
-  agent_ptr_.Bind(std::move(devtools_agent_ptr_info));
-  if (!sessions().empty()) {
-    base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO},
-                             base::BindOnce(&SetDevToolsAttachedOnIO,
-                                            context_weak_, version_id_, true));
-  }
-
-  for (DevToolsSession* session : sessions()) {
-    session->SetRenderer(worker_process_id_, nullptr);
-    session->AttachToAgent(agent_ptr_);
-  }
+  blink::mojom::DevToolsAgentAssociatedPtr agent_ptr;
+  agent_ptr.Bind(std::move(devtools_agent_ptr_info));
+  GetRendererChannel()->SetRenderer(std::move(agent_ptr), worker_process_id_,
+                                    nullptr);
+  if (!sessions().empty())
+    UpdateIsAttached(true);
 }
 
 void ServiceWorkerDevToolsAgentHost::WorkerRestarted(int worker_process_id,
@@ -170,18 +155,24 @@
   worker_route_id_ = worker_route_id;
   for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
     inspector->TargetReloadedAfterCrash();
-  for (DevToolsSession* session : sessions())
-    session->SetRenderer(worker_process_id_, nullptr);
 }
 
 void ServiceWorkerDevToolsAgentHost::WorkerDestroyed() {
   DCHECK_NE(WORKER_TERMINATED, state_);
   state_ = WORKER_TERMINATED;
-  agent_ptr_.reset();
   for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
     inspector->TargetCrashed();
-  for (DevToolsSession* session : sessions())
-    session->SetRenderer(-1, nullptr);
+  GetRendererChannel()->SetRenderer(nullptr, ChildProcessHost::kInvalidUniqueID,
+                                    nullptr);
+  if (!sessions().empty())
+    UpdateIsAttached(false);
+}
+
+void ServiceWorkerDevToolsAgentHost::UpdateIsAttached(bool attached) {
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(&SetDevToolsAttachedOnIO, context_weak_, version_id_,
+                     attached));
 }
 
 }  // namespace content
diff --git a/content/browser/devtools/service_worker_devtools_agent_host.h b/content/browser/devtools/service_worker_devtools_agent_host.h
index b261209..50e66dd7 100644
--- a/content/browser/devtools/service_worker_devtools_agent_host.h
+++ b/content/browser/devtools/service_worker_devtools_agent_host.h
@@ -46,11 +46,6 @@
   void Reload() override;
   bool Close() override;
 
-  // DevToolsAgentHostImpl overrides.
-  bool AttachSession(DevToolsSession* session,
-                     TargetRegistry* registry) override;
-  void DetachSession(DevToolsSession* session) override;
-
   void WorkerRestarted(int worker_process_id, int worker_route_id);
   void WorkerReadyForInspection(
       blink::mojom::DevToolsAgentAssociatedPtrInfo devtools_agent_ptr_info);
@@ -77,6 +72,12 @@
 
  private:
   ~ServiceWorkerDevToolsAgentHost() override;
+  void UpdateIsAttached(bool attached);
+
+  // DevToolsAgentHostImpl overrides.
+  bool AttachSession(DevToolsSession* session,
+                     TargetRegistry* registry) override;
+  void DetachSession(DevToolsSession* session) override;
 
   enum WorkerState {
     WORKER_NOT_READY,
@@ -94,7 +95,6 @@
   GURL scope_;
   base::Time version_installed_time_;
   base::Time version_doomed_time_;
-  blink::mojom::DevToolsAgentAssociatedPtr agent_ptr_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDevToolsAgentHost);
 };
diff --git a/content/browser/devtools/shared_worker_devtools_agent_host.cc b/content/browser/devtools/shared_worker_devtools_agent_host.cc
index c09e04a..6a3afed 100644
--- a/content/browser/devtools/shared_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/shared_worker_devtools_agent_host.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/devtools/shared_worker_devtools_agent_host.h"
 
+#include "content/browser/devtools/devtools_renderer_channel.h"
 #include "content/browser/devtools/devtools_session.h"
 #include "content/browser/devtools/protocol/inspector_handler.h"
 #include "content/browser/devtools/protocol/network_handler.h"
@@ -15,6 +16,7 @@
 #include "content/browser/shared_worker/shared_worker_service_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
+#include "third_party/blink/public/web/devtools_agent.mojom.h"
 
 namespace content {
 
@@ -72,9 +74,6 @@
   session->AddHandler(std::make_unique<protocol::NetworkHandler>(
       GetId(), devtools_worker_token_, GetIOContext()));
   session->AddHandler(std::make_unique<protocol::SchemaHandler>());
-  session->SetRenderer(worker_host_ ? worker_host_->process_id() : -1, nullptr);
-  if (state_ == WORKER_READY)
-    session->AttachToAgent(EnsureAgent());
   return true;
 }
 
@@ -90,8 +89,7 @@
   DCHECK_EQ(WORKER_NOT_READY, state_);
   DCHECK(worker_host_);
   state_ = WORKER_READY;
-  for (DevToolsSession* session : sessions())
-    session->AttachToAgent(EnsureAgent());
+  UpdateRendererChannel(IsAttached());
 }
 
 void SharedWorkerDevToolsAgentHost::WorkerRestarted(
@@ -102,8 +100,7 @@
   worker_host_ = worker_host;
   for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
     inspector->TargetReloadedAfterCrash();
-  for (DevToolsSession* session : sessions())
-    session->SetRenderer(worker_host_->process_id(), nullptr);
+  UpdateRendererChannel(IsAttached());
 }
 
 void SharedWorkerDevToolsAgentHost::WorkerDestroyed() {
@@ -112,19 +109,20 @@
   state_ = WORKER_TERMINATED;
   for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
     inspector->TargetCrashed();
-  for (DevToolsSession* session : sessions())
-    session->SetRenderer(-1, nullptr);
   worker_host_ = nullptr;
-  agent_ptr_.reset();
+  UpdateRendererChannel(IsAttached());
 }
 
-const blink::mojom::DevToolsAgentAssociatedPtr&
-SharedWorkerDevToolsAgentHost::EnsureAgent() {
-  DCHECK_EQ(WORKER_READY, state_);
-  DCHECK(worker_host_);
-  if (!agent_ptr_)
-    worker_host_->BindDevToolsAgent(mojo::MakeRequest(&agent_ptr_));
-  return agent_ptr_;
+void SharedWorkerDevToolsAgentHost::UpdateRendererChannel(bool force) {
+  if (state_ == WORKER_READY && force) {
+    blink::mojom::DevToolsAgentAssociatedPtr agent_ptr;
+    worker_host_->BindDevToolsAgent(mojo::MakeRequest(&agent_ptr));
+    GetRendererChannel()->SetRenderer(std::move(agent_ptr),
+                                      worker_host_->process_id(), nullptr);
+  } else {
+    GetRendererChannel()->SetRenderer(
+        nullptr, ChildProcessHost::kInvalidUniqueID, nullptr);
+  }
 }
 
 }  // namespace content
diff --git a/content/browser/devtools/shared_worker_devtools_agent_host.h b/content/browser/devtools/shared_worker_devtools_agent_host.h
index c0a867de..09a66d80 100644
--- a/content/browser/devtools/shared_worker_devtools_agent_host.h
+++ b/content/browser/devtools/shared_worker_devtools_agent_host.h
@@ -8,7 +8,6 @@
 #include "base/macros.h"
 #include "base/unguessable_token.h"
 #include "content/browser/devtools/devtools_agent_host_impl.h"
-#include "third_party/blink/public/web/devtools_agent.mojom.h"
 
 namespace content {
 
@@ -32,11 +31,6 @@
   void Reload() override;
   bool Close() override;
 
-  // DevToolsAgentHostImpl overrides.
-  bool AttachSession(DevToolsSession* session,
-                     TargetRegistry* registry) override;
-  void DetachSession(DevToolsSession* session) override;
-
   bool Matches(SharedWorkerHost* worker_host);
   void WorkerReadyForInspection();
   void WorkerRestarted(SharedWorkerHost* worker_host);
@@ -48,7 +42,12 @@
 
  private:
   ~SharedWorkerDevToolsAgentHost() override;
-  const blink::mojom::DevToolsAgentAssociatedPtr& EnsureAgent();
+
+  // DevToolsAgentHostImpl overrides.
+  bool AttachSession(DevToolsSession* session,
+                     TargetRegistry* registry) override;
+  void DetachSession(DevToolsSession* session) override;
+  void UpdateRendererChannel(bool force) override;
 
   enum WorkerState {
     WORKER_NOT_READY,
@@ -57,7 +56,6 @@
   };
   WorkerState state_;
   SharedWorkerHost* worker_host_;
-  blink::mojom::DevToolsAgentAssociatedPtr agent_ptr_;
   base::UnguessableToken devtools_worker_token_;
   std::unique_ptr<SharedWorkerInstance> instance_;
 
diff --git a/content/browser/frame_host/frame_tree_node.h b/content/browser/frame_host/frame_tree_node.h
index a3a64a35..aa21efda 100644
--- a/content/browser/frame_host/frame_tree_node.h
+++ b/content/browser/frame_host/frame_tree_node.h
@@ -377,6 +377,12 @@
     return replication_state_.has_received_user_gesture;
   }
 
+  // Returns whether the frame received a user gesture on a previous navigation
+  // on the same eTLD+1.
+  bool has_received_user_gesture_before_nav() const {
+    return replication_state_.has_received_user_gesture_before_nav;
+  }
+
   // When a tab is discarded, WebContents sets was_discarded on its
   // root FrameTreeNode.
   // In addition, when a child frame is created, this bit is passed on from
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index fe7d47d..bdb0819 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -982,7 +982,7 @@
   // `ShouldPropagateUserActivation` requirements (same eTLD+1).
   // There are two different checks:
   // 1. if the `frame_tree_node_` has an origin and is following the rules above
-  //    with the target URL, it is used and the bit is set iif the navigation is
+  //    with the target URL, it is used and the bit is set if the navigation is
   //    renderer initiated and the `frame_tree_node_` had a gesture. This should
   //    apply to same page navigations and is preferred over using the referrer
   //    as it can be changed.
@@ -996,7 +996,8 @@
     request_params_.was_activated = WasActivatedOption::kNo;
 
     if (navigation_handle_->IsRendererInitiated() &&
-        frame_tree_node_->has_received_user_gesture() &&
+        (frame_tree_node_->has_received_user_gesture() ||
+         frame_tree_node_->has_received_user_gesture_before_nav()) &&
         ShouldPropagateUserActivation(
             frame_tree_node_->current_origin(),
             url::Origin::Create(navigation_handle_->GetURL()))) {
diff --git a/content/browser/renderer_host/hit_test_debug_key_event_observer.cc b/content/browser/renderer_host/hit_test_debug_key_event_observer.cc
new file mode 100644
index 0000000..ee542014
--- /dev/null
+++ b/content/browser/renderer_host/hit_test_debug_key_event_observer.cc
@@ -0,0 +1,76 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/hit_test_debug_key_event_observer.h"
+
+#include "components/viz/common/hit_test/hit_test_region_list.h"
+#include "components/viz/host/host_frame_sink_manager.h"
+#include "content/browser/compositor/surface_utils.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+
+typedef blink::WebInputEvent::Type Type;
+typedef blink::WebInputEvent::Modifiers Modifiers;
+
+namespace {
+
+viz::HitTestQuery* GetHitTestQuery(
+    viz::HostFrameSinkManager* host_frame_sink_manager,
+    const viz::FrameSinkId& frame_sink_id) {
+  if (!frame_sink_id.is_valid())
+    return nullptr;
+  const auto& display_hit_test_query_map =
+      host_frame_sink_manager->display_hit_test_query();
+  const auto iter = display_hit_test_query_map.find(frame_sink_id);
+  if (iter == display_hit_test_query_map.end())
+    return nullptr;
+  return iter->second.get();
+}
+
+}  // namespace
+
+namespace content {
+
+HitTestDebugKeyEventObserver::HitTestDebugKeyEventObserver(
+    RenderWidgetHostImpl* host)
+    : host_(host), hit_test_query_(nullptr) {
+  host_->AddInputEventObserver(this);
+}
+
+HitTestDebugKeyEventObserver::~HitTestDebugKeyEventObserver() {
+  host_->RemoveInputEventObserver(this);
+}
+
+void HitTestDebugKeyEventObserver::OnInputEventAck(
+    InputEventAckSource source,
+    InputEventAckState state,
+    const blink::WebInputEvent& event) {
+  if (INPUT_EVENT_ACK_STATE_CONSUMED == state ||
+      (event.GetType() != Type::kRawKeyDown &&
+       event.GetType() != Type::kKeyDown)) {
+    return;
+  }
+
+  const blink::WebKeyboardEvent& key_event =
+      static_cast<const blink::WebKeyboardEvent&>(event);
+
+  if (key_event.windows_key_code != ui::VKEY_H ||
+      key_event.GetModifiers() !=
+          (Modifiers::kControlKey | Modifiers::kShiftKey)) {
+    return;
+  }
+
+  if (!hit_test_query_) {
+    hit_test_query_ = GetHitTestQuery(GetHostFrameSinkManager(),
+                                      host_->GetView()->GetRootFrameSinkId());
+  }
+  if (hit_test_query_) {
+    std::string printed_hit_test_data = hit_test_query_->PrintHitTestData();
+    VLOG(1) << (printed_hit_test_data.empty() ? "No hit-test data."
+                                              : printed_hit_test_data);
+  }
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/hit_test_debug_key_event_observer.h b/content/browser/renderer_host/hit_test_debug_key_event_observer.h
new file mode 100644
index 0000000..90e20ef
--- /dev/null
+++ b/content/browser/renderer_host/hit_test_debug_key_event_observer.h
@@ -0,0 +1,40 @@
+// 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 CONTENT_BROWSER_RENDERER_HOST_HIT_TEST_DEBUG_KEY_EVENT_OBSERVER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_HIT_TEST_DEBUG_KEY_EVENT_OBSERVER_H_
+
+#include "content/public/browser/render_widget_host.h"
+
+namespace viz {
+
+class HitTestQuery;
+
+}  // namespace viz
+
+namespace content {
+
+class RenderWidgetHostImpl;
+
+// Implements the RenderWidgetHost::InputEventObserver interface, and acts on
+// keyboard input events to print hit-test data.
+class HitTestDebugKeyEventObserver
+    : public RenderWidgetHost::InputEventObserver {
+ public:
+  explicit HitTestDebugKeyEventObserver(RenderWidgetHostImpl* host);
+  ~HitTestDebugKeyEventObserver() override;
+
+  // RenderWidgetHost::InputEventObserver:
+  void OnInputEventAck(InputEventAckSource source,
+                       InputEventAckState state,
+                       const blink::WebInputEvent&) override;
+
+ private:
+  RenderWidgetHostImpl* host_;
+  viz::HitTestQuery* hit_test_query_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_HIT_TEST_DEBUG_KEY_EVENT_OBSERVER_H_
diff --git a/content/browser/renderer_host/input/fling_browsertest.cc b/content/browser/renderer_host/input/fling_browsertest.cc
index cba336b6..14afc422 100644
--- a/content/browser/renderer_host/input/fling_browsertest.cc
+++ b/content/browser/renderer_host/input/fling_browsertest.cc
@@ -204,11 +204,11 @@
     }
   }
 
-  void GiveItSomeTime() {
+  void GiveItSomeTime(int64_t time_delta_ms = 10) {
     base::RunLoop run_loop;
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE, run_loop.QuitClosure(),
-        base::TimeDelta::FromMilliseconds(10));
+        base::TimeDelta::FromMilliseconds(time_delta_ms));
     run_loop.Run();
   }
 
@@ -250,6 +250,34 @@
   WaitForScroll();
 }
 
+// Tests that flinging does not continue after navigating to a page that uses
+// the same renderer.
+IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
+                       FlingingStopsAfterNavigation) {
+  GURL first_url(embedded_test_server()->GetURL(
+      "b.a.com", "/scrollable_page_with_iframe.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), first_url));
+  MainThreadFrameObserver main_thread_sync1(GetWidgetHost());
+  main_thread_sync1.Wait();
+  SimulateTouchscreenFling(GetWidgetHost());
+  WaitForScroll();
+
+  // Navigate to a second page with the same domain.
+  GURL second_url(
+      embedded_test_server()->GetURL("a.com", "/scrollable_page.html"));
+  NavigateToURL(shell(), second_url);
+  MainThreadFrameObserver main_thread_sync2(GetWidgetHost());
+  main_thread_sync2.Wait();
+
+  // Wait for 100ms. Then check that the second page has not scrolled.
+  GiveItSomeTime(100);
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  EXPECT_EQ(
+      0, EvalJs(root->current_frame_host(), "window.scrollY").ExtractDouble());
+}
+
 IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, TouchscreenFlingInOOPIF) {
   LoadPageWithOOPIF();
   SimulateTouchscreenFling(child_view_->host());
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 9d16263..61220b8 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -1156,6 +1156,9 @@
   current_content_source_id_ = next_source_id;
   did_receive_first_frame_after_navigation_ = false;
 
+  // Stop the flinging after navigating to a new page.
+  StopFling();
+
   if (enable_surface_synchronization_) {
     // Resize messages before navigation are not acked, so reset
     // |visual_properties_ack_pending_| and make sure the next resize will be
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 404a1e7..5d465d70 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -22,9 +22,11 @@
 #include "components/viz/common/features.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/copy_output_result.h"
+#include "components/viz/host/host_frame_sink_manager.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
 #include "content/browser/bad_message.h"
+#include "content/browser/compositor/surface_utils.h"
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
@@ -929,8 +931,23 @@
 }
 
 void RenderWidgetHostViewAura::TransformPointToRootSurface(gfx::PointF* point) {
-  aura::Window::ConvertPointToTarget(window_, window_->GetRootWindow(), point);
-  window_->GetRootWindow()->transform().TransformPoint(point);
+  aura::Window* root = window_->GetRootWindow();
+  aura::Window::ConvertPointToTarget(window_, root, point);
+  root->GetRootWindow()->transform().TransformPoint(point);
+
+// On ChromeOS, the root surface is the whole desktop. When using the
+// window-service converting to screen coordinates gives us that.
+#if defined(OS_CHROMEOS)
+  if (features::IsUsingWindowService()) {
+    aura::client::ScreenPositionClient* screen_client =
+        aura::client::GetScreenPositionClient(root);
+    if (screen_client) {
+      gfx::Point rounded_point(point->x(), point->y());
+      screen_client->ConvertPointToScreen(root, &rounded_point);
+      *point = gfx::PointF(rounded_point);
+    }
+  }
+#endif
 }
 
 gfx::Rect RenderWidgetHostViewAura::GetBoundsInRootWindow() {
@@ -1705,9 +1722,18 @@
 }
 
 viz::FrameSinkId RenderWidgetHostViewAura::GetRootFrameSinkId() {
-  if (window_ && window_->GetHost() && window_->GetHost()->compositor())
-    return window_->GetHost()->compositor()->frame_sink_id();
-  return viz::FrameSinkId();
+  if (!window_ || !window_->GetHost() || !window_->GetHost()->compositor())
+    return viz::FrameSinkId();
+
+  // In single-process mash the root is provided by Ash. Have
+  // HostFrameSinkManager walk the tree to find the right root.
+  if (features::IsSingleProcessMash()) {
+    base::Optional<viz::FrameSinkId> root =
+        GetHostFrameSinkManager()->FindRootFrameSinkId(frame_sink_id_);
+    return root ? *root : viz::FrameSinkId();
+  }
+
+  return window_->GetHost()->compositor()->frame_sink_id();
 }
 
 viz::SurfaceId RenderWidgetHostViewAura::GetCurrentSurfaceId() const {
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
index 7318323b..caea824 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
@@ -4,8 +4,11 @@
 
 #include "content/browser/renderer_host/render_widget_host_view_event_handler.h"
 
+#include "base/command_line.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
+#include "components/viz/common/features.h"
+#include "content/browser/renderer_host/hit_test_debug_key_event_observer.h"
 #include "content/browser/renderer_host/input/touch_selection_controller_client_aura.h"
 #include "content/browser/renderer_host/overscroll_controller.h"
 #include "content/browser/renderer_host/render_view_host_delegate.h"
@@ -101,6 +104,15 @@
   return view->GetWidgetType() == content::WidgetType::kPopup;
 }
 
+// Enables hit-test debug logging.
+const char kEnableVizHitTestDebug[] = "enable-viz-hit-test-debug";
+
+inline bool IsVizHitTestingDebugEnabled() {
+  return features::IsVizHitTestingEnabled() &&
+         base::CommandLine::ForCurrentProcess()->HasSwitch(
+             kEnableVizHitTestDebug);
+}
+
 }  // namespace
 
 namespace content {
@@ -128,7 +140,10 @@
       popup_child_event_handler_(nullptr),
       delegate_(delegate),
       window_(nullptr),
-      mouse_wheel_phase_handler_(host_view) {}
+      mouse_wheel_phase_handler_(host_view),
+      debug_observer_(IsVizHitTestingDebugEnabled()
+                          ? std::make_unique<HitTestDebugKeyEventObserver>(host)
+                          : nullptr) {}
 
 RenderWidgetHostViewEventHandler::~RenderWidgetHostViewEventHandler() {}
 
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.h b/content/browser/renderer_host/render_widget_host_view_event_handler.h
index 5db1ce0..8b28ee30 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.h
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.h
@@ -42,6 +42,7 @@
 class RenderWidgetHostImpl;
 class RenderWidgetHostViewBase;
 class TouchSelectionControllerClientAura;
+class HitTestDebugKeyEventObserver;
 
 // Provides an implementation of ui::EventHandler for use with
 // RenderWidgetHostViewBase. A delegate is required in order to provide platform
@@ -290,6 +291,8 @@
   aura::Window* window_;
   MouseWheelPhaseHandler mouse_wheel_phase_handler_;
 
+  std::unique_ptr<HitTestDebugKeyEventObserver> debug_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewEventHandler);
 };
 
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 4887d13..67e8094 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -124,22 +124,24 @@
   CheckQuotaManagedDataDeletionStatus(deletion_task_count, std::move(callback));
 }
 
-void ClearedShaderCache(const base::Closure& callback) {
+void ClearedShaderCache(base::OnceClosure callback) {
   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
-                             base::BindOnce(&ClearedShaderCache, callback));
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::UI},
+        base::BindOnce(&ClearedShaderCache, std::move(callback)));
     return;
   }
-  callback.Run();
+  std::move(callback).Run();
 }
 
 void ClearShaderCacheOnIOThread(const base::FilePath& path,
                                 const base::Time begin,
                                 const base::Time end,
-                                const base::Closure& callback) {
+                                base::OnceClosure callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   GetShaderCacheFactorySingleton()->ClearByPath(
-      path, begin, end, base::Bind(&ClearedShaderCache, callback));
+      path, begin, end,
+      base::BindOnce(&ClearedShaderCache, std::move(callback)));
 }
 
 void OnLocalStorageUsageInfo(
diff --git a/content/browser/web_contents/web_contents_view_mac.h b/content/browser/web_contents/web_contents_view_mac.h
index 0b37270..498ec1d 100644
--- a/content/browser/web_contents/web_contents_view_mac.h
+++ b/content/browser/web_contents/web_contents_view_mac.h
@@ -19,8 +19,8 @@
 #include "content/browser/web_contents/web_contents_view.h"
 #include "content/common/content_export.h"
 #include "content/common/drag_event_source_info.h"
-#import "ui/base/cocoa/accessibility_hostable.h"
 #import "ui/base/cocoa/base_view.h"
+#import "ui/base/cocoa/views_hostable.h"
 #include "ui/gfx/geometry/size.h"
 
 @class WebDragDest;
@@ -42,7 +42,7 @@
 }
 
 CONTENT_EXPORT
-@interface WebContentsViewCocoa : BaseView<AccessibilityHostable> {
+@interface WebContentsViewCocoa : BaseView<ViewsHostable> {
  @private
   // Instances of this class are owned by both webContentsView_ and AppKit. It
   // is possible for an instance to outlive its webContentsView_. The
@@ -56,6 +56,12 @@
 
 - (void)setMouseDownCanMoveWindow:(BOOL)canMove;
 
+// Sets |accessibilityParent| as the object returned when the
+// receiver is queried for its accessibility parent.
+// TODO(lgrey/ellyjones): Remove this in favor of setAccessibilityParent:
+// when we switch to the new accessibility API.
+- (void)setAccessibilityParentElement:(id)accessibilityParent;
+
 // Returns the available drag operations. This is a required method for
 // NSDraggingSource. It is supposedly deprecated, but the non-deprecated API
 // -[NSWindow dragImage:...] still relies on it.
@@ -69,7 +75,8 @@
 // contains all of the contents of the tab and associated child views.
 class WebContentsViewMac : public WebContentsView,
                            public RenderViewHostDelegateView,
-                           public PopupMenuHelper::Delegate {
+                           public PopupMenuHelper::Delegate,
+                           public ui::ViewsHostableView {
  public:
   // The corresponding WebContentsImpl is passed in the constructor, and manages
   // our lifetime. This doesn't need to be the case, but is this way currently
@@ -132,12 +139,14 @@
   // PopupMenuHelper::Delegate:
   void OnMenuClosed() override;
 
+  // ViewsHostableView:
+  void OnViewsHostableAttached(ViewsHostableView::Host* host) override;
+  void OnViewsHostableDetached() override;
+
   // A helper method for closing the tab in the
   // CloseTabAfterEventTracking() implementation.
   void CloseTab();
 
-  void SetParentUiLayer(ui::Layer* parent_ui_layer);
-
   WebContentsImpl* web_contents() { return web_contents_; }
   WebContentsViewDelegate* delegate() { return delegate_.get(); }
 
@@ -149,6 +158,8 @@
       RenderWidgetHostViewCreateFunction create_render_widget_host_view);
 
  private:
+  void SetParentUiLayer(ui::Layer* parent_ui_layer);
+
   // Returns the fullscreen view, if one exists; otherwise, returns the content
   // native view. This ensures that the view currently attached to a NSWindow is
   // being used to query or set first responder state.
@@ -170,7 +181,8 @@
   // destroyed.
   std::list<base::WeakPtr<RenderWidgetHostViewBase>> child_views_;
 
-  ui::Layer* parent_ui_layer_ = nullptr;
+  // Interface to the views::View host of this view.
+  ViewsHostableView::Host* views_host_ = nullptr;
 
   std::unique_ptr<PopupMenuHelper> popup_menu_helper_;
 
diff --git a/content/browser/web_contents/web_contents_view_mac.mm b/content/browser/web_contents/web_contents_view_mac.mm
index 325c9c8..176aa38 100644
--- a/content/browser/web_contents/web_contents_view_mac.mm
+++ b/content/browser/web_contents/web_contents_view_mac.mm
@@ -108,6 +108,9 @@
     : web_contents_(web_contents), delegate_(delegate) {}
 
 WebContentsViewMac::~WebContentsViewMac() {
+  if (views_host_)
+    views_host_->OnHostableViewDestroying();
+  DCHECK(!views_host_);
   // This handles the case where a renderer close call was deferred
   // while the user was operating a UI control which resulted in a
   // close.  In that case, the Cocoa view outlives the
@@ -354,7 +357,8 @@
 
   // Add the RenderWidgetHostView to the ui::Layer heirarchy.
   child_views_.push_back(view->GetWeakPtr());
-  SetParentUiLayer(parent_ui_layer_);
+  if (views_host_)
+    SetParentUiLayer(views_host_->GetUiLayer());
 
   // Fancy layout comes later; for now just make it our size and resize it
   // with us. In case there are other siblings of the content area, we want
@@ -431,7 +435,6 @@
 }
 
 void WebContentsViewMac::SetParentUiLayer(ui::Layer* parent_ui_layer) {
-  parent_ui_layer_ = parent_ui_layer;
   // Remove any child NSViews that have been destroyed.
   for (auto iter = child_views_.begin(); iter != child_views_.end();) {
     if (*iter)
@@ -441,6 +444,26 @@
   }
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// WebContentsViewMac, ViewsHostableView:
+
+void WebContentsViewMac::OnViewsHostableAttached(
+    ViewsHostableView::Host* host) {
+  views_host_ = host;
+
+  SetParentUiLayer(views_host_->GetUiLayer());
+  [cocoa_view_
+      setAccessibilityParentElement:views_host_->GetAccessibilityElement()];
+}
+
+void WebContentsViewMac::OnViewsHostableDetached() {
+  DCHECK(views_host_);
+  views_host_ = nullptr;
+
+  SetParentUiLayer(nullptr);
+  [cocoa_view_ setAccessibilityParentElement:nil];
+}
+
 }  // namespace content
 
 @implementation WebContentsViewCocoa
@@ -655,11 +678,6 @@
       FocusThroughTabTraversal(direction == NSSelectingPrevious);
 }
 
-- (void)cr_setParentUiLayer:(ui::Layer*)parentUiLayer {
-  if (webContentsView_)
-    webContentsView_->SetParentUiLayer(parentUiLayer);
-}
-
 - (void)updateWebContentsVisibility {
   WebContentsImpl* webContents = [self webContents];
   if (!webContents || webContents->IsBeingDestroyed())
@@ -724,11 +742,15 @@
   [self updateWebContentsVisibility];
 }
 
-// AccessibilityHostable protocol implementation.
 - (void)setAccessibilityParentElement:(id)accessibilityParent {
   accessibilityParent_.reset([accessibilityParent retain]);
 }
 
+// ViewsHostable protocol implementation.
+- (ui::ViewsHostableView*)viewsHostableView {
+  return webContentsView_;
+}
+
 // NSAccessibility informal protocol implementation.
 - (id)accessibilityAttributeValue:(NSString*)attribute {
   if (accessibilityParent_ &&
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index 25856a5..ccd811b 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -34,6 +34,7 @@
 import org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl;
 import org.chromium.content.browser.framehost.RenderFrameHostDelegate;
 import org.chromium.content.browser.framehost.RenderFrameHostImpl;
+import org.chromium.content.browser.input.TextSuggestionHost;
 import org.chromium.content.browser.selection.SelectionPopupControllerImpl;
 import org.chromium.content_public.browser.AccessibilitySnapshotCallback;
 import org.chromium.content_public.browser.AccessibilitySnapshotNode;
@@ -229,6 +230,7 @@
 
         ViewEventSinkImpl.from(this).setAccessDelegate(accessDelegate);
         getRenderCoordinates().setDeviceScaleFactor(windowAndroid.getDisplay().getDipScale());
+        TextSuggestionHost.fromWebContents(this);
     }
 
     @Nullable
diff --git a/content/renderer/dom_serializer_browsertest.cc b/content/renderer/dom_serializer_browsertest.cc
index 8dfad4b4..50347bd 100644
--- a/content/renderer/dom_serializer_browsertest.cc
+++ b/content/renderer/dom_serializer_browsertest.cc
@@ -139,8 +139,8 @@
       // html contents use UTF-8 encoding.
       WebData data(contents.data(), contents.length());
       GetMainFrame()->CommitDataNavigation(
-          data, "text/html", encoding_info, base_url, WebURL(),
-          false /* replace */, blink::WebFrameLoadType::kStandard,
+          blink::WebURLRequest(base_url), data, "text/html", encoding_info,
+          WebURL(), false /* replace */, blink::WebFrameLoadType::kStandard,
           blink::WebHistoryItem(), false /* is_client_redirect */,
           nullptr /* navigation_params */, nullptr /* navigation_data */);
     }
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index fcafcc9..9619fcf 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2862,7 +2862,7 @@
   // should not inherit the cache mode from |failed_request|).
   new_request.SetCacheMode(blink::mojom::FetchCacheMode::kNoStore);
 
-  frame_->CommitDataNavigationWithRequest(
+  frame_->CommitDataNavigation(
       new_request, error_html, "text/html", "UTF-8", error_url, replace,
       frame_load_type, history_item, is_client_redirect,
       std::move(navigation_params), std::move(navigation_data));
@@ -3313,7 +3313,7 @@
     should_load_data_url |= !request_params.data_url_as_string.empty();
 #endif
     if (is_main_frame_ && should_load_data_url) {
-      LoadDataURL(common_params, request_params, frame_, load_type,
+      LoadDataURL(common_params, request_params, load_type,
                   item_for_history_navigation, is_client_redirect,
                   std::move(document_state));
     } else {
@@ -6865,7 +6865,6 @@
 void RenderFrameImpl::LoadDataURL(
     const CommonNavigationParams& common_params,
     const RequestNavigationParams& request_params,
-    WebLocalFrame* frame,
     blink::WebFrameLoadType load_type,
     blink::WebHistoryItem item_for_history_navigation,
     bool is_client_redirect,
@@ -6895,9 +6894,9 @@
     bool replace = load_type == WebFrameLoadType::kReloadBypassingCache ||
                    load_type == WebFrameLoadType::kReload;
 
-    frame->CommitDataNavigation(
-        WebData(data.c_str(), data.length()), WebString::FromUTF8(mime_type),
-        WebString::FromUTF8(charset), base_url,
+    frame_->CommitDataNavigation(
+        WebURLRequest(base_url), WebData(data.c_str(), data.length()),
+        WebString::FromUTF8(mime_type), WebString::FromUTF8(charset),
         // Needed so that history-url-only changes don't become reloads.
         common_params.history_url_for_data_url, replace, load_type,
         item_for_history_navigation, is_client_redirect,
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 63418b1..1b27a77 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1190,7 +1190,6 @@
   void LoadDataURL(
       const CommonNavigationParams& common_params,
       const RequestNavigationParams& request_params,
-      blink::WebLocalFrame* frame,
       blink::WebFrameLoadType load_type,
       blink::WebHistoryItem item_for_history_navigation,
       bool is_client_redirect,
diff --git a/content/shell/test_runner/test_runner.cc b/content/shell/test_runner/test_runner.cc
index 8d3df86..185275bc 100644
--- a/content/shell/test_runner/test_runner.cc
+++ b/content/shell/test_runner/test_runner.cc
@@ -1565,7 +1565,7 @@
     delegate_->SetDatabaseQuota(kDefaultDatabaseQuota);
     delegate_->SetDeviceColorSpace("reset");
     delegate_->SetDeviceScaleFactor(GetDefaultDeviceScaleFactor());
-    delegate_->SetBlockThirdPartyCookies(true);
+    delegate_->SetBlockThirdPartyCookies(false);
     delegate_->SetLocale("");
     delegate_->UseUnfortunateSynchronousResizeMode(false);
     delegate_->DisableAutoResizeMode(blink::WebSize());
diff --git a/extensions/browser/api/web_request/web_request_permissions.cc b/extensions/browser/api/web_request/web_request_permissions.cc
index f33db4d..57a1172 100644
--- a/extensions/browser/api/web_request/web_request_permissions.cc
+++ b/extensions/browser/api/web_request/web_request_permissions.cc
@@ -5,6 +5,7 @@
 #include "extensions/browser/api/web_request/web_request_permissions.h"
 
 #include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -45,6 +46,10 @@
 // Returns true if the scheme is one we want to allow extensions to have access
 // to. Extensions still need specific permissions for a given URL, which is
 // covered by CanExtensionAccessURL.
+
+// TODO(karandeepb): This allows more schemes than
+// ExtensionWebRequestEventRouter::RequestFiler, which specifies the schemes
+// allowed by web request event listeners. Consolidate the two.
 bool HasWebRequestScheme(const GURL& url) {
   return (url.SchemeIs(url::kAboutScheme) || url.SchemeIs(url::kFileScheme) ||
           url.SchemeIs(url::kFileSystemScheme) ||
@@ -172,94 +177,60 @@
   return access;
 }
 
-}  // namespace
-
-// Returns true if the given |request| is sensitive and must not be
-// modified/canceled by extensions, e.g. because it is targeted to the webstore
-// to check for updates, extension blacklisting, etc.
-bool IsSensitiveRequest(const extensions::WebRequestInfo& request,
-                        bool is_request_from_browser,
-                        bool is_request_from_webui_renderer) {
-  const bool is_request_from_sensitive_source =
-      is_request_from_browser || is_request_from_webui_renderer;
+// Returns true if |request|.url is of the form clients[0-9]*.google.com.
+bool IsSensitiveGoogleClientUrl(const extensions::WebRequestInfo& request) {
   const GURL& url = request.url;
 
-  const bool is_network_request =
-      url.SchemeIsHTTPOrHTTPS() || url.SchemeIsWSOrWSS();
-  if (is_network_request && is_request_from_webui_renderer) {
-    // WebUI renderers should never be making network requests, but we may make
-    // some exceptions for now. See https://crbug.com/829412 for details.
-    //
-    // The DCHECK helps avoid proliferation of such behavior. In any case, we
-    // treat the requests as sensitive to ensure that the Web Request API
-    // doesn't see them.
-    DCHECK(request.initiator.has_value());
-    DCHECK(extensions::ExtensionsBrowserClient::Get()
-               ->IsWebUIAllowedToMakeNetworkRequests(*request.initiator))
-        << "Unsupported network request from "
-        << request.initiator->GetURL().spec() << " for " << url.spec();
-    return true;
-  }
-
   // TODO(battre) Merge this, CanExtensionAccessURL and
   // PermissionsData::CanAccessPage into one function.
-  bool sensitive_chrome_url = false;
-  const char kGoogleCom[] = "google.com";
-  const char kClient[] = "clients";
-  url::Origin origin = url::Origin::Create(url);
-  if (origin.DomainIs(kGoogleCom)) {
-    base::StringPiece host = url.host_piece();
-    while (host.ends_with("."))
-      host.remove_suffix(1u);
-    // Check for "clients[0-9]*.google.com" hosts.
-    // This protects requests to several internal services such as sync,
-    // extension update pings, captive portal detection, fraudulent certificate
-    // reporting, autofill and others.
-    //
-    // These URLs are only protected for requests from the browser and webui
-    // renderers, not for requests from common renderers, because
-    // clients*.google.com are also used by websites.
-    if (is_request_from_sensitive_source) {
-      base::StringPiece::size_type pos = host.rfind(kClient);
-      if (pos != base::StringPiece::npos) {
-        bool match = true;
-        if (pos > 0 && host[pos - 1] != '.') {
-          match = false;
-        } else {
-          for (base::StringPiece::const_iterator
-                   i = host.begin() + pos + strlen(kClient),
-                   end = host.end() - (strlen(kGoogleCom) + 1);
-               i != end; ++i) {
-            if (!isdigit(*i)) {
-              match = false;
-              break;
-            }
-          }
-        }
-        sensitive_chrome_url = sensitive_chrome_url || match;
-      }
-    }
+  static constexpr char kGoogleCom[] = "google.com";
+  static constexpr char kClient[] = "clients";
+  constexpr size_t kGoogleComLength = base::size(kGoogleCom) - 1;
+  constexpr size_t kClientLength = base::size(kClient) - 1;
 
-    // Safebrowsing and Chrome Webstore URLs are always protected, i.e. also
-    // for requests from common renderers.
-    sensitive_chrome_url = sensitive_chrome_url ||
-                           (url.DomainIs("chrome.google.com") &&
-                            base::StartsWith(url.path_piece(), "/webstore",
-                                             base::CompareCase::SENSITIVE));
+  if (!url.DomainIs(kGoogleCom))
+    return false;
+
+  base::StringPiece host = url.host_piece();
+
+  while (host.ends_with("."))
+    host.remove_suffix(1u);
+
+  // Check for "clients[0-9]*.google.com" hosts.
+  // This protects requests to several internal services such as sync,
+  // extension update pings, captive portal detection, fraudulent certificate
+  // reporting, autofill and others.
+  //
+  // These URLs are only protected for requests from the browser, and not for
+  // requests from common renderers, because clients*.google.com are also used
+  // by websites.
+  base::StringPiece::size_type pos = host.rfind(kClient);
+  if (pos == base::StringPiece::npos)
+    return false;
+
+  if (pos > 0 && host[pos - 1] != '.')
+    return false;
+
+  for (base::StringPiece::const_iterator
+           i = host.begin() + pos + kClientLength,
+           end = host.end() - (kGoogleComLength + 1);
+       i != end; ++i) {
+    if (!isdigit(*i))
+      return false;
   }
 
-  return sensitive_chrome_url ||
-         extensions::ExtensionsAPIClient::Get()
-             ->ShouldHideBrowserNetworkRequest(request) ||
-         extension_urls::IsWebstoreUpdateUrl(url) ||
-         extension_urls::IsBlacklistUpdateUrl(url) ||
-         extension_urls::IsSafeBrowsingUrl(origin, url.path_piece());
+  return true;
 }
 
+}  // namespace
+
 // static
 bool WebRequestPermissions::HideRequest(
     const extensions::InfoMap* extension_info_map,
     const extensions::WebRequestInfo& request) {
+  if (!HasWebRequestScheme(request.url))
+    return true;
+
   // Requests from <webview> are never hidden.
   if (request.is_web_view)
     return false;
@@ -269,37 +240,82 @@
   if (request.is_pac_request)
     return true;
 
-  // Requests from the browser and webui get special protection for
-  // clients*.google.com URLs.
-  bool is_request_from_browser =
-      request.render_process_id == -1 &&
-      // Browser requests are often of the "other" resource type.
-      // Main frame requests are not unconditionally seen as a sensitive browser
-      // request, because a request can also be browser-driven if there is no
-      // process to associate the request with. E.g. navigations via the
-      // chrome.tabs.update extension API.
-      request.type != content::RESOURCE_TYPE_MAIN_FRAME;
-  bool is_request_from_webui_renderer = false;
-  if (!is_request_from_browser) {
-    // Requests from guest processes are never hidden.
-    if (request.is_web_view)
-      return false;
+  bool is_request_from_browser = request.render_process_id == -1;
 
-    // Hide requests from the Chrome WebStore App, signin process, and WebUI.
-    if (extension_info_map &&
-        extension_info_map->process_map().Contains(extensions::kWebStoreAppId,
-                                                   request.render_process_id)) {
+  if (is_request_from_browser) {
+    // Hide all non-navigation requests made by the browser. crbug.com/884932.
+    if (!request.is_browser_side_navigation)
+      return true;
+
+    DCHECK(request.type == content::RESOURCE_TYPE_MAIN_FRAME ||
+           request.type == content::RESOURCE_TYPE_SUB_FRAME);
+
+    // Hide sub-frame requests to clientsX.google.com.
+    // TODO(crbug.com/890006): Determine if the code here can be cleaned up
+    // since browser initiated non-navigation requests are now hidden from
+    // extensions.
+    if (request.type != content::RESOURCE_TYPE_MAIN_FRAME &&
+        IsSensitiveGoogleClientUrl(request)) {
       return true;
     }
-
-    is_request_from_webui_renderer =
-        content::ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(
-            request.render_process_id);
   }
 
-  return IsSensitiveRequest(request, is_request_from_browser,
-                            is_request_from_webui_renderer) ||
-         !HasWebRequestScheme(request.url);
+  // Hide requests from the Chrome WebStore App.
+  if (!is_request_from_browser && extension_info_map &&
+      extension_info_map->process_map().Contains(extensions::kWebStoreAppId,
+                                                 request.render_process_id)) {
+    return true;
+  }
+
+  const GURL& url = request.url;
+
+  bool is_request_from_webui_renderer =
+      !is_request_from_browser &&
+      content::ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(
+          request.render_process_id);
+
+  if (is_request_from_webui_renderer) {
+#if DCHECK_IS_ON()
+    const bool is_network_request =
+        url.SchemeIsHTTPOrHTTPS() || url.SchemeIsWSOrWSS();
+    if (is_network_request) {
+      // WebUI renderers should never be making network requests, but we may
+      // make some exceptions for now. See https://crbug.com/829412 for
+      // details.
+      //
+      // The DCHECK helps avoid proliferation of such behavior.
+      DCHECK(request.initiator.has_value());
+      DCHECK(extensions::ExtensionsBrowserClient::Get()
+                 ->IsWebUIAllowedToMakeNetworkRequests(*request.initiator))
+          << "Unsupported network request from "
+          << request.initiator->GetURL().spec() << " for " << url.spec();
+    }
+#endif  // DCHECK_IS_ON()
+
+    // In any case, we treat the requests as sensitive to ensure that the Web
+    // Request API doesn't see them.
+    return true;
+  }
+
+  // Allow the extension embedder to hide the request.
+  if (extensions::ExtensionsAPIClient::Get()->ShouldHideBrowserNetworkRequest(
+          request)) {
+    return true;
+  }
+
+  // Safebrowsing and Chrome Webstore URLs are always protected, i.e. also
+  // for requests from common renderers.
+  if (extension_urls::IsWebstoreUpdateUrl(url) ||
+      extension_urls::IsBlacklistUpdateUrl(url) ||
+      extension_urls::IsSafeBrowsingUrl(url::Origin::Create(url),
+                                        url.path_piece()) ||
+      (url.DomainIs("chrome.google.com") &&
+       base::StartsWith(url.path_piece(), "/webstore",
+                        base::CompareCase::SENSITIVE))) {
+    return true;
+  }
+
+  return false;
 }
 
 // static
diff --git a/extensions/browser/api/web_request/web_request_permissions.h b/extensions/browser/api/web_request/web_request_permissions.h
index b0059ca..fa6c926 100644
--- a/extensions/browser/api/web_request/web_request_permissions.h
+++ b/extensions/browser/api/web_request/web_request_permissions.h
@@ -20,11 +20,6 @@
 struct WebRequestInfo;
 }
 
-// Exposed for unit testing.
-bool IsSensitiveRequest(const extensions::WebRequestInfo& request,
-                        bool is_request_from_browser,
-                        bool is_request_from_webui_renderer);
-
 // This class is used to test whether extensions may modify web requests. It
 // should be used on the IO thread.
 class WebRequestPermissions {
diff --git a/extensions/browser/api/web_request/web_request_permissions_unittest.cc b/extensions/browser/api/web_request/web_request_permissions_unittest.cc
index b6d8fcf3..8441a29 100644
--- a/extensions/browser/api/web_request/web_request_permissions_unittest.cc
+++ b/extensions/browser/api/web_request/web_request_permissions_unittest.cc
@@ -21,67 +21,130 @@
 namespace extensions {
 
 TEST(ExtensionWebRequestPermissions, IsSensitiveRequest) {
+  enum HideRequestMask {
+    HIDE_NONE = 0,
+    HIDE_RENDERER_REQUEST = 1,
+    HIDE_SUB_FRAME_NAVIGATION = 2,
+    HIDE_MAIN_FRAME_NAVIGATION = 4,
+    HIDE_BROWSER_SUB_RESOURCE_REQUEST = 8,
+    HIDE_ALL = HIDE_RENDERER_REQUEST | HIDE_SUB_FRAME_NAVIGATION |
+               HIDE_MAIN_FRAME_NAVIGATION | HIDE_BROWSER_SUB_RESOURCE_REQUEST,
+  };
+
   ExtensionsAPIClient api_client;
+
   struct TestCase {
     const char* url;
-    bool is_sensitive_if_request_from_common_renderer;
-    bool is_sensitive_if_request_from_browser_or_webui_renderer;
+    int expected_hide_request_mask;
   } cases[] = {
-      {"https://www.google.com", false, false},
-      {"http://www.example.com", false, false},
-      {"https://www.example.com", false, false},
-      {"https://clients.google.com", false, true},
-      {"https://clients4.google.com", false, true},
-      {"https://clients9999.google.com", false, true},
-      {"https://clients9999..google.com", false, false},
-      {"https://clients9999.example.google.com", false, false},
-      {"https://clients.google.com.", false, true},
-      {"https://.clients.google.com.", false, true},
-      {"http://google.example.com", false, false},
-      {"http://www.example.com", false, false},
-      {"https://www.example.com", false, false},
-      {"https://clients.google.com", false, true},
-      {"https://sb-ssl.google.com", true, true},
-      {"https://sb-ssl.random.google.com", false, false},
-      {"https://safebrowsing.googleapis.com", true, true},
+      {"https://www.google.com", HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"http://www.example.com", HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://www.example.com", HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://clients.google.com",
+       HIDE_BROWSER_SUB_RESOURCE_REQUEST | HIDE_SUB_FRAME_NAVIGATION},
+      {"https://clients4.google.com",
+       HIDE_BROWSER_SUB_RESOURCE_REQUEST | HIDE_SUB_FRAME_NAVIGATION},
+      {"https://clients9999.google.com",
+       HIDE_BROWSER_SUB_RESOURCE_REQUEST | HIDE_SUB_FRAME_NAVIGATION},
+      {"https://clients9999..google.com", HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://clients9999.example.google.com",
+       HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://clients.google.com.",
+       HIDE_BROWSER_SUB_RESOURCE_REQUEST | HIDE_SUB_FRAME_NAVIGATION},
+      {"https://.clients.google.com.",
+       HIDE_BROWSER_SUB_RESOURCE_REQUEST | HIDE_SUB_FRAME_NAVIGATION},
+      {"http://google.example.com", HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"http://www.example.com", HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://www.example.com", HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://sb-ssl.google.com", HIDE_ALL},
+      {"https://sb-ssl.random.google.com", HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://safebrowsing.googleapis.com", HIDE_ALL},
+      // Unsupported scheme.
       {"blob:https://safebrowsing.googleapis.com/"
        "fc3f440b-78ed-469f-8af8-7a1717ff39ae",
-       true, true},
-      {"filesystem:https://safebrowsing.googleapis.com/path", true, true},
-      {"https://safebrowsing.googleapis.com.", true, true},
-      {"https://safebrowsing.googleapis.com/v4", true, true},
-      {"https://safebrowsing.googleapis.com:80/v4", true, true},
-      {"https://safebrowsing.googleapis.com./v4", true, true},
-      {"https://safebrowsing.googleapis.com/v5", true, true},
-      {"https://safebrowsing.google.com/safebrowsing", true, true},
-      {"https://safebrowsing.google.com/safebrowsing/anything", true, true},
-      {"https://safebrowsing.google.com", false, false},
-      {"https://chrome.google.com", false, false},
-      {"https://chrome.google.com/webstore", true, true},
-      {"https://chrome.google.com./webstore", true, true},
+       HIDE_ALL},
+      {"filesystem:https://safebrowsing.googleapis.com/path", HIDE_ALL},
+      {"https://safebrowsing.googleapis.com.", HIDE_ALL},
+      {"https://safebrowsing.googleapis.com/v4", HIDE_ALL},
+      {"https://safebrowsing.googleapis.com:80/v4", HIDE_ALL},
+      {"https://safebrowsing.googleapis.com./v4", HIDE_ALL},
+      {"https://safebrowsing.googleapis.com/v5", HIDE_ALL},
+      {"https://safebrowsing.google.com/safebrowsing", HIDE_ALL},
+      {"https://safebrowsing.google.com/safebrowsing/anything", HIDE_ALL},
+      {"https://safebrowsing.google.com", HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://chrome.google.com", HIDE_BROWSER_SUB_RESOURCE_REQUEST},
+      {"https://chrome.google.com/webstore", HIDE_ALL},
+      {"https://chrome.google.com./webstore", HIDE_ALL},
+      // Unsupported scheme.
       {"blob:https://chrome.google.com/fc3f440b-78ed-469f-8af8-7a1717ff39ae",
-       false, false},
-      {"https://chrome.google.com:80/webstore", true, true},
-      {"https://chrome.google.com/webstore?query", true, true},
+       HIDE_ALL},
+      {"https://chrome.google.com:80/webstore", HIDE_ALL},
+      {"https://chrome.google.com/webstore?query", HIDE_ALL},
   };
-  for (const TestCase& test : cases) {
+  const int kRendererProcessId = 1;
+  const int kBrowserProcessId = -1;
+  extensions::InfoMap* info_map = nullptr;
+
+  // Returns a WebRequestInfo instance constructed as per the given parameters.
+  auto create_request = [](const GURL& url, content::ResourceType type,
+                           int render_process_id) {
     WebRequestInfo request;
-    request.url = GURL(test.url);
-    EXPECT_TRUE(request.url.is_valid()) << test.url;
+    request.url = url;
+    request.type = type;
+    request.render_process_id = render_process_id;
 
-    request.initiator = url::Origin::Create(request.url);
-    EXPECT_EQ(test.is_sensitive_if_request_from_common_renderer,
-              IsSensitiveRequest(request, false /* is_request_from_browser */,
-                                 false /* is_request_from_web_ui_renderer */))
-        << test.url;
+    request.web_request_type = ToWebRequestResourceType(type);
+    request.is_browser_side_navigation =
+        type == content::RESOURCE_TYPE_MAIN_FRAME ||
+        type == content::RESOURCE_TYPE_SUB_FRAME;
+    return request;
+  };
 
-    const bool supported_in_webui_renderers =
-        !request.url.SchemeIsHTTPOrHTTPS();
-    request.initiator = base::nullopt;
-    EXPECT_EQ(test.is_sensitive_if_request_from_browser_or_webui_renderer,
-              IsSensitiveRequest(request, true /* is_request_from_browser */,
-                                 supported_in_webui_renderers))
-        << test.url;
+  for (const TestCase& test_case : cases) {
+    SCOPED_TRACE(test_case.url);
+
+    GURL request_url(test_case.url);
+    ASSERT_TRUE(request_url.is_valid());
+
+    {
+      SCOPED_TRACE("Renderer initiated sub-resource request");
+      WebRequestInfo request = create_request(
+          request_url, content::RESOURCE_TYPE_SUB_RESOURCE, kRendererProcessId);
+      bool expect_hidden =
+          test_case.expected_hide_request_mask & HIDE_RENDERER_REQUEST;
+      EXPECT_EQ(expect_hidden,
+                WebRequestPermissions::HideRequest(info_map, request));
+    }
+
+    {
+      SCOPED_TRACE("Browser initiated sub-resource request");
+      WebRequestInfo request = create_request(
+          request_url, content::RESOURCE_TYPE_SUB_RESOURCE, kBrowserProcessId);
+      bool expect_hidden = test_case.expected_hide_request_mask &
+                           HIDE_BROWSER_SUB_RESOURCE_REQUEST;
+      EXPECT_EQ(expect_hidden,
+                WebRequestPermissions::HideRequest(info_map, request));
+    }
+
+    {
+      SCOPED_TRACE("Main-frame navigation");
+      WebRequestInfo request = create_request(
+          request_url, content::RESOURCE_TYPE_MAIN_FRAME, kBrowserProcessId);
+      bool expect_hidden =
+          test_case.expected_hide_request_mask & HIDE_MAIN_FRAME_NAVIGATION;
+      EXPECT_EQ(expect_hidden,
+                WebRequestPermissions::HideRequest(info_map, request));
+    }
+
+    {
+      SCOPED_TRACE("Sub-frame navigation");
+      WebRequestInfo request = create_request(
+          request_url, content::RESOURCE_TYPE_SUB_FRAME, kBrowserProcessId);
+      bool expect_hidden =
+          test_case.expected_hide_request_mask & HIDE_SUB_FRAME_NAVIGATION;
+      EXPECT_EQ(expect_hidden,
+                WebRequestPermissions::HideRequest(info_map, request));
+    }
   }
 }
 
diff --git a/gpu/ipc/host/shader_disk_cache.cc b/gpu/ipc/host/shader_disk_cache.cc
index 2f3a9cad..66d2cc9 100644
--- a/gpu/ipc/host/shader_disk_cache.cc
+++ b/gpu/ipc/host/shader_disk_cache.cc
@@ -122,7 +122,7 @@
                     const base::FilePath& path,
                     const base::Time& delete_begin,
                     const base::Time& delete_end,
-                    const base::Closure& callback);
+                    base::OnceClosure callback);
   ~ShaderClearHelper();
 
   void Clear();
@@ -138,7 +138,7 @@
   base::FilePath path_;
   base::Time delete_begin_;
   base::Time delete_end_;
-  base::Closure callback_;
+  base::OnceClosure callback_;
   base::WeakPtrFactory<ShaderClearHelper> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ShaderClearHelper);
@@ -390,14 +390,14 @@
                                      const base::FilePath& path,
                                      const base::Time& delete_begin,
                                      const base::Time& delete_end,
-                                     const base::Closure& callback)
+                                     base::OnceClosure callback)
     : factory_(factory),
       cache_(std::move(cache)),
       op_type_(VERIFY_CACHE_SETUP),
       path_(path),
       delete_begin_(delete_begin),
       delete_end_(delete_end),
-      callback_(callback),
+      callback_(std::move(callback)),
       weak_ptr_factory_(this) {}
 
 ShaderClearHelper::~ShaderClearHelper() {
@@ -426,7 +426,7 @@
         op_type_ = TERMINATE;
         break;
       case TERMINATE:
-        callback_.Run();
+        std::move(callback_).Run();
         // Calling CacheCleared() destroys |this|.
         factory_->CacheCleared(path_);
         rv = net::ERR_IO_PENDING;  // Break the loop.
@@ -487,12 +487,13 @@
 void ShaderCacheFactory::ClearByPath(const base::FilePath& path,
                                      const base::Time& delete_begin,
                                      const base::Time& delete_end,
-                                     const base::Closure& callback) {
+                                     base::OnceClosure callback) {
   DCHECK(CalledOnValidThread());
   DCHECK(!callback.is_null());
 
-  auto helper = std::make_unique<ShaderClearHelper>(
-      this, GetByPath(path), path, delete_begin, delete_end, callback);
+  auto helper = std::make_unique<ShaderClearHelper>(this, GetByPath(path), path,
+                                                    delete_begin, delete_end,
+                                                    std::move(callback));
 
   // We could receive requests to clear the same path with different
   // begin/end times. So, we keep a list of requests. If we haven't seen this
@@ -517,12 +518,13 @@
 void ShaderCacheFactory::ClearByClientId(int32_t client_id,
                                          const base::Time& delete_begin,
                                          const base::Time& delete_end,
-                                         const base::Closure& callback) {
+                                         base::OnceClosure callback) {
   DCHECK(CalledOnValidThread());
   ClientIdToPathMap::iterator iter = client_id_to_path_map_.find(client_id);
   if (iter == client_id_to_path_map_.end())
     return;
-  return ClearByPath(iter->second, delete_begin, delete_end, callback);
+  return ClearByPath(iter->second, delete_begin, delete_end,
+                     std::move(callback));
 }
 
 void ShaderCacheFactory::CacheCleared(const base::FilePath& path) {
diff --git a/gpu/ipc/host/shader_disk_cache.h b/gpu/ipc/host/shader_disk_cache.h
index a1c0681..500c8786 100644
--- a/gpu/ipc/host/shader_disk_cache.h
+++ b/gpu/ipc/host/shader_disk_cache.h
@@ -31,7 +31,7 @@
 class ShaderDiskCache : public base::RefCounted<ShaderDiskCache> {
  public:
   using ShaderLoadedCallback =
-      base::Callback<void(const std::string&, const std::string&)>;
+      base::RepeatingCallback<void(const std::string&, const std::string&)>;
 
   void set_shader_loaded_callback(const ShaderLoadedCallback& callback) {
     shader_loaded_callback_ = callback;
@@ -118,14 +118,14 @@
   void ClearByPath(const base::FilePath& path,
                    const base::Time& begin_time,
                    const base::Time& end_time,
-                   const base::Closure& callback);
+                   base::OnceClosure callback);
 
   // Same as ClearByPath, but looks up the cache by |client_id|. The |callback|
   // will be executed when the clear is complete.
   void ClearByClientId(int32_t client_id,
                        const base::Time& begin_time,
                        const base::Time& end_time,
-                       const base::Closure& callback);
+                       base::OnceClosure callback);
 
   // Retrieve the shader disk cache for the provided |client_id|.
   scoped_refptr<ShaderDiskCache> Get(int32_t client_id);
diff --git a/gpu/ipc/host/shader_disk_cache_unittest.cc b/gpu/ipc/host/shader_disk_cache_unittest.cc
index 0dd1a59c7..dbb811c 100644
--- a/gpu/ipc/host/shader_disk_cache_unittest.cc
+++ b/gpu/ipc/host/shader_disk_cache_unittest.cc
@@ -5,6 +5,7 @@
 #include "gpu/ipc/host/shader_disk_cache.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/scoped_task_environment.h"
 #include "net/base/test_completion_callback.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -15,6 +16,8 @@
 const int kDefaultClientId = 42;
 const char kCacheKey[] = "key";
 const char kCacheValue[] = "cached value";
+const char kCacheKey2[] = "key2";
+const char kCacheValue2[] = "cached value2";
 
 }  // namespace
 
@@ -102,4 +105,39 @@
   ASSERT_EQ(net::OK, available_cb2.GetResult(rv2));
 };
 
+TEST_F(ShaderDiskCacheTest, MultipleLoaderCallbacks) {
+  InitCache();
+
+  // Create a cache and wait for it to open.
+  scoped_refptr<ShaderDiskCache> cache = factory()->Get(kDefaultClientId);
+  ASSERT_TRUE(cache.get() != nullptr);
+  net::TestCompletionCallback available_cb;
+  int rv = cache->SetAvailableCallback(available_cb.callback());
+  ASSERT_EQ(net::OK, available_cb.GetResult(rv));
+  EXPECT_EQ(0, cache->Size());
+
+  // Write two entries, wait for them to complete.
+  const int32_t count = 2;
+  cache->Cache(kCacheKey, kCacheValue);
+  cache->Cache(kCacheKey2, kCacheValue2);
+  net::TestCompletionCallback complete_cb;
+  rv = cache->SetCacheCompleteCallback(complete_cb.callback());
+  ASSERT_EQ(net::OK, complete_cb.GetResult(rv));
+  EXPECT_EQ(count, cache->Size());
+
+  // Close, re-open, and verify that two entries were loaded.
+  cache = nullptr;
+  cache = factory()->Get(kDefaultClientId);
+  ASSERT_TRUE(cache.get() != nullptr);
+  int loaded_calls = 0;
+  cache->set_shader_loaded_callback(base::BindLambdaForTesting(
+      [&loaded_calls](const std::string& key, const std::string& value) {
+        ++loaded_calls;
+      }));
+  net::TestCompletionCallback available_cb2;
+  int rv2 = cache->SetAvailableCallback(available_cb2.callback());
+  ASSERT_EQ(net::OK, available_cb2.GetResult(rv2));
+  EXPECT_EQ(count, loaded_calls);
+};
+
 }  // namespace gpu
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 7516b4bf..2f1fbc4 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -2278,6 +2278,12 @@
       mixins: "lkgr-ci"
     }
     builders {
+      name: "mac-osxbeta-rel"
+      mixins: "fyi-ci"
+      mixins: "mac"
+      dimensions:"os:Mac-10.14"
+    }
+    builders {
       name: "mac-views-rel"
       dimensions: "os:Mac-10.13"
       dimensions: "cores:4"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 576f3c2..27b4491 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -2834,9 +2834,14 @@
     category: "cronet"
   }
   builders {
-    name: "buildbot/chromium.fyi/mac-views-rel"
+    name: "buildbucket/luci.chromium.ci/mac-osxbeta-rel"
+    category: "mac"
+    short_name: "beta"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/mac-views-rel"
-    category: "default"
+    category: "mac"
+    short_name: "views"
   }
   builders {
     name: "buildbot/chromium.fyi/Chromium Mac 10.13"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 71ff0cc8..222cf97d 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -1951,6 +1951,17 @@
 }
 
 job {
+  id: "mac-osxbeta-rel"
+  # Triggered by "Mac Builder".
+  acl_sets: "triggered-by-parent-builders"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "mac-osxbeta-rel"
+  }
+}
+
+job {
   id: "Mac FYI Debug (Intel)"
   # Triggered by "GPU FYI Mac Builder (dbg)".
   acl_sets: "triggered-by-parent-builders"
diff --git a/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm b/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
index 1650258..02df437 100644
--- a/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
+++ b/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
@@ -124,8 +124,7 @@
                               bool has_user_gesture) WARN_UNUSED_RESULT {
     NSURL* url = [NSURL URLWithString:url_string];
     web::WebStatePolicyDecider::RequestInfo request_info(
-        ui::PageTransition::PAGE_TRANSITION_LINK,
-        /*source_url=*/GURL::EmptyGURL(), target_frame_is_main,
+        ui::PageTransition::PAGE_TRANSITION_LINK, target_frame_is_main,
         has_user_gesture);
     return tab_helper_->ShouldAllowRequest([NSURLRequest requestWithURL:url],
                                            request_info);
@@ -174,7 +173,7 @@
     NSURL* url = [NSURL
         URLWithString:@"itms-apps://itunes.apple.com/us/app/appname/id123"];
     web::WebStatePolicyDecider::RequestInfo request_info(
-        transition_type, pending_url,
+        transition_type,
         /*target_frame_is_main=*/true, /*has_user_gesture=*/true);
     EXPECT_FALSE(tab_helper_->ShouldAllowRequest(
         [NSURLRequest requestWithURL:url], request_info));
diff --git a/ios/chrome/browser/file_metadata_util.mm b/ios/chrome/browser/file_metadata_util.mm
index d64ce8d3..711dfe6 100644
--- a/ios/chrome/browser/file_metadata_util.mm
+++ b/ios/chrome/browser/file_metadata_util.mm
@@ -8,7 +8,7 @@
 
 #include "base/logging.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/threading/thread_restrictions.h"
+#include "base/threading/scoped_blocking_call.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -16,7 +16,7 @@
 
 void SetSkipSystemBackupAttributeToItem(const base::FilePath& path,
                                         bool skip_system_backup) {
-  base::AssertBlockingAllowed();
+  base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
 
   NSURL* file_url =
       [NSURL fileURLWithPath:base::SysUTF8ToNSString(path.value())];
diff --git a/ios/chrome/browser/itunes_urls/itunes_urls_handler_tab_helper_unittest.mm b/ios/chrome/browser/itunes_urls/itunes_urls_handler_tab_helper_unittest.mm
index 638c0bc..43c272a6 100644
--- a/ios/chrome/browser/itunes_urls/itunes_urls_handler_tab_helper_unittest.mm
+++ b/ios/chrome/browser/itunes_urls/itunes_urls_handler_tab_helper_unittest.mm
@@ -43,8 +43,7 @@
     fake_launcher_.launchedProductID = nil;
     fake_launcher_.launchedProductParams = nil;
     web::WebStatePolicyDecider::RequestInfo request_info(
-        ui::PageTransition::PAGE_TRANSITION_LINK,
-        /*source_url=*/GURL::EmptyGURL(), main_frame,
+        ui::PageTransition::PAGE_TRANSITION_LINK, main_frame,
         /*has_user_gesture=*/false);
     bool request_allowed = web_state_.ShouldAllowRequest(
         [NSURLRequest requestWithURL:[NSURL URLWithString:url_string]],
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
index b86d500..db61696 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
@@ -8,6 +8,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_block.h"
 #include "components/autofill/core/common/autofill_features.h"
+#include "components/autofill/ios/browser/autofill_switches.h"
 #import "components/autofill/ios/browser/js_suggestion_manager.h"
 #import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
 #include "components/autofill/ios/form_util/form_activity_params.h"
@@ -24,6 +25,7 @@
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/web/public/url_scheme_util.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
+#include "ios/web/public/web_state/web_frame.h"
 #include "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -180,6 +182,12 @@
     return;
   }
 
+  if (autofill::switches::IsAutofillIFrameMessagingEnabled() &&
+      (!frame || !frame->CanCallJavaScriptFunction())) {
+    [self reset];
+    return;
+  }
+
   if (params.type == "blur" || params.type == "change" ||
       params.type == "form_changed") {
     return;
diff --git a/ios/web/public/test/fakes/crw_fake_web_state_policy_decider.mm b/ios/web/public/test/fakes/crw_fake_web_state_policy_decider.mm
index 9c8a599..75aae3d 100644
--- a/ios/web/public/test/fakes/crw_fake_web_state_policy_decider.mm
+++ b/ios/web/public/test/fakes/crw_fake_web_state_policy_decider.mm
@@ -14,7 +14,6 @@
 
 FakeShouldAllowRequestInfo::FakeShouldAllowRequestInfo()
     : request_info(ui::PageTransition::PAGE_TRANSITION_FIRST,
-                   GURL::EmptyGURL(),
                    /*target_frame_is_main=*/false,
                    /*has_user_gesture=*/false) {}
 FakeShouldAllowRequestInfo::~FakeShouldAllowRequestInfo() = default;
diff --git a/ios/web/public/web_state/web_state_policy_decider.h b/ios/web/public/web_state/web_state_policy_decider.h
index a80f342..e560327 100644
--- a/ios/web/public/web_state/web_state_policy_decider.h
+++ b/ios/web/public/web_state/web_state_policy_decider.h
@@ -23,18 +23,14 @@
   // request passed to WebStatePolicyDecider::ShouldAllowRequest().
   struct RequestInfo {
     RequestInfo(ui::PageTransition transition_type,
-                const GURL& source_url,
                 bool target_frame_is_main,
                 bool has_user_gesture)
         : transition_type(transition_type),
-          source_url(source_url),
           target_frame_is_main(target_frame_is_main),
           has_user_gesture(has_user_gesture) {}
     // The navigation page transition type.
     ui::PageTransition transition_type =
         ui::PageTransition::PAGE_TRANSITION_FIRST;
-    // The source URL that the request was initiated from.
-    GURL source_url;
     // Indicates whether the navigation target frame is the main frame.
     bool target_frame_is_main = false;
     // Indicates if there was a recent user interaction with the request frame.
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index f3989b7..ae8bca796 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -4394,11 +4394,8 @@
       [self userClickedRecently] &&
       net::GURLWithNSURL(action.request.mainDocumentURL) ==
           _lastUserInteraction->main_document_url;
-  web::NavigationItem* item = self.currentNavItem;
-  const GURL& sourceURL =
-      item ? item->GetOriginalRequestURL() : GURL::EmptyGURL();
   web::WebStatePolicyDecider::RequestInfo requestInfo(
-      transition, sourceURL, isMainFrameNavigationAction,
+      transition, isMainFrameNavigationAction,
       userInteractedWithRequestMainFrame);
   // First check if the navigation action should be blocked by the controller
   // and make sure to update the controller in the case that the controller
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index 1d2b675..3b657aa 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -719,7 +719,6 @@
 MATCHER_P(RequestInfoMatch, expected_request_info, /* argument_name = */ "") {
   return ui::PageTransitionTypeIncludingQualifiersIs(
              arg.transition_type, expected_request_info.transition_type) &&
-         arg.source_url == expected_request_info.source_url &&
          arg.target_frame_is_main ==
              expected_request_info.target_frame_is_main &&
          arg.has_user_gesture == expected_request_info.has_user_gesture;
@@ -741,7 +740,7 @@
   // Test that ShouldAllowRequest() is called for the same parameters.
   WebStatePolicyDecider::RequestInfo request_info_main_frame(
       ui::PageTransition::PAGE_TRANSITION_LINK,
-      /*source_url=*/GURL::EmptyGURL(), /*target_main_frame=*/true,
+      /*target_main_frame=*/true,
       /*has_user_gesture=*/false);
   EXPECT_CALL(decider, ShouldAllowRequest(
                            request, RequestInfoMatch(request_info_main_frame)))
@@ -756,7 +755,7 @@
 
   WebStatePolicyDecider::RequestInfo request_info_iframe(
       ui::PageTransition::PAGE_TRANSITION_LINK,
-      /*source_url=*/GURL::EmptyGURL(), /*target_main_frame=*/false,
+      /*target_main_frame=*/false,
       /*has_user_gesture=*/false);
 
   EXPECT_CALL(decider, ShouldAllowRequest(
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm
index bede7a971..0f6aea5 100644
--- a/ios/web/web_state/web_state_observer_inttest.mm
+++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -554,7 +554,6 @@
 MATCHER_P(RequestInfoMatch, expected_request_info, /* argument_name = */ "") {
   return ui::PageTransitionTypeIncludingQualifiersIs(
              arg.transition_type, expected_request_info.transition_type) &&
-         arg.source_url == expected_request_info.source_url &&
          arg.target_frame_is_main ==
              expected_request_info.target_frame_is_main &&
          arg.has_user_gesture == expected_request_info.has_user_gesture;
@@ -668,7 +667,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -702,7 +701,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -740,7 +739,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -776,7 +775,7 @@
   // Perform new page navigation.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -796,7 +795,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo reload_request_info(
-      ui::PageTransition::PAGE_TRANSITION_RELOAD, url,
+      ui::PageTransition::PAGE_TRANSITION_RELOAD,
       /*target_main_frame=*/true,
       /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
@@ -836,7 +835,7 @@
   // Perform new page navigation.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -889,7 +888,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -911,14 +910,14 @@
 
   // Perform same-document navigation.
   const GURL hash_url = test_server_->GetURL("/echo#1");
-  WebStatePolicyDecider::RequestInfo hash_url_expected_request_info_(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, hash_url,
+  WebStatePolicyDecider::RequestInfo hash_url_expected_request_info(
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
 
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   EXPECT_CALL(
       *decider_,
-      ShouldAllowRequest(_, RequestInfoMatch(hash_url_expected_request_info_)))
+      ShouldAllowRequest(_, RequestInfoMatch(hash_url_expected_request_info)))
       .WillOnce(Return(true));
   EXPECT_CALL(observer_, DidStartNavigation(web_state(), _))
       .WillOnce(VerifySameDocumentStartedContext(
@@ -968,7 +967,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1023,7 +1022,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1160,7 +1159,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, web_url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1228,7 +1227,7 @@
     int32_t nav_id = 0;
     EXPECT_CALL(observer_, DidStartLoading(web_state()));
     WebStatePolicyDecider::RequestInfo expected_request_info(
-        ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+        ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
         /*target_main_frame=*/true, /*has_user_gesture=*/false);
     EXPECT_CALL(*decider_,
                 ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1257,7 +1256,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1295,7 +1294,7 @@
   // Perform new page navigation.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1316,7 +1315,7 @@
   NavigationContext* context = nullptr;
   int32_t nav_id = 0;
   WebStatePolicyDecider::RequestInfo form_request_info(
-      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT, url,
+      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(form_request_info)))
@@ -1352,7 +1351,7 @@
   // Perform new page navigation.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1371,7 +1370,7 @@
 
   // Submit the form using JavaScript.
   WebStatePolicyDecider::RequestInfo form_request_info(
-      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT, url,
+      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(form_request_info)))
@@ -1401,7 +1400,7 @@
     // repost before calling policy decider.
   } else {
     WebStatePolicyDecider::RequestInfo form_reload_request_info(
-        ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT, action,
+        ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT,
         /*target_main_frame=*/true, /*has_user_gesture=*/false);
 
     EXPECT_CALL(*decider_, ShouldAllowRequest(
@@ -1453,7 +1452,7 @@
   // Perform new page navigation.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1472,7 +1471,7 @@
 
   // Submit the form using JavaScript.
   WebStatePolicyDecider::RequestInfo form_request_info(
-      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT, url,
+      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(form_request_info)))
@@ -1495,7 +1494,7 @@
 
   // Go Back.
   WebStatePolicyDecider::RequestInfo back_request_info(
-      ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK, url,
+      ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   if (GetWebClient()->IsSlimNavigationManagerEnabled()) {
     EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())).Times(2);
@@ -1528,7 +1527,7 @@
 
   // Go forward.
   WebStatePolicyDecider::RequestInfo forward_request_info(
-      ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK, action,
+      ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   NavigationContext* context = nullptr;
   int32_t nav_id = 0;
@@ -1574,7 +1573,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1608,7 +1607,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1642,7 +1641,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1679,7 +1678,6 @@
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
       ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
-      test_server_->GetURL("/echo"),
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1699,7 +1697,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1725,7 +1723,6 @@
   EXPECT_CALL(observer_, DidStopLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
       ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
-      /*source_url=*/GURL::EmptyGURL(),
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1743,7 +1740,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1788,7 +1785,7 @@
   // Callbacks due to loading of the main frame.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1799,7 +1796,7 @@
   EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _));
   // Callbacks due to initial loading of iframe.
   WebStatePolicyDecider::RequestInfo iframe_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/false, /*has_user_gesture=*/true);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(iframe_request_info)))
diff --git a/ios/web/web_state/web_state_policy_decider_bridge_unittest.mm b/ios/web/web_state/web_state_policy_decider_bridge_unittest.mm
index 4243c51..4a00fa3 100644
--- a/ios/web/web_state/web_state_policy_decider_bridge_unittest.mm
+++ b/ios/web/web_state/web_state_policy_decider_bridge_unittest.mm
@@ -35,9 +35,8 @@
   ui::PageTransition transition_type = ui::PageTransition::PAGE_TRANSITION_LINK;
   bool target_frame_is_main = true;
   bool has_user_gesture = false;
-  GURL source_url("http://source.url");
   WebStatePolicyDecider::RequestInfo request_info(
-      transition_type, source_url, target_frame_is_main, has_user_gesture);
+      transition_type, target_frame_is_main, has_user_gesture);
   decider_bridge_.ShouldAllowRequest(request, request_info);
   FakeShouldAllowRequestInfo* should_allow_request_info =
       [decider_ shouldAllowRequestInfo];
@@ -47,7 +46,6 @@
             should_allow_request_info->request_info.target_frame_is_main);
   EXPECT_EQ(has_user_gesture,
             should_allow_request_info->request_info.has_user_gesture);
-  EXPECT_EQ(source_url, should_allow_request_info->request_info.source_url);
   EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
       transition_type,
       should_allow_request_info->request_info.transition_type));
diff --git a/ios/web_view/internal/signin/ios_web_view_signin_client.mm b/ios/web_view/internal/signin/ios_web_view_signin_client.mm
index 2f16af00..249d151 100644
--- a/ios/web_view/internal/signin/ios_web_view_signin_client.mm
+++ b/ios/web_view/internal/signin/ios_web_view_signin_client.mm
@@ -104,7 +104,9 @@
                                            url_loader_factory);
 }
 
-void IOSWebViewSigninClient::OnErrorChanged() {}
+void IOSWebViewSigninClient::OnErrorChanged() {
+  [sync_controller_ didUpdateAuthError:signin_error_controller_->auth_error()];
+}
 
 void IOSWebViewSigninClient::SetSyncController(
     CWVSyncController* sync_controller) {
diff --git a/ios/web_view/internal/sync/cwv_sync_controller.mm b/ios/web_view/internal/sync/cwv_sync_controller.mm
index bb6619ec..1266bb85 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller.mm
+++ b/ios/web_view/internal/sync/cwv_sync_controller.mm
@@ -23,6 +23,42 @@
 #error "This file requires ARC support."
 #endif
 
+NSErrorDomain const CWVSyncErrorDomain =
+    @"org.chromium.chromewebview.SyncErrorDomain";
+
+namespace {
+CWVSyncError CWVConvertGoogleServiceAuthErrorStateToCWVSyncError(
+    GoogleServiceAuthError::State state) {
+  switch (state) {
+    case GoogleServiceAuthError::NONE:
+      return CWVSyncErrorNone;
+    case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
+      return CWVSyncErrorInvalidGAIACredentials;
+    case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
+      return CWVSyncErrorUserNotSignedUp;
+    case GoogleServiceAuthError::CONNECTION_FAILED:
+      return CWVSyncErrorConnectionFailed;
+    case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
+      return CWVSyncErrorServiceUnavailable;
+    case GoogleServiceAuthError::REQUEST_CANCELED:
+      return CWVSyncErrorRequestCanceled;
+    case GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE:
+      return CWVSyncErrorUnexpectedServiceResponse;
+    // The following errors are unexpected on iOS.
+    case GoogleServiceAuthError::CAPTCHA_REQUIRED:
+    case GoogleServiceAuthError::ACCOUNT_DELETED:
+    case GoogleServiceAuthError::ACCOUNT_DISABLED:
+    case GoogleServiceAuthError::TWO_FACTOR:
+    case GoogleServiceAuthError::HOSTED_NOT_ALLOWED_DEPRECATED:
+    case GoogleServiceAuthError::SERVICE_ERROR:
+    case GoogleServiceAuthError::WEB_LOGIN_REQUIRED:
+    case GoogleServiceAuthError::NUM_STATES:
+      NOTREACHED();
+      return CWVSyncErrorNone;
+  }
+}
+}  // namespace
+
 @interface CWVSyncController ()
 
 // Called by WebViewSyncServiceObserverBridge's |OnSyncConfigurationCompleted|.
@@ -225,4 +261,17 @@
   [_delegate syncController:self didStopSyncWithReason:reason];
 }
 
+- (void)didUpdateAuthError:(const GoogleServiceAuthError&)authError {
+  CWVSyncError code =
+      CWVConvertGoogleServiceAuthErrorStateToCWVSyncError(authError.state());
+  if (code != CWVSyncErrorNone) {
+    if ([_delegate
+            respondsToSelector:@selector(syncController:didFailWithError:)]) {
+      NSError* error =
+          [NSError errorWithDomain:CWVSyncErrorDomain code:code userInfo:nil];
+      [_delegate syncController:self didFailWithError:error];
+    }
+  }
+}
+
 @end
diff --git a/ios/web_view/internal/sync/cwv_sync_controller_internal.h b/ios/web_view/internal/sync/cwv_sync_controller_internal.h
index 1d0ee27..f00b564 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller_internal.h
+++ b/ios/web_view/internal/sync/cwv_sync_controller_internal.h
@@ -8,6 +8,7 @@
 #include <set>
 
 #include "components/signin/core/browser/signin_metrics.h"
+#include "google_apis/gaia/google_service_auth_error.h"
 #include "ios/web_view/internal/signin/web_view_profile_oauth2_token_service_ios_provider_impl.h"
 #import "ios/web_view/public/cwv_sync_controller.h"
 
@@ -38,6 +39,9 @@
 // Called by IOSWebViewSigninClient when signing out.
 - (void)didSignoutWithSourceMetric:(signin_metrics::ProfileSignout)metric;
 
+// Called by IOSWebViewSigninClient when auth error changes.
+- (void)didUpdateAuthError:(const GoogleServiceAuthError&)authError;
+
 @end
 
 #endif  // IOS_WEB_VIEW_INTERNAL_SYNC_CWV_SYNC_CONTROLLER_INTERNAL_H_
diff --git a/ios/web_view/internal/sync/web_view_profile_sync_service_factory.mm b/ios/web_view/internal/sync/web_view_profile_sync_service_factory.mm
index 0344133..30e2f97 100644
--- a/ios/web_view/internal/sync/web_view_profile_sync_service_factory.mm
+++ b/ios/web_view/internal/sync/web_view_profile_sync_service_factory.mm
@@ -90,6 +90,8 @@
   init_params.network_time_update_callback = base::DoNothing();
   init_params.signin_scoped_device_id_callback = base::BindRepeating(
       &signin::GetSigninScopedDeviceId, browser_state->GetPrefs());
+  init_params.network_connection_tracker =
+      ApplicationContext::GetInstance()->GetNetworkConnectionTracker();
 
   auto profile_sync_service =
       std::make_unique<ProfileSyncService>(std::move(init_params));
diff --git a/ios/web_view/public/cwv_sync_controller.h b/ios/web_view/public/cwv_sync_controller.h
index d14f2da..cd342e3 100644
--- a/ios/web_view/public/cwv_sync_controller.h
+++ b/ios/web_view/public/cwv_sync_controller.h
@@ -15,6 +15,32 @@
 @protocol CWVSyncControllerDataSource;
 @protocol CWVSyncControllerDelegate;
 
+// The error domain for sync errors.
+FOUNDATION_EXPORT CWV_EXPORT NSErrorDomain const CWVSyncErrorDomain;
+
+// Possible error codes during syncing.
+typedef NS_ENUM(NSInteger, CWVSyncError) {
+  // No error.
+  CWVSyncErrorNone = 0,
+  // The credentials supplied to GAIA were either invalid, or the locally
+  // cached credentials have expired.
+  CWVSyncErrorInvalidGAIACredentials = -100,
+  // The GAIA user is not authorized to use the service.
+  CWVSyncErrorUserNotSignedUp = -200,
+  // Could not connect to server to verify credentials. This could be in
+  // response to either failure to connect to GAIA or failure to connect to
+  // the service needing GAIA tokens during authentication.
+  CWVSyncErrorConnectionFailed = -300,
+  // The service is not available; try again later.
+  CWVSyncErrorServiceUnavailable = -400,
+  // The requestor of the authentication step cancelled the request
+  // prior to completion.
+  CWVSyncErrorRequestCanceled = -500,
+  // Indicates the service responded to a request, but we cannot
+  // interpret the response.
+  CWVSyncErrorUnexpectedServiceResponse = -600,
+};
+
 CWV_EXPORT
 // Used to manage syncing for autofill and password data. Usage:
 // 1. Call |startSyncWithIdentity:dataSource:| to start syncing with identity.
diff --git a/ios/web_view/public/cwv_sync_controller_delegate.h b/ios/web_view/public/cwv_sync_controller_delegate.h
index 3b71011..1a801c0 100644
--- a/ios/web_view/public/cwv_sync_controller_delegate.h
+++ b/ios/web_view/public/cwv_sync_controller_delegate.h
@@ -27,8 +27,12 @@
 // property to see if |unlockWithPassphrase:| is necessary.
 - (void)syncControllerDidStartSync:(CWVSyncController*)syncController;
 
-// Called after the sync was stopped.
-// |reason| Indicates why sync was stopped.
+// Called when sync fails. |error|'s code is a CWVSyncError.
+// May need to call |stopSyncAndClearIdentity| and try starting again later.
+- (void)syncController:(CWVSyncController*)syncController
+      didFailWithError:(NSError*)error;
+
+// Called after sync was stopped. |reason| Indicates why sync was stopped.
 - (void)syncController:(CWVSyncController*)syncController
     didStopSyncWithReason:(CWVStopSyncReason)reason;
 
diff --git a/media/audio/fuchsia/audio_output_stream_fuchsia.cc b/media/audio/fuchsia/audio_output_stream_fuchsia.cc
index 4c3f404..db239da 100644
--- a/media/audio/fuchsia/audio_output_stream_fuchsia.cc
+++ b/media/audio/fuchsia/audio_output_stream_fuchsia.cc
@@ -13,9 +13,9 @@
 
 namespace media {
 
-// Current AudioOut implementation allows only one buffer with id=0.
+// Current AudioRenderer implementation allows only one buffer with id=0.
 // TODO(sergeyu): Replace with an incrementing buffer id once AddPayloadBuffer()
-// and RemovePayloadBuffer() are implemented properly in AudioOut.
+// and RemovePayloadBuffer() are implemented properly in AudioRenderer.
 const uint32_t kBufferId = 0;
 
 AudioOutputStreamFuchsia::AudioOutputStreamFuchsia(
@@ -27,36 +27,36 @@
 
 AudioOutputStreamFuchsia::~AudioOutputStreamFuchsia() {
   // Close() must be called first.
-  DCHECK(!audio_out_);
+  DCHECK(!audio_renderer_);
 }
 
 bool AudioOutputStreamFuchsia::Open() {
-  DCHECK(!audio_out_);
+  DCHECK(!audio_renderer_);
 
-  // Connect |audio_out_| to the audio service.
+  // Connect |audio_renderer_| to the audio service.
   fuchsia::media::AudioPtr audio_server =
       base::fuchsia::ComponentContext::GetDefault()
           ->ConnectToService<fuchsia::media::Audio>();
-  audio_server->CreateAudioOut(audio_out_.NewRequest());
-  audio_out_.set_error_handler(
+  audio_server->CreateAudioRenderer(audio_renderer_.NewRequest());
+  audio_renderer_.set_error_handler(
       fit::bind_member(this, &AudioOutputStreamFuchsia::OnRendererError));
 
-  // Inform the |audio_out_| of the format required by the caller.
+  // Inform the |audio_renderer_| of the format required by the caller.
   fuchsia::media::AudioStreamType format;
   format.sample_format = fuchsia::media::AudioSampleFormat::FLOAT;
   format.channels = parameters_.channels();
   format.frames_per_second = parameters_.sample_rate();
-  audio_out_->SetPcmStreamType(std::move(format));
+  audio_renderer_->SetPcmStreamType(std::move(format));
 
   // Use number of samples to specify media position.
-  audio_out_->SetPtsUnits(parameters_.sample_rate(), 1);
+  audio_renderer_->SetPtsUnits(parameters_.sample_rate(), 1);
 
   // Setup OnMinLeadTimeChanged event listener. This event is used to get
   // |min_lead_time_|, which indicates how far ahead audio samples need to be
   // sent to the renderer.
-  audio_out_.events().OnMinLeadTimeChanged =
+  audio_renderer_.events().OnMinLeadTimeChanged =
       fit::bind_member(this, &AudioOutputStreamFuchsia::OnMinLeadTimeChanged);
-  audio_out_->EnableMinLeadTimeEvents(true);
+  audio_renderer_->EnableMinLeadTimeEvents(true);
 
   // The renderer may fail initialization asynchronously, which is handled in
   // OnRendererError().
@@ -75,8 +75,8 @@
 void AudioOutputStreamFuchsia::Stop() {
   callback_ = nullptr;
   reference_time_ = base::TimeTicks();
-  audio_out_->PauseNoReply();
-  audio_out_->DiscardAllPacketsNoReply();
+  audio_renderer_->PauseNoReply();
+  audio_renderer_->DiscardAllPacketsNoReply();
   timer_.Stop();
 }
 
@@ -91,7 +91,7 @@
 
 void AudioOutputStreamFuchsia::Close() {
   Stop();
-  audio_out_.Unbind();
+  audio_renderer_.Unbind();
 
   // Signal to the manager that we're closed and can be removed. This should be
   // the last call in the function as it deletes |this|.
@@ -125,7 +125,7 @@
   }
 
   payload_buffer_pos_ = 0;
-  audio_out_->AddPayloadBuffer(
+  audio_renderer_->AddPayloadBuffer(
       kBufferId, zx::vmo(payload_buffer_.handle().Duplicate().GetHandle()));
 
   return true;
@@ -146,7 +146,7 @@
 }
 
 void AudioOutputStreamFuchsia::OnRendererError() {
-  LOG(WARNING) << "AudioOut has failed.";
+  LOG(WARNING) << "AudioRenderer has failed.";
   ReportError();
 }
 
@@ -158,7 +158,7 @@
 }
 
 void AudioOutputStreamFuchsia::PumpSamples() {
-  DCHECK(audio_out_);
+  DCHECK(audio_renderer_);
 
   // Allocate payload buffer if necessary.
   if (!payload_buffer_.mapped_size() && !InitializePayloadBuffer()) {
@@ -187,8 +187,8 @@
   if (reference_time_.is_null()) {
     stream_position_samples_ = 0;
     reference_time_ = now + min_lead_time_;
-    audio_out_->PlayNoReply(reference_time_.ToZxTime(),
-                            stream_position_samples_);
+    audio_renderer_->PlayNoReply(reference_time_.ToZxTime(),
+                                 stream_position_samples_);
   }
 
   // Request more samples from |callback_|.
@@ -212,7 +212,7 @@
   packet.payload_offset = payload_buffer_pos_;
   packet.payload_size = packet_size;
   packet.flags = 0;
-  audio_out_->SendPacketNoReply(std::move(packet));
+  audio_renderer_->SendPacketNoReply(std::move(packet));
 
   stream_position_samples_ += frames_filled;
   payload_buffer_pos_ =
diff --git a/media/audio/fuchsia/audio_output_stream_fuchsia.h b/media/audio/fuchsia/audio_output_stream_fuchsia.h
index da710f0d..9c1837a 100644
--- a/media/audio/fuchsia/audio_output_stream_fuchsia.h
+++ b/media/audio/fuchsia/audio_output_stream_fuchsia.h
@@ -61,7 +61,7 @@
   AudioManagerFuchsia* manager_;
   AudioParameters parameters_;
 
-  fuchsia::media::AudioOutPtr audio_out_;
+  fuchsia::media::AudioRendererPtr audio_renderer_;
 
   // |audio_bus_| is used only in PumpSamples(). It is kept here to avoid
   // reallocating the memory every time.
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index fbc0324..db1266a7 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -278,6 +278,8 @@
     "video_renderer.h",
     "video_rotation.cc",
     "video_rotation.h",
+    "video_thumbnail_decoder.cc",
+    "video_thumbnail_decoder.h",
     "video_types.cc",
     "video_types.h",
     "video_util.cc",
@@ -521,6 +523,7 @@
     "video_frame_layout_unittest.cc",
     "video_frame_pool_unittest.cc",
     "video_frame_unittest.cc",
+    "video_thumbnail_decoder_unittest.cc",
     "video_types_unittest.cc",
     "video_util_unittest.cc",
     "wall_clock_time_source_unittest.cc",
diff --git a/media/base/video_thumbnail_decoder.cc b/media/base/video_thumbnail_decoder.cc
new file mode 100644
index 0000000..18fbcfc
--- /dev/null
+++ b/media/base/video_thumbnail_decoder.cc
@@ -0,0 +1,81 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/video_thumbnail_decoder.h"
+
+#include "base/bind_helpers.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/video_frame.h"
+
+namespace media {
+
+VideoThumbnailDecoder::VideoThumbnailDecoder(
+    std::unique_ptr<VideoDecoder> decoder,
+    const VideoDecoderConfig& config,
+    std::vector<uint8_t> encoded_data)
+    : decoder_(std::move(decoder)),
+      config_(config),
+      encoded_data_(std::move(encoded_data)),
+      weak_factory_(this) {
+  DCHECK(!encoded_data_.empty());
+  DCHECK(config_.IsValidConfig());
+}
+
+VideoThumbnailDecoder::~VideoThumbnailDecoder() = default;
+
+void VideoThumbnailDecoder::Start(VideoFrameCallback video_frame_callback) {
+  video_frame_callback_ = std::move(video_frame_callback);
+  DCHECK(video_frame_callback_);
+  decoder_->Initialize(
+      config_, false, nullptr,
+      base::BindRepeating(&VideoThumbnailDecoder::OnVideoDecoderInitialized,
+                          weak_factory_.GetWeakPtr()),
+      base::BindRepeating(&VideoThumbnailDecoder::OnVideoFrameDecoded,
+                          weak_factory_.GetWeakPtr()),
+      base::DoNothing());
+}
+
+void VideoThumbnailDecoder::OnVideoDecoderInitialized(bool success) {
+  if (!success) {
+    NotifyComplete(nullptr);
+    return;
+  }
+
+  auto buffer =
+      DecoderBuffer::CopyFrom(&encoded_data_[0], encoded_data_.size());
+  encoded_data_.clear();
+  decoder_->Decode(
+      buffer, base::BindRepeating(&VideoThumbnailDecoder::OnVideoBufferDecoded,
+                                  weak_factory_.GetWeakPtr()));
+}
+
+void VideoThumbnailDecoder::OnVideoBufferDecoded(DecodeStatus status) {
+  if (status != DecodeStatus::OK) {
+    NotifyComplete(nullptr);
+    return;
+  }
+
+  // Enqueue eos since only one video frame is needed for thumbnail.
+  decoder_->Decode(
+      DecoderBuffer::CreateEOSBuffer(),
+      base::BindRepeating(&VideoThumbnailDecoder::OnEosBufferDecoded,
+                          weak_factory_.GetWeakPtr()));
+}
+
+void VideoThumbnailDecoder::OnEosBufferDecoded(DecodeStatus status) {
+  if (status != DecodeStatus::OK)
+    NotifyComplete(nullptr);
+}
+
+void VideoThumbnailDecoder::OnVideoFrameDecoded(
+    const scoped_refptr<VideoFrame>& frame) {
+  NotifyComplete(std::move(frame));
+}
+
+void VideoThumbnailDecoder::NotifyComplete(scoped_refptr<VideoFrame> frame) {
+  DCHECK(video_frame_callback_);
+  std::move(video_frame_callback_).Run(std::move(frame));
+}
+
+}  // namespace media
diff --git a/media/base/video_thumbnail_decoder.h b/media/base/video_thumbnail_decoder.h
new file mode 100644
index 0000000..8f9518f
--- /dev/null
+++ b/media/base/video_thumbnail_decoder.h
@@ -0,0 +1,65 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_VIDEO_THUMBNAIL_DECODER_H_
+#define MEDIA_BASE_VIDEO_THUMBNAIL_DECODER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "media/base/media_export.h"
+#include "media/base/video_decoder.h"
+#include "media/base/video_decoder_config.h"
+
+namespace media {
+
+class VideoFrame;
+
+// Class to decode a single video frame for video thumbnail generation.
+class MEDIA_EXPORT VideoThumbnailDecoder {
+ public:
+  using VideoFrameCallback =
+      base::OnceCallback<void(scoped_refptr<VideoFrame>)>;
+
+  // Creates the thumbnail decoder with |decoder| that performs the actual work.
+  // |encoded_data| is the encoded frame extracted with ffmpeg. A video frame
+  // will be returned through |video_frame_callback|.
+  VideoThumbnailDecoder(std::unique_ptr<VideoDecoder> decoder,
+                        const VideoDecoderConfig& config,
+                        std::vector<uint8_t> encoded_data);
+  ~VideoThumbnailDecoder();
+
+  // Starts to decode the video frame.
+  void Start(VideoFrameCallback video_frame_callback);
+
+ private:
+  void OnVideoDecoderInitialized(bool success);
+  void OnVideoBufferDecoded(DecodeStatus status);
+  void OnEosBufferDecoded(DecodeStatus status);
+
+  // Called when the output frame is generated.
+  void OnVideoFrameDecoded(const scoped_refptr<VideoFrame>& frame);
+
+  void NotifyComplete(scoped_refptr<VideoFrame> frame);
+
+  // The decoder that does the actual decoding.
+  std::unique_ptr<VideoDecoder> decoder_;
+  const VideoDecoderConfig config_;
+
+  // The demuxed, encoded video frame waiting to be decoded.
+  std::vector<uint8_t> encoded_data_;
+
+  VideoFrameCallback video_frame_callback_;
+  base::WeakPtrFactory<VideoThumbnailDecoder> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(VideoThumbnailDecoder);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_VIDEO_THUMBNAIL_DECODER_H_
diff --git a/media/base/video_thumbnail_decoder_unittest.cc b/media/base/video_thumbnail_decoder_unittest.cc
new file mode 100644
index 0000000..8edfd5a
--- /dev/null
+++ b/media/base/video_thumbnail_decoder_unittest.cc
@@ -0,0 +1,117 @@
+// 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 <memory>
+#include <vector>
+
+#include "base/bind_helpers.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "media/base/gmock_callback_support.h"
+#include "media/base/media_util.h"
+#include "media/base/mock_filters.h"
+#include "media/base/video_decoder_config.h"
+#include "media/base/video_frame.h"
+#include "media/base/video_thumbnail_decoder.h"
+#include "media/base/video_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+
+namespace media {
+namespace {
+
+class VideoThumbnailDecoderTest : public testing::Test {
+ public:
+  VideoThumbnailDecoderTest() {}
+  ~VideoThumbnailDecoderTest() override {}
+
+ protected:
+  void SetUp() override {
+    auto mock_video_decoder = std::make_unique<MockVideoDecoder>();
+    mock_video_decoder_ = mock_video_decoder.get();
+    VideoDecoderConfig valid_config(
+        kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420, COLOR_SPACE_UNSPECIFIED,
+        VIDEO_ROTATION_0, gfx::Size(1, 1), gfx::Rect(1, 1), gfx::Size(1, 1),
+        EmptyExtraData(), Unencrypted());
+
+    thumbnail_decoder_ = std::make_unique<VideoThumbnailDecoder>(
+        std::move(mock_video_decoder), valid_config, std::vector<uint8_t>{0u});
+  }
+
+  void TearDown() override {}
+
+  void Start() {
+    thumbnail_decoder_->Start(base::BindOnce(
+        &VideoThumbnailDecoderTest::OnFrameDecoded, base::Unretained(this)));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  scoped_refptr<VideoFrame> CreateFrame() {
+    return VideoFrame::CreateZeroInitializedFrame(
+        VideoPixelFormat::PIXEL_FORMAT_I420, gfx::Size(1, 1), gfx::Rect(1, 1),
+        gfx::Size(1, 1), base::TimeDelta());
+  }
+
+  VideoThumbnailDecoder* thumbnail_decoder() {
+    return thumbnail_decoder_.get();
+  }
+  MockVideoDecoder* mock_video_decoder() { return mock_video_decoder_; }
+  const scoped_refptr<VideoFrame>& frame() { return frame_; }
+
+ private:
+  void OnFrameDecoded(scoped_refptr<VideoFrame> frame) {
+    frame_ = std::move(frame);
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  MockVideoDecoder* mock_video_decoder_;
+  std::unique_ptr<VideoThumbnailDecoder> thumbnail_decoder_;
+
+  // The video frame returned from the thumbnail decoder.
+  scoped_refptr<VideoFrame> frame_;
+
+  DISALLOW_COPY_AND_ASSIGN(VideoThumbnailDecoderTest);
+};
+
+// Verifies a video frame can be delivered when decoder successfully created
+// the video frame.
+TEST_F(VideoThumbnailDecoderTest, Success) {
+  auto expected_frame = CreateFrame();
+  EXPECT_CALL(*mock_video_decoder(), Initialize(_, _, _, _, _, _))
+      .WillOnce(DoAll(RunCallback<3>(true), RunCallback<4>(expected_frame)));
+  EXPECT_CALL(*mock_video_decoder(), Decode(_, _))
+      .Times(2)
+      .WillRepeatedly(RunCallback<1>(DecodeStatus::OK));
+
+  Start();
+  EXPECT_TRUE(frame());
+}
+
+// No output video frame when decoder failed to initialize.
+TEST_F(VideoThumbnailDecoderTest, InitializationFailed) {
+  auto expected_frame = CreateFrame();
+  EXPECT_CALL(*mock_video_decoder(), Initialize(_, _, _, _, _, _))
+      .WillOnce(RunCallback<3>(false));
+
+  Start();
+  EXPECT_FALSE(frame());
+}
+
+// No output video frame when decoder failed to decode.
+TEST_F(VideoThumbnailDecoderTest, DecodingFailed) {
+  auto expected_frame = CreateFrame();
+  EXPECT_CALL(*mock_video_decoder(), Initialize(_, _, _, _, _, _))
+      .WillOnce(RunCallback<3>(true));
+  EXPECT_CALL(*mock_video_decoder(), Decode(_, _))
+      .WillOnce(RunCallback<1>(DecodeStatus::DECODE_ERROR));
+
+  Start();
+  EXPECT_FALSE(frame());
+}
+
+}  // namespace
+}  // namespace media
diff --git a/media/cdm/cdm_helpers.cc b/media/cdm/cdm_helpers.cc
index 945143f..eb98759 100644
--- a/media/cdm/cdm_helpers.cc
+++ b/media/cdm/cdm_helpers.cc
@@ -17,15 +17,14 @@
 
 gfx::ColorSpace::RangeID ToGfxColorSpaceRange(cdm::ColorRange range) {
   switch (range) {
+    case cdm::ColorRange::kInvalid:
+      return gfx::ColorSpace::RangeID::INVALID;
     case cdm::ColorRange::kLimited:
       return gfx::ColorSpace::RangeID::LIMITED;
     case cdm::ColorRange::kFull:
       return gfx::ColorSpace::RangeID::FULL;
     case cdm::ColorRange::kDerived:
       return gfx::ColorSpace::RangeID::DERIVED;
-    default:
-      DVLOG(1) << "Invalid color range";
-      return gfx::ColorSpace::RangeID::INVALID;
   }
 }
 
diff --git a/media/cdm/library_cdm/clear_key_cdm/BUILD.gn b/media/cdm/library_cdm/clear_key_cdm/BUILD.gn
index 932e2bd..bfbd633 100644
--- a/media/cdm/library_cdm/clear_key_cdm/BUILD.gn
+++ b/media/cdm/library_cdm/clear_key_cdm/BUILD.gn
@@ -7,6 +7,7 @@
 import("//media/media_options.gni")
 
 loadable_module("clear_key_cdm") {
+  testonly = true
   output_dir = "$root_out_dir/$clearkey_cdm_path"
   output_name = "clearkeycdm"
   sources = [
@@ -32,32 +33,24 @@
   deps = [
     ":cdm_proxy_common",
     "//base",
-    "//media",  # For media::AudioTimestampHelper
+    "//media",
+    "//media:media_buildflags",
     "//media:shared_memory_support",  # For media::AudioBus.
     "//media/cdm:cdm_api",  # For content_decryption_module.h
     "//media/cdm/library_cdm:cdm_host_proxy",
+    "//third_party/libyuv",
     "//url",
   ]
 
+  # TODO(xhwang): Convert this to use media::FFmpegAudioDecoder.
   if (media_use_ffmpeg) {
     sources += [
       "ffmpeg_cdm_audio_decoder.cc",
       "ffmpeg_cdm_audio_decoder.h",
-      "ffmpeg_cdm_video_decoder.cc",
-      "ffmpeg_cdm_video_decoder.h",
     ]
     defines += [ "CLEAR_KEY_CDM_USE_FFMPEG_DECODER" ]
     deps += [ "//third_party/ffmpeg" ]
   }
-
-  if (media_use_libvpx) {
-    sources += [
-      "libvpx_cdm_video_decoder.cc",
-      "libvpx_cdm_video_decoder.h",
-    ]
-    defines += [ "CLEAR_KEY_CDM_USE_LIBVPX_DECODER" ]
-    deps += [ "//third_party/libvpx" ]
-  }
 }
 
 source_set("clear_key_cdm_proxy") {
diff --git a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
index a709efed..156d4ae 100644
--- a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
+++ b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
@@ -4,40 +4,388 @@
 
 #include "media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.h"
 
-#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
-#include "media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_video_decoder.h"
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/containers/queue.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/no_destructor.h"
+#include "base/optional.h"
+// Necessary to convert async media::VideoDecoder to sync CdmVideoDecoder.
+// Typically not recommended for production code, but is ok here since
+// ClearKeyCdm is only for testing.
+#include "base/run_loop.h"
+#include "media/base/decode_status.h"
+#include "media/base/media_util.h"
+#include "media/cdm/library_cdm/cdm_host_proxy.h"
+#include "media/media_buildflags.h"
+#include "third_party/libyuv/include/libyuv/planar_functions.h"
+
+#if BUILDFLAG(ENABLE_FFMPEG)
+#include "media/filters/ffmpeg_video_decoder.h"
 #endif
 
-#if defined(CLEAR_KEY_CDM_USE_LIBVPX_DECODER)
-#include "media/cdm/library_cdm/clear_key_cdm/libvpx_cdm_video_decoder.h"
+#if BUILDFLAG(ENABLE_LIBVPX)
+#include "media/filters/vpx_video_decoder.h"
 #endif
 
 namespace media {
 
+namespace {
+
+VideoCodec ToMediaVideoCodec(cdm::VideoCodec codec) {
+  switch (codec) {
+    case cdm::kUnknownVideoCodec:
+      return kUnknownVideoCodec;
+    case cdm::kCodecVp8:
+      return kCodecVP8;
+    case cdm::kCodecH264:
+      return kCodecH264;
+    case cdm::kCodecVp9:
+      return kCodecVP9;
+    case cdm::kCodecAv1:
+      return kCodecAV1;
+  }
+}
+
+VideoCodecProfile ToMediaVideoCodecProfile(cdm::VideoCodecProfile profile) {
+  switch (profile) {
+    case cdm::kUnknownVideoCodecProfile:
+      return VIDEO_CODEC_PROFILE_UNKNOWN;
+    case cdm::kProfileNotNeeded:
+      // There's no corresponding value for "not needed". Given CdmAdapter only
+      // converts VP8PROFILE_ANY to cdm::kProfileNotNeeded, and this code is
+      // only used for testing, it's okay to convert it back to VP8PROFILE_ANY.
+      return VP8PROFILE_ANY;
+    case cdm::kVP9Profile0:
+      return VP9PROFILE_PROFILE0;
+    case cdm::kVP9Profile1:
+      return VP9PROFILE_PROFILE1;
+    case cdm::kVP9Profile2:
+      return VP9PROFILE_PROFILE2;
+    case cdm::kVP9Profile3:
+      return VP9PROFILE_PROFILE3;
+    case cdm::kH264ProfileBaseline:
+      return H264PROFILE_BASELINE;
+    case cdm::kH264ProfileMain:
+      return H264PROFILE_MAIN;
+    case cdm::kH264ProfileExtended:
+      return H264PROFILE_EXTENDED;
+    case cdm::kH264ProfileHigh:
+      return H264PROFILE_HIGH;
+    case cdm::kH264ProfileHigh10:
+      return H264PROFILE_HIGH10PROFILE;
+    case cdm::kH264ProfileHigh422:
+      return H264PROFILE_HIGH422PROFILE;
+    case cdm::kH264ProfileHigh444Predictive:
+      return H264PROFILE_HIGH444PREDICTIVEPROFILE;
+    case cdm::kAv1ProfileMain:
+      return AV1PROFILE_PROFILE_MAIN;
+    case cdm::kAv1ProfileHigh:
+      return AV1PROFILE_PROFILE_HIGH;
+    case cdm::kAv1ProfilePro:
+      return AV1PROFILE_PROFILE_PRO;
+  }
+}
+
+// TODO(xhwang): Support all media video formats.
+VideoPixelFormat ToMediaVideoFormat(cdm::VideoFormat format) {
+  switch (format) {
+    case cdm::kYv12:
+      return PIXEL_FORMAT_YV12;
+    case cdm::kI420:
+      return PIXEL_FORMAT_I420;
+    default:
+      DVLOG(1) << "Unsupported VideoFormat " << format;
+      return PIXEL_FORMAT_UNKNOWN;
+  }
+}
+
+gfx::ColorSpace::RangeID ToGfxColorRange(cdm::ColorRange range) {
+  switch (range) {
+    case cdm::ColorRange::kInvalid:
+      return gfx::ColorSpace::RangeID::INVALID;
+    case cdm::ColorRange::kLimited:
+      return gfx::ColorSpace::RangeID::LIMITED;
+    case cdm::ColorRange::kFull:
+      return gfx::ColorSpace::RangeID::FULL;
+    case cdm::ColorRange::kDerived:
+      return gfx::ColorSpace::RangeID::DERIVED;
+  }
+}
+
+VideoColorSpace ToMediaColorSpace(const cdm::ColorSpace& color_space) {
+  return VideoColorSpace(color_space.primary_id, color_space.transfer_id,
+                         color_space.matrix_id,
+                         ToGfxColorRange(color_space.range));
+}
+
+media::VideoDecoderConfig ToClearMediaVideoDecoderConfig(
+    const cdm::VideoDecoderConfig_3& config) {
+  gfx::Size coded_size(config.coded_size.width, config.coded_size.width);
+
+  VideoDecoderConfig media_config(
+      ToMediaVideoCodec(config.codec), ToMediaVideoCodecProfile(config.profile),
+      ToMediaVideoFormat(config.format), COLOR_SPACE_UNSPECIFIED,
+      VideoRotation::VIDEO_ROTATION_0, coded_size, gfx::Rect(coded_size),
+      coded_size,
+      std::vector<uint8_t>(config.extra_data,
+                           config.extra_data + config.extra_data_size),
+      Unencrypted());
+
+  media_config.set_color_space_info(ToMediaColorSpace(config.color_space));
+
+  return media_config;
+}
+
+bool ToCdmVideoFrame(const VideoFrame& video_frame,
+                     CdmHostProxy* cdm_host_proxy,
+                     CdmVideoDecoder::CdmVideoFrame* cdm_video_frame) {
+  DCHECK(cdm_video_frame);
+
+  // TODO(xhwang): Support more pixel formats.
+  if (!video_frame.IsMappable() ||
+      video_frame.format() != media::PIXEL_FORMAT_I420) {
+    DVLOG(1) << "VideoFrame is not mappable or has unsupported format";
+    return false;
+  }
+
+  const int width = video_frame.coded_size().width();
+  const int height = video_frame.coded_size().height();
+  DCHECK_EQ(width % 2, 0);
+  DCHECK_EQ(height % 2, 0);
+  const int width_uv = width / 2;
+  const int height_uv = height / 2;
+  const int dst_stride_y = width;
+  const int dst_stride_uv = width_uv;
+  const int y_size = dst_stride_y * height;
+  const int uv_size = dst_stride_uv * height_uv;
+  const int space_required = y_size + (uv_size * 2);
+
+  auto* buffer = cdm_host_proxy->Allocate(space_required);
+  if (!buffer) {
+    LOG(ERROR) << __func__ << ": Buffer allocation failed.";
+    return false;
+  }
+
+  // Values from the source |video_frame|.
+  const auto* src_y = video_frame.data(VideoFrame::kYPlane);
+  const auto* src_u = video_frame.data(VideoFrame::kUPlane);
+  const auto* src_v = video_frame.data(VideoFrame::kVPlane);
+  auto src_stride_y = video_frame.stride(VideoFrame::kYPlane);
+  auto src_stride_u = video_frame.stride(VideoFrame::kUPlane);
+  auto src_stride_v = video_frame.stride(VideoFrame::kVPlane);
+
+  // Values for the destinaton frame buffer.
+  uint8_t* dst_y = buffer->Data();
+  uint8_t* dst_u = dst_y + y_size;
+  uint8_t* dst_v = dst_u + uv_size;
+
+  // Actually copy video frame planes.
+  using libyuv::CopyPlane;
+  CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+  CopyPlane(src_u, src_stride_u, dst_u, dst_stride_uv, width_uv, height_uv);
+  CopyPlane(src_v, src_stride_v, dst_v, dst_stride_uv, width_uv, height_uv);
+
+  buffer->SetSize(space_required);
+  cdm_video_frame->SetFrameBuffer(buffer);
+
+  cdm_video_frame->SetFormat(cdm::kI420);
+  cdm_video_frame->SetSize({width, height});
+  cdm_video_frame->SetPlaneOffset(cdm::kYPlane, 0);
+  cdm_video_frame->SetPlaneOffset(cdm::kUPlane, y_size);
+  cdm_video_frame->SetPlaneOffset(cdm::kVPlane, y_size + uv_size);
+  cdm_video_frame->SetStride(cdm::kYPlane, dst_stride_y);
+  cdm_video_frame->SetStride(cdm::kUPlane, dst_stride_uv);
+  cdm_video_frame->SetStride(cdm::kVPlane, dst_stride_uv);
+  cdm_video_frame->SetTimestamp(video_frame.timestamp().InMicroseconds());
+
+  return true;
+}
+
+// Media VideoDecoders typically assumes a global environment where a lot of
+// things are already setup in the process, e.g. base::ThreadTaskRunnerHandle
+// and base::CommandLine. These will be available in the component build because
+// the CDM and the host is depending on the same base/ target. In static build,
+// they will not be available and we have to setup it by ourselves.
+void SetupGlobalEnvironmentIfNeeded() {
+  // Creating a base::MessageLoop to setup base::ThreadTaskRunnerHandle.
+  if (!base::MessageLoop::current()) {
+    static base::NoDestructor<base::MessageLoop> message_loop;
+  }
+
+  if (!base::CommandLine::InitializedForCurrentProcess())
+    base::CommandLine::Init(0, nullptr);
+}
+
+// Adapts a media::VideoDecoder to a CdmVideoDecoder. Media VideoDecoders
+// operations are asynchronous, often posting callbacks to the task runner. The
+// CdmVideoDecoder operations are synchronous. Therefore, after calling
+// media::VideoDecoder, we need to run a RunLoop manually and wait for the
+// asynchronous operation to finish. The RunLoop must be of type
+// |kNestableTasksAllowed| because we could be running the RunLoop in a task,
+// e.g. in component builds when we share the same task runner as the host. In
+// a static build, this is not necessary.
+class VideoDecoderAdapter : public CdmVideoDecoder {
+ public:
+  VideoDecoderAdapter(CdmHostProxy* cdm_host_proxy,
+                      std::unique_ptr<VideoDecoder> video_decoder)
+      : cdm_host_proxy_(cdm_host_proxy),
+        video_decoder_(std::move(video_decoder)),
+        weak_factory_(this) {
+    DCHECK(cdm_host_proxy_);
+  }
+
+  ~VideoDecoderAdapter() final = default;
+
+  // CdmVideoDecoder implementation.
+  bool Initialize(const cdm::VideoDecoderConfig_3& config) final {
+    auto clear_config = ToClearMediaVideoDecoderConfig(config);
+    DVLOG(1) << __func__ << ": " << clear_config.AsHumanReadableString();
+    DCHECK(!last_init_result_.has_value());
+
+    // Initialize |video_decoder_| and wait for completion.
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    video_decoder_->Initialize(
+        clear_config,
+        /* low_delay = */ false,
+        /* cdm_context = */ nullptr,
+        base::BindRepeating(&VideoDecoderAdapter::OnInitialized,
+                            weak_factory_.GetWeakPtr(), run_loop.QuitClosure()),
+        base::BindRepeating(&VideoDecoderAdapter::OnVideoFrameReady,
+                            weak_factory_.GetWeakPtr()),
+        /* waiting_for_decryption_key_cb = */ base::DoNothing());
+    run_loop.Run();
+
+    auto result = last_init_result_.value();
+    last_init_result_.reset();
+
+    return result;
+  }
+
+  void Deinitialize() final {
+    // Do nothing since |video_decoder_| supports reinitialization without
+    // the need to deinitialize first.
+  }
+
+  void Reset() final {
+    DVLOG(2) << __func__;
+
+    // Reset |video_decoder_| and wait for completion.
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    video_decoder_->Reset(base::BindRepeating(&VideoDecoderAdapter::OnReset,
+                                              weak_factory_.GetWeakPtr(),
+                                              run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+  cdm::Status Decode(scoped_refptr<DecoderBuffer> buffer,
+                     CdmVideoFrame* decoded_frame) final {
+    DVLOG(3) << __func__;
+    DCHECK(!last_decode_status_.has_value());
+
+    // Call |video_decoder_| Decode() and wait for completion.
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    video_decoder_->Decode(std::move(buffer),
+                           base::BindRepeating(&VideoDecoderAdapter::OnDecoded,
+                                               weak_factory_.GetWeakPtr(),
+                                               run_loop.QuitClosure()));
+    run_loop.Run();
+
+    auto decode_status = last_decode_status_.value();
+    last_decode_status_.reset();
+
+    if (decode_status == DecodeStatus::DECODE_ERROR)
+      return cdm::kDecodeError;
+
+    // "ABORTED" shouldn't happen during a sync decode, so treat it as an error.
+    DCHECK_EQ(decode_status, DecodeStatus::OK);
+
+    if (decoded_video_frames_.empty())
+      return cdm::kNeedMoreData;
+
+    auto video_frame = decoded_video_frames_.front();
+    decoded_video_frames_.pop();
+
+    return ToCdmVideoFrame(*video_frame, cdm_host_proxy_, decoded_frame)
+               ? cdm::kSuccess
+               : cdm::kDecodeError;
+  }
+
+ private:
+  void OnInitialized(base::OnceClosure quit_closure, bool success) {
+    DVLOG(1) << __func__ << " success = " << success;
+    DCHECK(!last_init_result_.has_value());
+    last_init_result_ = success;
+    std::move(quit_closure).Run();
+  }
+
+  void OnVideoFrameReady(const scoped_refptr<VideoFrame>& video_frame) {
+    // Do not queue EOS frames, which is not needed.
+    if (video_frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM))
+      return;
+
+    decoded_video_frames_.push(video_frame);
+  }
+
+  void OnReset(base::OnceClosure quit_closure) {
+    VideoFrameQueue empty_queue;
+    std::swap(decoded_video_frames_, empty_queue);
+    std::move(quit_closure).Run();
+  }
+
+  void OnDecoded(base::OnceClosure quit_closure, DecodeStatus decode_status) {
+    DCHECK(!last_decode_status_.has_value());
+    last_decode_status_ = decode_status;
+    std::move(quit_closure).Run();
+  }
+
+  CdmHostProxy* const cdm_host_proxy_;
+  std::unique_ptr<VideoDecoder> video_decoder_;
+
+  // Results of |video_decoder_| operations. Set iff the callback of the
+  // operation has been called.
+  base::Optional<bool> last_init_result_;
+  base::Optional<DecodeStatus> last_decode_status_;
+
+  // Queue of decoded video frames.
+  using VideoFrameQueue = base::queue<scoped_refptr<VideoFrame>>;
+  VideoFrameQueue decoded_video_frames_;
+
+  base::WeakPtrFactory<VideoDecoderAdapter> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(VideoDecoderAdapter);
+};
+
+}  // namespace
+
 std::unique_ptr<CdmVideoDecoder> CreateVideoDecoder(
     CdmHostProxy* cdm_host_proxy,
     const cdm::VideoDecoderConfig_3& config) {
-  std::unique_ptr<CdmVideoDecoder> video_decoder;
+  SetupGlobalEnvironmentIfNeeded();
 
-#if defined(CLEAR_KEY_CDM_USE_LIBVPX_DECODER)
-  if (config.codec == cdm::kCodecVp8 || config.codec == cdm::kCodecVp9) {
-    video_decoder.reset(new LibvpxCdmVideoDecoder(cdm_host_proxy));
+  static base::NoDestructor<media::NullMediaLog> null_media_log;
+  std::unique_ptr<VideoDecoder> video_decoder;
 
-    if (!video_decoder->Initialize(config))
-      video_decoder.reset();
-
-    return video_decoder;
-  }
+#if BUILDFLAG(ENABLE_LIBVPX)
+  if (config.codec == cdm::kCodecVp8 || config.codec == cdm::kCodecVp9)
+    video_decoder.reset(new VpxVideoDecoder());
 #endif
 
-#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
-  video_decoder.reset(new FFmpegCdmVideoDecoder(cdm_host_proxy));
-
-  if (!video_decoder->Initialize(config))
-    video_decoder.reset();
+#if BUILDFLAG(ENABLE_FFMPEG)
+  if (!video_decoder)
+    video_decoder.reset(new FFmpegVideoDecoder(null_media_log.get()));
 #endif
 
-  return video_decoder;
+  if (!video_decoder)
+    return nullptr;
+
+  return std::make_unique<VideoDecoderAdapter>(cdm_host_proxy,
+                                               std::move(video_decoder));
 }
 
 }  // namespace media
diff --git a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.h b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.h
index 64006ce9..efba0a0 100644
--- a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.h
+++ b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.h
@@ -9,6 +9,8 @@
 
 #include <memory>
 
+#include "base/memory/ref_counted.h"
+#include "media/base/decoder_buffer.h"
 #include "media/cdm/api/content_decryption_module.h"
 
 namespace media {
@@ -23,24 +25,12 @@
   virtual bool Initialize(const cdm::VideoDecoderConfig_3& config) = 0;
   virtual void Deinitialize() = 0;
   virtual void Reset() = 0;
-  virtual bool is_initialized() const = 0;
-
-  // Decodes |compressed_frame|. Stores output frame in |decoded_frame| and
-  // returns |cdm::kSuccess| when an output frame is available. Returns
-  // |cdm::kNeedMoreData| when |compressed_frame| does not produce an output
-  // frame. Returns |cdm::kDecodeError| when decoding fails.
-  //
-  // A null |compressed_frame| will attempt to flush the decoder of any
-  // remaining frames. |compressed_frame_size| and |timestamp| are ignored.
-  virtual cdm::Status DecodeFrame(const uint8_t* compressed_frame,
-                                  int32_t compressed_frame_size,
-                                  int64_t timestamp,
-                                  CdmVideoFrame* decoded_frame) = 0;
+  virtual cdm::Status Decode(scoped_refptr<DecoderBuffer> buffer,
+                             CdmVideoFrame* decoded_frame) = 0;
 };
 
-// Initializes the appropriate video decoder based on build flags and the value
-// of |config.codec|. Returns a scoped_ptr containing a non-null initialized
-// CdmVideoDecoder pointer upon success.
+// Creates a CdmVideoDecoder based on the |config|. Returns nullptr if no
+// decoder can be created.
 std::unique_ptr<CdmVideoDecoder> CreateVideoDecoder(
     CdmHostProxy* cdm_host_proxy,
     const cdm::VideoDecoderConfig_3& config);
diff --git a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
index df63b79d..21c00d8 100644
--- a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
+++ b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
@@ -230,10 +230,7 @@
 
 void INITIALIZE_CDM_MODULE() {
   DVLOG(1) << __func__;
-#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
   media::InitializeMediaLibrary();
-#endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
-
   g_is_cdm_module_initialized = true;
 }
 
@@ -817,15 +814,14 @@
     return cdm::kInitializationError;
   }
 
-  if (video_decoder_ && video_decoder_->is_initialized()) {
-    DCHECK(!video_decoder_->is_initialized());
-    return cdm::kInitializationError;
+  if (!video_decoder_) {
+    video_decoder_ =
+        CreateVideoDecoder(cdm_host_proxy_.get(), video_decoder_config);
+    if (!video_decoder_)
+      return cdm::kInitializationError;
   }
 
-  // Any uninitialized decoder will be replaced.
-  video_decoder_ =
-      CreateVideoDecoder(cdm_host_proxy_.get(), video_decoder_config);
-  if (!video_decoder_)
+  if (!video_decoder_->Initialize(video_decoder_config))
     return cdm::kInitializationError;
 
   return cdm::kSuccess;
@@ -889,16 +885,7 @@
   if (status != cdm::kSuccess)
     return status;
 
-  const uint8_t* data = NULL;
-  int32_t size = 0;
-  int64_t timestamp = 0;
-  if (!buffer->end_of_stream()) {
-    data = buffer->data();
-    size = buffer->data_size();
-    timestamp = encrypted_buffer.timestamp;
-  }
-
-  return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
+  return video_decoder_->Decode(buffer, decoded_frame);
 }
 
 cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
diff --git a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h
index 61f1d4dd..1daf8d8 100644
--- a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h
+++ b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h
@@ -211,6 +211,7 @@
   std::unique_ptr<UpdateParams> pending_update_params_;
 
   std::unique_ptr<CdmVideoDecoder> video_decoder_;
+
   std::unique_ptr<FileIOTestRunner> file_io_test_runner_;
   std::unique_ptr<CdmProxyHandler> cdm_proxy_handler_;
 
diff --git a/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_video_decoder.cc b/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_video_decoder.cc
deleted file mode 100644
index 15cc346..0000000
--- a/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_video_decoder.cc
+++ /dev/null
@@ -1,312 +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.
-
-#include "media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_video_decoder.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback.h"
-#include "base/logging.h"
-#include "media/base/limits.h"
-#include "media/cdm/library_cdm/cdm_host_proxy.h"
-#include "media/ffmpeg/ffmpeg_common.h"
-#include "media/ffmpeg/ffmpeg_decoding_loop.h"
-
-namespace media {
-
-static const int kDecodeThreads = 1;
-
-static cdm::VideoFormat AVPixelFormatToCdmVideoFormat(
-    AVPixelFormat pixel_format) {
-  switch (pixel_format) {
-    case AV_PIX_FMT_YUV420P:
-      return cdm::kYv12;
-    default:
-      DVLOG(1) << "Unsupported AVPixelFormat: " << pixel_format;
-  }
-  return cdm::kUnknownVideoFormat;
-}
-
-static AVPixelFormat CdmVideoFormatToAVPixelFormat(
-    cdm::VideoFormat video_format) {
-  switch (video_format) {
-    case cdm::kYv12:
-    case cdm::kI420:
-      return AV_PIX_FMT_YUV420P;
-    case cdm::kUnknownVideoFormat:
-    default:
-      DVLOG(1) << "Unsupported cdm::VideoFormat: " << video_format;
-  }
-  return AV_PIX_FMT_NONE;
-}
-
-static AVCodecID CdmVideoCodecToCodecID(cdm::VideoCodec video_codec) {
-  switch (video_codec) {
-    case cdm::kCodecVp8:
-      return AV_CODEC_ID_VP8;
-    case cdm::kCodecH264:
-      return AV_CODEC_ID_H264;
-    case cdm::kCodecVp9:
-      return AV_CODEC_ID_VP9;
-    case cdm::kUnknownVideoCodec:
-    default:
-      NOTREACHED() << "Unsupported cdm::VideoCodec: " << video_codec;
-      return AV_CODEC_ID_NONE;
-  }
-}
-
-static int CdmVideoCodecProfileToProfileID(cdm::VideoCodecProfile profile) {
-  switch (profile) {
-    case cdm::kProfileNotNeeded:
-      // For codecs that do not need a profile (e.g. VP8/VP9), does not define
-      // an FFmpeg profile.
-      return FF_PROFILE_UNKNOWN;
-    case cdm::kH264ProfileBaseline:
-      return FF_PROFILE_H264_BASELINE;
-    case cdm::kH264ProfileMain:
-      return FF_PROFILE_H264_MAIN;
-    case cdm::kH264ProfileExtended:
-      return FF_PROFILE_H264_EXTENDED;
-    case cdm::kH264ProfileHigh:
-      return FF_PROFILE_H264_HIGH;
-    case cdm::kH264ProfileHigh10:
-      return FF_PROFILE_H264_HIGH_10;
-    case cdm::kH264ProfileHigh422:
-      return FF_PROFILE_H264_HIGH_422;
-    case cdm::kH264ProfileHigh444Predictive:
-      return FF_PROFILE_H264_HIGH_444_PREDICTIVE;
-    case cdm::kUnknownVideoCodecProfile:
-    default:
-      NOTREACHED() << "Unknown cdm::VideoCodecProfile: " << profile;
-      return FF_PROFILE_UNKNOWN;
-  }
-}
-
-static void CdmVideoDecoderConfigToAVCodecContext(
-    const cdm::VideoDecoderConfig_3& config,
-    AVCodecContext* codec_context) {
-  codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
-  codec_context->codec_id = CdmVideoCodecToCodecID(config.codec);
-  codec_context->profile = CdmVideoCodecProfileToProfileID(config.profile);
-  codec_context->coded_width = config.coded_size.width;
-  codec_context->coded_height = config.coded_size.height;
-  codec_context->pix_fmt = CdmVideoFormatToAVPixelFormat(config.format);
-
-  if (config.extra_data) {
-    codec_context->extradata_size = config.extra_data_size;
-    codec_context->extradata = reinterpret_cast<uint8_t*>(
-        av_malloc(config.extra_data_size + AV_INPUT_BUFFER_PADDING_SIZE));
-    memcpy(codec_context->extradata, config.extra_data, config.extra_data_size);
-    memset(codec_context->extradata + config.extra_data_size, 0,
-           AV_INPUT_BUFFER_PADDING_SIZE);
-  } else {
-    codec_context->extradata = NULL;
-    codec_context->extradata_size = 0;
-  }
-}
-
-static void CopyPlane(const uint8_t* source,
-                      int32_t source_stride,
-                      int32_t target_stride,
-                      int32_t rows,
-                      int32_t copy_bytes_per_row,
-                      uint8_t* target) {
-  DCHECK(source);
-  DCHECK(target);
-  DCHECK_LE(copy_bytes_per_row, source_stride);
-  DCHECK_LE(copy_bytes_per_row, target_stride);
-
-  for (int i = 0; i < rows; ++i) {
-    const int source_offset = i * source_stride;
-    const int target_offset = i * target_stride;
-    memcpy(target + target_offset, source + source_offset, copy_bytes_per_row);
-  }
-}
-
-FFmpegCdmVideoDecoder::FFmpegCdmVideoDecoder(CdmHostProxy* cdm_host_proxy)
-    : cdm_host_proxy_(cdm_host_proxy) {}
-
-FFmpegCdmVideoDecoder::~FFmpegCdmVideoDecoder() {
-  ReleaseFFmpegResources();
-}
-
-bool FFmpegCdmVideoDecoder::Initialize(
-    const cdm::VideoDecoderConfig_3& config) {
-  DVLOG(1) << "Initialize()";
-
-  if (!IsValidOutputConfig(config.format, config.coded_size)) {
-    LOG(ERROR) << "Initialize(): invalid video decoder configuration.";
-    return false;
-  }
-
-  if (is_initialized_) {
-    LOG(ERROR) << "Initialize(): Already initialized.";
-    return false;
-  }
-
-  // Initialize AVCodecContext structure.
-  codec_context_.reset(avcodec_alloc_context3(NULL));
-  CdmVideoDecoderConfigToAVCodecContext(config, codec_context_.get());
-
-  // Enable motion vector search (potentially slow), strong deblocking filter
-  // for damaged macroblocks, and set our error detection sensitivity.
-  codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
-  codec_context_->err_recognition = AV_EF_CAREFUL;
-  codec_context_->thread_count = kDecodeThreads;
-  codec_context_->opaque = this;
-
-  AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
-  if (!codec) {
-    LOG(ERROR) << "Initialize(): avcodec_find_decoder failed.";
-    return false;
-  }
-
-  int status;
-  if ((status = avcodec_open2(codec_context_.get(), codec, NULL)) < 0) {
-    LOG(ERROR) << "Initialize(): avcodec_open2 failed: " << status;
-    return false;
-  }
-
-  decoding_loop_.reset(new FFmpegDecodingLoop(codec_context_.get()));
-  is_initialized_ = true;
-
-  return true;
-}
-
-void FFmpegCdmVideoDecoder::Deinitialize() {
-  DVLOG(1) << "Deinitialize()";
-  ReleaseFFmpegResources();
-  is_initialized_ = false;
-}
-
-void FFmpegCdmVideoDecoder::Reset() {
-  DVLOG(1) << "Reset()";
-  avcodec_flush_buffers(codec_context_.get());
-}
-
-// static
-bool FFmpegCdmVideoDecoder::IsValidOutputConfig(cdm::VideoFormat format,
-                                                const cdm::Size& data_size) {
-  return ((format == cdm::kYv12 || format == cdm::kI420) &&
-          (data_size.width % 2) == 0 && (data_size.height % 2) == 0 &&
-          data_size.width > 0 && data_size.height > 0 &&
-          data_size.width <= limits::kMaxDimension &&
-          data_size.height <= limits::kMaxDimension &&
-          data_size.width * data_size.height <= limits::kMaxCanvas);
-}
-
-cdm::Status FFmpegCdmVideoDecoder::DecodeFrame(const uint8_t* compressed_frame,
-                                               int32_t compressed_frame_size,
-                                               int64_t timestamp,
-                                               CdmVideoFrame* decoded_frame) {
-  DVLOG(1) << "DecodeFrame()";
-  DCHECK(decoded_frame);
-
-  // Create a packet for input data.
-  AVPacket packet;
-  av_init_packet(&packet);
-
-  // The FFmpeg API does not allow us to have const read-only pointers.
-  packet.data = const_cast<uint8_t*>(compressed_frame);
-  packet.size = compressed_frame_size;
-
-  // Let FFmpeg handle presentation timestamp reordering.
-  codec_context_->reordered_opaque = timestamp;
-
-  if (decoding_loop_->DecodePacket(
-          &packet, base::BindRepeating(&FFmpegCdmVideoDecoder::OnNewFrame,
-                                       base::Unretained(this))) !=
-      FFmpegDecodingLoop::DecodeStatus::kOkay) {
-    return cdm::kDecodeError;
-  }
-
-  if (pending_frames_.empty())
-    return cdm::kNeedMoreData;
-
-  auto frame = std::move(pending_frames_.front());
-  pending_frames_.pop_front();
-
-  if (!CopyAvFrameTo(frame.get(), decoded_frame)) {
-    LOG(ERROR) << "DecodeFrame() could not copy video frame to output buffer.";
-    return cdm::kDecodeError;
-  }
-
-  // If no frame was produced then signal that more data is required to produce
-  // more frames.
-  return cdm::kSuccess;
-}
-
-bool FFmpegCdmVideoDecoder::is_initialized() const {
-  return is_initialized_;
-}
-
-bool FFmpegCdmVideoDecoder::OnNewFrame(AVFrame* frame) {
-  // The decoder is in a bad state and not decoding correctly.
-  // Checking for NULL avoids a crash.
-  if (!frame->data[cdm::kYPlane] || !frame->data[cdm::kUPlane] ||
-      !frame->data[cdm::kVPlane]) {
-    LOG(ERROR) << "DecodeFrame(): Video frame has invalid frame data.";
-    return false;
-  }
-
-  pending_frames_.emplace_back(av_frame_clone(frame));
-  return true;
-}
-
-bool FFmpegCdmVideoDecoder::CopyAvFrameTo(AVFrame* frame,
-                                          CdmVideoFrame* cdm_video_frame) {
-  DCHECK(cdm_video_frame);
-  DCHECK_EQ(frame->format, AV_PIX_FMT_YUV420P);
-  DCHECK_EQ(frame->width % 2, 0);
-  DCHECK_EQ(frame->height % 2, 0);
-
-  const int y_size = frame->width * frame->height;
-  const int uv_size = y_size / 2;
-  const int space_required = y_size + (uv_size * 2);
-
-  auto* frame_buffer = cdm_host_proxy_->Allocate(space_required);
-  if (!frame_buffer) {
-    LOG(ERROR) << __func__ << ": Buffer allocation failed.";
-    return false;
-  }
-
-  // Prepare and set the frame buffer.
-  const int uv_stride = frame->width / 2;
-  const int uv_rows = frame->height / 2;
-  uint8_t* data = frame_buffer->Data();
-  CopyPlane(frame->data[cdm::kYPlane], frame->linesize[cdm::kYPlane],
-            frame->width, frame->height, frame->width, data);
-  CopyPlane(frame->data[cdm::kUPlane], frame->linesize[cdm::kUPlane], uv_stride,
-            uv_rows, uv_stride, data + y_size);
-  CopyPlane(frame->data[cdm::kVPlane], frame->linesize[cdm::kVPlane], uv_stride,
-            uv_rows, uv_stride, data + y_size + uv_size);
-  frame_buffer->SetSize(space_required);
-  cdm_video_frame->SetFrameBuffer(frame_buffer);
-
-  AVPixelFormat format = static_cast<AVPixelFormat>(frame->format);
-  cdm_video_frame->SetFormat(AVPixelFormatToCdmVideoFormat(format));
-
-  cdm_video_frame->SetSize({frame->width, frame->height});
-
-  cdm_video_frame->SetPlaneOffset(cdm::kYPlane, 0);
-  cdm_video_frame->SetPlaneOffset(cdm::kUPlane, y_size);
-  cdm_video_frame->SetPlaneOffset(cdm::kVPlane, y_size + uv_size);
-
-  cdm_video_frame->SetStride(cdm::kYPlane, frame->width);
-  cdm_video_frame->SetStride(cdm::kUPlane, uv_stride);
-  cdm_video_frame->SetStride(cdm::kVPlane, uv_stride);
-
-  cdm_video_frame->SetTimestamp(frame->reordered_opaque);
-
-  return true;
-}
-
-void FFmpegCdmVideoDecoder::ReleaseFFmpegResources() {
-  DVLOG(1) << "ReleaseFFmpegResources()";
-
-  decoding_loop_.reset();
-  codec_context_.reset();
-}
-
-}  // namespace media
diff --git a/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_video_decoder.h b/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_video_decoder.h
deleted file mode 100644
index 49e5b5ed..0000000
--- a/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_video_decoder.h
+++ /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.
-
-#ifndef MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_FFMPEG_CDM_VIDEO_DECODER_H_
-#define MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_FFMPEG_CDM_VIDEO_DECODER_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/containers/circular_deque.h"
-#include "base/macros.h"
-#include "media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.h"
-#include "media/ffmpeg/ffmpeg_deleters.h"
-
-struct AVCodecContext;
-struct AVFrame;
-
-namespace media {
-
-class CdmHostProxy;
-class FFmpegDecodingLoop;
-
-class FFmpegCdmVideoDecoder : public CdmVideoDecoder {
- public:
-  explicit FFmpegCdmVideoDecoder(CdmHostProxy* cdm_host_proxy);
-  ~FFmpegCdmVideoDecoder() override;
-
-  // CdmVideoDecoder implementation.
-  bool Initialize(const cdm::VideoDecoderConfig_3& config) override;
-  void Deinitialize() override;
-  void Reset() override;
-  cdm::Status DecodeFrame(const uint8_t* compressed_frame,
-                          int32_t compressed_frame_size,
-                          int64_t timestamp,
-                          CdmVideoFrame* decoded_frame) override;
-  bool is_initialized() const override;
-
-  // Returns true when |format| and |data_size| specify a supported video
-  // output configuration.
-  static bool IsValidOutputConfig(cdm::VideoFormat format,
-                                  const cdm::Size& data_size);
-
- private:
-  bool OnNewFrame(AVFrame* frame);
-
-  // Allocates storage, then copies video frame stored in |frame| to
-  // |cdm_video_frame|. Returns true when allocation and copy succeed.
-  bool CopyAvFrameTo(AVFrame* frame, CdmVideoFrame* cdm_video_frame);
-
-  void ReleaseFFmpegResources();
-
-  // FFmpeg structures owned by this object.
-  std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext> codec_context_;
-  std::unique_ptr<FFmpegDecodingLoop> decoding_loop_;
-
-  bool is_initialized_ = false;
-
-  CdmHostProxy* const cdm_host_proxy_ = nullptr;
-
-  base::circular_deque<std::unique_ptr<AVFrame, ScopedPtrAVFreeFrame>>
-      pending_frames_;
-
-  DISALLOW_COPY_AND_ASSIGN(FFmpegCdmVideoDecoder);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_FFMPEG_CDM_VIDEO_DECODER_H_
diff --git a/media/cdm/library_cdm/clear_key_cdm/libvpx_cdm_video_decoder.cc b/media/cdm/library_cdm/clear_key_cdm/libvpx_cdm_video_decoder.cc
deleted file mode 100644
index 77ba7c2f..0000000
--- a/media/cdm/library_cdm/clear_key_cdm/libvpx_cdm_video_decoder.cc
+++ /dev/null
@@ -1,175 +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.
-
-#include "media/cdm/library_cdm/clear_key_cdm/libvpx_cdm_video_decoder.h"
-
-#include "base/logging.h"
-#include "media/base/limits.h"
-#include "media/cdm/library_cdm/cdm_host_proxy.h"
-#include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
-#include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
-
-// Enable USE_COPYPLANE_WITH_LIBVPX to use |CopyPlane()| instead of memcpy to
-// copy video frame data.
-// #define USE_COPYPLANE_WITH_LIBVPX 1
-
-namespace media {
-
-static const int kDecodeThreads = 2;
-
-LibvpxCdmVideoDecoder::LibvpxCdmVideoDecoder(CdmHostProxy* cdm_host_proxy)
-    : cdm_host_proxy_(cdm_host_proxy) {}
-
-LibvpxCdmVideoDecoder::~LibvpxCdmVideoDecoder() {
-  Deinitialize();
-}
-
-bool LibvpxCdmVideoDecoder::Initialize(
-    const cdm::VideoDecoderConfig_3& config) {
-  DVLOG(1) << "Initialize()";
-
-  if (!IsValidOutputConfig(config.format, config.coded_size)) {
-    LOG(ERROR) << "Initialize(): invalid video decoder configuration.";
-    return false;
-  }
-
-  if (is_initialized_) {
-    LOG(ERROR) << "Initialize(): Already initialized.";
-    return false;
-  }
-
-  vpx_codec_ = new vpx_codec_ctx();
-  vpx_codec_dec_cfg_t vpx_config = {0};
-  vpx_config.w = config.coded_size.width;
-  vpx_config.h = config.coded_size.height;
-  vpx_config.threads = kDecodeThreads;
-
-  vpx_codec_err_t status = vpx_codec_dec_init(
-      vpx_codec_,
-      config.codec == cdm::kCodecVp9 ? vpx_codec_vp9_dx() : vpx_codec_vp8_dx(),
-      &vpx_config, 0);
-  if (status != VPX_CODEC_OK) {
-    LOG(ERROR) << "InitializeLibvpx(): vpx_codec_dec_init failed, ret="
-               << status;
-    delete vpx_codec_;
-    vpx_codec_ = NULL;
-  }
-
-  is_initialized_ = true;
-  return true;
-}
-
-void LibvpxCdmVideoDecoder::Deinitialize() {
-  DVLOG(1) << "Deinitialize()";
-
-  if (vpx_codec_) {
-    vpx_codec_destroy(vpx_codec_);
-    vpx_codec_ = NULL;
-  }
-
-  is_initialized_ = false;
-}
-
-void LibvpxCdmVideoDecoder::Reset() {
-  DVLOG(1) << "Reset()";
-}
-
-// static
-bool LibvpxCdmVideoDecoder::IsValidOutputConfig(cdm::VideoFormat format,
-                                                const cdm::Size& data_size) {
-  return ((format == cdm::kYv12 || format == cdm::kI420) &&
-          (data_size.width % 2) == 0 && (data_size.height % 2) == 0 &&
-          data_size.width > 0 && data_size.height > 0 &&
-          data_size.width <= limits::kMaxDimension &&
-          data_size.height <= limits::kMaxDimension &&
-          data_size.width * data_size.height <= limits::kMaxCanvas);
-}
-
-cdm::Status LibvpxCdmVideoDecoder::DecodeFrame(const uint8_t* compressed_frame,
-                                               int32_t compressed_frame_size,
-                                               int64_t timestamp,
-                                               CdmVideoFrame* decoded_frame) {
-  DVLOG(3) << __func__ << ": frame size = " << compressed_frame_size;
-  DCHECK(decoded_frame);
-
-  // Pass |compressed_frame| to libvpx.
-  void* user_priv = reinterpret_cast<void*>(&timestamp);
-  vpx_codec_err_t status = vpx_codec_decode(
-      vpx_codec_, compressed_frame, compressed_frame_size, user_priv, 0);
-  if (status != VPX_CODEC_OK) {
-    LOG(ERROR) << "DecodeFrameLibvpx(): vpx_codec_decode failed, status="
-               << status;
-    return cdm::kDecodeError;
-  }
-
-  // Gets pointer to decoded data.
-  vpx_codec_iter_t iter = NULL;
-  vpx_image_ = vpx_codec_get_frame(vpx_codec_, &iter);
-  if (!vpx_image_)
-    return cdm::kNeedMoreData;
-
-  if (vpx_image_->user_priv != reinterpret_cast<void*>(&timestamp)) {
-    LOG(ERROR) << "DecodeFrameLibvpx() invalid output timestamp.";
-    return cdm::kDecodeError;
-  }
-  decoded_frame->SetTimestamp(timestamp);
-
-  if (!CopyVpxImageTo(decoded_frame)) {
-    LOG(ERROR) << "DecodeFrameLibvpx() could not copy vpx image to output "
-               << "buffer.";
-    return cdm::kDecodeError;
-  }
-
-  return cdm::kSuccess;
-}
-
-bool LibvpxCdmVideoDecoder::is_initialized() const {
-  return is_initialized_;
-}
-
-bool LibvpxCdmVideoDecoder::CopyVpxImageTo(CdmVideoFrame* cdm_video_frame) {
-  DCHECK(cdm_video_frame);
-  DCHECK_EQ(vpx_image_->fmt, VPX_IMG_FMT_I420);
-  DCHECK_EQ(vpx_image_->d_w % 2, 0U);
-  DCHECK_EQ(vpx_image_->d_h % 2, 0U);
-
-  const int y_size = vpx_image_->stride[VPX_PLANE_Y] * vpx_image_->d_h;
-  const int uv_rows = vpx_image_->d_h / 2;
-  const int u_size = vpx_image_->stride[VPX_PLANE_U] * uv_rows;
-  const int v_size = vpx_image_->stride[VPX_PLANE_V] * uv_rows;
-  const int space_required = y_size + u_size + v_size;
-
-  auto* frame_buffer = cdm_host_proxy_->Allocate(space_required);
-  if (!frame_buffer) {
-    LOG(ERROR) << __func__ << ": Buffer allocation failed.";
-    return false;
-  }
-
-  // Prepare and set the frame buffer.
-  uint8_t* data = frame_buffer->Data();
-  memcpy(data, vpx_image_->planes[VPX_PLANE_Y], y_size);
-  memcpy(data + y_size, vpx_image_->planes[VPX_PLANE_U], u_size);
-  memcpy(data + y_size + u_size, vpx_image_->planes[VPX_PLANE_V], v_size);
-  frame_buffer->SetSize(space_required);
-  cdm_video_frame->SetFrameBuffer(frame_buffer);
-
-  cdm_video_frame->SetFormat(cdm::kYv12);
-
-  cdm::Size video_frame_size;
-  video_frame_size.width = vpx_image_->d_w;
-  video_frame_size.height = vpx_image_->d_h;
-  cdm_video_frame->SetSize(video_frame_size);
-
-  cdm_video_frame->SetPlaneOffset(cdm::kYPlane, 0);
-  cdm_video_frame->SetPlaneOffset(cdm::kUPlane, y_size);
-  cdm_video_frame->SetPlaneOffset(cdm::kVPlane, y_size + u_size);
-
-  cdm_video_frame->SetStride(cdm::kYPlane, vpx_image_->stride[VPX_PLANE_Y]);
-  cdm_video_frame->SetStride(cdm::kUPlane, vpx_image_->stride[VPX_PLANE_U]);
-  cdm_video_frame->SetStride(cdm::kVPlane, vpx_image_->stride[VPX_PLANE_V]);
-
-  return true;
-}
-
-}  // namespace media
diff --git a/media/cdm/library_cdm/clear_key_cdm/libvpx_cdm_video_decoder.h b/media/cdm/library_cdm/clear_key_cdm/libvpx_cdm_video_decoder.h
deleted file mode 100644
index dda586b..0000000
--- a/media/cdm/library_cdm/clear_key_cdm/libvpx_cdm_video_decoder.h
+++ /dev/null
@@ -1,59 +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 MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_LIBVPX_CDM_VIDEO_DECODER_H_
-#define MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_LIBVPX_CDM_VIDEO_DECODER_H_
-
-#include <stdint.h>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "media/cdm/api/content_decryption_module.h"
-#include "media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.h"
-
-struct vpx_codec_ctx;
-struct vpx_image;
-
-namespace media {
-
-class CdmHostProxy;
-
-class LibvpxCdmVideoDecoder : public CdmVideoDecoder {
- public:
-  explicit LibvpxCdmVideoDecoder(CdmHostProxy* cdm_host_proxy);
-  ~LibvpxCdmVideoDecoder() override;
-
-  // CdmVideoDecoder implementation.
-  bool Initialize(const cdm::VideoDecoderConfig_3& config) override;
-  void Deinitialize() override;
-  void Reset() override;
-  cdm::Status DecodeFrame(const uint8_t* compressed_frame,
-                          int32_t compressed_frame_size,
-                          int64_t timestamp,
-                          CdmVideoFrame* decoded_frame) override;
-  bool is_initialized() const override;
-
-  // Returns true when |format| and |data_size| specify a supported video
-  // output configuration.
-  static bool IsValidOutputConfig(cdm::VideoFormat format,
-                                  const cdm::Size& data_size);
-
- private:
-  // Allocates storage, then copies video frame stored in |vpx_image_| to
-  // |cdm_video_frame|. Returns true when allocation and copy succeed.
-  bool CopyVpxImageTo(CdmVideoFrame* cdm_video_frame);
-
-  bool is_initialized_ = false;
-
-  CdmHostProxy* const cdm_host_proxy_ = nullptr;
-
-  vpx_codec_ctx* vpx_codec_ = nullptr;
-  vpx_image* vpx_image_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(LibvpxCdmVideoDecoder);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_LIBVPX_CDM_VIDEO_DECODER_H_
diff --git a/media/filters/android/video_frame_extractor.cc b/media/filters/android/video_frame_extractor.cc
index 3c7b8bf..cdbe3cc 100644
--- a/media/filters/android/video_frame_extractor.cc
+++ b/media/filters/android/video_frame_extractor.cc
@@ -107,7 +107,8 @@
       break;
   }
 
-  bitstream_converter_->ConvertPacket(packet);
+  if (bitstream_converter_)
+    bitstream_converter_->ConvertPacket(packet);
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 }
 
diff --git a/media/filters/android/video_frame_extractor.h b/media/filters/android/video_frame_extractor.h
index 21731394..9117d35 100644
--- a/media/filters/android/video_frame_extractor.h
+++ b/media/filters/android/video_frame_extractor.h
@@ -38,7 +38,7 @@
  public:
   using VideoFrameCallback =
       base::OnceCallback<void(bool success,
-                              const std::vector<uint8_t>& data,
+                              std::vector<uint8_t> data,
                               const VideoDecoderConfig& decoder_config)>;
 
   explicit VideoFrameExtractor(DataSource* data_source);
diff --git a/media/filters/android/video_frame_extractor_unittest.cc b/media/filters/android/video_frame_extractor_unittest.cc
index ce7dc5bfc..549ebb6 100644
--- a/media/filters/android/video_frame_extractor_unittest.cc
+++ b/media/filters/android/video_frame_extractor_unittest.cc
@@ -23,13 +23,13 @@
   VideoDecoderConfig decoder_config;
 };
 
-void OnThumbnailGenerated(ExtractVideoFrameResult* result,
-                          base::RepeatingClosure quit_closure,
-                          bool success,
-                          const std::vector<uint8_t>& encoded_frame,
-                          const VideoDecoderConfig& decoder_config) {
+void OnFrameExtracted(ExtractVideoFrameResult* result,
+                      base::RepeatingClosure quit_closure,
+                      bool success,
+                      std::vector<uint8_t> encoded_frame,
+                      const VideoDecoderConfig& decoder_config) {
   result->success = success;
-  result->encoded_frame = encoded_frame;
+  result->encoded_frame = std::move(encoded_frame);
   result->decoder_config = decoder_config;
 
   quit_closure.Run();
@@ -65,7 +65,7 @@
   ExtractVideoFrameResult result;
   base::RunLoop loop;
   extractor()->Start(
-      base::BindOnce(&OnThumbnailGenerated, &result, loop.QuitClosure()));
+      base::BindOnce(&OnFrameExtracted, &result, loop.QuitClosure()));
   loop.Run();
   EXPECT_TRUE(result.success);
   EXPECT_GT(result.encoded_frame.size(), 0u);
diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc
index 22983daf..d205e65 100644
--- a/media/filters/ffmpeg_video_decoder.cc
+++ b/media/filters/ffmpeg_video_decoder.cc
@@ -116,6 +116,7 @@
 
 FFmpegVideoDecoder::FFmpegVideoDecoder(MediaLog* media_log)
     : media_log_(media_log), state_(kUninitialized), decode_nalus_(false) {
+  DVLOG(1) << __func__;
   thread_checker_.DetachFromThread();
 }
 
@@ -254,6 +255,7 @@
     const InitCB& init_cb,
     const OutputCB& output_cb,
     const WaitingForDecryptionKeyCB& /* waiting_for_decryption_key_cb */) {
+  DVLOG(1) << __func__ << ": " << config.AsHumanReadableString();
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(config.IsValidConfig());
   DCHECK(output_cb);
@@ -279,6 +281,7 @@
 
 void FFmpegVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
                                 const DecodeCB& decode_cb) {
+  DVLOG(3) << __func__;
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(buffer.get());
   DCHECK(decode_cb);
@@ -331,6 +334,7 @@
 }
 
 void FFmpegVideoDecoder::Reset(const base::Closure& closure) {
+  DVLOG(2) << __func__;
   DCHECK(thread_checker_.CalledOnValidThread());
 
   avcodec_flush_buffers(codec_context_.get());
diff --git a/media/filters/vpx_video_decoder.cc b/media/filters/vpx_video_decoder.cc
index 016fc991..3eac5c90 100644
--- a/media/filters/vpx_video_decoder.cc
+++ b/media/filters/vpx_video_decoder.cc
@@ -138,6 +138,7 @@
     const InitCB& init_cb,
     const OutputCB& output_cb,
     const WaitingForDecryptionKeyCB& /* waiting_for_decryption_key_cb */) {
+  DVLOG(1) << __func__ << ": " << config.AsHumanReadableString();
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(config.IsValidConfig());
 
diff --git a/media/mojo/common/mojo_shared_buffer_video_frame.cc b/media/mojo/common/mojo_shared_buffer_video_frame.cc
index 22268d6..30476dae 100644
--- a/media/mojo/common/mojo_shared_buffer_video_frame.cc
+++ b/media/mojo/common/mojo_shared_buffer_video_frame.cc
@@ -13,6 +13,7 @@
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/numerics/safe_math.h"
+#include "mojo/public/cpp/system/platform_handle.h"
 
 namespace media {
 
@@ -56,6 +57,64 @@
                 coded_size.width() / 2, coded_size.width() / 2, timestamp);
 }
 
+scoped_refptr<MojoSharedBufferVideoFrame>
+MojoSharedBufferVideoFrame::CreateFromYUVFrame(const VideoFrame& frame) {
+  DCHECK_EQ(VideoFrame::NumPlanes(frame.format()), 3u);
+
+  // The data from |frame| may not be consecutive between planes. Copy data
+  // into a shared memory buffer which is tightly packed.
+  size_t allocation_size =
+      VideoFrame::AllocationSize(frame.format(), frame.coded_size());
+  mojo::ScopedSharedBufferHandle handle =
+      mojo::SharedBufferHandle::Create(allocation_size);
+
+  const size_t y_size =
+      VideoFrame::PlaneSize(frame.format(), VideoFrame::kYPlane,
+                            frame.coded_size())
+          .GetArea();
+  const size_t u_size =
+      VideoFrame::PlaneSize(frame.format(), VideoFrame::kUPlane,
+                            frame.coded_size())
+          .GetArea();
+  const size_t v_size =
+      VideoFrame::PlaneSize(frame.format(), VideoFrame::kVPlane,
+                            frame.coded_size())
+          .GetArea();
+
+  // Computes the offset of planes in shared memory buffer.
+  const size_t y_offset = 0u;
+  const size_t u_offset = y_offset + y_size;
+  const size_t v_offset = y_offset + y_size + u_size;
+
+  // Create a mojo video frame backed by shared memory, so it can be sent to
+  // the browser process.
+  scoped_refptr<MojoSharedBufferVideoFrame> mojo_frame =
+      MojoSharedBufferVideoFrame::Create(
+          frame.format(), frame.coded_size(), frame.visible_rect(),
+          frame.natural_size(), std::move(handle), allocation_size, y_offset,
+          u_offset, v_offset, frame.stride(VideoFrame::kYPlane),
+          frame.stride(VideoFrame::kUPlane), frame.stride(VideoFrame::kVPlane),
+          frame.timestamp());
+
+  // Copy Y plane.
+  memcpy(mojo_frame->shared_buffer_data(),
+         static_cast<const void*>(frame.data(VideoFrame::kYPlane)), y_size);
+
+  // Copy U plane.
+  memcpy(mojo_frame->shared_buffer_data() + u_offset,
+         static_cast<const void*>(frame.data(VideoFrame::kUPlane)), u_size);
+
+  // Copy V plane.
+  memcpy(mojo_frame->shared_buffer_data() + v_offset,
+         static_cast<const void*>(frame.data(VideoFrame::kVPlane)), v_size);
+
+  // TODO(xingliu): Maybe also copy the alpha plane in
+  // |MojoSharedBufferVideoFrame|. The alpha plane is ignored here, but
+  // the |shared_memory| should contain the space for alpha plane.
+
+  return mojo_frame;
+}
+
 // static
 scoped_refptr<MojoSharedBufferVideoFrame> MojoSharedBufferVideoFrame::Create(
     VideoPixelFormat format,
diff --git a/media/mojo/common/mojo_shared_buffer_video_frame.h b/media/mojo/common/mojo_shared_buffer_video_frame.h
index bc690bf0..6d4a84e 100644
--- a/media/mojo/common/mojo_shared_buffer_video_frame.h
+++ b/media/mojo/common/mojo_shared_buffer_video_frame.h
@@ -39,6 +39,12 @@
       const gfx::Size& dimensions,
       base::TimeDelta timestamp);
 
+  // Creates a YUV frame backed by shared memory from in-memory YUV frame.
+  // Internally the data from in-memory YUV frame will be copied to a
+  // consecutive block in shared memory. Will return null on failure.
+  static scoped_refptr<MojoSharedBufferVideoFrame> CreateFromYUVFrame(
+      const VideoFrame& frame);
+
   // Creates a MojoSharedBufferVideoFrame that uses the memory in |handle|.
   // This will take ownership of |handle|, so the caller can no longer use it.
   // |mojo_shared_buffer_done_cb|, if not null, is called on destruction,
diff --git a/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc b/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc
index c2fce253..45b397f 100644
--- a/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc
+++ b/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc
@@ -225,4 +225,28 @@
   EXPECT_TRUE(frame->data(VideoFrame::kVPlane));
 }
 
+TEST(MojoSharedBufferVideoFrameTest, YUVFrameToMojoFrame) {
+  std::vector<uint8_t> data = std::vector<uint8_t>(12, 1u);
+  const auto pixel_format = VideoPixelFormat::PIXEL_FORMAT_I420;
+  const auto size = gfx::Size(1, 1);
+  scoped_refptr<VideoFrame> frame = VideoFrame::WrapExternalYuvData(
+      pixel_format, size, gfx::Rect(1, 1), size, 4, 4, 4, &data[0], &data[4],
+      &data[8], base::TimeDelta());
+  auto mojo_frame = MojoSharedBufferVideoFrame::CreateFromYUVFrame(*frame);
+  EXPECT_TRUE(mojo_frame);
+
+  const size_t u_offset =
+      VideoFrame::PlaneSize(pixel_format, VideoFrame::kYPlane, size).GetArea();
+  const size_t v_offset =
+      u_offset +
+      VideoFrame::PlaneSize(pixel_format, VideoFrame::kUPlane, size).GetArea();
+
+  // Verifies mapped size and offset.
+  EXPECT_EQ(mojo_frame->MappedSize(),
+            frame->AllocationSize(pixel_format, size));
+  EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kYPlane), 0u);
+  EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kUPlane), u_offset);
+  EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kVPlane), v_offset);
+}
+
 }  // namespace media
diff --git a/media/mojo/services/mojo_cdm_service.cc b/media/mojo/services/mojo_cdm_service.cc
index e7095d2a..9790fcc 100644
--- a/media/mojo/services/mojo_cdm_service.cc
+++ b/media/mojo/services/mojo_cdm_service.cc
@@ -176,8 +176,11 @@
         new MojoDecryptorService(cdm_context->GetDecryptor(), nullptr));
     decryptor_binding_ = std::make_unique<mojo::Binding<mojom::Decryptor>>(
         decryptor_.get(), MakeRequest(&decryptor_ptr));
+    // base::Unretained is safe because |decryptor_binding_| is owned by |this|.
+    // If |this| is destructed, |decryptor_binding_| will be destructed as well
+    // and the error handler should never be called.
     decryptor_binding_->set_connection_error_handler(base::BindOnce(
-        &MojoCdmService::OnDecryptorConnectionError, weak_this_));
+        &MojoCdmService::OnDecryptorConnectionError, base::Unretained(this)));
   }
 
   // If the |context_| is not null, we should support connecting the |cdm| with
diff --git a/media/mojo/services/mojo_cdm_service.h b/media/mojo/services/mojo_cdm_service.h
index b8308cc..fd26546 100644
--- a/media/mojo/services/mojo_cdm_service.h
+++ b/media/mojo/services/mojo_cdm_service.h
@@ -115,7 +115,6 @@
 
   mojom::ContentDecryptionModuleClientAssociatedPtr client_;
 
-  base::WeakPtr<MojoCdmService> weak_this_;
   base::WeakPtrFactory<MojoCdmService> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(MojoCdmService);
diff --git a/media/test/PRESUBMIT.py b/media/test/PRESUBMIT.py
index d622b9c..a2a051d 100644
--- a/media/test/PRESUBMIT.py
+++ b/media/test/PRESUBMIT.py
@@ -10,10 +10,10 @@
 
 def _CheckTestDataReadmeUpdated(input_api, output_api):
   """
-  Checks to make sure the README file is updated when changing test files.
+  Checks to make sure the README.md file is updated when changing test files.
   """
   test_data_dir = input_api.os_path.join('media', 'test', 'data')
-  readme_path = input_api.os_path.join('media', 'test', 'data', 'README')
+  readme_path = input_api.os_path.join('media', 'test', 'data', 'README.md')
   test_files = []
   readme_updated = False
   errors = []
diff --git a/media/test/data/README b/media/test/data/README
deleted file mode 100644
index 92f7b65..0000000
--- a/media/test/data/README
+++ /dev/null
@@ -1,424 +0,0 @@
-// Copyright (c) 2011 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.
-
-bear-320x240.webm - WebM encode of bear.1280x720.mp4 resized to 320x240.
-bear-320x240-video-only.webm - The video track of bear-320x240.webm.
-bear-320x240-audio-only.webm - The audio track of bear-320x240.webm.
-bear-vp9.webm - VP9 video only WebM file.
-bear-vp9-opus.webm - VP9 Video with Opus Audio.
-bear-vp8-webvtt.webm - WebM VP8 video with WebVTT subtitle track.
-bear-1280x720-avt_subt_frag.mp4 - Fragmented bear_1280x720.mp4 with text track
-                                  containing srt from bear-vp8-webvtt.webm as
-                                  a 'subt' handler type.
-bear-1280x720-av_frag.mp4 - Fragmented bear_1280x720.mp4.
-bear-1280x720-av_frag-initsegment-mvhd_version_0-mvhd_duration_bits_all_set.mp4:
-  Just the first initialization segment of bear-1280x720_av_frag.mp4, modified to
-  have the mvhd version 0 32-bit duration field set to all 1's.
-bear-flac.mp4 - Unfragmented audio-only 44.1kHz FLAC in MP4 file, created using:
-  ffmpeg -i bear-1280x720.mp4 -map 0:0 -acodec flac -strict -2 bear-flac.mp4
-  Note, "-strict -2" was required because current ffmpeg libavformat version
-  57.75.100 indicates that flac in MP4 support is experimental.
-bear-flac_frag.mp4 - Fragmented audio-only 44.1kHz FLAC in MP4 file, created using:
-  ffmpeg -i bear-flac.mp4 -acodec copy -strict -2 -movflags frag_keyframe+empty_moov+default_base_moof bear-flac_frag.mp4
-bear-flac-192kHz.mp4 - Unfragmented audio-only high-sample-rate FLAC in MP4 file, created using:
-  ffmpeg -i bear-1280x720.mp4 -map 0:0 -acodec flac -strict -2 -ar 192000 bear-flac-192kHz.mp4
-bear-flac-192kHz_frag.mp4 - Fragmented audio-only high-sample-rate FLAC in MP4 file, created using:
-  ffmpeg -i bear-flac-192kHz.mp4 -acodec copy -strict -2 -movflags frag_keyframe+empty_moov+default_base_moof bear-flac-192kHz_frag.mp4
-sfx-flac.mp4 - Unfragmented audio-only 44.1kHz FLAC in MP4 file, created using:
-  ffmpeg -i sfx.flac -map 0:0 -acodec copy -strict -2 sfx-flac.mp4
-sfx-flac_frag.mp4 - Fragmented audio-only 44.1kHz FLAC in MP4 file, created using:
-  ffmpeg -i sfx.flac -map 0:0 -acodec copy -strict -2 -movflags frag_keyframe+empty_moov+default_base_moof sfx-flac_frag.mp4
-bear-vp8a.webm - WebM VP8 video with alpha channel.
-bear-vp8a-odd-dimensions.webm - WebM VP8 video with alpha channel and odd dimensions.
-bear-opus.webm - Opus Audio only WebM file.
-no_streams.webm - Header, Info, & Tracks element from bear-320x240.webm slightly corrupted so it looks
-                  like there are no tracks.
-nonzero-start-time.webm - Has the same headers as bear-320x240.webm but the first cluster of this file
-                          is the second cluster of bear-320x240.webm. This creates the situation where
-                          the media data doesn't start at time 0.
-bear-320x240_corrupted_after_init_segment.webm - bear-320x240.webm's initialization segment followed by "CORRUPTED\n"
-bear-320x240-live.webm - bear-320x240.webm remuxed w/o a duration and using clusters with unknown sizes.
-                         ffmpeg -i bear-320x240.webm -acodec copy -vcodec copy -f webm pipe:1 > bear-320x240-live.webm
-four-colors.mp4 - A 960x540 H.264 mp4 video with 4 color blocks (Y,R,G,B) in
-                  every frame. The video playback looks like a still image.
-                  An image of 4 color blocks (.png file) is first created by
-                  Windows Paint.exe. This image is then used as a basic video
-                  frame in making this 2-second video from Mac iStopMotion.
-vp8-I-frame-160x240 - The first I frame of a 160x240 reencode of bear-320x240.webm.
-vp8-I-frame-320x120 - The first I frame of a 320x120 reencode of bear-320x240.webm.
-vp8-I-frame-320x240 - The first I frame of bear-320x240.webm.
-vp8-P-frame-320x240 - The second P frame of bear-320x240.webm.
-vp8-I-frame-320x480 - The first I frame of a 320x480 reencode of bear-320x240.webm.
-vp8-I-frame-640x240 - The first I frame of a 640x240 reencode of bear-320x240.webm.
-vp8-corrupt-I-frame - A copy of vp8-I-frame-320x240 w/ all bytes XORed w/ 0xA5.
-
-colour.webm - a WebM file containing color metadata in MKV/WebM Colour element
-    copied from libwebm/testing/testdata/colour.webm
-
-AAC test data from MPEG-DASH demoplayer (44100 Hz, stereo)
-Duration of each packet is (1024/44100 Hz), approximately 23.22 ms.
-aac-44100-packet-0  - timestamp: 0ms
-aac-44100-packet-1  - timestamp: 23.22ms
-aac-44100-packet-2  - timestamp: 46.44ms
-aac-44100-packet-3  - timestamp: 69.66ms
-
-Vorbis test data from bear.ogv (44100 Hz, 16 bits, stereo)
-vorbis-extradata - Vorbis extradata section
-vorbis-packet-0  - timestamp: 0ms, duration: 0ms
-vorbis-packet-1  - timestamp: 0ms, duration: 0ms
-vorbis-packet-2  - timestamp: 0ms, duration: 0ms
-vorbis-packet-3  - timestamp: 2902ms, duration: 0ms
-
-// MSE MP4 keyframe-metadata versus encoded AVC keyframe-ness test media:
-bear-640x360-v-2frames_frag.mp4 - Just first 2 video frames of bear-640x360-v_frag.mp4, created with:
-  ffmpeg -i bear-640x360-v_frag.mp4 -vcodec copy -movflags frag_keyframe+empty_moov+default_base_moof \
-    -vframes 2 bear-640x360-v-2frames_frag.mp4
-  It's 1 keyframe + 1 non-keyframe, with container's frame keyframe-ness correct.
-bear-640x360-v-2frames-keyframe-is-non-sync-sample_frag.mp4
-  This is bear-640x360-v-2frames_frag.mp4, with manually updated trun.first_sample_flags:
-    s/0x02000000/0x01010000 (first frame is non-sync-sample, depends on another
-    frame, mismatches compressed h264 first frame's keyframe-ness).
-bear-640x360-v-2frames-nonkeyframe-is-sync-sample_frag.mp4
-  This is bear-640x360-v-2frames_frag.mp4, with manually updated tfhd.default_sample_flags:
-    s/0x01010000/0x02000000 (second frame is sync-sample, doesn't depend on other
-    frames, mismatches compressed h264 second frame's nonkeyframe-ness).
-
-// 10-bit test file(s)
-bear-320x180-hi10p.mp4
-bear-320x240-vp9_profile2.webm - VP9 encoded video with profile 2 (10-bit, 4:2:0). Codec string: vp09.02.10.10.01.02.02.02.00.
-
-// Encrypted Files
-bear-1280x720-a_frag-cenc.mp4 - A fragmented MP4 version of the audio track of bear-1280x720.mp4 encrypted (ISO CENC) using key ID [1] and key [2].
-bear-1280x720-a_frag-cenc-key_rotation.mp4 - A fragmented MP4 version of the audio track of bear-1280x720.mp4 encrypted (ISO CENC) using key ID [1] and key [2] with key rotation [3].
-bear-1280x720-a_frag-cenc_clear-all.mp4 - Same as bear-1280x720-a_frag-cenc.mp4 but no fragments are encrypted.
-bear-1280x720-v_frag-cenc.mp4 - A fragmented MP4 version of the video track of bear-1280x720.mp4 encrypted (ISO CENC) using key ID [1] and key [2].
-bear-1280x720-v_frag-cenc-key_rotation.mp4 - A fragmented MP4 version of the video track of bear-1280x720.mp4 encrypted (ISO CENC) using key ID [1] and key [2] with key rotation [3].
-bear-1280x720-v_frag-cenc_clear-all.mp4 - Same as bear-1280x720-v_frag-cenc.mp4 but no fragments are encrypted.
-bear-1280x720-a_frag-cenc_missing-saiz-saio.mp4 - An invalid file similar to bear-1280x720-a_frag-cenc.mp4 but has no saiz and saio boxes. To save space, it has only one encrypted sample.
-bear-320x240-v_frag-vp9.mp4 - Bear video with VP9 codec in MP4 container. Generated with shaka-packager 1e2da22c8809c17cc4dfdb45924ec45e42058393 (https://github.com/google/shaka-packager):
-                              packager in=bear-vp9.webm,stream=video,out=bear-320x240-v_frag-vp9.mp4
-bear-320x240-v_frag-vp9-cenc.mp4 - Same as above, with video encrypted using key ID [1] and key [2]. Generated with shaka-packager 1e2da22c8809c17cc4dfdb45924ec45e42058393 (https://github.com/google/shaka-packager):
-                                   packager in=bear-vp9.webm,stream=video,out=bear-320x240-v_frag-vp9-cenc.mp4 --enable_fixed_key_encryption --key_id 30313233343536373839303132333435
-                                            --key ebdd62f16814d27b68ef122afce4ae3c --clear_lead 0
-                                            --pssh 0000003470737368010000001077EFECC0B24D02ACE33C1E52E2FB4B000000013031323334353637383930313233343500000000000000467073736800000000EDEF8BA979D64ACEA3C827DCD51D21ED000000261210303132333435363738393031323334351A00221030313233343536373839303132333435
-bear-320x240-16x9-aspect-av_enc-av.webm - bear-320x240-16x9-aspect.webm with audio & video encrypted using key ID [1] and key [2]
-bear-320x240-av_enc-av.webm - bear-320x240.webm with audio & video encrypted using key ID [1] and key [2].
-bear-320x240-av_enc-av_clear-1s.webm - Same as bear-320x240-av_enc-av.webm but with no frames in the first second encrypted.
-bear-320x240-av_enc-av_clear-all.webm - Same as bear-320x240-av_enc-av.webm but with no frames encrypted.
-bear-320x240-v-vp9_profile2_subsample_cenc-v.webm - Encrypted Bear video with VP9 codec (profile 2) in WebM container, using key ID [1] and key [2]. Codec string: vp09.02.10.10.01.02.02.02.00.
-                                                    Generated with shaka-packager 4ba5bec66054cfd4af13c07ac62a97f1a1a2e5f9 (https://github.com/google/shaka-packager):
-                                                    packager in=bear-320x240-vp9_profile2.webm,stream=video,out=bear-320x240-v-vp9_profile2_subsample_cenc-v.webm --enable_fixed_key_encryption --key_id 30313233343536373839303132333435
-                                                             --key ebdd62f16814d27b68ef122afce4ae3c --clear_lead 0
-                                                             --pssh 0000003470737368010000001077EFECC0B24D02ACE33C1E52E2FB4B000000013031323334353637383930313233343500000000000000467073736800000000EDEF8BA979D64ACEA3C827DCD51D21ED000000261210303132333435363738393031323334351A00221030313233343536373839303132333435
-bear-320x240-v-vp9_profile2_subsample_cenc-v.mp4 - Same as above, in MP4 container. Codec string: vp09.02.10.10.01.02.02.02.00.
-                                                   Generated with shaka-packager 4ba5bec66054cfd4af13c07ac62a97f1a1a2e5f9 (https://github.com/google/shaka-packager):
-                                                   packager in=bear-320x240-vp9_profile2.webm,stream=video,out=bear-320x240-v-vp9_profile2_subsample_cenc-v.mp4 --enable_fixed_key_encryption --key_id 30313233343536373839303132333435
-                                                            --key ebdd62f16814d27b68ef122afce4ae3c --clear_lead 0
-                                                            --pssh 0000003470737368010000001077EFECC0B24D02ACE33C1E52E2FB4B000000013031323334353637383930313233343500000000000000467073736800000000EDEF8BA979D64ACEA3C827DCD51D21ED000000261210303132333435363738393031323334351A00221030313233343536373839303132333435
-bear-640x360-av_enc-av.webm - bear-640x360.webm with audio & video encrypted using key ID [1] and key [2].
-bear-320x240-av_enc-v.webm - bear-320x240.webm with video track encrypted using key ID [1] and key [2].
-bear-320x240-av_enc-a.webm - bear-320x240.webm with audio track encrypted using key ID [1] and key [2].
-bear-320x240-v_enc-v.webm - bear-320x240-video-only.webm encrypted using key ID [1] and key [2].
-bear-320x240-v-vp9_fullsample_enc-v.webm - bear-vp9.webm VP9 video only encrypted using key ID [1] and key [2] with full sample encryption.
-bear-320x240-v-vp9_subsample_enc-v.webm - bear-vp9.webm VP9 video only encrypted using key ID [1] and key [2] with subsample encryption [4].
-bear-320x240-opus-a_enc-a.webm - bear-opus.webm encrypted using key ID [1] and key[2].
-bear-320x240-opus-av_enc-av.webm - bear-vp9-opus.webm with audio & video encrypted using key ID [1] and key[2].
-bear-320x240-opus-av_enc-v.webm - bear-vp9-opus.webm with video track encrypted using key ID [1] and key[2].
-bear-640x360-a_frag-cenc.mp4 - A fragmented MP4 version of the audio track of bear-640x360.mp4 encrypted (ISO CENC) using key ID [1] and key [2].
-bear-640x360-a_frag-cenc-key_rotation.mp4 - A fragmented MP4 version of the audio track of bear-640x360.mp4 encrypted (ISO CENC) using key ID [1] and key [2] with key rotation [3].
-bear-640x360-v_frag-cenc-mdat.mp4 - A fragmented MP4 version of the video track of bear-640x360.mp4 encrypted (ISO CENC) using key ID [1] and key [2]  and with sample encryption auxiliary information in the beginning of mdat box.
-bear-640x360-v_frag-cenc-senc.mp4 - Same as above, but with sample encryption information stored in SampleEncryption ('senc') box.
-bear-640x360-v_frag-cenc-senc-no-saiz-saio.mp4 - Same as above, but without saiz and saio boxes.
-bear-640x360-v_frag-cenc-key_rotation.mp4 - A fragmented MP4 version of the video track of bear-640x360.mp4 encrypted (ISO CENC) using key ID [1] and key [2] with key rotation [3].
-
-bear-640x360-v_frag-cenc.mp4
-bear-640x360-v_frag-cbc1.mp4
-bear-640x360-v_frag-cbcs.mp4
-bear-640x360-v_frag-cens.mp4
-- Encrypted versions of bear-640x360-v_frag.mp4, encrypted by Shaka Packager
-  using key ID [1] and key [2]. Sample encryption information stored in
-  SampleEncryption ('senc') box (in each encrypted fragment).
-  Shaka Packager: https://github.com/google/shaka-packager/releases/tag/v2.0.0
-  Command: (replace both places of 'cenc' with desired scheme)
-      packager in=bear-640x360-v_frag.mp4,stream=video,output=bear-640x360-v-cenc.mp4
-               --enable_raw_key_encryption
-               --protection_scheme cenc
-               --clear_lead 0.5
-               --segment_duration 0.5
-               --keys label=:key_id=30313233343536373839303132333435:key=ebdd62f16814d27b68ef122afce4ae3c
-               --pssh 000000327073736800000000EDEF8BA979D64ACEA3C827DCD51D21ED000000121210303132333435363738393031323334350000003470737368010000001077EFECC0B24D02ACE33C1E52E2FB4B000000013031323334353637383930313233343500000000
-  'pssh' data includes entries for both Widevine and Common SystemID [5].
-  It was generated from concatenating the output of:
-      shaka/packager/tools/pssh/pssh-box.py --widevine-system-id --key-id 30313233343536373839303132333435 --hex
-      shaka/packager/tools/pssh/pssh-box.py --common-system-id --key-id 30313233343536373839303132333435 --hex
-
-bear-640x360-a_frag-cbcs.mp4
-- Same as previous instructions, except source is bear-640x360-a_frag.mp4
-  and no clear lead (i.e. --clear_lead 0).
-
-bear-flac-cenc.mp4
-- Encrypted version of bear-flac.mp4, encrypted by Shaka Packager
-  using key ID [1] and key [2]. Sample encryption information stored in
-  SampleEncryption ('senc') box (in each encrypted fragment).
-  Shaka Packager: https://github.com/google/shaka-packager/releases/tag/v2.1.0
-  Command: packager in=bear-flac.mp4,stream=audio,output=bear-flac-cenc.mp4
-               --enable_raw_key_encryption
-               --clear_lead 0.5
-               --segment_duration 0.5
-               --keys label=:key_id=30313233343536373839303132333435:key=ebdd62f16814d27b68ef122afce4ae3c
-               --pssh 000000327073736800000000EDEF8BA979D64ACEA3C827DCD51D21ED000000121210303132333435363738393031323334350000003470737368010000001077EFECC0B24D02ACE33C1E52E2FB4B000000013031323334353637383930313233343500000000
-
-bear-a_enc-a.webm - bear-320x240-audio-only.webm encrypted using key ID [1] and key [2].
-frame_size_change-av_enc-v.webm - third_party/WebKit/LayoutTests/media/resources/frame_size_change.webm encrypted using key ID [1] and key [2].
-
-bear-1280x720-hls.ts: produced using Apple's mediafilesegmenter tool with bear-1280x720.ts as input, with no encryption.
-  mediafilesegmenter -t 10 -start-segments-with-iframe -f 'output_clear/' bear-1280x720.ts
-
-bear-1280x720-hls-sample-aes.ts: produced using Apple's mediafilesegmenter tool also with bear-1280x720.ts as input, but with SAMPLE_AES encryption.
-  (Additional TS packets were constructed manually and prepended to convey the encryption metadata, notably key id and IV).
-  mediafilesegmenter -S -P -k 'key_iv.bin' -t 10 -start-segments-with-iframe -f 'output/' bear-1280x720.ts
-
-bear-1280x720-hls-with-CAT.ts: same as bear-1280x720-hls.ts but with an extra TS packet prepended. This is the same as the first of the metadata packets in
-  bear-1280x720-hls-sample-aes.ts. Its presence indicates that SAMPLE_AES encryption may occur later in the stream, and causes the initial audio and video configs
-  to have an encryption_scheme (of AES-CBC). The actual data is unencrypted, which is indicated by the lack of key ID and IV. This ends up very similar to how
-  clear leader of an otherwise encrypted stream can occur in MP4.
-
-[1] 30313233343536373839303132333435
-[2] ebdd62f16814d27b68ef122afce4ae3c
-[3] KeyIds and Keys are created by left rotating key ID [1] and key [2] using
-    std::rotate for every new crypto period. This is only for testing. The
-    actual key rotation algorithm is often much more complicated.
-[4] http://www.webmproject.org/docs/webm-encryption/#46-subsample-encrypted-block-format
-[5] https://w3c.github.io/encrypted-media/format-registry/initdata/cenc.html#common-system
-
-// Container Tests (additional containers derived from bear.ogv)
-bear.ac3    -- created using "avconv -i bear.ogv -f ac3 -b 192k bear.ac3".
-bear.adts   -- created using "avconv -i bear.ogv -f adts -strict experimental bear.adts".
-bear.aiff   -- created using "avconv -i bear.ogv -f aiff bear.aiff".
-bear.asf    -- created using "avconv -i bear.ogv -f asf bear.asf".
-bear.avi    -- created using "avconv -i bear.ogv -f avi -b 192k bear.avi".
-bear.eac3   -- created using "avconv -i bear.ogv -f eac3 bear.eac3".
-bear.flac   -- created using "avconv -i bear.ogv -f flac bear.flac".
-bear.flv    -- created using "avconv -i bear.ogv -f flv bear.flv".
-bear.h261   -- created using "avconv -i bear.ogv -f h261 -s:0 cif bear.h261".
-bear.h263   -- created using "avconv -i bear.ogv -f h263 -s:0 cif bear.h263".
-bear.m2ts   -- created using "avconv -i bear.ogv -f mpegts bear.m2ts".
-bear.mjpeg  -- created using "avconv -i bear.ogv -f mjpeg bear.mjpeg".
-bear.mpeg   -- created using "avconv -i bear.ogv -f mpeg bear.mpeg".
-bear.rm     -- created using "avconv -i bear.ogv -f rm -b 192k bear.rm".
-bear.swf    -- created using "avconv -i bear.ogv -f swf -an bear.swf".
-
-// VDA test files: test-25fps
-test-25fps.h264:
-  Using ffmpeg SVN-r0.5.9-4:0.5.9-0ubuntu0.10.04.1 @ WebKit r122718, generated
-  with:
-  ffmpeg -i third_party/WebKit/LayoutTests/media/content/test-25fps.mp4 \
-      -vcodec copy -vbsf h264_mp4toannexb -an test-25fps.h264
-
-test-25fps.h264.md5:
-  MD5s of RGB thumbnail rendered version of test-25fps.h264 decoded with Intel
-  VAAPI and V4L2VDA on various platforms.
-  Written out by video_decode_accelerator_unittest.
-  These differ between implementations because color space-converted frames are
-  not specified to the last bit and GLES shader/texture filtering
-  precision varies.
-
-test-25fps.vp8:
-  ffmpeg git-2012-07-19-a8d8e86, libvpx ToT 7/19, chromium r147247,
-  mkvextract v5.0.1
-  ffmpeg -i test-25fps.h264 -vcodec libvpx -an test-25fps.webm && \
-      mkvextract tracks test-25fps.webm 1:test-25fps.vp8 && rm test-25fps.webm
-
-test-25fps.vp8.md5:
-  MD5 of RGB thumbnail rendered version of test-25fps.vp8. Written out by
-  video_decode_accelerator_unittest.
-
-test-25fps.vp9:
-  avconv 9.16-6:9.16-0ubuntu0.14.04.1, vpxenc v1.3.0
-  avconv -i test-25fps.h264 -c:v rawvideo -pix_fmt yuv420p test-25fps_i420.yuv
-  vpxenc test-25fps_i420.yuv -o test-25fps.vp9 --codec=vp9 -w 320 -h 240 --ivf \
-      --profile=0 --kf-min-dist=0 --kf-max-dist=150 --lag-in-frames=24 \
-      --drop-frame=0 --target-bitrate=140 --cq-level=23 --min-q=4 --max-q=56 \
-      --static-thresh=1000 --arnr-maxframes=7 --arnr-strength=5 --arnr-type=3 \
-      --cpu-used=1 --good --tile-columns=1 --passes=2 --threads=1 --fps=25/1 \
-      --end-usage=cq --auto-alt-ref=1 --bias-pct=50 --minsection-pct=0 \
-      --maxsection-pct=2000 --undershoot-pct=100
-
-test-25fps.vp9.md5:
-  MD5 of RGB thumbnail rendered version of test-25fps.vp9. Written out by
-  video_decode_accelerator_unittest.
-
-test-25fps.vp9_2:
-  Similar to test-25fps.vp9, substituting the option "--profile=0" with
-  "--profile=2 --bit-depth=10" to vpxenc. (Note that vpxenc must have been
-  configured with the option --enable-vp9-highbitdepth).
-
-test-25fps.vp9_2.md5:
-  MD5 of RGB thumbnail rendered version of test-25fps.vp9_2. Written out by
-  video_decode_accelerator_unittest.
-
-vp90_2_10_show_existing_frame2.vp9.ivf:
-  VP9 video with show_existing_frame flag. The original test stream comes from
-  Android CTS.
-  ffmpeg -i vp90_2_17_show_existing_frame.vp9 -vcodec copy -an -f ivf \
-      vp90_2_17_show_existing_frame.vp9.ivf
-
-vp90_2_10_show_existing_frame2.vp9.ivf.md5:
-  MD5 of RGB thumbnail rendered version of
-  vp90_2_10_show_existing_frame2.vp9.ivf. Written out by
-  video_decode_accelerator_unittest.
-
-// VDA test files: bear
-bear.h264:
-  Using ffmpeg version 0.8.6-4:0.8.6-0ubuntu0.12.04.1, generated with
-  bear.mp4 (https://chromiumcodereview.appspot.com/10805089):
-  ffmpeg -i bear.mp4 -vcodec copy -vbsf h264_mp4toannexb \
-      -an bear.h264
-
-bear.h264.md5:
-  MD5s of RGB thumbnail rendered version of bear.h264 decoded with Intel
-  VAAPI on Ivy Bridge and Sandy Bridge and V4L2VDA on Exynos.
-  Written out by video_decode_accelerator_unittest.
-  These differ between implementations because color space-converted frames are
-  not specified to the last bit and GLES shader/texture filtering
-  precision varies.
-
-// VDA test files: npot-video
-npot-video.h264:
-  Using ffmpeg version 0.8.6-4:0.8.6-0ubuntu0.12.04.1, generated with
-  npot-video.mp4 (https://codereview.chromium.org/8342021):
-  ffmpeg -i npot-video.mp4 -vcodec copy -vbsf h264_mp4toannexb \
-      -an npot-video.h264
-
-npot-video.h264.md5:
-  MD5s of RGB thumbnail rendered version of npot-video.h264 decoded with Intel
-  VAAPI on Ivy Bridge and Sandy Bridge and V4L2VDA on Exynos.
-  Written out by video_decode_accelerator_unittest.
-  These differ between implementations because color space-converted frames are
-  not specified to the last bit and GLES shader/texture filtering
-  precision varies.
-
-// VDA test files: red-green
-red-green.h264:
-  Using ffmpeg version 0.8.6-4:0.8.6-0ubuntu0.12.04.1, generated with
-  red-green.mp4 (https://codereview.chromium.org/8342021):
-  ffmpeg -i red-green.mp4 -vcodec copy -vbsf h264_mp4toannexb \
-      -an red-green.h264
-
-red-green.h264.md5:
-  MD5s of RGB thumbnail rendered version of red-green.h264 decoded with Intel
-  VAAPI on Ivy Bridge and Sandy Bridge and V4L2VDA on Exynos.
-  Written out by video_decode_accelerator_unittest.
-  These differ between implementations because color space-converted frames are
-  not specified to the last bit and GLES shader/texture filtering
-  precision varies.
-
-// VDA test files: resolution_change_500frames
-resolution_change_500frames-vp8.ivf
-resolution_change_500frames-vp9.ivf
-  Dumped compressed stream of videos on http://crosvideo.appspot.com manually
-  changing resolutions at random.
-  Those contain 144p, 240p, 360p, 480p, 720p, and 1080p frames.
-  Those frame sizes can be found by
-  ffprobe -show_frames resolution_change_500frames.vp8
-
-// VDA test files: switch_1080p_720p_240frames
-switch_1080p_720p_240frames.h264
-  Extract 240 frames using ffmpeg from
-  http://commondatastorage.googleapis.com/chromiumos-test-assets-public/MSE/switch_1080p_720p.mp4.
-  The frame sizes change between 1080p and 720p every 24 frames.
-
-// VEA test files:
-bear_320x192_40frames.yuv
-  First 40 raw i420 frames of bear-1280x720.mp4 scaled down to 320x192 for
-  video_encode_accelerator_unittest.
-
-// VP9 parser test files:
-bear-vp9.ivf
-  - Created using "avconv -i bear-vp9.webm -vcodec copy -an -f ivf bear-vp9.ivf".
-bear-vp9.ivf.context
-test-25fps.vp9.context
-  - Manually dumped from libvpx with bear-vp9.ivf and test-25fps.vp9. See
-    vp9_parser_unittest.cc for description of their format.
-
-// WebM files for testing multiple tracks.
-green-a300hz.webm - WebM file containing 12 seconds of solid green video + 300Hz sine wave audio
-red-a500hz.webm - WebM file containing 10 seconds of solid red video + 500Hz sine wave audio
-  - Created with the following commands:
-    ffmpeg -f lavfi -i color=c=green:size=160x120 -t 12 -c:v libvpx green.webm
-    ffmpeg -f lavfi -i color=c=red:size=320x240 -t 10 -c:v libvpx red.webm
-    ffmpeg -f lavfi -i "sine=frequency=300:sample_rate=48000" -t 12 -c:v libvpx a300hz.webm
-    ffmpeg -f lavfi -i "sine=frequency=500:sample_rate=48000" -t 10 -c:v libvpx a500hz.webm
-    ffmpeg -i green.webm -i a300hz.webm -map 0 -map 1 green-a300hz.webm
-    ffmpeg -i red.webm -i a500hz.webm -map 0 -map 1 red-a500hz.webm
-
-// JPEG test files:
-pixel-1280x720.jpg - Single MJEPG encoded frame of 1280x720, captured on Chromebook Pixel. This image does not have Huffman table.
-peach_pi-1280x720.jpg - Single MJPEG encoded frame of 1280x720, captured on Samsung Chromebook 2(13"). This image has Huffman table.
-blank-1x1.jpg - 1x1 small picture to test special cases.
-
-// MP4 files with non-square pixels.
-bear-640x360-non_square_pixel-with_pasp.mp4
-  Size in TKHD is (639.2x360) and size in STSD is (470x360). A PASP box is
-  present with hSpacing=34 and vSpacing=25. Note that 470.0 * 34 / 25 = 639.2.
-
-bear-640x360-non_square_pixel-without_pasp.mp4
-  Size in TKHD is (639.2x360) and size in STSD is (470x360). No PASP box is
-  present.
-
-// MP4 files with AC3 and EAC3 audio
-bear-ac3-only-frag.mp4
-  AC3 audio in framented MP4, generated with
-  ffmpeg -i bear.ac3 -acodec copy -movflags frag_keyframe bear-ac3-only-frag.mp4
-
-bear-eac3-only-frag.mp4
-  EAC3 audio in framented MP4, generated with
-  ffmpeg -i bear.eac3 -acodec copy -movflags frag_keyframe bear-eac3-only-frag.mp4
-
-// Mpeg2ts stream with AAC HE audio that uses SBR
-bear-1280x720-aac_he.ts
-  Generated by the following command:
-  ffmpeg -i bear-1280x720.mp4 -c:v libx264 -c:a libfdk_aac -profile:a aac_he  bear-1280x720-aac_he.ts
-
-// Mpeg2ts streams MP3 audio
-bear-audio-mp4a.6B.ts
-  Generated by the following commands:
-    ffmpeg -i bear_pcm.wav -c:a mp3 -ar 44100 bear-audio-mp4a.6B.ts
-bear-audio-mp4a.69.ts
-  Generated by the following commands:
-    ffmpeg -i bear_pcm.wav -c:a mp3 -ar 22050 bear-audio-mp4a.69.ts
-
-// MP4 file with HEVC
-bear-320x240-v_frag-hevc.mp4
-  HEVC video stream in fragmented MP4 container, generated with
-  ffmpeg -i bear-320x240.webm -c:v libx265 -an -movflags faststart+frag_keyframe bear-320x240-v_frag-hevc.mp4
-
-// Multi-track MP4 file
-// (c) copyright 2008, Blender Foundation / www.bigbuckbunny.org
-bbb-320x240-2video-2audio.mp4
-  Generated using following commands
-  // Download the source file with 1 video and 1 audio stream.
-  wget http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4
-  // Generate a scaled down to 320x240 video + 2 channel AAC LC audio from the source file.
-  ffmpeg -i bbb_sunflower_1080p_30fps_normal.mp4 -c:v libx264 -crf 36 -vf  scale=320:240 -c:a libfdk_aac -ac 2 -t 24 bbb1.mp4
-  // Generate a file with the original video scaled down to 320x240 and flipped upside down and sine wave instead of audio.
-  ffmpeg -i bbb_sunflower_1080p_30fps_normal.mp4 -f lavfi -i "sine=frequency=500:sample_rate=48000" -map 0:v -map 1:a -c:v libx264 -crf 36 -vf scale=320:240,vflip -c:a libfdk_aac -ac 2 -t 24 bbb2.mp4
-  // Combine the two files generated above into a single fragmented .mp4 file with 2 video and 2 audio tracks.
-  ffmpeg -i bbb1.mp4 -i bbb2.mp4 -map 0:0 -map 0:1 -map 1:0 -map 1:1 -c:v copy -c:a copy -movflags frag_keyframe+omit_tfhd_offset+separate_moof bbb-320x240-2video-2audio.mp4
-
-// Multi-track WebM file
-multitrack-3video-2audio.webm
-    //Generated using following commands:
-    ffmpeg -f lavfi -i color=c=red:size=320x240 -t 5 -c:v libvpx red.webm
-    ffmpeg -f lavfi -i color=c=green:size=320x240 -t 5 -c:v libvpx green.webm
-    ffmpeg -f lavfi -i color=c=blue:size=160x120 -t 10 -c:v libvpx blue.webm
-    ffmpeg -f lavfi -i "sine=frequency=300:sample_rate=48000" -t 10 -c:v libvpx a300hz.webm
-    ffmpeg -f lavfi -i "sine=frequency=500:sample_rate=48000" -t 5 -c:v libvpx a500hz.webm
-    ffmpeg -i red.webm -i green.webm -i blue.webm -i a300hz.webm -i a500hz.webm -map 0 -map 1 -map 2 -map 3 -map 4  multitrack-3video-2audio.webm
-
-// Opus pre-skip and end-trimming test clips.
-// https://people.xiph.org/~greg/opus_testvectors/
-opus-trimming-test.mp4
-opus-trimming-test.ogg
-opus-trimming-test.webm
diff --git a/media/test/data/README.md b/media/test/data/README.md
new file mode 100644
index 0000000..f3d9625
--- /dev/null
+++ b/media/test/data/README.md
@@ -0,0 +1,732 @@
+# Media Test Data
+
+[TOC]
+
+## Instructions
+
+To add, update or remove a test file, please update the list below.
+
+Please provide full reference and steps to generate the test file so that
+any people can regenerate or update the file in the future.
+
+## List of Test Files
+
+### General Test Files
+
+#### bear-320x240.webm
+WebM encode of bear.1280x720.mp4 resized to 320x240.
+
+#### bear-320x240-video-only.webm
+The video track of bear-320x240.webm.
+
+#### bear-320x240-audio-only.webm
+The audio track of bear-320x240.webm.
+
+#### bear-vp9.webm
+VP9 video only WebM file.
+
+#### bear-vp9-opus.webm
+VP9 Video with Opus Audio.
+
+#### bear-opus.webm
+Opus Audio only WebM file.
+
+#### bear-vp8-webvtt.webm
+WebM VP8 video with WebVTT subtitle track.
+
+#### bear-1280x720-avt_subt_frag.mp4
+Fragmented bear_1280x720.mp4 with text track containing srt from
+bear-vp8-webvtt.webm as a 'subt' handler type.
+
+#### bear-1280x720-av_frag.mp4 - Fragmented bear_1280x720.mp4.
+#### bear-1280x720-av_frag-initsegment-mvhd_version_0-mvhd_duration_bits_all_set.mp4:
+Just the first initialization segment of bear-1280x720_av_frag.mp4, modified to
+have the mvhd version 0 32-bit duration field set to all 1's.
+
+### FLAC
+
+#### bear-flac.mp4
+Unfragmented audio-only 44.1kHz FLAC in MP4 file, created using:
+```
+ffmpeg -i bear-1280x720.mp4 -map 0:0 -acodec flac -strict -2 bear-flac.mp4
+```
+**Note**: "-strict -2" was required because current ffmpeg libavformat version
+57.75.100 indicates that flac in MP4 support is experimental.
+
+#### bear-flac_frag.mp4
+Fragmented audio-only 44.1kHz FLAC in MP4 file, created using:
+```
+ffmpeg -i bear-flac.mp4 -acodec copy -strict -2 -movflags frag_keyframe+empty_moov+default_base_moof bear-flac_frag.mp4
+```
+
+#### bear-flac-192kHz.mp4
+Unfragmented audio-only high-sample-rate FLAC in MP4 file, created using:
+```
+ffmpeg -i bear-1280x720.mp4 -map 0:0 -acodec flac -strict -2 -ar 192000 bear-flac-192kHz.mp4
+```
+
+#### bear-flac-192kHz_frag.mp4
+Fragmented audio-only high-sample-rate FLAC in MP4 file, created using:
+```
+ffmpeg -i bear-flac-192kHz.mp4 -acodec copy -strict -2 -movflags frag_keyframe+empty_moov+default_base_moof bear-flac-192kHz_frag.mp4
+```
+
+#### sfx-flac.mp4
+Unfragmented audio-only 44.1kHz FLAC in MP4 file, created using:
+```
+ffmpeg -i sfx.flac -map 0:0 -acodec copy -strict -2 sfx-flac.mp4
+```
+
+#### sfx-flac_frag.mp4
+Fragmented audio-only 44.1kHz FLAC in MP4 file, created using:
+```
+ffmpeg -i sfx.flac -map 0:0 -acodec copy -strict -2 -movflags frag_keyframe+empty_moov+default_base_moof sfx-flac_frag.mp4
+```
+
+### Alpha Channel
+
+#### bear-vp8a.webm
+WebM VP8 video with alpha channel.
+
+#### bear-vp8a-odd-dimensions.webm
+WebM VP8 video with alpha channel and odd dimensions.
+
+### VP8 Frame Data
+
+#### vp8-I-frame-160x240
+The first I frame of a 160x240 re-encode of bear-320x240.webm.
+
+#### vp8-I-frame-320x120
+The first I frame of a 320x120 re-encode of bear-320x240.webm.
+
+#### vp8-I-frame-320x240
+The first I frame of bear-320x240.webm.
+
+#### vp8-P-frame-320x240
+The second P frame of bear-320x240.webm.
+
+#### vp8-I-frame-320x480
+The first I frame of a 320x480 re-encode of bear-320x240.webm.
+
+#### vp8-I-frame-640x240
+The first I frame of a 640x240 re-encode of bear-320x240.webm.
+
+#### vp8-corrupt-I-frame
+A copy of vp8-I-frame-320x240 w/ all bytes XORed w/ 0xA5.
+
+### Corrupted Files
+
+#### no_streams.webm
+Header, Info, & Tracks element from bear-320x240.webm slightly corrupted so it
+looks like there are no tracks.
+
+#### nonzero-start-time.webm
+Has the same headers as bear-320x240.webm but the first cluster of this file
+is the second cluster of bear-320x240.webm. This creates the situation where
+the media data doesn't start at time 0.
+
+#### bear-320x240_corrupted_after_init_segment.webm
+bear-320x240.webm's initialization segment followed by "CORRUPTED\n"
+
+### Live
+
+#### bear-320x240-live.webm
+bear-320x240.webm remuxed w/o a duration and using clusters with unknown sizes.
+```
+ffmpeg -i bear-320x240.webm -acodec copy -vcodec copy -f webm pipe:1 > bear-320x240-live.webm
+```
+
+### Color / High Bit Depth
+
+#### colour.webm
+a WebM file containing color metadata in MKV/WebM Colour element copied from
+libwebm/testing/testdata/colour.webm
+
+#### four-colors.mp4
+A 960x540 H.264 mp4 video with 4 color blocks (Y,R,G,B) in every frame. The
+video playback looks like a still image. An image of 4 color blocks (.png file)
+is first created by Windows Paint.exe. This image is then used as a basic video
+frame in making this 2-second video from Mac iStopMotion.
+
+#### bear-320x180-hi10p.mp4
+#### bear-320x240-vp9_profile2.webm
+VP9 encoded video with profile 2 (10-bit, 4:2:0).
+Codec string: vp09.02.10.10.01.02.02.02.00.
+
+### AAC test data from MPEG-DASH demoplayer (44100 Hz, stereo)
+Duration of each packet is (1024/44100 Hz), approximately 23.22 ms.
+
+* aac-44100-packet-0  - timestamp: 0ms
+* aac-44100-packet-1  - timestamp: 23.22ms
+* aac-44100-packet-2  - timestamp: 46.44ms
+* aac-44100-packet-3  - timestamp: 69.66ms
+
+### Vorbis test data from bear.ogv (44100 Hz, 16 bits, stereo)
+
+* vorbis-extradata - Vorbis extradata section
+* vorbis-packet-0  - timestamp: 0ms, duration: 0ms
+* vorbis-packet-1  - timestamp: 0ms, duration: 0ms
+* vorbis-packet-2  - timestamp: 0ms, duration: 0ms
+* vorbis-packet-3  - timestamp: 2902ms, duration: 0ms
+
+### MSE MP4 keyframe-metadata versus encoded AVC keyframe-ness test media
+
+#### bear-640x360-v-2frames_frag.mp4
+Just first 2 video frames of bear-640x360-v_frag.mp4, created with:
+```
+ffmpeg -i bear-640x360-v_frag.mp4 -vcodec copy -movflags frag_keyframe+empty_moov+default_base_moof \
+    -vframes 2 bear-640x360-v-2frames_frag.mp4
+```
+It's 1 keyframe + 1 non-keyframe, with container's frame keyframe-ness correct.
+
+#### bear-640x360-v-2frames-keyframe-is-non-sync-sample_frag.mp4
+This is bear-640x360-v-2frames_frag.mp4, with manually updated
+trun.first_sample_flags: s/0x02000000/0x01010000 (first frame is
+non-sync-sample, depends on another frame, mismatches compressed h264 first
+frame's keyframe-ness).
+
+#### bear-640x360-v-2frames-nonkeyframe-is-sync-sample_frag.mp4
+This is bear-640x360-v-2frames_frag.mp4, with manually updated
+tfhd.default_sample_flags: s/0x01010000/0x02000000 (second frame is sync-sample,
+doesn't depend on other frames, mismatches compressed h264 second frame's
+nonkeyframe-ness).
+
+## Encrypted Test Files
+
+[Shaka Packager]: https://github.com/google/shaka-packager
+
+[1] Test key ID: 30313233343536373839303132333435
+
+[2] Test key: ebdd62f16814d27b68ef122afce4ae3c
+
+[3] KeyIds and Keys are created by left rotating key ID [1] and key [2] using
+    std::rotate for every new crypto period. This is only for testing. The
+    actual key rotation algorithm is often much more complicated.
+
+### General
+
+#### bear-1280x720-a_frag-cenc.mp4
+A fragmented MP4 version of the audio track of bear-1280x720.mp4 encrypted
+(ISO CENC) using key ID [1] and key [2].
+
+#### bear-1280x720-a_frag-cenc-key_rotation.mp4
+A fragmented MP4 version of the audio track of bear-1280x720.mp4 encrypted
+(ISO CENC) using key ID [1] and key [2] with key rotation [3].
+
+#### bear-1280x720-a_frag-cenc_clear-all.mp4
+Same as bear-1280x720-a_frag-cenc.mp4 but no fragments are encrypted.
+
+#### bear-1280x720-v_frag-cenc.mp4
+A fragmented MP4 version of the video track of bear-1280x720.mp4 encrypted
+(ISO CENC) using key ID [1] and key [2].
+
+#### bear-1280x720-v_frag-cenc-key_rotation.mp4
+A fragmented MP4 version of the video track of bear-1280x720.mp4 encrypted
+(ISO CENC) using key ID [1] and key [2] with key rotation [3].
+
+#### bear-1280x720-v_frag-cenc_clear-all.mp4
+Same as bear-1280x720-v_frag-cenc.mp4 but no fragments are encrypted.
+
+#### bear-1280x720-a_frag-cenc_missing-saiz-saio.mp4
+An invalid file similar to bear-1280x720-a_frag-cenc.mp4 but has no saiz and
+saio boxes. To save space, it has only one encrypted sample.
+
+#### bear-320x240-v_frag-vp9.mp4
+Bear video with VP9 codec in MP4 container. Generated with [Shaka Packager] at
+1e2da22c8809c17cc4dfdb45924ec45e42058393:
+```
+packager in=bear-vp9.webm,stream=video,out=bear-320x240-v_frag-vp9.mp4
+```
+
+#### bear-320x240-v_frag-vp9-cenc.mp4
+Same as above, with video encrypted using key ID [1] and key [2]. Generated with
+[Shaka Packager] at 1e2da22c8809c17cc4dfdb45924ec45e42058393:
+```
+packager in=bear-vp9.webm,stream=video,out=bear-320x240-v_frag-vp9-cenc.mp4
+         --enable_fixed_key_encryption --key_id 30313233343536373839303132333435
+         --key ebdd62f16814d27b68ef122afce4ae3c --clear_lead 0
+         --pssh 0000003470737368010000001077EFECC0B24D02ACE33C1E52E2FB4B000000013031323334353637383930313233343500000000000000467073736800000000EDEF8BA979D64ACEA3C827DCD51D21ED000000261210303132333435363738393031323334351A00221030313233343536373839303132333435
+```
+
+#### bear-320x240-16x9-aspect-av_enc-av.webm
+bear-320x240-16x9-aspect.webm with audio & video encrypted using key ID [1] and
+key [2]
+
+#### bear-320x240-av_enc-av.webm
+bear-320x240.webm with audio & video encrypted using key ID [1] and key [2].
+
+#### bear-320x240-av_enc-av_clear-1s.webm
+Same as bear-320x240-av_enc-av.webm but with no frames in the first second
+encrypted.
+
+#### bear-320x240-av_enc-av_clear-all.webm
+Same as bear-320x240-av_enc-av.webm but with no frames encrypted.
+
+#### bear-320x240-v-vp9_profile2_subsample_cenc-v.webm
+Encrypted Bear video with VP9 codec (profile 2) in WebM container, using key ID
+[1] and key [2]. Codec string: `vp09.02.10.10.01.02.02.02.00`.
+Generated with [Shaka Packager] at 4ba5bec66054cfd4af13c07ac62a97f1a1a2e5f9:
+```
+packager in=bear-320x240-vp9_profile2.webm,stream=video,out=bear-320x240-v-vp9_profile2_subsample_cenc-v.web--enable_fixed_key_encryption --key_id 30313233343536373839303132333435
+         --key ebdd62f16814d27b68ef122afce4ae3c --clear_lead 0
+         --pssh 0000003470737368010000001077EFECC0B24D02ACE33C1E52E2FB4B000000013031323334353637383930313233343500000000000000467073736800000000EDEF8BA979D64ACEA3C827DCD51D21ED000000261210303132333435363738393031323334351A00221030313233343536373839303132333435
+```
+
+#### bear-320x240-v-vp9_profile2_subsample_cenc-v.mp4
+Same as above, in MP4 container. Codec string: vp09.02.10.10.01.02.02.02.00.
+Generated with [Shaka Packager] at 4ba5bec66054cfd4af13c07ac62a97f1a1a2e5f9:
+```
+packager in=bear-320x240-vp9_profile2.webm,stream=video,out=bear-320x240-v-vp9_profile2_subsample_cenc-v.mp--enable_fixed_key_encryption --key_id 30313233343536373839303132333435
+         --key ebdd62f16814d27b68ef122afce4ae3c --clear_lead 0
+         --pssh 0000003470737368010000001077EFECC0B24D02ACE33C1E52E2FB4B000000013031323334353637383930313233343500000000000000467073736800000000EDEF8BA979D64ACEA3C827DCD51D21ED000000261210303132333435363738393031323334351A00221030313233343536373839303132333435
+```
+
+#### bear-640x360-av_enc-av.webm
+bear-640x360.webm with audio & video encrypted using key ID [1] and key [2].
+
+#### bear-320x240-av_enc-v.webm
+bear-320x240.webm with video track encrypted using key ID [1] and key [2].
+
+#### bear-320x240-av_enc-a.webm
+bear-320x240.webm with audio track encrypted using key ID [1] and key [2].
+
+#### bear-320x240-v_enc-v.webm
+bear-320x240-video-only.webm encrypted using key ID [1] and key [2].
+
+#### bear-320x240-v-vp9_fullsample_enc-v.webm
+bear-vp9.webm VP9 video only encrypted using key ID [1] and key [2] with full
+sample encryption.
+
+#### bear-320x240-v-vp9_subsample_enc-v.webm
+bear-vp9.webm VP9 video only encrypted using key ID [1] and key [2] with
+[subsample encryption](http://www.webmproject.org/docs/webm-encryption/#46-subsample-encrypted-block-format).
+
+#### bear-320x240-opus-a_enc-a.webm
+bear-opus.webm encrypted using key ID [1] and key [2].
+
+#### bear-320x240-opus-av_enc-av.webm
+bear-vp9-opus.webm with audio & video encrypted using key ID [1] and key [2].
+
+#### bear-320x240-opus-av_enc-v.webm
+bear-vp9-opus.webm with video track encrypted using key ID [1] and key [2].
+
+#### bear-640x360-a_frag-cenc.mp4
+A fragmented MP4 version of the audio track of bear-640x360.mp4 encrypted (ISO
+CENC) using key ID [1] and key [2].
+
+#### bear-640x360-a_frag-cenc-key_rotation.mp4
+A fragmented MP4 version of the audio track of bear-640x360.mp4 encrypted (ISO
+CENC) using key ID [1] and key [2] with key rotation [3].
+
+#### bear-640x360-v_frag-cenc-mdat.mp4
+A fragmented MP4 version of the video track of bear-640x360.mp4 encrypted (ISO
+CENC) using key ID [1] and key [2]  and with sample encryption auxiliary
+information in the beginning of mdat box.
+
+#### bear-640x360-v_frag-cenc-senc.mp4
+Same as above, but with sample encryption information stored in SampleEncryption
+('senc') box.
+
+#### bear-640x360-v_frag-cenc-senc-no-saiz-saio.mp4
+Same as above, but without saiz and saio boxes.
+
+#### bear-640x360-v_frag-cenc-key_rotation.mp4
+A fragmented MP4 version of the video track of bear-640x360.mp4 encrypted (ISO
+CENC) using key ID [1] and key [2] with key rotation [3].
+
+#### bear-640x360-a_frag-cbcs.mp4
+- Same as previous instructions, except source is bear-640x360-a_frag.mp4
+  and no clear lead (i.e. --clear_lead 0).
+
+#### bear-flac-cenc.mp4
+Encrypted version of bear-flac.mp4, encrypted by [Shaka Packager] v2.1.0 using
+key ID [1] and key [2]. Sample encryption information stored in SampleEncryption
+('senc') box (in each encrypted fragment).
+
+```
+packager in=bear-flac.mp4,stream=audio,output=bear-flac-cenc.mp4
+         --enable_raw_key_encryption
+         --clear_lead 0.5
+         --segment_duration 0.5
+         --keys label=:key_id=30313233343536373839303132333435:key=ebdd62f16814d27b68ef122afce4ae3c
+         --pssh 000000327073736800000000EDEF8BA979D64ACEA3C827DCD51D21ED000000121210303132333435363738393031323334350000003470737368010000001077EFECC0B24D02ACE33C1E52E2FB4B000000013031323334353637383930313233343500000000
+```
+
+#### bear-a_enc-a.webm
+bear-320x240-audio-only.webm encrypted using key ID [1] and key [2].
+
+#### frame_size_change-av_enc-v.webm
+third_party/WebKit/LayoutTests/media/resources/frame_size_change.webm encrypted
+using key ID [1] and key [2].
+
+### Encryption Scheme Test
+
+* bear-640x360-v_frag-cenc.mp4
+* bear-640x360-v_frag-cbc1.mp4
+* bear-640x360-v_frag-cbcs.mp4
+* bear-640x360-v_frag-cens.mp4
+
+Encrypted versions of bear-640x360-v_frag.mp4, encrypted by [Shaka Packager]
+v2.0.0 using key ID [1] and key [2]. Sample encryption information stored in
+SampleEncryption ('senc') box (in each encrypted fragment).
+
+Command: (replace both places of 'cenc' with desired scheme)
+
+```
+packager in=bear-640x360-v_frag.mp4,stream=video,output=bear-640x360-v-cenc.mp4
+         --enable_raw_key_encryption
+         --protection_scheme cenc
+         --clear_lead 0.5
+         --segment_duration 0.5
+         --keys label=:key_id=30313233343536373839303132333435:key=ebdd62f16814d27b68ef122afce4ae3c
+         --pssh 000000327073736800000000EDEF8BA979D64ACEA3C827DCD51D21ED000000121210303132333435363738393031323334350000003470737368010000001077EFECC0B24D02ACE33C1E52E2FB4B000000013031323334353637383930313233343500000000
+```
+
+The 'pssh' data includes entries for both Widevine and
+[Common SystemID](https://w3c.github.io/encrypted-media/format-registry/initdata/cenc.html#common-system).
+It was generated from concatenating the output of:
+```
+shaka/packager/tools/pssh/pssh-box.py --widevine-system-id --key-id 30313233343536373839303132333435 --hex
+shaka/packager/tools/pssh/pssh-box.py --common-system-id --key-id 30313233343536373839303132333435 --hex
+```
+
+### HLS
+
+#### bear-1280x720-hls.ts
+Produced using Apple's mediafilesegmenter tool with bear-1280x720.ts as input,
+with no encryption.
+```
+mediafilesegmenter -t 10 -start-segments-with-iframe -f 'output_clear/' bear-1280x720.ts
+```
+
+#### bear-1280x720-hls-sample-aes.ts
+Produced using Apple's mediafilesegmenter tool also with bear-1280x720.ts as
+input, but with SAMPLE_AES encryption. (Additional TS packets were constructed
+manually and prepended to convey the encryption metadata, notably key id and IV).
+```
+mediafilesegmenter -S -P -k 'key_iv.bin' -t 10 -start-segments-with-iframe -f 'output/' bear-1280x720.ts
+```
+
+#### bear-1280x720-hls-with-CAT.ts
+Same as bear-1280x720-hls.ts but with an extra TS packet prepended. This is the
+same as the first of the metadata packets in bear-1280x720-hls-sample-aes.ts.
+Its presence indicates that SAMPLE_AES encryption may occur later in the stream,
+and causes the initial audio and video configs to have an encryption_scheme (of
+AES-CBC). The actual data is unencrypted, which is indicated by the lack of key
+ID and IV. This ends up very similar to how clear leader of an otherwise
+encrypted stream can occur in MP4.
+
+## Container Test Files
+
+Additional containers derived from bear.ogv:
+
+* bear.ac3    -- created using `avconv -i bear.ogv -f ac3 -b 192k bear.ac3`.
+* bear.adts   -- created using `avconv -i bear.ogv -f adts -strict experimental bear.adts`.
+* bear.aiff   -- created using `avconv -i bear.ogv -f aiff bear.aiff`.
+* bear.asf    -- created using `avconv -i bear.ogv -f asf bear.asf`.
+* bear.avi    -- created using `avconv -i bear.ogv -f avi -b 192k bear.avi`.
+* bear.eac3   -- created using `avconv -i bear.ogv -f eac3 bear.eac3`.
+* bear.flac   -- created using `avconv -i bear.ogv -f flac bear.flac`.
+* bear.flv    -- created using `avconv -i bear.ogv -f flv bear.flv`.
+* bear.h261   -- created using `avconv -i bear.ogv -f h261 -s:0 cif bear.h261`.
+* bear.h263   -- created using `avconv -i bear.ogv -f h263 -s:0 cif bear.h263`.
+* bear.m2ts   -- created using `avconv -i bear.ogv -f mpegts bear.m2ts`.
+* bear.mjpeg  -- created using `avconv -i bear.ogv -f mjpeg bear.mjpeg`.
+* bear.mpeg   -- created using `avconv -i bear.ogv -f mpeg bear.mpeg`.
+* bear.rm     -- created using `avconv -i bear.ogv -f rm -b 192k bear.rm`.
+* bear.swf    -- created using `avconv -i bear.ogv -f swf -an bear.swf`.
+
+## VDA Test Files:
+
+### test-25fps
+
+#### test-25fps.h264
+Using ffmpeg SVN-r0.5.9-4:0.5.9-0ubuntu0.10.04.1 @ WebKit r122718, generated
+with:
+```
+ffmpeg -i third_party/WebKit/LayoutTests/media/content/test-25fps.mp4 \
+      -vcodec copy -vbsf h264_mp4toannexb -an test-25fps.h264
+```
+
+#### test-25fps.h264.md5
+MD5s of RGB thumbnail rendered version of test-25fps.h264 decoded with Intel
+VAAPI and V4L2VDA on various platforms.
+Written out by video_decode_accelerator_unittest.
+These differ between implementations because color space-converted frames are
+not specified to the last bit and GLES shader/texture filtering
+precision varies.
+
+#### test-25fps.vp8
+ffmpeg git-2012-07-19-a8d8e86, libvpx ToT 7/19, chromium r147247,
+mkvextract v5.0.1
+```
+ffmpeg -i test-25fps.h264 -vcodec libvpx -an test-25fps.webm && \
+    mkvextract tracks test-25fps.webm 1:test-25fps.vp8 && rm test-25fps.webm
+```
+
+#### test-25fps.vp8.md5
+MD5 of RGB thumbnail rendered version of test-25fps.vp8. Written out by
+video_decode_accelerator_unittest.
+
+#### test-25fps.vp9
+avconv 9.16-6:9.16-0ubuntu0.14.04.1, vpxenc v1.3.0
+```
+avconv -i test-25fps.h264 -c:v rawvideo -pix_fmt yuv420p test-25fps_i420.yuv
+vpxenc test-25fps_i420.yuv -o test-25fps.vp9 --codec=vp9 -w 320 -h 240 --ivf \
+    --profile=0 --kf-min-dist=0 --kf-max-dist=150 --lag-in-frames=24 \
+    --drop-frame=0 --target-bitrate=140 --cq-level=23 --min-q=4 --max-q=56 \
+    --static-thresh=1000 --arnr-maxframes=7 --arnr-strength=5 --arnr-type=3 \
+    --cpu-used=1 --good --tile-columns=1 --passes=2 --threads=1 --fps=25/1 \
+    --end-usage=cq --auto-alt-ref=1 --bias-pct=50 --minsection-pct=0 \
+    --maxsection-pct=2000 --undershoot-pct=100
+```
+
+#### test-25fps.vp9.md5
+MD5 of RGB thumbnail rendered version of test-25fps.vp9. Written out by
+video_decode_accelerator_unittest.
+
+#### test-25fps.vp9_2
+Similar to test-25fps.vp9, substituting the option `--profile=0` with
+`--profile=2 --bit-depth=10` to vpxenc. (Note that vpxenc must have been
+configured with the option --enable-vp9-highbitdepth).
+
+#### test-25fps.vp9_2.md5
+MD5 of RGB thumbnail rendered version of test-25fps.vp9_2. Written out by
+video_decode_accelerator_unittest.
+
+### VP9 video with show_existing_frame flag
+
+#### vp90_2_10_show_existing_frame2.vp9.ivf
+VP9 video with show_existing_frame flag. The original test stream comes from
+Android CTS.
+```
+ffmpeg -i vp90_2_17_show_existing_frame.vp9 -vcodec copy -an -f ivf \
+    vp90_2_17_show_existing_frame.vp9.ivf
+```
+
+#### vp90_2_10_show_existing_frame2.vp9.ivf.md5
+MD5 of RGB thumbnail rendered version of vp90_2_10_show_existing_frame2.vp9.ivf.
+Written out by video_decode_accelerator_unittest.
+
+### bear
+
+#### bear.h264
+Using ffmpeg version 0.8.6-4:0.8.6-0ubuntu0.12.04.1, generated with
+bear.mp4 (https://chromiumcodereview.appspot.com/10805089):
+```
+ffmpeg -i bear.mp4 -vcodec copy -vbsf h264_mp4toannexb -an bear.h264
+```
+
+#### bear.h264.md5
+MD5s of RGB thumbnail rendered version of bear.h264 decoded with Intel
+VAAPI on Ivy Bridge and Sandy Bridge and V4L2VDA on Exynos.
+Written out by video_decode_accelerator_unittest.
+These differ between implementations because color space-converted frames are
+not specified to the last bit and GLES shader/texture filtering
+precision varies.
+
+### npot-video
+
+#### npot-video.h264
+Using ffmpeg version 0.8.6-4:0.8.6-0ubuntu0.12.04.1, generated with
+npot-video.mp4 (https://codereview.chromium.org/8342021):
+```
+ffmpeg -i npot-video.mp4 -vcodec copy -vbsf h264_mp4toannexb -an npot-video.h264
+```
+
+#### npot-video.h264.md5
+MD5s of RGB thumbnail rendered version of npot-video.h264 decoded with Intel
+VAAPI on Ivy Bridge and Sandy Bridge and V4L2VDA on Exynos.
+Written out by video_decode_accelerator_unittest.
+These differ between implementations because color space-converted frames are
+not specified to the last bit and GLES shader/texture filtering
+precision varies.
+
+### red-green
+
+#### red-green.h264
+Using ffmpeg version 0.8.6-4:0.8.6-0ubuntu0.12.04.1, generated with
+red-green.mp4 (https://codereview.chromium.org/8342021):
+```
+ffmpeg -i red-green.mp4 -vcodec copy -vbsf h264_mp4toannexb -an red-green.h264
+```
+
+#### red-green.h264.md5
+MD5s of RGB thumbnail rendered version of red-green.h264 decoded with Intel
+VAAPI on Ivy Bridge and Sandy Bridge and V4L2VDA on Exynos.
+Written out by video_decode_accelerator_unittest.
+These differ between implementations because color space-converted frames are
+not specified to the last bit and GLES shader/texture filtering
+precision varies.
+
+## Misc Test Files
+
+### resolution_change_500frames
+
+#### resolution_change_500frames-vp8.ivf
+#### resolution_change_500frames-vp9.ivf
+Dumped compressed stream of videos on
+[http://crosvideo.appspot.com](http://crosvideo.appspot.com) manually
+changing resolutions at random. Those contain 144p, 240p, 360p, 480p, 720p, and
+1080p frames. Those frame sizes can be found by
+```
+ffprobe -show_frames resolution_change_500frames.vp8
+```
+
+#### switch_1080p_720p_240frames
+#### switch_1080p_720p_240frames.h264
+Extract 240 frames using ffmpeg from
+http://commondatastorage.googleapis.com/chromiumos-test-assets-public/MSE/switch_1080p_720p.mp4.
+
+The frame sizes change between 1080p and 720p every 24 frames.
+
+### VEA test files:
+
+#### bear_320x192_40frames.yuv
+First 40 raw i420 frames of bear-1280x720.mp4 scaled down to 320x192 for
+video_encode_accelerator_unittest.
+
+###  VP9 parser test files:
+
+#### bear-vp9.ivf
+Created using "avconv -i bear-vp9.webm -vcodec copy -an -f ivf bear-vp9.ivf".
+
+#### bear-vp9.ivf.context
+
+#### test-25fps.vp9.context
+Manually dumped from libvpx with bear-vp9.ivf and test-25fps.vp9. See
+vp9_parser_unittest.cc for description of their format.
+
+###  WebM files for testing multiple tracks.
+
+#### green-a300hz.webm
+WebM file containing 12 seconds of solid green video + 300Hz sine wave audio
+
+#### red-a500hz.webm
+WebM file containing 10 seconds of solid red video + 500Hz sine wave audio
+
+Created with the following commands:
+```
+ffmpeg -f lavfi -i color=c=green:size=160x120 -t 12 -c:v libvpx green.webm
+ffmpeg -f lavfi -i color=c=red:size=320x240 -t 10 -c:v libvpx red.webm
+ffmpeg -f lavfi -i "sine=frequency=300:sample_rate=48000" -t 12 -c:v libvpx a300hz.webm
+ffmpeg -f lavfi -i "sine=frequency=500:sample_rate=48000" -t 10 -c:v libvpx a500hz.webm
+ffmpeg -i green.webm -i a300hz.webm -map 0 -map 1 green-a300hz.webm
+ffmpeg -i red.webm -i a500hz.webm -map 0 -map 1 red-a500hz.webm
+```
+
+### JPEG Test Files
+
+#### pixel-1280x720.jpg
+Single MJEPG encoded frame of 1280x720, captured on Chromebook Pixel. This image
+does not have Huffman table.
+
+#### peach_pi-1280x720.jpg
+Single MJPEG encoded frame of 1280x720, captured on Samsung Chromebook 2(13").
+This image has Huffman table.
+
+#### blank-1x1.jpg
+1x1 small picture to test special cases.
+
+### MP4 files with non-square pixels.
+
+#### bear-640x360-non_square_pixel-with_pasp.mp4
+Size in TKHD is (639.2x360) and size in STSD is (470x360). A PASP box is
+present with hSpacing=34 and vSpacing=25. Note that 470.0 * 34 / 25 = 639.2.
+
+#### bear-640x360-non_square_pixel-without_pasp.mp4
+Size in TKHD is (639.2x360) and size in STSD is (470x360). No PASP box is
+present.
+
+### MP4 files with AC3 and EAC3 audio
+
+#### bear-ac3-only-frag.mp4
+AC3 audio in framented MP4, generated with
+```
+ffmpeg -i bear.ac3 -acodec copy -movflags frag_keyframe bear-ac3-only-frag.mp4
+```
+
+#### bear-eac3-only-frag.mp4
+EAC3 audio in framented MP4, generated with
+```
+ffmpeg -i bear.eac3 -acodec copy -movflags frag_keyframe bear-eac3-only-frag.mp4
+```
+
+### Mpeg2ts stream with AAC HE audio that uses SBR
+
+#### bear-1280x720-aac_he.ts
+Generated by the following command:
+```
+ffmpeg -i bear-1280x720.mp4 -c:v libx264 -c:a libfdk_aac -profile:a aac_he  bear-1280x720-aac_he.ts
+```
+
+### Mpeg2ts streams MP3 audio
+
+#### bear-audio-mp4a.6B.ts
+Generated by the following commands:
+```
+ffmpeg -i bear_pcm.wav -c:a mp3 -ar 44100 bear-audio-mp4a.6B.ts
+```
+
+#### bear-audio-mp4a.69.ts
+Generated by the following commands:
+```
+ffmpeg -i bear_pcm.wav -c:a mp3 -ar 22050 bear-audio-mp4a.69.ts
+```
+
+### MP4 file with HEVC
+
+#### bear-320x240-v_frag-hevc.mp4
+HEVC video stream in fragmented MP4 container, generated with
+```
+ffmpeg -i bear-320x240.webm -c:v libx265 -an -movflags faststart+frag_keyframe bear-320x240-v_frag-hevc.mp4
+```
+
+### Multi-track MP4 file
+
+(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org
+
+#### bbb-320x240-2video-2audio.mp4
+
+Generated using following steps:
+
+1.  Download the source file with 1 video and 1 audio stream.
+    ```
+    wget http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4
+    ```
+2.  Generate a scaled down to 320x240 video + 2 channel AAC LC audio from the
+    source file.
+    ```
+    ffmpeg -i bbb_sunflower_1080p_30fps_normal.mp4 -c:v libx264 -crf 36 -vf  scale=320:240 -c:a libfdk_aac -ac 2 -t 24 bbb1.mp4
+    ```
+3.  Generate a file with the original video scaled down to 320x240 and flipped
+    upside down and sine wave instead of audio.
+    ```
+    ffmpeg -i bbb_sunflower_1080p_30fps_normal.mp4 -f lavfi -i "sine=frequency=500:sample_rate=48000" -map 0:v -map 1:a -c:v libx264 -crf 36 -vf scale=320:240,vflip -c:a libfdk_aac -ac 2 -t 24 bbb2.mp4
+    ```
+4.  Combine the two files generated above into a single fragmented .mp4 file
+    with 2 video and 2 audio tracks.
+    ```
+    ffmpeg -i bbb1.mp4 -i bbb2.mp4 -map 0:0 -map 0:1 -map 1:0 -map 1:1 -c:v copy -c:a copy -movflags frag_keyframe+omit_tfhd_offset+separate_moof bbb-320x240-2video-2audio.mp4
+    ```
+
+### Multi-track WebM file
+
+#### multitrack-3video-2audio.webm
+
+Generated using following commands:
+```
+ffmpeg -f lavfi -i color=c=red:size=320x240 -t 5 -c:v libvpx red.webm
+ffmpeg -f lavfi -i color=c=green:size=320x240 -t 5 -c:v libvpx green.webm
+ffmpeg -f lavfi -i color=c=blue:size=160x120 -t 10 -c:v libvpx blue.webm
+ffmpeg -f lavfi -i "sine=frequency=300:sample_rate=48000" -t 10 -c:v libvpx a300hz.webm
+ffmpeg -f lavfi -i "sine=frequency=500:sample_rate=48000" -t 5 -c:v libvpx a500hz.webm
+ffmpeg -i red.webm -i green.webm -i blue.webm -i a300hz.webm -i a500hz.webm -map 0 -map 1 -map 2 -map 3 -map 4  multitrack-3video-2audio.webm
+```
+
+### Opus pre-skip and end-trimming test clips
+https://people.xiph.org/~greg/opus_testvectors/
+
+* opus-trimming-test.mp4
+* opus-trimming-test.ogg
+* opus-trimming-test.webm
diff --git a/net/base/network_change_notifier_fuchsia_unittest.cc b/net/base/network_change_notifier_fuchsia_unittest.cc
index 000c0da..d6ac1ff 100644
--- a/net/base/network_change_notifier_fuchsia_unittest.cc
+++ b/net/base/network_change_notifier_fuchsia_unittest.cc
@@ -146,8 +146,6 @@
   void GetStats(uint32_t nicid, GetStatsCallback callback) override {}
   void GetAggregateStats(GetAggregateStatsCallback callback) override {}
   void SetInterfaceStatus(uint32_t nicid, bool enabled) override {}
-  void SetRouteTable(
-      ::fidl::VectorPtr<fuchsia::netstack::RouteTableEntry> rt) override {}
   void SetInterfaceAddress(uint32_t nicid,
                            fuchsia::netstack::NetAddress addr,
                            uint8_t prefixLen,
@@ -167,6 +165,13 @@
   void GetFilterStatus(GetFilterStatusCallback callback) override {}
   void SetNameServers(
       ::fidl::VectorPtr<::fuchsia::netstack::NetAddress> servers) override {}
+  void AddEthernetDevice(
+      ::fidl::StringPtr topological_path,
+      ::fidl::InterfaceHandle<::zircon::ethernet::Device> device) override {}
+  void StartRouteTableTransaction(
+      ::fidl::InterfaceRequest<::fuchsia::netstack::RouteTableTransaction>
+          routeTableTransaction,
+      StartRouteTableTransactionCallback callback) override {}
 
   ::fidl::VectorPtr<fuchsia::netstack::NetInterface> interfaces_ =
       fidl::VectorPtr<fuchsia::netstack::NetInterface>::New(0);
diff --git a/net/disk_cache/blockfile/entry_impl.cc b/net/disk_cache/blockfile/entry_impl.cc
index 1a219e5b..2a0c045 100644
--- a/net/disk_cache/blockfile/entry_impl.cc
+++ b/net/disk_cache/blockfile/entry_impl.cc
@@ -6,7 +6,6 @@
 
 #include <limits>
 
-#include "base/debug/alias.h"
 #include "base/hash.h"
 #include "base/macros.h"
 #include "base/strings/string_util.h"
@@ -1148,19 +1147,6 @@
   backend_->OnWrite(buf_len);
 
   if (user_buffers_[index].get()) {
-    // Temporary to debug https://crbug.com/882246
-    base::debug::Alias(&index);
-    base::debug::Alias(&offset);
-    base::debug::Alias(&buf);
-    const char* raw_data = nullptr;
-    void* buf_vptr = nullptr;
-    if (buf) {
-      raw_data = buf->data();
-      memcpy(&buf_vptr, static_cast<void*>(buf), sizeof(void*));
-    }
-    base::debug::Alias(&raw_data);
-    base::debug::Alias(&buf_vptr);
-
     // Complete the operation locally.
     user_buffers_[index]->Write(offset, buf, buf_len);
     ReportIOTime(kWrite, start);
diff --git a/printing/printing_context_android.cc b/printing/printing_context_android.cc
index 1f07f49..3c92d72 100644
--- a/printing/printing_context_android.cc
+++ b/printing/printing_context_android.cc
@@ -28,10 +28,6 @@
 
 namespace {
 
-int Round(double x) {
-  return static_cast<int>(x + 0.5);
-}
-
 // Sets the page sizes for a |PrintSettings| object.  |width| and |height|
 // arguments should be in device units.
 void SetSizes(PrintSettings* settings, int dpi, int width, int height) {
@@ -128,8 +124,8 @@
   int dpi = Java_PrintingContext_getDpi(env, j_printing_context_);
   int width = Java_PrintingContext_getWidth(env, j_printing_context_);
   int height = Java_PrintingContext_getHeight(env, j_printing_context_);
-  width = Round(ConvertUnitDouble(width, kMilsPerInch, 1.0) * dpi);
-  height = Round(ConvertUnitDouble(height, kMilsPerInch, 1.0) * dpi);
+  width = ConvertUnit(width, kMilsPerInch, dpi);
+  height = ConvertUnit(height, kMilsPerInch, dpi);
   SetSizes(&settings_, dpi, width, height);
 
   std::move(callback_).Run(OK);
diff --git a/services/ws/BUILD.gn b/services/ws/BUILD.gn
index 53cd5c69..e92fdc7 100644
--- a/services/ws/BUILD.gn
+++ b/services/ws/BUILD.gn
@@ -119,6 +119,8 @@
   testonly = true
 
   sources = [
+    "client_root_test_helper.cc",
+    "client_root_test_helper.h",
     "event_test_utils.cc",
     "event_test_utils.h",
     "server_window_test_helper.cc",
diff --git a/services/ws/client_root.cc b/services/ws/client_root.cc
index 5571acd..5baf778 100644
--- a/services/ws/client_root.cc
+++ b/services/ws/client_root.cc
@@ -29,9 +29,6 @@
   window_->AddObserver(this);
   if (window_->GetHost())
     window->GetHost()->AddObserver(this);
-  // TODO: wire up gfx::Insets() correctly below. See usage in
-  // aura::ClientSurfaceEmbedder for details. Insets here are used for
-  // guttering.
   client_surface_embedder_ = std::make_unique<aura::ClientSurfaceEmbedder>(
       window_, is_top_level, gfx::Insets());
   // Ensure there is a valid LocalSurfaceId (if necessary).
@@ -50,6 +47,13 @@
       server_window->frame_sink_id());
 }
 
+void ClientRoot::SetClientAreaInsets(const gfx::Insets& client_area_insets) {
+  if (!is_top_level_)
+    return;
+
+  client_surface_embedder_->SetClientAreaInsets(client_area_insets);
+}
+
 void ClientRoot::RegisterVizEmbeddingSupport() {
   // This function should only be called once.
   viz::HostFrameSinkManager* host_frame_sink_manager =
diff --git a/services/ws/client_root.h b/services/ws/client_root.h
index 1e8c051..24ad8a00 100644
--- a/services/ws/client_root.h
+++ b/services/ws/client_root.h
@@ -21,6 +21,10 @@
 class Window;
 }  // namespace aura
 
+namespace gfx {
+class Insets;
+}
+
 namespace viz {
 class SurfaceInfo;
 }
@@ -43,6 +47,11 @@
   ClientRoot(WindowTree* window_tree, aura::Window* window, bool is_top_level);
   ~ClientRoot() override;
 
+  // Called when the client area of the window changes. If the window is a
+  // top-level window, then this propagates the insets to the
+  // ClientSurfaceEmbedder.
+  void SetClientAreaInsets(const gfx::Insets& client_area_insets);
+
   // Registers the necessary state needed for embedding in viz.
   void RegisterVizEmbeddingSupport();
 
@@ -61,6 +70,8 @@
   void UnattachChildFrameSinkIdRecursive(ServerWindow* server_window);
 
  private:
+  friend class ClientRootTestHelper;
+
   void UpdatePrimarySurfaceId();
 
   // Returns true if the WindowService should assign the LocalSurfaceId. A value
diff --git a/services/ws/client_root_test_helper.cc b/services/ws/client_root_test_helper.cc
new file mode 100644
index 0000000..7e660b0
--- /dev/null
+++ b/services/ws/client_root_test_helper.cc
@@ -0,0 +1,20 @@
+// 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 "services/ws/client_root_test_helper.h"
+
+#include "services/ws/client_root.h"
+
+namespace ws {
+
+ClientRootTestHelper::ClientRootTestHelper(ClientRoot* client_root)
+    : client_root_(client_root) {}
+
+ClientRootTestHelper::~ClientRootTestHelper() = default;
+
+aura::ClientSurfaceEmbedder* ClientRootTestHelper::GetClientSurfaceEmbedder() {
+  return client_root_->client_surface_embedder_.get();
+}
+
+}  // namespace ws
diff --git a/services/ws/client_root_test_helper.h b/services/ws/client_root_test_helper.h
new file mode 100644
index 0000000..7674df3b
--- /dev/null
+++ b/services/ws/client_root_test_helper.h
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_WS_CLIENT_ROOT_TEST_HELPER_H_
+#define SERVICES_WS_CLIENT_ROOT_TEST_HELPER_H_
+
+#include "base/macros.h"
+#include "ui/events/event.h"
+
+namespace aura {
+class ClientSurfaceEmbedder;
+}
+
+namespace ws {
+
+class ClientRoot;
+
+// Used for accessing private members of ServerWindow in tests.
+class ClientRootTestHelper {
+ public:
+  explicit ClientRootTestHelper(ClientRoot* client_root);
+  ~ClientRootTestHelper();
+
+  aura::ClientSurfaceEmbedder* GetClientSurfaceEmbedder();
+
+ private:
+  ClientRoot* client_root_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientRootTestHelper);
+};
+
+}  // namespace ws
+
+#endif  // SERVICES_WS_CLIENT_ROOT_TEST_HELPER_H_
diff --git a/services/ws/server_window.cc b/services/ws/server_window.cc
index 20548a61..865254b 100644
--- a/services/ws/server_window.cc
+++ b/services/ws/server_window.cc
@@ -8,6 +8,7 @@
 
 #include "base/containers/flat_map.h"
 #include "components/viz/host/host_frame_sink_manager.h"
+#include "services/ws/client_root.h"
 #include "services/ws/drag_drop_delegate.h"
 #include "services/ws/embedding.h"
 #include "services/ws/public/mojom/window_tree_constants.mojom.h"
@@ -498,6 +499,11 @@
 
   additional_client_areas_ = additional_client_areas;
   client_area_ = insets;
+  ClientRoot* client_root =
+      owning_window_tree_ ? owning_window_tree_->GetClientRootForWindow(window_)
+                          : nullptr;
+  if (client_root)
+    client_root->SetClientAreaInsets(insets);
 }
 
 void ServerWindow::SetHitTestInsets(const gfx::Insets& mouse,
diff --git a/services/ws/server_window_unittest.cc b/services/ws/server_window_unittest.cc
index 0338490..47bd9cd 100644
--- a/services/ws/server_window_unittest.cc
+++ b/services/ws/server_window_unittest.cc
@@ -7,11 +7,13 @@
 #include <memory>
 
 #include "base/run_loop.h"
+#include "services/ws/client_root_test_helper.h"
 #include "services/ws/window_service_test_setup.h"
 #include "services/ws/window_tree.h"
 #include "services/ws/window_tree_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/mus/client_surface_embedder.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
 #include "ui/wm/core/easy_resize_window_targeter.h"
@@ -87,4 +89,22 @@
                            setup.root(), &mouse_event_2));
 }
 
+TEST(ServerWindow, SetClientAreaPropagatesToClientSurfaceEmbedder) {
+  WindowServiceTestSetup setup;
+
+  aura::Window* top_level =
+      setup.window_tree_test_helper()->NewTopLevelWindow();
+  ASSERT_TRUE(top_level);
+  const gfx::Rect top_level_bounds(100, 200, 200, 200);
+  top_level->SetBounds(top_level_bounds);
+  const gfx::Insets top_level_insets(1, 2, 11, 12);
+  setup.window_tree_test_helper()->SetClientArea(top_level, top_level_insets);
+  aura::ClientSurfaceEmbedder* client_surface_embedder =
+      ClientRootTestHelper(
+          setup.window_tree()->GetClientRootForWindow(top_level))
+          .GetClientSurfaceEmbedder();
+  ASSERT_TRUE(client_surface_embedder);
+  EXPECT_EQ(top_level_insets, client_surface_embedder->client_area_insets());
+}
+
 }  // namespace ws
diff --git a/services/ws/window_tree.cc b/services/ws/window_tree.cc
index da5b3e5..339a76e9 100644
--- a/services/ws/window_tree.cc
+++ b/services/ws/window_tree.cc
@@ -307,6 +307,11 @@
                                           : iter->second.client_window_id;
 }
 
+ClientRoot* WindowTree::GetClientRootForWindow(aura::Window* window) {
+  auto iter = FindClientRootWithRoot(window);
+  return iter == client_roots_.end() ? nullptr : iter->get();
+}
+
 ClientRoot* WindowTree::CreateClientRoot(aura::Window* window,
                                          bool is_top_level) {
   DCHECK(window);
diff --git a/services/ws/window_tree.h b/services/ws/window_tree.h
index ac15787..c48de670 100644
--- a/services/ws/window_tree.h
+++ b/services/ws/window_tree.h
@@ -135,6 +135,10 @@
 
   ClientWindowId ClientWindowIdForWindow(aura::Window* window) const;
 
+  // If |window| is a client root, the ClientRoot is returned. This does not
+  // recurse.
+  ClientRoot* GetClientRootForWindow(aura::Window* window);
+
  private:
   friend class ClientRoot;
   // TODO(sky): WindowTree should be refactored such that it is not
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 2b14077..0dc358d 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -173,6 +173,8 @@
 
 #define SK_LEGACY_COLORSPACE_XFORM_STEPS_IMPL
 
+#define SK_LEGACY_MAKE_COLOR_SPACE_IMPL
+
 ///////////////////////// Imported from BUILD.gn and skia_common.gypi
 
 /* In some places Skia can use static initializers for global initialization,
diff --git a/storage/browser/quota/quota_database.cc b/storage/browser/quota/quota_database.cc
index 27e45923..012cc11 100644
--- a/storage/browser/quota/quota_database.cc
+++ b/storage/browser/quota/quota_database.cc
@@ -211,12 +211,10 @@
         " (used_count, last_access_time, origin, type, last_modified_time)"
         " VALUES (?, ?, ?, ?, ?)";
     statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
-    statement.BindInt64(
-        4, last_access_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+    statement.BindInt64(4, TimeToSqlValue(last_access_time));
   }
   statement.BindInt(0, entry.used_count);
-  statement.BindInt64(
-      1, last_access_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+  statement.BindInt64(1, TimeToSqlValue(last_access_time));
   statement.BindString(2, origin.GetURL().spec());
   statement.BindInt(3, static_cast<int>(type));
 
@@ -248,11 +246,9 @@
         "INSERT INTO OriginInfoTable"
         " (last_modified_time, origin, type, last_access_time)  VALUES (?, ?, ?, ?)";
     statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
-    statement.BindInt64(
-        3, last_modified_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+    statement.BindInt64(3, TimeToSqlValue(last_modified_time));
   }
-  statement.BindInt64(
-      0, last_modified_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+  statement.BindInt64(0, TimeToSqlValue(last_modified_time));
 
   statement.BindString(1, origin.GetURL().spec());
   statement.BindInt(2, static_cast<int>(type));
@@ -283,8 +279,7 @@
   if (!statement.Step())
     return false;
 
-  *last_modified_time = base::Time::FromDeltaSinceWindowsEpoch(
-      base::TimeDelta::FromMicroseconds(statement.ColumnInt64(0)));
+  *last_modified_time = TimeFromSqlValue(statement.ColumnInt64(0));
   return true;
 }
 
@@ -299,8 +294,7 @@
       " (last_eviction_time, origin, type)"
       " VALUES (?, ?, ?)";
   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
-  statement.BindInt64(
-      0, last_modified_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+  statement.BindInt64(0, TimeToSqlValue(last_modified_time));
   statement.BindString(1, origin.GetURL().spec());
   statement.BindInt(2, static_cast<int>(type));
 
@@ -373,10 +367,8 @@
   *entry = OriginInfoTableEntry(
       url::Origin::Create(GURL(statement.ColumnString(0))),
       static_cast<StorageType>(statement.ColumnInt(1)), statement.ColumnInt(2),
-      base::Time::FromDeltaSinceWindowsEpoch(
-          base::TimeDelta::FromMicroseconds(statement.ColumnInt64(3))),
-      base::Time::FromDeltaSinceWindowsEpoch(
-          base::TimeDelta::FromMicroseconds(statement.ColumnInt64(4))));
+      TimeFromSqlValue(statement.ColumnInt64(3)),
+      TimeFromSqlValue(statement.ColumnInt64(4)));
 
   return true;
 }
@@ -483,8 +475,7 @@
 
   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
   statement.BindInt(0, static_cast<int>(type));
-  statement.BindInt64(
-      1, modified_since.ToDeltaSinceWindowsEpoch().InMicroseconds());
+  statement.BindInt64(1, TimeToSqlValue(modified_since));
 
   origins->clear();
   while (statement.Step())
@@ -766,11 +757,8 @@
     OriginInfoTableEntry entry(
         url::Origin::Create(GURL(statement.ColumnString(0))),
         static_cast<StorageType>(statement.ColumnInt(1)),
-        statement.ColumnInt(2),
-        base::Time::FromDeltaSinceWindowsEpoch(
-            base::TimeDelta::FromMicroseconds(statement.ColumnInt64(3))),
-        base::Time::FromDeltaSinceWindowsEpoch(
-            base::TimeDelta::FromMicroseconds(statement.ColumnInt64(4))));
+        statement.ColumnInt(2), TimeFromSqlValue(statement.ColumnInt64(3)),
+        TimeFromSqlValue(statement.ColumnInt64(4)));
 
     if (!callback.Run(entry))
       return true;
@@ -779,6 +767,17 @@
   return statement.Succeeded();
 }
 
+// static
+base::Time QuotaDatabase::TimeFromSqlValue(int64_t time) {
+  return base::Time::FromDeltaSinceWindowsEpoch(
+      base::TimeDelta::FromMicroseconds(time));
+}
+
+// static
+int64_t QuotaDatabase::TimeToSqlValue(const base::Time& time) {
+  return time.ToDeltaSinceWindowsEpoch().InMicroseconds();
+}
+
 bool operator<(const QuotaDatabase::QuotaTableEntry& lhs,
                const QuotaDatabase::QuotaTableEntry& rhs) {
   return std::tie(lhs.host, lhs.type, lhs.quota) <
diff --git a/storage/browser/quota/quota_database.h b/storage/browser/quota/quota_database.h
index e8a1f3ea..2c7471e 100644
--- a/storage/browser/quota/quota_database.h
+++ b/storage/browser/quota/quota_database.h
@@ -195,6 +195,12 @@
   bool DumpQuotaTable(const QuotaTableCallback& callback);
   bool DumpOriginInfoTable(const OriginInfoTableCallback& callback);
 
+  // Serialize/deserialize base::Time objects to a stable representation for
+  // persistence in the database.
+  // TODO(pwnall): Add support for base::Time values to //sql directly.
+  static base::Time TimeFromSqlValue(int64_t time);
+  static int64_t TimeToSqlValue(const base::Time& time);
+
   base::FilePath db_file_path_;
 
   std::unique_ptr<sql::Database> db_;
diff --git a/storage/browser/quota/quota_database_unittest.cc b/storage/browser/quota/quota_database_unittest.cc
index 9755dd8..b6f9532 100644
--- a/storage/browser/quota/quota_database_unittest.cc
+++ b/storage/browser/quota/quota_database_unittest.cc
@@ -164,24 +164,16 @@
     const url::Origin kOrigin4 = url::Origin::Create(GURL("http://p/"));
 
     // Adding three temporary storages, and
-    EXPECT_TRUE(
-        db.SetOriginLastAccessTime(kOrigin1, kTemporary,
-                                   base::Time::FromDeltaSinceWindowsEpoch(
-                                       base::TimeDelta::FromMicroseconds(10))));
-    EXPECT_TRUE(
-        db.SetOriginLastAccessTime(kOrigin2, kTemporary,
-                                   base::Time::FromDeltaSinceWindowsEpoch(
-                                       base::TimeDelta::FromMicroseconds(20))));
-    EXPECT_TRUE(
-        db.SetOriginLastAccessTime(kOrigin3, kTemporary,
-                                   base::Time::FromDeltaSinceWindowsEpoch(
-                                       base::TimeDelta::FromMicroseconds(30))));
+    EXPECT_TRUE(db.SetOriginLastAccessTime(
+        kOrigin1, kTemporary, QuotaDatabase::TimeFromSqlValue(10)));
+    EXPECT_TRUE(db.SetOriginLastAccessTime(
+        kOrigin2, kTemporary, QuotaDatabase::TimeFromSqlValue(20)));
+    EXPECT_TRUE(db.SetOriginLastAccessTime(
+        kOrigin3, kTemporary, QuotaDatabase::TimeFromSqlValue(30)));
 
     // one persistent.
-    EXPECT_TRUE(
-        db.SetOriginLastAccessTime(kOrigin4, kPersistent,
-                                   base::Time::FromDeltaSinceWindowsEpoch(
-                                       base::TimeDelta::FromMicroseconds(40))));
+    EXPECT_TRUE(db.SetOriginLastAccessTime(
+        kOrigin4, kPersistent, QuotaDatabase::TimeFromSqlValue(40)));
 
     EXPECT_TRUE(db.GetLRUOrigin(kTemporary, exceptions, nullptr, &origin));
     EXPECT_EQ(kOrigin1, origin);
@@ -243,17 +235,11 @@
 
     // Report last mod time for the test origins.
     EXPECT_TRUE(db.SetOriginLastModifiedTime(
-        kOrigin1, kTemporary,
-        base::Time::FromDeltaSinceWindowsEpoch(
-            base::TimeDelta::FromMicroseconds(0))));
+        kOrigin1, kTemporary, QuotaDatabase::TimeFromSqlValue(0)));
     EXPECT_TRUE(db.SetOriginLastModifiedTime(
-        kOrigin2, kTemporary,
-        base::Time::FromDeltaSinceWindowsEpoch(
-            base::TimeDelta::FromMicroseconds(10))));
+        kOrigin2, kTemporary, QuotaDatabase::TimeFromSqlValue(10)));
     EXPECT_TRUE(db.SetOriginLastModifiedTime(
-        kOrigin3, kTemporary,
-        base::Time::FromDeltaSinceWindowsEpoch(
-            base::TimeDelta::FromMicroseconds(20))));
+        kOrigin3, kTemporary, QuotaDatabase::TimeFromSqlValue(20)));
 
     EXPECT_TRUE(db.GetOriginsModifiedSince(kTemporary, &origins, base::Time()));
     EXPECT_EQ(3U, origins.size());
@@ -261,41 +247,31 @@
     EXPECT_EQ(1U, origins.count(kOrigin2));
     EXPECT_EQ(1U, origins.count(kOrigin3));
 
-    EXPECT_TRUE(
-        db.GetOriginsModifiedSince(kTemporary, &origins,
-                                   base::Time::FromDeltaSinceWindowsEpoch(
-                                       base::TimeDelta::FromMicroseconds(5))));
+    EXPECT_TRUE(db.GetOriginsModifiedSince(kTemporary, &origins,
+                                           QuotaDatabase::TimeFromSqlValue(5)));
     EXPECT_EQ(2U, origins.size());
     EXPECT_EQ(0U, origins.count(kOrigin1));
     EXPECT_EQ(1U, origins.count(kOrigin2));
     EXPECT_EQ(1U, origins.count(kOrigin3));
 
-    EXPECT_TRUE(
-        db.GetOriginsModifiedSince(kTemporary, &origins,
-                                   base::Time::FromDeltaSinceWindowsEpoch(
-                                       base::TimeDelta::FromMicroseconds(15))));
+    EXPECT_TRUE(db.GetOriginsModifiedSince(
+        kTemporary, &origins, QuotaDatabase::TimeFromSqlValue(15)));
     EXPECT_EQ(1U, origins.size());
     EXPECT_EQ(0U, origins.count(kOrigin1));
     EXPECT_EQ(0U, origins.count(kOrigin2));
     EXPECT_EQ(1U, origins.count(kOrigin3));
 
-    EXPECT_TRUE(
-        db.GetOriginsModifiedSince(kTemporary, &origins,
-                                   base::Time::FromDeltaSinceWindowsEpoch(
-                                       base::TimeDelta::FromMicroseconds(25))));
+    EXPECT_TRUE(db.GetOriginsModifiedSince(
+        kTemporary, &origins, QuotaDatabase::TimeFromSqlValue(25)));
     EXPECT_TRUE(origins.empty());
 
     // Update origin1's mod time but for persistent storage.
     EXPECT_TRUE(db.SetOriginLastModifiedTime(
-        kOrigin1, kPersistent,
-        base::Time::FromDeltaSinceWindowsEpoch(
-            base::TimeDelta::FromMicroseconds(30))));
+        kOrigin1, kPersistent, QuotaDatabase::TimeFromSqlValue(30)));
 
     // Must have no effects on temporary origins info.
-    EXPECT_TRUE(
-        db.GetOriginsModifiedSince(kTemporary, &origins,
-                                   base::Time::FromDeltaSinceWindowsEpoch(
-                                       base::TimeDelta::FromMicroseconds(5))));
+    EXPECT_TRUE(db.GetOriginsModifiedSince(kTemporary, &origins,
+                                           QuotaDatabase::TimeFromSqlValue(5)));
     EXPECT_EQ(2U, origins.size());
     EXPECT_EQ(0U, origins.count(kOrigin1));
     EXPECT_EQ(1U, origins.count(kOrigin2));
@@ -303,23 +279,17 @@
 
     // One more update for persistent origin2.
     EXPECT_TRUE(db.SetOriginLastModifiedTime(
-        kOrigin2, kPersistent,
-        base::Time::FromDeltaSinceWindowsEpoch(
-            base::TimeDelta::FromMicroseconds(40))));
+        kOrigin2, kPersistent, QuotaDatabase::TimeFromSqlValue(40)));
 
-    EXPECT_TRUE(
-        db.GetOriginsModifiedSince(kPersistent, &origins,
-                                   base::Time::FromDeltaSinceWindowsEpoch(
-                                       base::TimeDelta::FromMicroseconds(25))));
+    EXPECT_TRUE(db.GetOriginsModifiedSince(
+        kPersistent, &origins, QuotaDatabase::TimeFromSqlValue(25)));
     EXPECT_EQ(2U, origins.size());
     EXPECT_EQ(1U, origins.count(kOrigin1));
     EXPECT_EQ(1U, origins.count(kOrigin2));
     EXPECT_EQ(0U, origins.count(kOrigin3));
 
-    EXPECT_TRUE(
-        db.GetOriginsModifiedSince(kPersistent, &origins,
-                                   base::Time::FromDeltaSinceWindowsEpoch(
-                                       base::TimeDelta::FromMicroseconds(35))));
+    EXPECT_TRUE(db.GetOriginsModifiedSince(
+        kPersistent, &origins, QuotaDatabase::TimeFromSqlValue(35)));
     EXPECT_EQ(1U, origins.size());
     EXPECT_EQ(0U, origins.count(kOrigin1));
     EXPECT_EQ(1U, origins.count(kOrigin2));
@@ -341,33 +311,21 @@
 
     // Report last eviction time for the test origins.
     EXPECT_TRUE(db.SetOriginLastEvictionTime(
-        kOrigin1, kTemporary,
-        base::Time::FromDeltaSinceWindowsEpoch(
-            base::TimeDelta::FromMicroseconds(10))));
+        kOrigin1, kTemporary, QuotaDatabase::TimeFromSqlValue(10)));
     EXPECT_TRUE(db.SetOriginLastEvictionTime(
-        kOrigin2, kTemporary,
-        base::Time::FromDeltaSinceWindowsEpoch(
-            base::TimeDelta::FromMicroseconds(20))));
+        kOrigin2, kTemporary, QuotaDatabase::TimeFromSqlValue(20)));
     EXPECT_TRUE(db.SetOriginLastEvictionTime(
-        kOrigin3, kTemporary,
-        base::Time::FromDeltaSinceWindowsEpoch(
-            base::TimeDelta::FromMicroseconds(30))));
+        kOrigin3, kTemporary, QuotaDatabase::TimeFromSqlValue(30)));
 
     EXPECT_TRUE(db.GetOriginLastEvictionTime(kOrigin1, kTemporary,
                                              &last_eviction_time));
-    EXPECT_EQ(base::Time::FromDeltaSinceWindowsEpoch(
-                  base::TimeDelta::FromMicroseconds(10)),
-              last_eviction_time);
+    EXPECT_EQ(QuotaDatabase::TimeFromSqlValue(10), last_eviction_time);
     EXPECT_TRUE(db.GetOriginLastEvictionTime(kOrigin2, kTemporary,
                                              &last_eviction_time));
-    EXPECT_EQ(base::Time::FromDeltaSinceWindowsEpoch(
-                  base::TimeDelta::FromMicroseconds(20)),
-              last_eviction_time);
+    EXPECT_EQ(QuotaDatabase::TimeFromSqlValue(20), last_eviction_time);
     EXPECT_TRUE(db.GetOriginLastEvictionTime(kOrigin3, kTemporary,
                                              &last_eviction_time));
-    EXPECT_EQ(base::Time::FromDeltaSinceWindowsEpoch(
-                  base::TimeDelta::FromMicroseconds(30)),
-              last_eviction_time);
+    EXPECT_EQ(QuotaDatabase::TimeFromSqlValue(30), last_eviction_time);
 
     // Delete last eviction times for the test origins.
     EXPECT_TRUE(db.DeleteOriginLastEvictionTime(kOrigin1, kTemporary));
@@ -547,11 +505,10 @@
       statement.BindString(0, itr->origin.GetURL().spec());
       statement.BindInt(1, static_cast<int>(itr->type));
       statement.BindInt(2, itr->used_count);
+      statement.BindInt64(3,
+                          QuotaDatabase::TimeToSqlValue(itr->last_access_time));
       statement.BindInt64(
-          3, itr->last_access_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
-      statement.BindInt64(
-          4,
-          itr->last_modified_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+          4, QuotaDatabase::TimeToSqlValue(itr->last_modified_time));
       EXPECT_TRUE(statement.Run());
     }
   }
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 8c05148..fd6fb9f 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -6450,6 +6450,1235 @@
       }
     ]
   },
+  "mac-osxbeta-rel": {
+    "gtest_tests": [
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "accessibility_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "app_shell_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "blink_common_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "blink_fuzzer_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "blink_heap_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "blink_platform_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "boringssl_crypto_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "boringssl_ssl_tests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mac_window_server_killers.browser_tests.filter",
+          "--gtest_shuffle"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14",
+              "pool": "Chrome-quarantine"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_browser_tests.filter"
+        ],
+        "name": "network_service_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ],
+          "shards": 15
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-features=WebUIPolymer2",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_polymer2_browser_tests.filter"
+        ],
+        "name": "webui_polymer2_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "browser_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "cast_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "cc_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "chrome_app_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "chromedriver_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "components_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+        ],
+        "name": "network_service_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=TracingPerfettoBackend",
+          "--gtest_filter=TracingControllerTest.*"
+        ],
+        "name": "perfetto_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "crashpad_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "cronet_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "cronet_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "device_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "display_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "events_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "extensions_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "filesystem_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "gcm_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "gfx_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "gin_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "headless_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "headless_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
+          "--enable-features=WebUIPolymer2",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter"
+        ],
+        "name": "webui_polymer2_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "latency_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "leveldb_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "libjingle_xmpp_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "mac_installer_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "media_blink_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "media_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "media_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "message_center_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "mojo_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "nacl_loader_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "native_theme_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "net_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "pdf_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "ppapi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "printing_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "remoting_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "sandbox_mac_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "service_manager_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "services_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "shell_dialogs_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "snapshot_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "storage_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "sync_integration_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "traffic_annotation_auditor_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "ui_base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "ui_touch_selection_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "url_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "views_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "viz_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "webkit_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        },
+        "test": "wtf_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "--test-type=integration"
+        ],
+        "isolate_name": "chromedriver_py_tests",
+        "name": "chromedriver_py_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        }
+      },
+      {
+        "isolate_name": "chromedriver_replay_unittests",
+        "name": "chromedriver_replay_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        }
+      },
+      {
+        "isolate_name": "components_perftests",
+        "name": "components_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        }
+      },
+      {
+        "isolate_name": "content_shell_crash_test",
+        "name": "content_shell_crash_test",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        }
+      },
+      {
+        "isolate_name": "metrics_python_tests",
+        "name": "metrics_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        }
+      },
+      {
+        "isolate_name": "telemetry_gpu_unittests",
+        "name": "telemetry_gpu_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "isolate_name": "telemetry_perf_unittests",
+        "name": "telemetry_perf_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ],
+          "hard_timeout": 960,
+          "idempotent": false,
+          "shards": 12
+        }
+      },
+      {
+        "args": [
+          "--jobs=1",
+          "--extra-browser-args=--disable-gpu"
+        ],
+        "isolate_name": "telemetry_unittests",
+        "name": "telemetry_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ],
+          "idempotent": false,
+          "shards": 4
+        }
+      },
+      {
+        "isolate_name": "views_perftests",
+        "name": "views_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "--num-retries=3"
+        ],
+        "isolate_name": "webkit_layout_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webkit_layout_tests",
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ],
+          "shards": 12
+        }
+      },
+      {
+        "isolate_name": "webkit_python_tests",
+        "name": "webkit_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "none",
+              "os": "Mac-10.14"
+            }
+          ]
+        }
+      }
+    ]
+  },
   "mac-views-rel": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter b/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
index 9231452..b0591af 100644
--- a/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
+++ b/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
@@ -52,6 +52,8 @@
 -TestAsNormalAndGuestUser/SpokenFeedbackTest.FocusToolbar/1
 -TestAsNormalAndGuestUser/SpokenFeedbackTest.OpenStatusTray/0
 -TestAsNormalAndGuestUser/SpokenFeedbackTest.OpenStatusTray/1
+-TestAsNormalAndGuestUser/SpokenFeedbackTest.OverviewMode/0
+-TestAsNormalAndGuestUser/SpokenFeedbackTest.OverviewMode/1
 
 # TabDragging: crbug.com/890071
 -DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest.CancelDragTabToWindowIn1stDisplay
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 5a60e28..62f7c7f3 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -164,6 +164,22 @@
           ],
         },
       },
+      'mac-osxbeta-rel': {
+        # A subset of tests seem to cause WindowServer deaths on VMs.
+        # crbug.com/828031 et al.
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/mac_window_server_killers.browser_tests.filter',
+          '--gtest_shuffle',
+        ],
+        'swarming': {
+          'dimension_sets': [
+            {
+              'pool': 'Chrome-quarantine',
+              'gpu': 'none',
+            },
+          ],
+        },
+      },
       # chromium.memory
       'Linux ASan LSan Tests (1)': {
         # These are very slow on the ASAN trybot for some reason.
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 7f447236..2b96344 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1363,6 +1363,16 @@
           'scripts': 'chromium_linux_scripts',
         },
       },
+      'mac-osxbeta-rel': {
+        'swarming_mixins': [
+            'mac_10.14',
+            'no_gpu',
+        ],
+        'test_suites': {
+          'gtest_tests': 'chromium_mac_gtests',
+          'isolated_scripts': 'chromium_rel_isolated_scripts',
+        },
+      },
       'mac-views-rel': {
         'test_suites': {
           'gtest_tests': 'chromium_mac_gtests',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index d0fb7d7b..fd7af3b 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1032,6 +1032,21 @@
             ]
         }
     ],
+    "BrowserScheduler": [
+        {
+            "platforms": [
+                "windows",
+                "mac",
+                "chromeos",
+                "linux"
+            ],
+            "experiments": [
+                {
+                    "name": "DetachTimeFg16x"
+                }
+            ]
+        }
+    ],
     "BundledConnectionHelp": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index b398290..4766be0 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -1420,3 +1420,4 @@
 Bug(none) fast/events/touch/compositor-touch-hit-rects-svg-root.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-table.html [ Failure ]
 Bug(none) virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-table.html [ Failure ]
+Bug(none) compositing/overflow/overlap-testing-ancestor-scroller-high-dpi.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 08f5a46c..2eb709a 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2822,6 +2822,20 @@
 crbug.com/875249 external/wpt/infrastructure/testdriver/bless.html [ Timeout Pass ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_50.html [ Failure ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped.html [ Failure ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50.html [ Failure ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent.html [ Timeout ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50.html [ Failure ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up.html [ Timeout ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent.html [ Timeout ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent.html [ Timeout ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent.html [ Timeout ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/basic.html [ Timeout ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left.html [ Timeout ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size.html [ Failure ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center.html [ Failure ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-masking/mask-svg-content/mask-text-001.svg [ Failure ]
 crbug.com/626703 external/wpt/css/css-backgrounds/border-image-width-008.html [ Failure ]
 crbug.com/626703 external/wpt/content-security-policy/generic/only-valid-whitespaces-are-allowed.html [ Timeout ]
@@ -3497,12 +3511,6 @@
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/3_tracks.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_end.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_end_wrapped.html [ Failure ]
-crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html [ Failure ]
-crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html [ Failure ]
-crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50.html [ Failure ]
-crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50.html [ Failure ]
-crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size.html [ Failure ]
-crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_start.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_start_wrapped.html [ Failure ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/basic.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/badging/badge-error.html b/third_party/WebKit/LayoutTests/badging/badge-error.html
new file mode 100644
index 0000000..783528f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/badging/badge-error.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Tests the values that the Badge API doesn't support.</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/third_party/blink/public/platform/modules/badging/badging.mojom.js"></script>
+<script src="resources/mock-badge-service.js"></script>
+</head>
+<body>
+<script>
+
+badge_test(() => { Badge.set(-1); }, 'setBadge', 'TypeError');
+
+badge_test(() => { Badge.set(0); }, 'setBadge', 'TypeError');
+
+badge_test(() => { Badge.set(""); }, 'setBadge', 'TypeError');
+
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/badging/badge-success.html b/third_party/WebKit/LayoutTests/badging/badge-success.html
new file mode 100644
index 0000000..47a1e50
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/badging/badge-success.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test that Badge API accepts expected types.</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/third_party/blink/public/platform/modules/badging/badging.mojom.js"></script>
+<script src="resources/mock-badge-service.js"></script>
+</head>
+<body>
+<script>
+
+badge_test(() => { Badge.set(); }, 'setBadge');
+
+badge_test(() => { Badge.set(undefined); }, 'setBadge');
+
+badge_test(() => { Badge.set(null); }, 'setBadge');
+
+badge_test(() => { Badge.set(1); }, 'setBadge');
+
+badge_test(() => { Badge.set("a string"); }, 'setBadge');
+
+badge_test(() => { Badge.clear(); }, 'clearBadge');
+
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/badging/idl.html b/third_party/WebKit/LayoutTests/badging/idl.html
new file mode 100644
index 0000000..e0834c9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/badging/idl.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/WICG/badging/blob/master/explainer.md">
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../resources/webidl2.js"></script>
+<script src="../resources/idlharness.js"></script>
+<script type="text/plain" id="tested">
+    interface Badge {
+      [CallWith=ScriptState, RaisesException]
+      static void set(optional (USVString or long) contents);
+      [CallWith=ScriptState] static void clear();
+    };
+</script>
+<script>
+    "use strict";
+    var idl_array = new IdlArray();
+    idl_array.add_idls(document.querySelector('#tested').textContent);
+    idl_array.test();
+</script>
diff --git a/third_party/WebKit/LayoutTests/badging/resources/mock-badge-service.js b/third_party/WebKit/LayoutTests/badging/resources/mock-badge-service.js
new file mode 100644
index 0000000..e836ed2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/badging/resources/mock-badge-service.js
@@ -0,0 +1,63 @@
+'use strict';
+
+class MockBadgeService {
+  constructor() {
+    this.bindingSet_ = new mojo.BindingSet(blink.mojom.BadgeService);
+    this.interceptor_ = new MojoInterfaceInterceptor(
+        blink.mojom.BadgeService.name);
+    this.interceptor_.oninterfacerequest =
+        e => this.bindingSet_.addBinding(this, e.handle);
+    this.interceptor_.start();
+  }
+
+  init_(expectCalled) {
+    this.expectCalled_ = expectCalled;
+    return new Promise((resolve, reject) => {
+      this.reject_ = reject;
+      this.resolve_ = resolve;
+    });
+  }
+
+  setBadge(contents) {
+    try {
+      assert_equals(this.expectCalled_, 'setBadge');
+      assert_equals(contents, undefined);
+      this.resolve_();
+    } catch (error) {
+      this.reject_(error);
+    }
+  }
+
+  clearBadge() {
+    try {
+      assert_equals(this.expectCalled_, 'clearBadge');
+      this.resolve_();
+    } catch (error) {
+      this.reject_(error);
+    }
+  }
+}
+
+let mockBadgeService = new MockBadgeService();
+
+function callAndObserveErrors(func, expectedErrorName) {
+  return new Promise((resolve, reject) => {
+    try {
+      func();
+    } catch (error) {
+      try {
+        assert_equals(error.name, expectedErrorName);
+        resolve();
+      } catch (reason) {
+        reject(reason);
+      }
+    }
+  });
+}
+
+function badge_test(func, expectCalled, expectError) {
+  promise_test(() => {
+    let mockPromise = mockBadgeService.init_(expectCalled);
+    return Promise.race([callAndObserveErrors(func, expectError), mockPromise]);
+  });
+}
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/overlap-testing-ancestor-scroller-high-dpi-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/overlap-testing-ancestor-scroller-high-dpi-expected.html
new file mode 100644
index 0000000..197dd830
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/overlap-testing-ancestor-scroller-high-dpi-expected.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<style>
+  html, body {
+    height: 100%;
+  }
+
+  #target {
+    height: 100%;
+    overflow: auto;
+    position: relative;
+    isolation: isolate;
+  }
+
+  .fixed-bg {
+    position: fixed;
+    background: white;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+  }
+
+  .item {
+    position: relative;
+    height: 800px;
+    background: lightgray;
+    width: 100%;
+  }
+</style>
+<body>
+  <div id=target>
+    <div class="item">Item 1</div>
+    <div class="item">Item 2</div>
+  </div>
+</body>
+
+</html>
+<script>
+if (window.testRunner)
+  window.testRunner.waitUntilDone();
+
+onload = () => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() =>
+      target.scrollBy(0, 800));
+     window.testRunner.notifyDone();
+  });
+}
+</script>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/overlap-testing-ancestor-scroller-high-dpi.html b/third_party/WebKit/LayoutTests/compositing/overflow/overlap-testing-ancestor-scroller-high-dpi.html
new file mode 100644
index 0000000..962c400
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/overlap-testing-ancestor-scroller-high-dpi.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<style>
+  html, body {
+    height: 100%;
+  }
+
+  #target {
+    height: 100%;
+    overflow: auto;
+    isolation: isolate;
+  }
+
+  .fixed-bg {
+    position: absolute;
+    backface-visibility: hidden;
+    background: white;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+  }
+
+  .item {
+    position: relative;
+    height: 800px;
+    background: lightgray;
+    width: 100%;
+  }
+</style>
+<body>
+  <div id=target>
+    <div class="fixed-bg"></div>
+    <div class="item" id=item1>Item 1</div>
+    <div class="item" id=item2>Item 2</div>
+  </div>
+</body>
+
+</html>
+<script>
+if (window.internals)
+  window.internals.settings.setPreferCompositingToLCDTextEnabled(true);
+if (window.testRunner)
+  window.testRunner.waitUntilDone();
+
+onload = () => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() =>
+      target.scrollBy(0, 800));
+      window.testRunner.notifyDone();
+  });
+}
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
index a731f0e..9919441 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
@@ -102009,6 +102009,78 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/align_center.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/align_center-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center_position_50.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/align_center_position_50.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/align_center_position_50-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center_wrapped.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/align_end.html": [
     [
      "/webvtt/rendering/cues-with-video/processing-model/align_end.html",
@@ -102033,78 +102105,6 @@
      {}
     ]
    ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle.html": [
-    [
-     "/webvtt/rendering/cues-with-video/processing-model/align_middle.html",
-     [
-      [
-       "/webvtt/rendering/cues-with-video/processing-model/align_middle-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html": [
-    [
-     "/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html",
-     [
-      [
-       "/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50.html": [
-    [
-     "/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50.html",
-     [
-      [
-       "/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50.html": [
-    [
-     "/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50.html",
-     [
-      [
-       "/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size.html": [
-    [
-     "/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size.html",
-     [
-      [
-       "/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html": [
-    [
-     "/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html",
-     [
-      [
-       "/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "webvtt/rendering/cues-with-video/processing-model/align_start.html": [
     [
      "/webvtt/rendering/cues-with-video/processing-model/align_start.html",
@@ -102597,6 +102597,102 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/basic.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/regions/basic.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/regions/basic-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/scroll_up.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent.html": [
+    [
+     "/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/repaint.html": [
     [
      "/webvtt/rendering/cues-with-video/processing-model/repaint.html",
@@ -158879,6 +158975,11 @@
      {}
     ]
    ],
+   "html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "html/rendering/non-replaced-elements/flow-content-0/support/dialog-framed.html": [
     [
      {}
@@ -160944,6 +161045,11 @@
      {}
     ]
    ],
+   "html/semantics/embedded-content/the-video-element/intrinsic_sizes-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "html/semantics/embedded-content/the-video-element/intrinsicsize/intrinsicsize-without-unsized-media.tentative.https.sub.html.headers": [
     [
      {}
@@ -179259,6 +179365,36 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center_position_50-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/align_center_wrapped-ref.html": [
+    [
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/align_end-ref.html": [
     [
      {}
@@ -179269,36 +179405,6 @@
      {}
     ]
    ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle-ref.html": [
-    [
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html": [
-    [
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50-ref.html": [
-    [
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html": [
-    [
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size-ref.html": [
-    [
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped-ref.html": [
-    [
-     {}
-    ]
-   ],
    "webvtt/rendering/cues-with-video/processing-model/align_start-ref.html": [
     [
      {}
@@ -179549,6 +179655,86 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/basic-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/scroll_up-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/support/basic.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_x_50_percent.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_y_50_percent.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/support/scroll_up.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/support/single_line_top_left.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_x_50_percent.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_y_50_percent.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/support/width_50_percent.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent-ref.html": [
+    [
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/repaint-ref.html": [
     [
      {}
@@ -180409,6 +180595,36 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/support/align_center.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/support/align_center_long.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/support/align_center_position_50.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/support/align_center_position_gt_50.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50_size_gt_maximum_size.vtt": [
+    [
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/support/align_end.vtt": [
     [
      {}
@@ -180419,36 +180635,6 @@
      {}
     ]
    ],
-   "webvtt/rendering/cues-with-video/processing-model/support/align_middle.vtt": [
-    [
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/support/align_middle_long.vtt": [
-    [
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_50.vtt": [
-    [
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_gt_50.vtt": [
-    [
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50.vtt": [
-    [
-     {}
-    ]
-   ],
-   "webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50_size_gt_maximum_size.vtt": [
-    [
-     {}
-    ]
-   ],
    "webvtt/rendering/cues-with-video/processing-model/support/align_start.vtt": [
     [
      {}
@@ -224902,6 +225088,12 @@
      {}
     ]
    ],
+   "html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk.html": [
+    [
+     "/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk.html",
+     {}
+    ]
+   ],
    "html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-quirks-mode.html": [
     [
      "/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-quirks-mode.html",
@@ -228310,6 +228502,12 @@
      {}
     ]
    ],
+   "html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm": [
+    [
+     "/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm",
+     {}
+    ]
+   ],
    "html/semantics/embedded-content/the-video-element/intrinsicsize/intrinsicsize-without-unsized-media.tentative.https.sub.html": [
     [
      "/html/semantics/embedded-content/the-video-element/intrinsicsize/intrinsicsize-without-unsized-media.tentative.https.sub.html",
@@ -248600,6 +248798,12 @@
      {}
     ]
    ],
+   "orientation-event/ondeviceorientationabsolute.html": [
+    [
+     "/orientation-event/ondeviceorientationabsolute.html",
+     {}
+    ]
+   ],
    "orientation-sensor/AbsoluteOrientationSensor-disabled-by-feature-policy.https.html": [
     [
      "/orientation-sensor/AbsoluteOrientationSensor-disabled-by-feature-policy.https.html",
@@ -383867,6 +384071,14 @@
    "943f38c3e03e0c654d1105c2f42d49cca3f1432a",
    "reftest"
   ],
+  "html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk-expected.txt": [
+   "e2ee2f7dabccad74c9c77cf656f1056792a309d9",
+   "support"
+  ],
+  "html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk.html": [
+   "7f6618bb782a0460d286930f05866ea9b432a1cd",
+   "testharness"
+  ],
   "html/rendering/non-replaced-elements/flow-content-0/support/dialog-framed.html": [
    "f9c414c246d1f89e697536f458e08ae33359e457",
    "support"
@@ -388343,6 +388555,14 @@
    "cac7d470aedd15e9087f20ba23947d59d0595dfb",
    "testharness"
   ],
+  "html/semantics/embedded-content/the-video-element/intrinsic_sizes-expected.txt": [
+   "7416b5fa0226aeddf8414b54859235df9d7eca49",
+   "support"
+  ],
+  "html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm": [
+   "7819ee1c1891887aadec9bb600bcb18e6cc562e3",
+   "testharness"
+  ],
   "html/semantics/embedded-content/the-video-element/intrinsicsize/intrinsicsize-without-unsized-media.tentative.https.sub.html": [
    "d749d5576e6881ea900f7cd26e0ff6adad2610f3",
    "testharness"
@@ -389160,7 +389380,7 @@
    "testharness"
   ],
   "html/semantics/forms/the-legend-element/HTMLLegendElement.html": [
-   "f196de25fe85397a2302dd5020b933a0d2e5a8b4",
+   "8600e5437a5ab96eee23662f875e2f0751139610",
    "testharness"
   ],
   "html/semantics/forms/the-legend-element/legend-form.html": [
@@ -406871,6 +407091,10 @@
    "ed8309d6480eaaec45e986a3391854d67b01ba2d",
    "testharness"
   ],
+  "orientation-event/ondeviceorientationabsolute.html": [
+   "73ce97ab5c1fd7220fb65e53fb51e0bfa86beb28",
+   "testharness"
+  ],
   "orientation-event/screen-upmost-manual.html": [
    "87ad62c58633768262144ed582931b7167948605",
    "manual"
@@ -422064,11 +422288,11 @@
    "testharness"
   ],
   "speech-api/SpeechSynthesisUtterance-basics.https-expected.txt": [
-   "17b83ebe478095f6aa0a25034b09717ddd21cbfa",
+   "b298faad294628c96925b89f8cacad395f00f97f",
    "support"
   ],
   "speech-api/SpeechSynthesisUtterance-basics.https.html": [
-   "9a90f086ac451d09922f05b55144818e787a5ded",
+   "2fd394150e941ccbeb8d63b99e598cc53e55446d",
    "testharness"
   ],
   "speech-api/historical-expected.txt": [
@@ -431767,6 +431991,54 @@
    "809e173b46db0f2c3c65a708ad841703602c40f9",
    "reftest"
   ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center-ref.html": [
+   "3663d925d958097ba80341aed86d87d3b264ca4a",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center.html": [
+   "9a339a64bad79df520c1e343a7f9e2ad6dcc372e",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center_position_50-ref.html": [
+   "65c7dee75aa073056bbb2c0df2f628bd9ed329d0",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center_position_50.html": [
+   "40cc8c4bbe1011aaf711751ead69d29824dc8933",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50-ref.html": [
+   "b8d30a73801b33f5dab57d2722179c93643ac588",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50.html": [
+   "05167743c0f67b95dad06f2b6e85641f51d6f4cb",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50-ref.html": [
+   "37217da49721d267208790b5fb773458cb2a64ed",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50.html": [
+   "32b3e3a6c4de905f85922bd887bde83914217b0f",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size-ref.html": [
+   "7241376f825d650722cad59582c188f2d2d2d1be",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size.html": [
+   "2f62c4500ed91086c49fff6b5b476edd1010b14e",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center_wrapped-ref.html": [
+   "a9e583d66f0cd4fa459c19bb4c922fd468cd9ce4",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/align_center_wrapped.html": [
+   "9292a8cc30141f9fab2add1151f265852cfa4491",
+   "reftest"
+  ],
   "webvtt/rendering/cues-with-video/processing-model/align_end-ref.html": [
    "247e6e24bf3aa2fd5de4614ee954114f8136b8f6",
    "support"
@@ -431783,54 +432055,6 @@
    "5c85a55082d3cc3cec73caaa9947f10a0c6c0df7",
    "reftest"
   ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle-ref.html": [
-   "fab75c8e0a5a6db1fb16552e66052200ae5d9a87",
-   "support"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle.html": [
-   "43732a549b8167aa673e35b9c9332d945f755ed5",
-   "reftest"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html": [
-   "88718e5ba5573ee0d87063bd491668744ab2dbc2",
-   "support"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html": [
-   "6be073fff84cec571c5028a13718bfd93f23fa99",
-   "reftest"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50-ref.html": [
-   "b9b6d2936da0f31b355a75c05141584665cc30ce",
-   "support"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50.html": [
-   "4f725f55572becf5c5d9ee302bf71b6f436a95e8",
-   "reftest"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html": [
-   "9fa1f28ea895b69f27b815e189247fe6d0b33efe",
-   "support"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50.html": [
-   "7d09fd9889640a56e5b0035c45de637223f3a583",
-   "reftest"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size-ref.html": [
-   "ddbdf1604deed32f65620d1484c1f4d27b411644",
-   "support"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size.html": [
-   "590db24c5fe5e1e6f5447ee595cf33a785ce3513",
-   "reftest"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped-ref.html": [
-   "d9852d86004bdc2f295694af117dfc20cf53f33b",
-   "support"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html": [
-   "2ef93c4b01a5f52cc0a0346495a87a9198325153",
-   "reftest"
-  ],
   "webvtt/rendering/cues-with-video/processing-model/align_start-ref.html": [
    "15f24fb74b958c082695cb5b71cc23fc5514523c",
    "support"
@@ -432195,6 +432419,102 @@
    "68c44beaefb4983903abe366971a401f53dbaeb0",
    "reftest"
   ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/basic-ref.html": [
+   "ff90d4829c4d23ece39cc473ac6d21b672557772",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/basic.html": [
+   "4ffed9193b9abedd04b76c8f4848c4d0d93f1fab",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent-ref.html": [
+   "251a04687f9ec5b4cb761fd5c0962ea8e7a0e5ef",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent.html": [
+   "b9ef28212a4f80e67378247c75afc30c70228281",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent-ref.html": [
+   "db1031093c7a118962a6928ba85e51974a2af71f",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent.html": [
+   "5067d12e6f40b40e7bcc75f809c1afb5000fbf2d",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/scroll_up-ref.html": [
+   "6a68c722e4dbb7a119577583054daf2bf4f8b2a7",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/scroll_up.html": [
+   "e1619af932a5bef76c5fb72ef7a97e11c8851bcc",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left-ref.html": [
+   "a183448cb1b3b03f10a0a48fb81a26ec9c72a8c8",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left.html": [
+   "c2edcb9f626a0096f8cd5d95408caa8a3da0c201",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/support/basic.vtt": [
+   "ed978274793480111586d0ef6e85156d142d3d03",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_x_50_percent.vtt": [
+   "eafea7fff94ec49caead41b412c323cf4ce84774",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_y_50_percent.vtt": [
+   "d51ad26769bfb6eb3b5595c4440e28ba5cbac2bb",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/support/scroll_up.vtt": [
+   "0b88020d29f680718e95d80a99babd926d40c039",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/support/single_line_top_left.vtt": [
+   "efe9d8793eda051aad5e2c800690cd15ae97e53f",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_x_50_percent.vtt": [
+   "afd9876c06d296b2b1ca43691c9355dacc370099",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_y_50_percent.vtt": [
+   "62e8a796877fddc9aec5ecc78717941081c92424",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/support/width_50_percent.vtt": [
+   "9fce99b03b3b9ca1108c3267ba3d981e288f9369",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent-ref.html": [
+   "c216838a6948b22bcde51993ab32f04731ba5e83",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent.html": [
+   "85e699a262b8e03fc8592a22e6acc6dabf73b29a",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent-ref.html": [
+   "f432047c5a6b8ffb785273fc7b3d92351e77c839",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent.html": [
+   "0641b176e6f617359b8f93912dfe88eff56d1032",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent-ref.html": [
+   "fd69b643f5693d8b24cbcb8deac4fac601a3cce9",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent.html": [
+   "4377e9b4c03b224d60d1fcd029804118346daf47",
+   "reftest"
+  ],
   "webvtt/rendering/cues-with-video/processing-model/repaint-ref.html": [
    "49f385b079dc5a1422a9984db4db1cced68c8abe",
    "support"
@@ -433551,6 +433871,30 @@
    "6c99ce3b1fe1ab60d41e1afdfb79bd689cba62f7",
    "support"
   ],
+  "webvtt/rendering/cues-with-video/processing-model/support/align_center.vtt": [
+   "4b8e14b28b7c7257437fea9ba747dcb8e37a15bf",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/support/align_center_long.vtt": [
+   "57c896d04b75d09454271b5a1d489331673d9f64",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/support/align_center_position_50.vtt": [
+   "21ab82bb08439e6bf570e44b0f8c0bc4a7700fc1",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/support/align_center_position_gt_50.vtt": [
+   "4d8538a2be773fc7a9277044a6713622b6262bb6",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50.vtt": [
+   "4ea1e656077b63830b9032517113d781dadbc95d",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50_size_gt_maximum_size.vtt": [
+   "a2e307c9cba59fef12598a0924e55d49a370a53f",
+   "support"
+  ],
   "webvtt/rendering/cues-with-video/processing-model/support/align_end.vtt": [
    "83f691e6ffa274df619fe49275c63472890821fb",
    "support"
@@ -433559,30 +433903,6 @@
    "09ace0d227843dd26505ca06ad062bcb1a9dc067",
    "support"
   ],
-  "webvtt/rendering/cues-with-video/processing-model/support/align_middle.vtt": [
-   "cdee051d77778ef6517ab6603cc215b28a8e0925",
-   "support"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/support/align_middle_long.vtt": [
-   "8c89441a974ba2c98bf09e9eeed81ea06ecb75fd",
-   "support"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_50.vtt": [
-   "c443773984e98555f45afe4876e19986fd90ef54",
-   "support"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_gt_50.vtt": [
-   "186ae1ecd2319046c39aa668e97be58b92db8720",
-   "support"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50.vtt": [
-   "6928c5dc21880374058057f43537fa67607bc1ba",
-   "support"
-  ],
-  "webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50_size_gt_maximum_size.vtt": [
-   "32bc4ed3fc8593274a1d9e574289401b6ab65d1c",
-   "support"
-  ],
   "webvtt/rendering/cues-with-video/processing-model/support/align_start.vtt": [
    "161c457465d2ad05c9144dc221d30458ac01c639",
    "support"
@@ -436304,11 +436624,11 @@
    "testharness"
   ],
   "xhr/overridemimetype-edge-cases.window-expected.txt": [
-   "a936e877b57a7a7e6131cd2f454cc47a25e4c8f4",
+   "8e3a758e38a6cf18e39414c61243fc20b0eea164",
    "support"
   ],
   "xhr/overridemimetype-edge-cases.window.js": [
-   "192a696759c4b82852eee753f417fb8a4cf2718d",
+   "3f57e9a48f00206b60b1912e1544aec39f07ced8",
    "testharness"
   ],
   "xhr/overridemimetype-headers-received-state-force-shiftjis.htm": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/values/shape-outside-inset-010.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/values/shape-outside-inset-010.html
new file mode 100644
index 0000000..b82abea8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/values/shape-outside-inset-010.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="help" href="http://www.w3.org/TR/css-shapes-1/#shape-outside-property">
+<link rel="match" href="../../../reference/ref-filled-green-100px-square-only.html">
+<meta name="assert" content="This checks that a float 'shadowing' a shape-outside float works as expected.">
+<style>
+#container { width: 100px; line-height: 0; }
+#float_1 { float: right; width: 30px; height: 30px; background: green; }
+#float_2 { float: left; width: 20px; height: 100px; background: green; shape-outside: inset(0 20px 0 0); }
+#float_3 { float: left; width: 30px; height: 50px; background: green; }
+.atomic { display: inline-block; background: green; }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<div id="container">
+  <div id="float_1"></div>
+  <div id="float_2"></div>
+  <div id="float_3"></div>
+  <div class="atomic" style="width: 20px; height: 30px;"></div>
+  <div class="atomic" style="width: 50px; height: 20px;"></div>
+  <div class="atomic" style="width: 80px; height: 50px;"></div><div class="atomic" style="width: 20px; height: 50px;"></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk-expected.txt
new file mode 100644
index 0000000..e2ee2f7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL form margin quirk assert_equals: marginRight expected "16px" but got "0px"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk.html b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk.html
new file mode 100644
index 0000000..7f6618b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk.html
@@ -0,0 +1,20 @@
+<!-- quirks -->
+<title>form margin quirk</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+form { writing-mode: vertical-lr; }
+#ref { margin: 0 1em 0 0; }
+</style>
+<form id=form></form>
+<div id=ref></div>
+<script>
+test(() => {
+  const formStyle = getComputedStyle(document.getElementById('form'));
+  const refStyle = getComputedStyle(document.getElementById('ref'));
+  assert_equals(formStyle.marginTop, refStyle.marginTop, 'marginTop');
+  assert_equals(formStyle.marginRight, refStyle.marginRight, 'marginRight');
+  assert_equals(formStyle.marginBottom, refStyle.marginBottom, 'marginBottom');
+  assert_equals(formStyle.marginLeft, refStyle.marginLeft, 'marginLeft');
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/the-video-element/intrinsic_sizes-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/the-video-element/intrinsic_sizes-expected.txt
new file mode 100644
index 0000000..7416b5fa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/the-video-element/intrinsic_sizes-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+PASS default object size is 300x150
+PASS default height is half the width
+PASS default width is twice the height
+FAIL default object size after src is removed assert_equals: expected "320px" but got "300px"
+FAIL default object size after poster is removed assert_equals: expected "102px" but got "300px"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm
new file mode 100644
index 0000000..7819ee1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm
@@ -0,0 +1,75 @@
+<!doctype html>
+<html>
+<head>
+<title>video element - intrinsic sizes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+</head>
+<body>
+<p><a href="https://html.spec.whatwg.org/multipage/#the-video-element">spec reference</a></p>
+<video id="v1"></video>
+<video id="v2" width="400"></video>
+<video id="v3" height="100"></video>
+<video id="v4"></video>
+<video id="v5" poster="/media/poster.png"></video>
+<div id="log"></div>
+<script>
+test(function() {
+  var s = getComputedStyle(document.getElementById("v1"));
+  assert_equals(s.width, "300px");
+  assert_equals(s.height, "150px");
+}, "default object size is 300x150");
+
+test(function() {
+  var s = getComputedStyle(document.getElementById("v2"));
+  assert_equals(s.width, "400px");
+  assert_equals(s.height, "200px");
+}, "default height is half the width");
+
+test(function() {
+  var s = getComputedStyle(document.getElementById("v3"));
+  assert_equals(s.width, "200px");
+  assert_equals(s.height, "100px");
+}, "default width is twice the height");
+
+async_test(function(t) {
+  var v = document.getElementById("v4");
+  var s = getComputedStyle(v);
+  v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+  v.onerror = t.unreached_func();
+  v.onloadedmetadata = t.step_func(function() {
+    assert_equals(s.width, '320px');
+    assert_equals(s.height, '240px');
+    v.removeAttribute("src");
+    v.load();
+    // Dimensions should be updated only on next layout.
+    assert_equals(s.width, '320px');
+    assert_equals(s.height, '240px');
+    requestAnimationFrame(t.step_func_done(function() {
+      assert_equals(s.width, "300px");
+      assert_equals(s.height, "150px");
+    }));
+  });
+}, "default object size after src is removed");
+
+async_test(function(t) {
+  var v = document.getElementById("v5");
+  var s = getComputedStyle(v);
+  v.onerror = t.unreached_func();
+  onload = t.step_func(function() {
+    assert_equals(s.width, '102px');
+    assert_equals(s.height, '77px');
+    v.removeAttribute("poster");
+    // Dimensions should be updated only on next layout.
+    assert_equals(s.width, '102px');
+    assert_equals(s.height, '77px');
+    requestAnimationFrame(t.step_func_done(function() {
+      assert_equals(s.width, "300px");
+      assert_equals(s.height, "150px");
+    }));
+  });
+}, "default object size after poster is removed");
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/forms/the-legend-element/HTMLLegendElement.html b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/forms/the-legend-element/HTMLLegendElement.html
index f196de2..8600e543 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/forms/the-legend-element/HTMLLegendElement.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/forms/the-legend-element/HTMLLegendElement.html
@@ -25,10 +25,12 @@
   test(function() {
     assert_equals(document.getElementById("lgd2").form, document.getElementById("fs").form,
                   "The legend.form should be same as fieldset.form.");
+    assert_equals(document.getElementById("lgd2").form, document.getElementById("fm"),
+                  "The legend.form should be the correct form.");
   }, "The legend.form must be same value as fieldset.form");
 
   test(function() {
-    assert_true(document.getElementById("lgd1") instanceof HTMLLegendElement, "form", "Check if the form is readonly");
+    assert_true(document.getElementById("lgd1") instanceof HTMLLegendElement, "legend should be a HTMLLegendElement");
     assert_readonly(document.getElementById("lgd1"), "form", "The form is not readonly");
   }, "Interface HTMLLegendElement");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-event/ondeviceorientationabsolute.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-event/ondeviceorientationabsolute.html
new file mode 100644
index 0000000..73ce97ab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-event/ondeviceorientationabsolute.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>User agents must also provide an event handler IDL attribute named ondeviceorientationabsolute on the window object</title>
+    <meta name=viewport content="width=device-width, maximum-scale=1.0">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <p>Rotate the device to run the tests.</p>
+    <div id="log"></div>
+    <script>
+      var t1 = async_test("Provide an event handler IDL attribute named ondeviceorientationabsolute");
+      var t2 = async_test("The type of this event handler must be 'DeviceOrientationEvent'");
+      var t3 = async_test("The absolute property must be set to true.");
+      var run = false;
+      if (window.ondeviceorientationabsolute === undefined) {
+        t1.step(t1.unreached_func("ondeviceorientationabsolute not supported"));
+        t2.step(t2.unreached_func("ondeviceorientationabsolute not supported"));
+        t3.step(t3.unreached_func("ondeviceorientationabsolute not supported"));
+      } else {
+        window.ondeviceorientationabsolute = function(e) {
+          if (!run) {
+            run = true;
+            t1.step(function() {
+              assert_equals(e.type, "deviceorientationabsolute");
+            });
+            t1.done();
+            t2.step(function() {
+              assert_true(e instanceof DeviceOrientationEvent);
+           });
+            t2.done();
+            t3.step(function() {
+              assert_true(e.absolute);
+            });
+            t3.done();
+          }
+        };
+      }
+    </script>
+  </body>
+</html>
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/speech-api/SpeechSynthesisUtterance-basics.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/speech-api/SpeechSynthesisUtterance-basics.https-expected.txt
index 17b83eb..b298faa 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/speech-api/SpeechSynthesisUtterance-basics.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/speech-api/SpeechSynthesisUtterance-basics.https-expected.txt
@@ -6,6 +6,8 @@
 FAIL new SpeechSynthesisUtterance() default rate assert_equals: rate expected 1 but got -1
 FAIL new SpeechSynthesisUtterance() default pitch assert_equals: pitch expected 1 but got -1
 FAIL new SpeechSynthesisUtterance("hello") text and defaults assert_equals: volume expected 1 but got -1
+PASS new SpeechSynthesisUtterance(null)
+PASS new SpeechSynthesisUtterance(undefined)
 PASS SpeechSynthesisUtterance text setter
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/speech-api/SpeechSynthesisUtterance-basics.https.html b/third_party/WebKit/LayoutTests/external/wpt/speech-api/SpeechSynthesisUtterance-basics.https.html
index 9a90f086a..2fd3941 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/speech-api/SpeechSynthesisUtterance-basics.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/speech-api/SpeechSynthesisUtterance-basics.https.html
@@ -31,6 +31,17 @@
 }, 'new SpeechSynthesisUtterance("hello") text and defaults');
 
 test(function() {
+  const utt = new SpeechSynthesisUtterance(null);
+  assert_equals(utt.text, 'null');
+}, 'new SpeechSynthesisUtterance(null)');
+
+test(function() {
+  const utt = new SpeechSynthesisUtterance(undefined);
+  // See https://github.com/w3c/speech-api/pull/48.
+  assert_equals(utt.text, '');
+}, 'new SpeechSynthesisUtterance(undefined)');
+
+test(function() {
   const utt = new SpeechSynthesisUtterance();
   utt.text = 'word';
   assert_equals(utt.text, 'word');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedTypePolicyFactory-createPolicy-createXYZTests.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedTypePolicyFactory-createPolicy-createXYZTests.tentative.html
index b20fcf243..a162d84c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedTypePolicyFactory-createPolicy-createXYZTests.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedTypePolicyFactory-createPolicy-createXYZTests.tentative.html
@@ -7,8 +7,10 @@
   //HTML tests
   function createHTMLTest(policyName, policy, expectedHTML, t) {
     let p = window.TrustedTypes.createPolicy(policyName, policy);
-    assert_true(p.createHTML('whatever') instanceof TrustedHTML);
-    assert_equals(p.createHTML('whatever') + "", expectedHTML);
+    let html = p.createHTML('whatever');
+    assert_true(html instanceof TrustedHTML);
+    assert_true(TrustedTypes.isHTML(html));
+    assert_equals(html + "", expectedHTML);
   }
 
   test(t => {
@@ -77,8 +79,10 @@
   //Script tests
   function createScriptTest(policyName, policy, expectedScript, t) {
     let p = window.TrustedTypes.createPolicy(policyName, policy);
-    assert_true(p.createScript('whatever') instanceof TrustedScript);
-    assert_equals(p.createScript('whatever') + "", expectedScript);
+    let script = p.createScript('whatever');
+    assert_true(script instanceof TrustedScript);
+    assert_true(TrustedTypes.isScript(script));
+    assert_equals(script + "", expectedScript);
   }
 
   test(t => {
@@ -150,8 +154,10 @@
   //ScriptURL tests
   function createScriptURLTest(policyName, policy, expectedScriptURL, t) {
     let p = window.TrustedTypes.createPolicy(policyName, policy);
-    assert_true(p.createScriptURL(INPUTS.SCRIPTURL) instanceof TrustedScriptURL);
-    assert_equals(p.createScriptURL(INPUTS.SCRIPTURL) + "", expectedScriptURL);
+    let scriptUrl = p.createScriptURL(INPUTS.SCRIPTURL);
+    assert_true(scriptUrl instanceof TrustedScriptURL);
+    assert_true(TrustedTypes.isScriptURL(scriptUrl));
+    assert_equals(scriptUrl + "", expectedScriptURL);
   }
 
   test(t => {
@@ -223,8 +229,10 @@
   //URL tests
   function createURLTest(policyName, policy, expectedURL, t) {
     let p = window.TrustedTypes.createPolicy(policyName, policy);
-    assert_true(p.createURL(INPUTS.URL) instanceof TrustedURL);
-    assert_equals(p.createURL(INPUTS.URL) + "", expectedURL);
+    let url = p.createURL(INPUTS.URL);
+    assert_true(url instanceof TrustedURL);
+    assert_true(TrustedTypes.isURL(url));
+    assert_equals(url + "", expectedURL);
   }
 
   test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedTypePolicyFactory-isXXX.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedTypePolicyFactory-isXXX.tentative.html
new file mode 100644
index 0000000..9b48fa7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedTypePolicyFactory-isXXX.tentative.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/helper.sub.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="trusted-types">
+<body>
+<script>
+  // Policy settings for all tests
+  const noopPolicy = {
+    'createHTML': (s) => s,
+    'createScriptURL': (s) => s,
+    'createURL': (s) => s,
+    'createScript': (s) => s,
+  };
+
+  // isHTML tests
+  test(t => {
+    const p = TrustedTypes.createPolicy('html', noopPolicy);
+    let html = p.createHTML(INPUTS.HTML);
+
+    assert_true(TrustedTypes.isHTML(html));
+    let html2 = Object.create(html);
+
+    // instanceof can pass, but we rely on isHTML
+    assert_true(html2 instanceof TrustedHTML);
+    assert_false(TrustedTypes.isHTML(html2));
+
+    let html3 = Object.assign({}, html, {toString: () => 'fake'});
+
+    assert_false(TrustedTypes.isHTML(html3));
+  }, 'TrustedTypePolicyFactory.isHTML requires the object to be created via policy.');
+
+  // isScript tests
+  test(t => {
+    const p = TrustedTypes.createPolicy('script', noopPolicy);
+    let script = p.createScript(INPUTS.SCRIPT);
+
+    assert_true(TrustedTypes.isScript(script));
+    let script2 = Object.create(script);
+
+    // instanceof can pass, but we rely on isScript
+    assert_true(script2 instanceof TrustedScript);
+    assert_false(TrustedTypes.isScript(script2));
+
+    let script3 = Object.assign({}, script, {toString: () => 'fake'});
+
+    assert_false(TrustedTypes.isScript(script3));
+  }, 'TrustedTypePolicyFactory.isScript requires the object to be created via policy.');
+
+  // isScriptURL tests
+  test(t => {
+    const p = TrustedTypes.createPolicy('script_url', noopPolicy);
+    let script = p.createScriptURL(INPUTS.SCRIPTURL);
+
+    assert_true(TrustedTypes.isScriptURL(script));
+    let script2 = Object.create(script);
+
+    // instanceof can pass, but we rely on isScript
+    assert_true(script2 instanceof TrustedScriptURL);
+    assert_false(TrustedTypes.isScriptURL(script2));
+
+    let script3 = Object.assign({}, script, {toString: () => 'fake'});
+
+    assert_false(TrustedTypes.isScriptURL(script3));
+  }, 'TrustedTypePolicyFactory.isScriptURL requires the object to be created via policy.');
+
+  // isURL tests
+  test(t => {
+    const p = TrustedTypes.createPolicy('url', noopPolicy);
+    let url = p.createURL(INPUTS.URL);
+
+    assert_true(TrustedTypes.isURL(url));
+    let url2 = Object.create(url);
+
+    // instanceof can pass, but we rely on isScript
+    assert_true(url2 instanceof TrustedURL);
+    assert_false(TrustedTypes.isURL(url2));
+
+    let url3 = Object.assign({}, url, {toString: () => 'fake'});
+
+    assert_false(TrustedTypes.isURL(url3));
+  }, 'TrustedTypePolicyFactory.isURL requires the object to be created via policy.');
+
+  // Redefinition tests
+  // TODO(vogelheim): Implement TrustedTypes (& policy objects) as 'frozen'.
+/*  test(t => {
+    assert_throws(new TypeError(), _ => {
+      TrustedTypes.isHTML = () => true;
+    });
+
+    assert_false(TrustedTypes.isHTML({}));
+  }, 'TrustedTypePolicyFactory.IsHTML cannot be redefined.');
+
+  test(t => {
+    assert_throws(new TypeError(), _ => {
+      TrustedTypes.isScript = () => true;
+    });
+
+    assert_false(TrustedTypes.isScript({}));
+  }, 'TrustedTypePolicyFactory.isScript cannot be redefined.');
+
+  test(t => {
+    assert_throws(new TypeError(), _ => {
+      TrustedTypes.isScriptURL = () => true;
+    });
+
+    assert_false(TrustedTypes.isScriptURL({}));
+  }, 'TrustedTypePolicyFactory.isScriptURL cannot be redefined.');
+
+  test(t => {
+    assert_throws(new TypeError(), _ => {
+      TrustedTypes.isURL = () => true;
+    });
+
+    assert_false(TrustedTypes.isURL({}));
+  }, 'TrustedTypePolicyFactory.isURL cannot be redefined.');*/
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/convolver-setBuffer-already-has-value.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/convolver-setBuffer-already-has-value.html
index c8dbeb94..ce2d5fc 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/convolver-setBuffer-already-has-value.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/convolver-setBuffer-already-has-value.html
@@ -27,7 +27,7 @@
 
         should(() => {
           convolver.buffer = audioBuffer;
-        }, 'Set buffer a second time').throw(DOMException, 'InvalidStateError');
+        }, 'Set buffer a second time').notThrow();
 
         should(() => {
           convolver.buffer = null;
@@ -39,8 +39,8 @@
 
         should(() => {
           convolver.buffer = audioBuffer;
-        }, 'Set buffer to non-null to verify to throw an error')
-        .throw(DOMException, 'InvalidStateError');
+        }, 'Set buffer to non-null to verify it is set')
+        .notThrow();
 
         task.done();
       });
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center-ref.html
similarity index 88%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle-ref.html
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center-ref.html
index fab75c8e..3663d92 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle</title>
+<title>Reference for WebVTT rendering, align:center</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center.html
index 43732a5..9a339a6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-<title>WebVTT rendering, align:middle</title>
-<link rel="match" href="align_middle-ref.html">
+<title>WebVTT rendering, align:center</title>
+<link rel="match" href="align_center-ref.html">
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -14,7 +14,7 @@
 <video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
     <source src="/media/white.webm" type="video/webm">
     <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle.vtt">
+    <track src="support/align_center.vtt">
     <script>
     document.getElementsByTagName('track')[0].track.mode = 'showing';
     </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_50-ref.html
similarity index 88%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_50-ref.html
index 88718e5..65c7dee 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_50-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position:50%</title>
+<title>Reference for WebVTT rendering, align:center, position:50%</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_50.html
similarity index 75%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_50.html
index 6be073f..40cc8c4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_50.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-<title>WebVTT rendering, align:middle, position:50%</title>
-<link rel="match" href="align_middle_position_50-ref.html">
+<title>WebVTT rendering, align:center, position:50%</title>
+<link rel="match" href="align_center_position_50-ref.html">
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -14,7 +14,7 @@
 <video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
     <source src="/media/white.webm" type="video/webm">
     <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle_position_50.vtt">
+    <track src="support/align_center_position_50.vtt">
     <script>
     document.getElementsByTagName('track')[0].track.mode = 'showing';
     </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50-ref.html
similarity index 88%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50-ref.html
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50-ref.html
index b9b6d293..b8d30a7 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position greater than 50%</title>
+<title>Reference for WebVTT rendering, align:center, position greater than 50%</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50.html
similarity index 75%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50.html
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50.html
index 4f725f5..0516774 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-<title>WebVTT rendering, align:middle, position greater than 50%</title>
-<link rel="match" href="align_middle_position_gt_50-ref.html">
+<title>WebVTT rendering, align:center, position greater than 50%</title>
+<link rel="match" href="align_center_position_gt_50-ref.html">
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -14,7 +14,7 @@
 <video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
     <source src="/media/white.webm" type="video/webm">
     <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle_position_gt_50.vtt">
+    <track src="support/align_center_position_gt_50.vtt">
     <script>
     document.getElementsByTagName('track')[0].track.mode = 'showing';
     </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50-ref.html
similarity index 88%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50-ref.html
index 9fa1f28e..37217da 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position less than 50%</title>
+<title>Reference for WebVTT rendering, align:center, position less than 50%</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50.html
similarity index 74%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50.html
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50.html
index 7d09fd98..32b3e3a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-<title>WebVTT rendering, align:middle, position less than 50%</title>
-<link rel="match" href="align_middle_position_lt_50-ref.html">
+<title>WebVTT rendering, align:center, position less than 50%</title>
+<link rel="match" href="align_center_position_lt_50-ref.html">
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -14,7 +14,7 @@
 <video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
     <source src="/media/white.webm" type="video/webm">
     <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle_position_lt_50.vtt">
+    <track src="support/align_center_position_lt_50.vtt">
     <script>
     document.getElementsByTagName('track')[0].track.mode = 'showing';
     </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size-ref.html
similarity index 89%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size-ref.html
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size-ref.html
index ddbdf16..7241376 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position less than 50%, size greater than maximum size</title>
+<title>Reference for WebVTT rendering, align:center, position less than 50%, size greater than maximum size</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size.html
similarity index 76%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size.html
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size.html
index 590db24c..2f62c45 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-<title>WebVTT rendering, align:middle, position less than 50%, size greater than maximum size</title>
-<link rel="match" href="align_middle_position_lt_50_size_gt_maximum_size-ref.html">
+<title>WebVTT rendering, align:center, position less than 50%, size greater than maximum size</title>
+<link rel="match" href="align_center_position_lt_50_size_gt_maximum_size-ref.html">
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -14,7 +14,7 @@
 <video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
     <source src="/media/white.webm" type="video/webm">
     <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle_position_lt_50_size_gt_maximum_size.vtt">
+    <track src="support/align_center_position_lt_50_size_gt_maximum_size.vtt">
 </video>
 <script>
 document.getElementsByTagName('track')[0].track.mode = 'showing';
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped-ref.html
similarity index 90%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped-ref.html
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped-ref.html
index d9852d86..a9e583d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle with long cues</title>
+<title>Reference for WebVTT rendering, align:center with long cues</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped.html
similarity index 76%
copy from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html
copy to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped.html
index 43732a5..9292a8c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-<title>WebVTT rendering, align:middle</title>
-<link rel="match" href="align_middle-ref.html">
+<title>WebVTT rendering, align:center with long cues</title>
+<link rel="match" href="align_center_wrapped-ref.html">
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -14,7 +14,7 @@
 <video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
     <source src="/media/white.webm" type="video/webm">
     <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle.vtt">
+    <track src="support/align_center_long.vtt">
     <script>
     document.getElementsByTagName('track')[0].track.mode = 'showing';
     </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html
deleted file mode 100644
index 2ef93c4b..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<title>WebVTT rendering, align:middle with long cues</title>
-<link rel="match" href="align_middle_wrapped-ref.html">
-<style>
-html { overflow:hidden }
-body { margin:0 }
-::cue {
-    font-family: Ahem, sans-serif;
-    color: green
-}
-</style>
-<script src="/common/reftest-wait.js"></script>
-<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
-    <source src="/media/white.webm" type="video/webm">
-    <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle_long.vtt">
-    <script>
-    document.getElementsByTagName('track')[0].track.mode = 'showing';
-    </script>
-</video>
-</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/basic-ref.html
similarity index 68%
copy from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle-ref.html
copy to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/basic-ref.html
index fab75c8e..ff90d482 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/basic-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle</title>
+<title>Reference for WebVTT rendering regions, default</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -15,7 +15,7 @@
     bottom: 0;
     left: 0;
     right: 0;
-    text-align: center;
+    text-align: center
 }
 .cue > span {
     font-family: Ahem, sans-serif;
@@ -23,4 +23,4 @@
     color: green;
 }
 </style>
-<div class=video><span class=cue><span>This is a test</span></span></div>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/basic.html
similarity index 63%
copy from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html
copy to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/basic.html
index 43732a5..4ffed91 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/basic.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-<title>WebVTT rendering, align:middle</title>
-<link rel="match" href="align_middle-ref.html">
+<title>WebVTT rendering regions, default</title>
+<link rel="match" href="basic-ref.html">
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -12,9 +12,9 @@
 </style>
 <script src="/common/reftest-wait.js"></script>
 <video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
-    <source src="/media/white.webm" type="video/webm">
-    <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle.vtt">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/basic.vtt">
     <script>
     document.getElementsByTagName('track')[0].track.mode = 'showing';
     </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent-ref.html
similarity index 63%
copy from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html
copy to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent-ref.html
index 9fa1f28e..251a046 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position less than 50%</title>
+<title>Reference for WebVTT rendering regions, regionanchor x 50%</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -13,9 +13,8 @@
 .cue {
     position: absolute;
     bottom: 0;
-    left: 23px;
-    right: 0;
-    width: 64px;
+    left: -50%;
+    right: 50%;
     text-align: center
 }
 .cue > span {
@@ -24,4 +23,4 @@
     color: green;
 }
 </style>
-<div class=video><span class=cue><span>Awesome<br>!!!</span></span></div>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent.html
new file mode 100644
index 0000000..b9ef2821
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, regionanchor x 50%</title>
+<link rel="match" href="regionanchor_x_50_percent-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/regionanchor_x_50_percent.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent-ref.html
new file mode 100644
index 0000000..db103109
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering regions, regionanchor y 50%</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+</style>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent.html
new file mode 100644
index 0000000..5067d12e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, regionanchor y 50%</title>
+<link rel="match" href="regionanchor_y_50_percent-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/regionanchor_y_50_percent.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up-ref.html
similarity index 62%
copy from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html
copy to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up-ref.html
index 9fa1f28e..6a68c722e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position less than 50%</title>
+<title>Reference for WebVTT rendering regions, scroll up</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -12,10 +12,9 @@
 }
 .cue {
     position: absolute;
-    bottom: 0;
-    left: 23px;
+    top: 0;
+    left: 0;
     right: 0;
-    width: 64px;
     text-align: center
 }
 .cue > span {
@@ -24,4 +23,4 @@
     color: green;
 }
 </style>
-<div class=video><span class=cue><span>Awesome<br>!!!</span></span></div>
+<div class="video"><span class="cue"><span>This is a second test subtitle<br/>This is a third test subtitle</span></span></div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up.html
new file mode 100644
index 0000000..e1619af
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, scroll up</title>
+<link rel="match" href="scroll_up-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshotDelayed(3000);">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/scroll_up.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left-ref.html
similarity index 64%
copy from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html
copy to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left-ref.html
index 88718e5..a183448c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position:50%</title>
+<title>Reference for WebVTT rendering regions, single line top left</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -12,10 +12,10 @@
 }
 .cue {
     position: absolute;
-    bottom: 0;
+    top: 0;
     left: 0;
     right: 0;
-    text-align: center;
+    text-align: left
 }
 .cue > span {
     font-family: Ahem, sans-serif;
@@ -23,4 +23,4 @@
     color: green;
 }
 </style>
-<div class=video><span class=cue><span>This is a test</span></span></div>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left.html
similarity index 60%
copy from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html
copy to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left.html
index 6be073f..c2edcb9f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-<title>WebVTT rendering, align:middle, position:50%</title>
-<link rel="match" href="align_middle_position_50-ref.html">
+<title>WebVTT rendering regions, single line top left</title>
+<link rel="match" href="single_line_top_left-ref.html">
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -12,9 +12,9 @@
 </style>
 <script src="/common/reftest-wait.js"></script>
 <video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
-    <source src="/media/white.webm" type="video/webm">
-    <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle_position_50.vtt">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/single_line_top_left.vtt">
     <script>
     document.getElementsByTagName('track')[0].track.mode = 'showing';
     </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/basic.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/basic.vtt
new file mode 100644
index 0000000..ed97827
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/basic.vtt
@@ -0,0 +1,7 @@
+WEBVTT
+
+REGION
+id:1
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_x_50_percent.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_x_50_percent.vtt
new file mode 100644
index 0000000..eafea7f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_x_50_percent.vtt
@@ -0,0 +1,8 @@
+WEBVTT
+
+REGION
+id:1
+regionanchor:50%,100%
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_y_50_percent.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_y_50_percent.vtt
new file mode 100644
index 0000000..d51ad26
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_y_50_percent.vtt
@@ -0,0 +1,8 @@
+WEBVTT
+
+REGION
+id:1
+regionanchor:0%,50%
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/scroll_up.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/scroll_up.vtt
new file mode 100644
index 0000000..0b88020
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/scroll_up.vtt
@@ -0,0 +1,16 @@
+WEVTT
+
+REGION
+id:1
+lines:2
+regionanchor:0%,0%
+scroll:up
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a first test subtitle
+
+00:00:01.000 --> 00:00:05.000 region:1
+This is a second test subtitle
+
+00:00:01.000 --> 00:00:05.000 region:1
+This is a third test subtitle
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/single_line_top_left.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/single_line_top_left.vtt
new file mode 100644
index 0000000..efe9d87
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/single_line_top_left.vtt
@@ -0,0 +1,10 @@
+WEBVTT
+
+REGION
+id:1
+viewportanchor:0%,0%
+regionanchor:0%,0%
+lines:1
+
+00:00:00.000 --> 00:00:05.000 region:1 align:left
+This is a test subtitle
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_x_50_percent.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_x_50_percent.vtt
new file mode 100644
index 0000000..afd9876
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_x_50_percent.vtt
@@ -0,0 +1,8 @@
+WEBVTT
+
+REGION
+id:1
+viewportanchor:50%,100%
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_y_50_percent.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_y_50_percent.vtt
new file mode 100644
index 0000000..62e8a79
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_y_50_percent.vtt
@@ -0,0 +1,8 @@
+WEBVTT
+
+REGION
+id:1
+viewportanchor:0%,50%
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/width_50_percent.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/width_50_percent.vtt
new file mode 100644
index 0000000..9fce99b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/support/width_50_percent.vtt
@@ -0,0 +1,8 @@
+WEBVTT
+
+REGION
+id:1
+width:50%
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent-ref.html
similarity index 62%
copy from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html
copy to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent-ref.html
index 9fa1f28e..c216838 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position less than 50%</title>
+<title>Reference for WebVTT rendering regions, viewportanchor x 50%</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -9,13 +9,13 @@
     height: 180px;
     position: relative;
     font-size: 9px;
+    overflow: hidden;
 }
 .cue {
     position: absolute;
     bottom: 0;
-    left: 23px;
-    right: 0;
-    width: 64px;
+    left: 50%;
+    right: -50%;
     text-align: center
 }
 .cue > span {
@@ -24,4 +24,4 @@
     color: green;
 }
 </style>
-<div class=video><span class=cue><span>Awesome<br>!!!</span></span></div>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent.html
new file mode 100644
index 0000000..85e699a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, viewportanchor x 50%</title>
+<link rel="match" href="viewportanchor_x_50_percent-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/viewportanchor_x_50_percent.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent-ref.html
similarity index 63%
copy from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html
copy to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent-ref.html
index 88718e5..f432047 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position:50%</title>
+<title>Reference for WebVTT rendering regions, viewportanchor y 50%</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -12,10 +12,10 @@
 }
 .cue {
     position: absolute;
-    bottom: 0;
+    bottom: 50%;
     left: 0;
     right: 0;
-    text-align: center;
+    text-align: center
 }
 .cue > span {
     font-family: Ahem, sans-serif;
@@ -23,4 +23,4 @@
     color: green;
 }
 </style>
-<div class=video><span class=cue><span>This is a test</span></span></div>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent.html
new file mode 100644
index 0000000..0641b17
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, viewportanchor y 50%</title>
+<link rel="match" href="viewportanchor_y_50_percent-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/viewportanchor_y_50_percent.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent-ref.html
similarity index 64%
copy from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html
copy to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent-ref.html
index 88718e5..fd69b64 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position:50%</title>
+<title>Reference for WebVTT rendering regions, width 50%</title>
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -14,8 +14,8 @@
     position: absolute;
     bottom: 0;
     left: 0;
-    right: 0;
-    text-align: center;
+    right: 50%;
+    text-align: center
 }
 .cue > span {
     font-family: Ahem, sans-serif;
@@ -23,4 +23,4 @@
     color: green;
 }
 </style>
-<div class=video><span class=cue><span>This is a test</span></span></div>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent.html
similarity index 62%
copy from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html
copy to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent.html
index 43732a5..4377e9b4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-<title>WebVTT rendering, align:middle</title>
-<link rel="match" href="align_middle-ref.html">
+<title>WebVTT rendering regions, width 50%</title>
+<link rel="match" href="width_50_percent-ref.html">
 <style>
 html { overflow:hidden }
 body { margin:0 }
@@ -12,9 +12,9 @@
 </style>
 <script src="/common/reftest-wait.js"></script>
 <video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
-    <source src="/media/white.webm" type="video/webm">
-    <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle.vtt">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/width_50_percent.vtt">
     <script>
     document.getElementsByTagName('track')[0].track.mode = 'showing';
     </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center.vtt
new file mode 100644
index 0000000..4b8e14b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 align:center
+This is a test
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_long.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_long.vtt
similarity index 74%
rename from third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_long.vtt
rename to third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_long.vtt
index 8c89441a..57c896d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_long.vtt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_long.vtt
@@ -1,4 +1,4 @@
 WEBVTT
 
-00:00:00.000 --> 00:00:05.000 align:middle
+00:00:00.000 --> 00:00:05.000 align:center
 This is a test subtitle that most likely will span over several rows since it is a pretty long cue with a lot of text.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_50.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_50.vtt
new file mode 100644
index 0000000..21ab82b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_50.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 align:center position:50%
+This is a test
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_gt_50.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_gt_50.vtt
new file mode 100644
index 0000000..4d8538a2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_gt_50.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 align:center position:90%
+Awesome!!!
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50.vtt
new file mode 100644
index 0000000..4ea1e656
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 align:center position:10%
+Awesome!!!
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50_size_gt_maximum_size.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50_size_gt_maximum_size.vtt
new file mode 100644
index 0000000..a2e307c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50_size_gt_maximum_size.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 align:center position:10% size:75%
+Awesome!!!
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle.vtt
deleted file mode 100644
index cdee051..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle.vtt
+++ /dev/null
@@ -1,4 +0,0 @@
-WEBVTT
-
-00:00:00.000 --> 00:00:05.000 align:middle
-This is a test
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_50.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_50.vtt
deleted file mode 100644
index c4437739..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_50.vtt
+++ /dev/null
@@ -1,4 +0,0 @@
-WEBVTT
-
-00:00:00.000 --> 00:00:05.000 align:middle position:50%
-This is a test
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_gt_50.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_gt_50.vtt
deleted file mode 100644
index 186ae1e..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_gt_50.vtt
+++ /dev/null
@@ -1,4 +0,0 @@
-WEBVTT
-
-00:00:00.000 --> 00:00:05.000 align:middle position:90%
-Awesome!!!
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50.vtt
deleted file mode 100644
index 6928c5dc..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50.vtt
+++ /dev/null
@@ -1,4 +0,0 @@
-WEBVTT
-
-00:00:00.000 --> 00:00:05.000 align:middle position:10%
-Awesome!!!
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50_size_gt_maximum_size.vtt b/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50_size_gt_maximum_size.vtt
deleted file mode 100644
index 32bc4ed3..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50_size_gt_maximum_size.vtt
+++ /dev/null
@@ -1,4 +0,0 @@
-WEBVTT
-
-00:00:00.000 --> 00:00:05.000 align:middle position:10% size:75%
-Awesome!!!
diff --git a/third_party/WebKit/LayoutTests/external/wpt/xhr/overridemimetype-edge-cases.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/xhr/overridemimetype-edge-cases.window-expected.txt
index a936e877..8e3a758e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/xhr/overridemimetype-edge-cases.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/xhr/overridemimetype-edge-cases.window-expected.txt
@@ -1,5 +1,6 @@
 This is a testharness.js-based test.
-FAIL overrideMimeType() state needs to be reset across requests assert_equals: expected "Âð" but got "\ufffd\ufffd"
+PASS overrideMimeType() is not reset by open(), basic
+PASS overrideMimeType() is not reset by open()
 PASS If charset is not overridden by overrideMimeType() the original continues to be used
 FAIL Charset can be overridden by overrideMimeType() with a bogus charset assert_equals: expected "\ufffd\ufffd" but got "Âð"
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/xhr/overridemimetype-edge-cases.window.js b/third_party/WebKit/LayoutTests/external/wpt/xhr/overridemimetype-edge-cases.window.js
index 192a696..3f57e9a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/xhr/overridemimetype-edge-cases.window.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/xhr/overridemimetype-edge-cases.window.js
@@ -2,6 +2,16 @@
 
 async_test(t => {
   const client = new XMLHttpRequest();
+  client.onload = t.step_func_done(() => {
+    assert_equals(client.responseText, "\uFFFD\uFFFD");
+  });
+  client.overrideMimeType("text/plain;charset=UTF-8");
+  client.open("GET", testURL);
+  client.send();
+}, "overrideMimeType() is not reset by open(), basic");
+
+async_test(t => {
+  const client = new XMLHttpRequest();
   let secondTime = false;
   client.onload = t.step_func(() => {
     if(!secondTime) {
@@ -10,14 +20,14 @@
       client.open("GET", testURL);
       client.send();
     } else {
-      assert_equals(client.responseText, "Âð");
+      assert_equals(client.responseText, "\uFFFD\uFFFD");
       t.done();
     }
   });
   client.open("GET", testURL);
   client.overrideMimeType("text/plain;charset=UTF-8")
   client.send();
-}, "overrideMimeType() state needs to be reset across requests");
+}, "overrideMimeType() is not reset by open()");
 
 async_test(t => {
   const client = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-click-expected.txt b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-click-expected.txt
deleted file mode 100644
index 7ef72d2ba..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-click-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-PASS autoscroll started
-Mouse cursor shape: type=SouthPanning hotSpot=0,0
-PASS autoscroll stopped
-PASS Mouse cursor cleared
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-click.html b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-click.html
index eb8ad4e..c0a644a 100644
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-click.html
+++ b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-click.html
@@ -1,32 +1,31 @@
-<html>
-<head>
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/gesture-util.js"></script>
+<script src="./resources/middleClickAutoscroll.js"></script>
 <style type="text/css">
 #scrollable {
-    height: 200px;
-    overflow: auto;
-    border: solid 3px #cc0000;
-    font-size: 80px;
+  height: 200px;
+  overflow: auto;
+  border: solid 3px #cc0000;
+  font-size: 80px;
 }
 </style>
-<script src="../../resources/js-test.js"></script>
-<script src="./resources/middleClickAutoscroll.js"></script>
 <script>
-function start()
-{
-    description('Check pan scroll by click mouse');
-    testPanScroll({
-        'clickOrDrag': 'click',
-        'scrollable': $('scrollable'),
-    });
+function start() {
+  testSetUp({
+    'clickOrDrag': 'click',
+    'scrollable': $('scrollable'),
+  });
 }
 </script>
-</head>
+
 <body onload="start()">
 <div id="container">
 <p id="description"></p>
-For manual testing, hold middle button in scrollable and move aroudn mouse pointer for scrolling, then release middle button to stop scrolling.
+For manual testing, click middle button in scrollable and move around mouse pointer for scrolling, then click middle button again to stop scrolling.
 <div id="scrollable"></div>
 </div>
 <div id="console"></div>
 </body>
-</html>
+
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-drag-expected.txt b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-drag-expected.txt
deleted file mode 100644
index 7ef72d2ba..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-drag-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-PASS autoscroll started
-Mouse cursor shape: type=SouthPanning hotSpot=0,0
-PASS autoscroll stopped
-PASS Mouse cursor cleared
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-drag.html b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-drag.html
index 0ef958f..bce49ae5 100644
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-drag.html
+++ b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-drag.html
@@ -1,26 +1,25 @@
-<html>
-<head>
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/gesture-util.js"></script>
+<script src="./resources/middleClickAutoscroll.js"></script>
 <style type="text/css">
 #scrollable {
-    height: 200px;
-    overflow: auto;
-    border: solid 3px #cc0000;
-    font-size: 80px;
+  height: 200px;
+  overflow: auto;
+  border: solid 3px #cc0000;
+  font-size: 80px;
 }
 </style>
-<script src="../../resources/js-test.js"></script>
-<script src="./resources/middleClickAutoscroll.js"></script>
 <script>
-function start()
-{
-    description('Check pan scroll by drag mouse');
-    testPanScroll({
-        'clickOrDrag': 'drag',
-        'scrollable': $('scrollable'),
-    });
+function start() {
+  testSetUp({
+    'clickOrDrag': 'drag',
+    'scrollable': $('scrollable'),
+  });
 }
 </script>
-</head>
+
 <body onload="start()">
 <div id="container">
 <p id="description"></p>
@@ -29,4 +28,3 @@
 </div>
 <div id="console"></div>
 </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-event-fired-expected.txt b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-event-fired-expected.txt
deleted file mode 100644
index 7ef72d2ba..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-event-fired-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-PASS autoscroll started
-Mouse cursor shape: type=SouthPanning hotSpot=0,0
-PASS autoscroll stopped
-PASS Mouse cursor cleared
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-event-fired.html b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-event-fired.html
index bd6b9eb..243c5c9 100644
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-event-fired.html
+++ b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-event-fired.html
@@ -1,29 +1,32 @@
-<html>
-<head>
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/gesture-util.js"></script>
+<script src="./resources/middleClickAutoscroll.js"></script>
 <style type="text/css">
 #scrollable {
-    height: 200px;
-    overflow: auto;
-    border: solid 3px #cc0000;
-    font-size: 80px;
+  height: 200px;
+  overflow: auto;
+  border: solid 3px #cc0000;
+  font-size: 80px;
 }
 </style>
-<script src="../../resources/js-test.js"></script>
-<script src="./resources/middleClickAutoscroll.js"></script>
 <script>
+var mousedown_fired = false;
 window.addEventListener('mousedown', function(event) {
+  mousedown_fired = true;
 });
 
-function start()
-{
-    description('Check pan scroll with mousedown event handler');
-    testPanScroll({
-        'clickOrDrag': 'drag',
-        'scrollable': $('scrollable'),
-    });
+function start() {
+  testSetUp({
+    'clickOrDrag': 'drag',
+    'scrollable': $('scrollable'),
+    'finishTest': function() {
+      assert_true(mousedown_fired);
+    },
+  });
 }
 </script>
-</head>
 <body onload="start()">
 <div id="container">
 <p id="description"></p>
@@ -34,4 +37,3 @@
 </div>
 <div id="console"></div>
 </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-in-iframe-expected.txt b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-in-iframe-expected.txt
deleted file mode 100644
index 7ef72d2ba..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-in-iframe-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-PASS autoscroll started
-Mouse cursor shape: type=SouthPanning hotSpot=0,0
-PASS autoscroll stopped
-PASS Mouse cursor cleared
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-in-iframe.html b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-in-iframe.html
index 24996b0..b79526c5 100644
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-in-iframe.html
+++ b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-in-iframe.html
@@ -1,31 +1,29 @@
-<html>
-<head>
-<script src="../../resources/js-test.js"></script>
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/gesture-util.js"></script>
 <script src="./resources/middleClickAutoscroll.js"></script>
 <script>
 function start() {
+  var iframe = $('iframe');
+  var offsetLeft = iframe.offsetLeft;
+  var offsetTop = iframe.offsetTop;
 
-    description('Check pan scroll in iframe');
-    var iframe = $('iframe');
-    var offsetLeft = iframe.offsetLeft;
-    var offsetTop = iframe.offsetTop;
-
-    testPanScroll({
-        'clickOrDrag': 'click',
-        'endX': offsetLeft + 5,
-        'endY': offsetTop + 50,
-        'scrollable': iframe.contentDocument.body,
-        'scrolledObject': iframe.contentWindow,
-        'startX': offsetLeft + 5,
-        'startY': offsetTop + 5,
-    });
+  testSetUp({
+    'clickOrDrag': 'click',
+    'endX': offsetLeft + 5,
+    'endY': offsetTop + 50,
+    'scrollable': iframe.contentDocument.body,
+    'scrolledObject': iframe.contentWindow.document.scrollingElement,
+    'startX': offsetLeft + 5,
+    'startY': offsetTop + 5,
+  });
 }
 </script>
-</head>
+
 <body onload="start()">
 <div id="container">
 <p id="description"></p>
 <iframe id="iframe" width="640" height="100"></iframe>
 </div>
 </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs-expected.txt b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs-expected.txt
deleted file mode 100644
index 7ef72d2ba..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-PASS autoscroll started
-Mouse cursor shape: type=SouthPanning hotSpot=0,0
-PASS autoscroll stopped
-PASS Mouse cursor cleared
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs-forbidden-expected.txt b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs-forbidden-expected.txt
deleted file mode 100644
index 8607ed0..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs-forbidden-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Top of outer div.
-
-Inner div.
-
-Bottom of outer div.
-
-Test for bug 232965 This tests that vertical pan scrolling does not propagate from the inner div to the outer div when the outer div has no vertical overflow.
-
-PASS autoscroll started
-Mouse cursor shape: type=NorthEastPanning hotSpot=0,0
-PASS autoscroll stopped
-PASS Mouse cursor cleared
-PASS outerdiv.scrollLeft is not 0
-PASS outerdiv.scrollTop is 0
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs-forbidden.html b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs-forbidden.html
index 5519373..05228ae 100644
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs-forbidden.html
+++ b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs-forbidden.html
@@ -1,35 +1,34 @@
 <!DOCTYPE html>
-<html>
-<head>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/gesture-util.js"></script>
+<script src="./resources/middleClickAutoscroll.js"></script>
 <style type="text/css">
 div {
-    line-height: 50px;
+  line-height: 50px;
 }
 #outerdiv {
-    overflow:auto;
-    border: 2px solid #000FFF;
+  overflow:auto;
+  border: 2px solid #000FFF;
 }
 #innerdiv {
-    overflow:auto;
-    border: 2px solid #000000;
-    width: 121%;
-    padding: 5px;
+  overflow:auto;
+  border: 2px solid #000000;
+  width: 121%;
+  padding: 5px;
 }
 </style>
-<script src="../../resources/js-test.js"></script>
-<script src="./resources/middleClickAutoscroll.js"></script>
 <script>
-function start()
-{
+function start() {
   outerdiv = $('outerdiv');
 
-  testPanScroll({
+  testSetUp({
     'clickOrDrag': 'drag',
     'endX': 225,
     'endY': 75,
     'finishTest': function() {
-      shouldNotBe('outerdiv.scrollLeft', '0');
-      shouldBeZero('outerdiv.scrollTop');
+      assert_not_equals(outerdiv.scrollLeft, 0);
+      assert_equals(outerdiv.scrollTop, 0);
     },
     'scrollable': outerdiv,
     'startX': 150,
@@ -37,16 +36,14 @@
   });
 }
 </script>
-</head>
 <body onload="start()">
 <div id="outerdiv">
-    <p>Top of outer div.</p>
-    <div id="innerdiv">
-        <p>Inner div.</p>
-    </div>
-    <p>Bottom of outer div.</p>
+  <p>Top of outer div.</p>
+  <div id="innerdiv">
+    <p>Inner div.</p>
+  </div>
+  <p>Bottom of outer div.</p>
 </div>
 <p>Test for <a href="http://crbug.com/232965">bug 232965</a> This tests that vertical pan scrolling does not propagate from the inner div to the outer div when the outer div has no vertical overflow.</p>
 <div id="console"></div>
 </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs.html b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs.html
index f1b882e..6c39bd4 100644
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs.html
+++ b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-nested-divs.html
@@ -1,46 +1,44 @@
-<html>
-<head>
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/gesture-util.js"></script>
+<script src="./resources/middleClickAutoscroll.js"></script>
 <style type="text/css">
 #scrollable {
-    width:500px;
-    height:300px;
-    overflow:auto;
-    border:2px solid red;
-    padding:10px";
+  width:500px;
+  height:300px;
+  overflow:auto;
+  border:2px solid red;
+  padding:10px;
 }
 </style>
-<script src="../../resources/js-test.js"></script>
-<script src="./resources/middleClickAutoscroll.js"></script>
+
 <script>
-function start()
-{
-    description('Check pan scroll in nested divs');
-    testPanScroll({
-        'clickOrDrag': 'click',
-        'scrollable': $('scrollable'),
-    });
+function start() {
+  testSetUp({
+    'clickOrDrag': 'click',
+    'scrollable': $('scrollable'),
+  });
 }
 </script>
-</head>
+
 <body onload="start()">
 <div id="container">
 <p id="description"></p>
 Test for <a href="https://bugs.webkit.org/show_bug.cgi?id=28023">bug 28023</a> This tests that pan scrolling
 propagates correctly up the DOM tree. On success, our scroll offset should be non-zero.
 <div id="scrollable">
-    <div style="height:200px; position:relative;">
-        <div style="height:150px; border:1px blue solid; overflow:auto;">
-            Panscrolling starting in the blue box should scroll the outer div.
-        </div>
-        Panscrolling outside the blue boxes should scroll the outer div.
+  <div style="height:200px; position:relative;">
+    <div style="height:150px; border:1px blue solid; overflow:auto;">
+      Panscrolling starting in the blue box should scroll the outer div.
     </div>
-    <div style="height:200px; position:relative;">
-        <div style="height:150px; border:1px blue solid; overflow:auto;">
-            Panscrolling starting in the blue box should scroll the outer div.
-        </div>
+    Panscrolling outside the blue boxes should scroll the outer div.
+  </div>
+  <div style="height:200px; position:relative;">
+    <div style="height:150px; border:1px blue solid; overflow:auto;">
+      Panscrolling starting in the blue box should scroll the outer div.
     </div>
+  </div>
 </div>
 </div>
-<div id="console"></div>
 </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/resources/middleClickAutoscroll.js b/third_party/WebKit/LayoutTests/fast/events/resources/middleClickAutoscroll.js
index f58baf64..b28dd57 100644
--- a/third_party/WebKit/LayoutTests/fast/events/resources/middleClickAutoscroll.js
+++ b/third_party/WebKit/LayoutTests/fast/events/resources/middleClickAutoscroll.js
@@ -1,90 +1,65 @@
-var autoscrollInterval = 50;
-var middleButton = 1;
 var middleClickAutoscrollRadius = 15; // from FrameView::noPanScrollRadius
+var waitTimeBeforeMoveInSeconds = 0.1;
+var scrollable;
+var scrolledObject;
+var startX;
+var startY;
+var endX;
+var endY;
+var autoscrollParam;
 
-window.jsTestIsAsync = true;
-
-function $(id)
-{
-    return document.getElementById(id);
+function $(id) {
+  return document.getElementById(id);
 }
 
-function testPanScroll(param)
-{
-    function finishTest()
-    {
-        if ($('container'))
-            $('container').innerHTML = '';
-        if (param.finishTest)
-            param.finishTest();
-        if (window.finishJSTest) {
-            finishJSTest();
-            return;
-        }
-        if (window.testRunner)
-            testRunner.notifyDone();
+function testSetUp(param) {
+  scrollable = param.scrollable;
+  scrolledObject = param.scrolledObject || scrollable;
+  startX = param.startX || scrollable.offsetLeft + 5;
+  startY = param.startY || scrollable.offsetTop + 5;
+  endX = param.endX || scrollable.offsetLeft + 5;
+  endY = param.endY || scrollable.offsetTop + middleClickAutoscrollRadius + 6;
+  autoscrollParam = param;
+  if (!scrollable.innerHTML) {
+    for (var i = 0; i < 100; ++i) {
+      var line = document.createElement('div');
+      line.innerHTML = "line " + i;
+      scrollable.appendChild(line);
+    }
+  }
+  promise_test (async () => {
+    // Start atuoscrolling.
+    if (autoscrollParam.clickOrDrag == 'click') {
+      await mouseMoveTo(startX, startY);
+      await mouseClickOn(startX, startY, 'middle');
+      await mouseMoveTo(endX, endY);
+    } else {
+      assert_equals('drag', autoscrollParam.clickOrDrag);
+      mouseDragAndDrop(startX, startY, endX, endY, 'middle',
+          waitTimeBeforeMoveInSeconds);
     }
 
-    var scrollable = param.scrollable;
-    var scrolledObject = param.scrolledObject || scrollable;
+    // Wait for some scrolling, then end the autoscroll.
+    await waitFor(() => {
+      return scrolledObject.scrollTop > 0 || scrolledObject.scrollLeft > 0;
+    });
+    if (autoscrollParam.clickOrDrag == 'click')
+      await mouseClickOn(endX, endY, 'middle');
 
-    if (!scrollable.innerHTML) {
-        for (var i = 0; i < 100; ++i) {
-            var line = document.createElement('div');
-            line.innerHTML = "line " + i;
-            scrollable.appendChild(line);
-        }
-    }
+    // Wait for the cursor shape to go back to normal.
+    await waitFor(() => {
+      var cursorInfo = internals.getCurrentCursorInfo();
+      return cursorInfo == "type=Pointer hotSpot=0,0" ||
+          cursorInfo == "type=IBeam hotSpot=0,0";
+    });
 
-    var noModeScroll = false;
-    var scrolled = false;
+    finishTest();
+  });
+}
 
-    scrolledObject.onscroll = function() {
-        if (noModeScroll) {
-            testFailed('still autoscroll');
-            finishTest();
-            return;
-        }
-
-        if (scrolled)
-            return;
-        scrolled = true;
-        testPassed('autoscroll started');
-        var cursorInfo = internals.getCurrentCursorInfo();
-        debug("Mouse cursor shape: " + cursorInfo);
-
-        if (window.eventSender) {
-            if (param.clickOrDrag == 'click')
-                eventSender.mouseDown(middleButton);
-            eventSender.mouseUp(middleButton);
-        }
-    };
-
-    scrollable.ownerDocument.onmouseup = function(e) {
-        if (!scrolled || e.button != middleButton)
-            return;
-        noMoreScroll = true;
-        window.setTimeout(function() {
-            testPassed('autoscroll stopped');
-            var cursorInfo = internals.getCurrentCursorInfo();
-            if (cursorInfo == "type=Pointer hotSpot=0,0" || cursorInfo == "type=IBeam hotSpot=0,0")
-                 testPassed('Mouse cursor cleared');
-            else
-                 testFailed('Mouse cursor shape: ' + cursorInfo);
-
-            finishTest();
-        }, autoscrollInterval * 2);
-    };
-
-    if (!window.eventSender)
-        return;
-    var startX = param.startX || scrollable.offsetLeft + 5;
-    var startY = param.startY || scrollable.offsetTop + 5;
-    var endX = param.endX || scrollable.offsetLeft + 5;
-    var endY = param.endY || scrollable.offsetTop + middleClickAutoscrollRadius + 6;
-    eventSender.mouseMoveTo(startX, startY);
-    eventSender.mouseDown(middleButton);
-    if (param.clickOrDrag == 'click')
-        eventSender.mouseUp(middleButton);
-    eventSender.mouseMoveTo(endX, endY);
+function finishTest() {
+  if ($('container'))
+    $('container').innerHTML = '';
+  if (autoscrollParam.finishTest)
+    autoscrollParam.finishTest();
 }
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/profiler/cpu-profiler-parsing-no-hitcounts-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/profiler/cpu-profiler-parsing-no-hitcounts-expected.txt
new file mode 100644
index 0000000..6bcd3f7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/profiler/cpu-profiler-parsing-no-hitcounts-expected.txt
@@ -0,0 +1,11 @@
+Tests profile without hitCounts is parsed correctly.
+
+Profile tree:
+  (root) id:1 total:5 self:0 depth:-1
+    (garbage collector) id:2 total:1 self:1 depth:0
+    foo id:3 total:4 self:1 depth:0
+      bar id:4 total:3 self:3 depth:1
+
+samples: 4, 4, 3, 4, 2
+timestamps: 1.5, 1.75, 2.75, 3, 4, 4.625
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/profiler/cpu-profiler-parsing-no-hitcounts.js b/third_party/WebKit/LayoutTests/http/tests/devtools/profiler/cpu-profiler-parsing-no-hitcounts.js
new file mode 100644
index 0000000..54cba55
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/profiler/cpu-profiler-parsing-no-hitcounts.js
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult(`Tests profile without hitCounts is parsed correctly.\n`);
+  await TestRunner.loadModule('cpu_profiler_test_runner');
+
+  var profile = {
+    startTime: 1000,
+    endTime: 6000,
+    nodes: [
+      {id: 1, callFrame: {functionName: '(root)'}, children: [2,3]},
+      {id: 2, callFrame: {functionName: '(garbage collector)'}},
+      {id: 3, callFrame: {functionName: 'foo'}, children: [4]},
+      {id: 4, callFrame: {functionName: 'bar'}}
+    ],
+    timeDeltas: [500, 250, 1000, 250, 1000],
+    samples: [4, 4, 3, 4, 2]
+  };
+
+  var model = new SDK.CPUProfileDataModel(profile);
+  TestRunner.addResult('Profile tree:');
+  printTree('  ', model.profileHead);
+  function printTree(padding, node) {
+    TestRunner.addResult(
+        `${padding}${node.functionName} id:${node.id} total:${node.total} self:${node.self} depth:${node.depth}`);
+    node.children.sort((a, b) => a.id - b.id).forEach(printTree.bind(null, padding + '  '));
+  }
+
+  TestRunner.addResult('\nsamples: ' + model.samples.join(', '));
+  TestRunner.addResult('timestamps: ' + model.timestamps.join(', '));
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-allow-disallow-transitions-expected.txt b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-allow-disallow-transitions-expected.txt
index a24b84c..c20b8f70a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-allow-disallow-transitions-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-allow-disallow-transitions-expected.txt
@@ -1,9 +1,9 @@
-CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/gamma.png on this site because this site tends to show ads that interrupt, distract, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
-CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/gamma.png on this site because this site tends to show ads that interrupt, distract, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
-CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/gamma.png on this site because this site tends to show ads that interrupt, distract, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
-CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/delta.png on this site because this site tends to show ads that interrupt, distract, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
-CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/delta.png on this site because this site tends to show ads that interrupt, distract, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
-CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/delta.png on this site because this site tends to show ads that interrupt, distract, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
+CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/gamma.png on this site because this site tends to show ads that interrupt, distract, mislead, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
+CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/gamma.png on this site because this site tends to show ads that interrupt, distract, mislead, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
+CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/gamma.png on this site because this site tends to show ads that interrupt, distract, mislead, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
+CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/delta.png on this site because this site tends to show ads that interrupt, distract, mislead, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
+CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/delta.png on this site because this site tends to show ads that interrupt, distract, mislead, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
+CONSOLE ERROR: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/delta.png on this site because this site tends to show ads that interrupt, distract, mislead, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
 This is a testharness.js-based test.
 PASS State transition primary to primary. 
 FAIL State transition primary to fallback.  assert_equals: Image has incorrect width for the expected final state. expected 0 but got 16
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/resource-disallowed-expected.txt b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/resource-disallowed-expected.txt
index 34030fa..0e9b06ae 100644
--- a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/resource-disallowed-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/resource-disallowed-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: line 34: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/beta.js on this site because this site tends to show ads that interrupt, distract, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
+CONSOLE ERROR: line 34: Chrome blocked resource http://127.0.0.1:8000/subresource_filter/resources/beta.js on this site because this site tends to show ads that interrupt, distract, mislead, or prevent user control. Learn more at https://www.chromestatus.com/feature/5738264052891648
 This is a testharness.js-based test.
 PASS Resources whose URLs are not disallowed are still loaded.
 PASS Resources whose initial URL (not after redirects) are disallowed are not loaded.
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk-jfif-marker.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk-jfif-marker.jpg
new file mode 100644
index 0000000..a458ebd9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk-jfif-marker.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk-no-jfif-or-adobe-markers.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk-no-jfif-or-adobe-markers.jpg
new file mode 100644
index 0000000..7a1dcb85
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk-no-jfif-or-adobe-markers.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk-unknown-transform.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk-unknown-transform.jpg
new file mode 100644
index 0000000..a7bd131
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk-unknown-transform.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk.jpg
new file mode 100644
index 0000000..5983d53
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-cmyk.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-grayscale.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-grayscale.jpg
new file mode 100644
index 0000000..2b6d05b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-grayscale.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-rgb-non-interleaved.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-rgb-non-interleaved.jpg
new file mode 100644
index 0000000..0aee87d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-rgb-non-interleaved.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-rgb-unknown-transform.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-rgb-unknown-transform.jpg
new file mode 100644
index 0000000..d67f433
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-rgb-unknown-transform.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-rgb.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-rgb.jpg
new file mode 100644
index 0000000..39baa47
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-rgb.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-two-channels-jfif-marker.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-two-channels-jfif-marker.jpg
new file mode 100644
index 0000000..cd59c2f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-two-channels-jfif-marker.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-410.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-410.jpg
new file mode 100644
index 0000000..b7c0223
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-410.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-411.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-411.jpg
new file mode 100644
index 0000000..c24a7581
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-411.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-420-both-jfif-adobe.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-420-both-jfif-adobe.jpg
new file mode 100644
index 0000000..dafef3f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-420-both-jfif-adobe.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-420-non-interleaved.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-420-non-interleaved.jpg
new file mode 100644
index 0000000..8741bff
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-420-non-interleaved.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-420.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-420.jpg
new file mode 100644
index 0000000..01c7004
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-420.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-422.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-422.jpg
new file mode 100644
index 0000000..3008c6a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-422.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-440.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-440.jpg
new file mode 100644
index 0000000..39ddf68
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-440.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-444.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-444.jpg
new file mode 100644
index 0000000..c02234d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-444.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-other.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-other.jpg
new file mode 100644
index 0000000..02750e9c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycbcr-other.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycck.jpg b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycck.jpg
new file mode 100644
index 0000000..dfa71a3c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/cs-uma-ycck.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/resources/gesture-util.js b/third_party/WebKit/LayoutTests/resources/gesture-util.js
index 0484df3..51a9ddc9 100644
--- a/third_party/WebKit/LayoutTests/resources/gesture-util.js
+++ b/third_party/WebKit/LayoutTests/resources/gesture-util.js
@@ -242,7 +242,7 @@
 
 // Simulate a mouse drag and drop. mouse down at {start_x, start_y}, move to
 // {end_x, end_y} and release.
-function mouseDragAndDrop(start_x, start_y, end_x, end_y, button = 'left') {
+function mouseDragAndDrop(start_x, start_y, end_x, end_y, button = 'left', t = 0) {
   return new Promise((resolve, reject) => {
     if (chrome && chrome.gpuBenchmarking) {
       let pointerActions = [{
@@ -250,7 +250,9 @@
         actions: [
           { 'name': 'pointerMove', 'x': start_x, 'y': start_y },
           { 'name': 'pointerDown', 'x': start_x, 'y': start_y, 'button': button },
+          { 'name': 'pause', 'duration': t},
           { 'name': 'pointerMove', 'x': end_x, 'y': end_y },
+          { 'name': 'pause', 'duration': t},
           { 'name': 'pointerUp', 'button': button },
         ]
       }];
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 d2a485f7..1f829ea 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -443,6 +443,11 @@
     method abort
     method constructor
     setter onprogress
+interface Badge
+    static method clear
+    static method set
+    attribute @@toStringTag
+    method constructor
 interface BarProp
     attribute @@toStringTag
     getter visible
@@ -7434,6 +7439,10 @@
     method createPolicy
     method getExposedPolicy
     method getPolicyNames
+    method isHTML
+    method isScript
+    method isScriptURL
+    method isURL
 interface TrustedURL
     attribute @@toStringTag
     method constructor
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 22712944..4c7ab11 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -682,6 +682,7 @@
     "platform/modules/app_banner/app_banner.mojom",
     "platform/modules/background_fetch/background_fetch.mojom",
     "platform/modules/background_sync/background_sync.mojom",
+    "platform/modules/badging/badging.mojom",
     "platform/modules/bluetooth/web_bluetooth.mojom",
     "platform/modules/cache_storage/cache_storage.mojom",
     "platform/modules/credentialmanager/credential_manager.mojom",
diff --git a/third_party/blink/public/platform/modules/badging/OWNERS b/third_party/blink/public/platform/modules/badging/OWNERS
new file mode 100644
index 0000000..558d843
--- /dev/null
+++ b/third_party/blink/public/platform/modules/badging/OWNERS
@@ -0,0 +1,4 @@
+file://third_party/blink/renderer/modules/badging/OWNERS
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/platform/modules/badging/badging.mojom b/third_party/blink/public/platform/modules/badging/badging.mojom
new file mode 100644
index 0000000..45ce81c
--- /dev/null
+++ b/third_party/blink/public/platform/modules/badging/badging.mojom
@@ -0,0 +1,19 @@
+// 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.
+
+module blink.mojom;
+
+// Interface for handling badge messages from frames and subframes.
+interface BadgeService {
+  // Sets a badge for the PWA corresponding to the context sending the request
+  // if such a PWA exists.
+  // TODO(estevenson): Pass the badge contents from the API. Chrome OS will be
+  // the first client and will not show the data anyway so for now this is
+  // sufficient.
+  SetBadge();
+
+  // Clear badge (if it exists) for the PWA corresponding to the context sending
+  // the request if such a PWA exists.
+  ClearBadge();
+};
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 395e648..b7d2aff 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -237,28 +237,6 @@
                               const WebURL& unreachable_url = WebURL(),
                               bool replace = false) = 0;
 
-  // Calls CommitDataNavigationWithRequest with a WebURLRequest that is built
-  // either 1) based on the previous, provisional request (if |replace| is true
-  // and |unreachable_url| is present) or 2) constructed from scratch otherwise.
-  //
-  // |base_url| indicates the security origin and is used to resolve links in
-  // the committed document.
-  //
-  // Please see documentation of CommitDataNavigationWithRequest for description
-  // of other parameters.
-  virtual void CommitDataNavigation(
-      const WebData& data,
-      const WebString& mime_type,
-      const WebString& text_encoding,
-      const WebURL& base_url,
-      const WebURL& unreachable_url,
-      bool replace,
-      WebFrameLoadType,
-      const WebHistoryItem&,
-      bool is_client_redirect,
-      std::unique_ptr<WebNavigationParams> navigation_params,
-      std::unique_ptr<WebDocumentLoader::ExtraData> navigation_data) = 0;
-
   // Navigates to the given |data| with specified |mime_type| and optional
   // |text_encoding|.
   //
@@ -268,10 +246,9 @@
   // If |replace| is false, then this data will be loaded as a normal
   // navigation.  Otherwise, the current history item will be replaced.
   //
-  // This method can be called instead of CommitDataNavigation when a
-  // ResourceRequest has already been precomputed (e.g. when trying to commit an
-  // error page, while mimicking the original failed request).
-  virtual void CommitDataNavigationWithRequest(
+  // Request's url indicates the security origin and is used as a base
+  // url to resolve links in the committed document.
+  virtual void CommitDataNavigation(
       const WebURLRequest&,
       const WebData&,
       const WebString& mime_type,
diff --git a/third_party/blink/renderer/bindings/modules/v8/generated.gni b/third_party/blink/renderer/bindings/modules/v8/generated.gni
index 57358a9..b2cf023 100644
--- a/third_party/blink/renderer/bindings/modules/v8/generated.gni
+++ b/third_party/blink/renderer/bindings/modules/v8/generated.gni
@@ -76,6 +76,8 @@
   "$bindings_modules_v8_output_dir/string_or_unsigned_long.h",
   "$bindings_modules_v8_output_dir/unsigned_long_or_unsigned_long_sequence.cc",
   "$bindings_modules_v8_output_dir/unsigned_long_or_unsigned_long_sequence.h",
+  "$bindings_modules_v8_output_dir/usv_string_or_long.cc",
+  "$bindings_modules_v8_output_dir/usv_string_or_long.h",
   "$bindings_modules_v8_output_dir/webgl_rendering_context_or_webgl2_rendering_context.cc",
   "$bindings_modules_v8_output_dir/webgl_rendering_context_or_webgl2_rendering_context.h",
 ]
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 00a4cd8..f76d227 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -952,10 +952,10 @@
                                        const WebURL& unreachable_url,
                                        bool replace) {
   DCHECK(GetFrame());
-  CommitDataNavigation(data, WebString::FromUTF8("text/html"),
-                       WebString::FromUTF8("UTF-8"), base_url, unreachable_url,
-                       replace, WebFrameLoadType::kStandard, WebHistoryItem(),
-                       false, nullptr, nullptr);
+  CommitDataNavigation(
+      WebURLRequest(base_url), data, WebString::FromUTF8("text/html"),
+      WebString::FromUTF8("UTF-8"), unreachable_url, replace,
+      WebFrameLoadType::kStandard, WebHistoryItem(), false, nullptr, nullptr);
 }
 
 void WebLocalFrameImpl::StopLoading() {
@@ -2125,58 +2125,6 @@
 }
 
 void WebLocalFrameImpl::CommitDataNavigation(
-    const WebData& data,
-    const WebString& mime_type,
-    const WebString& text_encoding,
-    const WebURL& base_url,
-    const WebURL& unreachable_url,
-    bool replace,
-    WebFrameLoadType web_frame_load_type,
-    const WebHistoryItem& item,
-    bool is_client_redirect,
-    std::unique_ptr<WebNavigationParams> navigation_params,
-    std::unique_ptr<WebDocumentLoader::ExtraData> navigation_data) {
-  DCHECK(GetFrame());
-
-  // TODO(dgozman): this whole logic of rewriting the params is odd,
-  // and should be moved to the callsites instead.
-  ResourceRequest request;
-  HistoryItem* history_item = item;
-  DocumentLoader* provisional_document_loader =
-      GetFrame()->Loader().GetProvisionalDocumentLoader();
-  // If we are loading substitute data to replace an existing load, then
-  // inherit all of the properties of that original request. This way,
-  // reload will re-attempt the original request. It is essential that
-  // we only do this when there is an |unreachable_url| since a non-empty
-  // |unreachable_url| informs FrameLoader::CommitNavigation to load
-  // |unreachable_url| instead of the currently loaded URL.
-  if (replace && !unreachable_url.IsEmpty() && provisional_document_loader) {
-    request = provisional_document_loader->OriginalRequest();
-
-    // When replacing a failed back/forward provisional navigation with an error
-    // page, retain the HistoryItem for the failed provisional navigation
-    // and reuse it for the error page navigation.
-    WebFrameLoadType previous_load_type =
-        provisional_document_loader->LoadType();
-    if (previous_load_type == WebFrameLoadType::kBackForward &&
-        provisional_document_loader->GetHistoryItem()) {
-      history_item = provisional_document_loader->GetHistoryItem();
-      web_frame_load_type = WebFrameLoadType::kBackForward;
-    } else if (previous_load_type == WebFrameLoadType::kReload ||
-               previous_load_type == WebFrameLoadType::kReloadBypassingCache) {
-      web_frame_load_type = previous_load_type;
-    }
-  }
-  request.SetURL(base_url);
-
-  CommitDataNavigationWithRequest(
-      WrappedResourceRequest(request), data, mime_type, text_encoding,
-      unreachable_url, replace, web_frame_load_type, history_item,
-      is_client_redirect, std::move(navigation_params),
-      std::move(navigation_data));
-}
-
-void WebLocalFrameImpl::CommitDataNavigationWithRequest(
     const WebURLRequest& request,
     const WebData& data,
     const WebString& mime_type,
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index b2b1f552..d4ed7b4 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -277,18 +277,6 @@
       std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) override;
   void LoadJavaScriptURL(const WebURL&) override;
   void CommitDataNavigation(
-      const WebData&,
-      const WebString& mime_type,
-      const WebString& text_encoding,
-      const WebURL& base_url,
-      const WebURL& unreachable_url,
-      bool replace,
-      WebFrameLoadType,
-      const WebHistoryItem&,
-      bool is_client_redirect,
-      std::unique_ptr<WebNavigationParams> navigation_params,
-      std::unique_ptr<WebDocumentLoader::ExtraData> navigation_data) override;
-  void CommitDataNavigationWithRequest(
       const WebURLRequest&,
       const WebData&,
       const WebString& mime_type,
diff --git a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc
index 1fefb93e..4b9f4e0 100644
--- a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc
+++ b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc
@@ -466,6 +466,12 @@
         CollectSolidEdges(shelf_copy->line_right_edges, new_shelf.block_offset,
                           &new_shelf.line_right_edges);
 
+        // The new shelf adopts the copy exclusions. This may contain
+        // exclusions which are above this shelf, however we'll filter these
+        // out when/if we need to calculate the line opportunity.
+        new_shelf.shape_exclusions = std::move(shelf_copy->shape_exclusions);
+        new_shelf.has_shape_exclusions = shelf_copy->has_shape_exclusions;
+
         // If we didn't find any edges, the line_left/line_right of the shelf
         // are pushed out to be the minimum/maximum.
         new_shelf.line_left = new_shelf.line_left_edges.IsEmpty()
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index 7a224de..4b5dee0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -78,27 +78,34 @@
 // Returns if the resulting fragment should be considered an "empty block".
 // There is special casing for fragments like this, e.g. margins "collapse
 // through", etc.
-bool IsEmptyBlock(const NGLayoutInputNode child,
-                  const NGLayoutResult& layout_result) {
+bool IsEmptyBlock(bool is_new_fc, const NGLayoutResult& layout_result) {
   // TODO(ikilpatrick): This should be a DCHECK.
-  if (child.CreatesNewFormattingContext())
+  if (is_new_fc)
     return false;
 
   if (layout_result.BfcBlockOffset())
     return false;
 
 #if DCHECK_IS_ON()
+  const NGPhysicalFragment& physical_fragment =
+      *layout_result.PhysicalFragment();
   // This just checks that the fragments block size is actually zero. We can
   // assume that its in the same writing mode as its parent, as a different
-  // writing mode child will be caught by the CreatesNewFormattingContext check.
-  NGFragment fragment(child.Style().GetWritingMode(),
-                      *layout_result.PhysicalFragment());
-  DCHECK_EQ(LayoutUnit(), fragment.BlockSize()) << child.ToString();
+  // writing mode child will be caught by the is_new_fc check.
+  NGFragment fragment(physical_fragment.Style().GetWritingMode(),
+                      physical_fragment);
+  DCHECK_EQ(LayoutUnit(), fragment.BlockSize());
 #endif
 
   return true;
 }
 
+// As above; for convenience if you have a child_space.
+bool IsEmptyBlock(const NGConstraintSpace& child_space,
+                  const NGLayoutResult& layout_result) {
+  return IsEmptyBlock(child_space.IsNewFormattingContext(), layout_result);
+}
+
 LayoutUnit LogicalFromBfcLineOffset(const NGFragment& fragment,
                                     LayoutUnit child_bfc_line_offset,
                                     LayoutUnit parent_bfc_line_offset,
@@ -197,6 +204,7 @@
 
     const ComputedStyle& child_style = child.Style();
     const EClear child_clear = child_style.Clear();
+    bool child_is_new_fc = child.CreatesNewFormattingContext();
 
     // Conceptually floats and a single new-FC would just get positioned on a
     // single "line". If there is a float/new-FC with clearance, this creates a
@@ -204,7 +212,7 @@
     //
     // Both of the float size trackers get reset for anything that isn't a float
     // (inflow and new-FC) at the end of the loop, as this creates a new "line".
-    if (child.IsFloating() || child.CreatesNewFormattingContext()) {
+    if (child.IsFloating() || child_is_new_fc) {
       LayoutUnit float_inline_size =
           float_left_inline_size + float_right_inline_size;
 
@@ -276,7 +284,7 @@
 
       max_inline_contribution =
           float_left_inline_size + float_right_inline_size;
-    } else if (child.CreatesNewFormattingContext()) {
+    } else if (child_is_new_fc) {
       // As floats are line relative, we perform the margin calculations in the
       // line relative coordinate system as well.
       LayoutUnit margin_line_left = margins.LineLeft(direction);
@@ -1111,7 +1119,7 @@
   bool relayout_child_when_bfc_resolved =
       layout_result->AdjoiningFloatTypes() && !child_bfc_block_offset &&
       !child_space.FloatsBfcBlockOffset();
-  bool is_empty_block = IsEmptyBlock(child, *layout_result);
+  bool is_empty_block = IsEmptyBlock(child_space, *layout_result);
 
   // A child may have aborted its layout if it resolved its BFC block offset.
   // If we don't have a BFC block offset yet, we need to propagate the abortion
@@ -1366,7 +1374,8 @@
   // Determine the child's end logical offset, for the next child to use.
   LayoutUnit logical_block_offset;
 
-  bool is_empty_block = IsEmptyBlock(child, layout_result);
+  bool is_empty_block =
+      IsEmptyBlock(child.CreatesNewFormattingContext(), layout_result);
   if (is_empty_block) {
     // The default behaviour for empty blocks is they just pass through the
     // previous inflow position.
@@ -1467,7 +1476,7 @@
     const NGConstraintSpace& child_space,
     const NGInflowChildData& child_data,
     const NGLayoutResult& layout_result) const {
-  DCHECK(IsEmptyBlock(child, layout_result));
+  DCHECK(IsEmptyBlock(child_space, layout_result));
 
   // The child must be an in-flow zero-block-size fragment, use its end margin
   // strut for positioning.
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 1b686252..99fef44 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
@@ -589,7 +589,7 @@
     LayoutBlockFlow* block_flow = ToLayoutBlockFlow(box_);
     block_flow->UpdateIsSelfCollapsing();
 
-    if (block_flow->CreatesNewFormattingContext())
+    if (constraint_space.IsNewFormattingContext())
       block_flow->AddOverflowFromFloats();
   }
 }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
index 9c093a8..1f0e3a7 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
@@ -403,7 +403,8 @@
               .SetPercentageResolutionSize(
                   constraint_space->PercentageResolutionSize())
               .SetFloatsBfcBlockOffset(LayoutUnit())
-              .SetIsNewFormattingContext(node.CreatesNewFormattingContext())
+              .SetIsNewFormattingContext(
+                  constraint_space->IsNewFormattingContext())
               .SetIsShrinkToFit(true)
               .ToConstraintSpace(node.Style().GetWritingMode());
       constraint_space = &adjusted_constraint_space;
diff --git a/third_party/blink/renderer/core/loader/subresource_filter.cc b/third_party/blink/renderer/core/loader/subresource_filter.cc
index a5ba9f7..b29d5b55 100644
--- a/third_party/blink/renderer/core/loader/subresource_filter.cc
+++ b/third_party/blink/renderer/core/loader/subresource_filter.cc
@@ -26,7 +26,7 @@
   builder.Append(url.GetString());
   builder.Append(
       " on this site because this site tends to show ads that interrupt, "
-      "distract, or prevent user control. Learn more at "
+      "distract, mislead, or prevent user control. Learn more at "
       "https://www.chromestatus.com/feature/5738264052891648");
   return builder.ToString();
 }
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc b/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc
index 32f33ec..a861ecdf 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc
@@ -275,7 +275,7 @@
   bool has_non_root_composited_scrolling_ancestor =
       layer->AncestorScrollingLayer() &&
       layer->AncestorScrollingLayer()->GetScrollableArea() &&
-      layer->AncestorScrollingLayer()->DirectCompositingReasons() &&
+      layer->AncestorScrollingLayer()->NeedsCompositedScrolling() &&
       !layer->AncestorScrollingLayer()->IsRootLayer();
 
   bool use_clipped_bounding_rect = !has_non_root_composited_scrolling_ancestor;
@@ -360,13 +360,13 @@
       unclipped_descendants.EraseAt(unclipped_descendants_to_remove.at(
           unclipped_descendants_to_remove.size() - i - 1));
     }
+  }
 
-    if (reasons_to_composite & CompositingReason::kOutOfFlowClipping) {
-      // TODO(schenney): We only need to promote when the clipParent is not a
-      // descendant of the ancestor scroller, which we do not check for here.
-      // Hence we might be promoting needlessly.
-      unclipped_descendants.push_back(layer);
-    }
+  if (reasons_to_composite & CompositingReason::kOutOfFlowClipping) {
+    // TODO(schenney): We only need to promote when the clipParent is not a
+    // descendant of the ancestor scroller, which we do not check for here.
+    // Hence we might be promoting needlessly.
+    unclipped_descendants.push_back(layer);
   }
 
   IntRect abs_bounds = use_clipped_bounding_rect
diff --git a/third_party/blink/renderer/core/paint/object_paint_properties.h b/third_party/blink/renderer/core/paint/object_paint_properties.h
index efd20c9..325cd9ac 100644
--- a/third_party/blink/renderer/core/paint/object_paint_properties.h
+++ b/third_party/blink/renderer/core/paint/object_paint_properties.h
@@ -234,6 +234,12 @@
            "scroll translation and a replaced content transform.";
     DCHECK(!ClipPathClip() || !ClipPath())
         << "ClipPathClip and ClipPathshould be mutually exclusive.";
+    DCHECK((!TransformIsolationNode() && !ClipIsolationNode() &&
+            !EffectIsolationNode()) ||
+           (TransformIsolationNode() && ClipIsolationNode() &&
+            EffectIsolationNode()))
+        << "Isolation nodes have to be created for all of transform, clip, and "
+           "effect trees.";
   }
 #endif
 
diff --git a/third_party/blink/renderer/core/paint/paint_invalidator.cc b/third_party/blink/renderer/core/paint/paint_invalidator.cc
index 681056ca..7a9f4ca 100644
--- a/third_party/blink/renderer/core/paint/paint_invalidator.cc
+++ b/third_party/blink/renderer/core/paint/paint_invalidator.cc
@@ -27,23 +27,6 @@
 
 namespace blink {
 
-template <typename Rect>
-static LayoutRect SlowMapToVisualRectInAncestorSpace(
-    const LayoutObject& object,
-    const LayoutBoxModelObject& ancestor,
-    const Rect& rect) {
-  if (object.IsSVGChild()) {
-    LayoutRect result;
-    SVGLayoutSupport::MapToVisualRectInAncestorSpace(object, &ancestor,
-                                                     FloatRect(rect), result);
-    return result;
-  }
-
-  LayoutRect result(rect);
-  object.MapToVisualRectInAncestorSpace(&ancestor, result);
-  return result;
-}
-
 // If needed, exclude composited layer's subpixel accumulation to avoid full
 // layer raster invalidations during animation with subpixels.
 // See crbug.com/833083 for details.
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 232eec3..4b864024 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
@@ -151,6 +151,17 @@
 
   bool PropertyChanged() const { return property_changed_; }
   bool PropertyAddedOrRemoved() const { return property_added_or_removed_; }
+  bool HasIsolationNodes() const {
+    // All or nothing check on the isolation nodes.
+    DCHECK(!properties_ ||
+           (properties_->TransformIsolationNode() &&
+            properties_->ClipIsolationNode() &&
+            properties_->EffectIsolationNode()) ||
+           (!properties_->TransformIsolationNode() &&
+            !properties_->ClipIsolationNode() &&
+            !properties_->EffectIsolationNode()));
+    return properties_ && properties_->TransformIsolationNode();
+  }
 
  private:
   ALWAYS_INLINE void UpdatePaintOffset();
@@ -2925,21 +2936,26 @@
   bool property_changed = false;
   bool property_added_or_removed = false;
   auto* fragment_data = &object_.GetMutableForPainting().FirstFragment();
+  // For now, only consider single fragment elements as possible isolation
+  // boundaries.
+  // TODO(crbug.com/890932): See if this is needed.
+  bool is_isolated = context_.fragments.size() == 1u;
   for (auto& fragment_context : context_.fragments) {
     FragmentPaintPropertyTreeBuilder builder(object_, context_,
                                              fragment_context, *fragment_data);
+    // The element establishes an isolation boundary if it has isolation nodes
+    // before and after updating the children. In other words, if it didn't have
+    // isolation nodes previously then we still want to do a subtree walk. If it
+    // now doesn't have isolation nodes, then of course it is also not isolated.
+    is_isolated &= builder.HasIsolationNodes();
     builder.UpdateForChildren();
+    is_isolated &= builder.HasIsolationNodes();
+
     property_changed |= builder.PropertyChanged();
     property_added_or_removed |= builder.PropertyAddedOrRemoved();
     fragment_data = fragment_data->NextFragment();
   }
   DCHECK(!fragment_data);
-  // TODO(vmpstr): With isolation nodes, we can be selective with which reasons
-  // cause a forced subtree update. We'll likely need to propagate these reasons
-  // via |context_| so that children can also clear this flag. For example, with
-  // "container chain may change" reason, the child might fully contains all
-  // out of flow elements (before and after the property tree update), which
-  // means it can clear that reason and possibly skip the subtree update.
   if (object_.SubtreePaintPropertyUpdateReasons() !=
       static_cast<unsigned>(SubtreePaintPropertyUpdateReason::kNone)) {
     if (AreSubtreeUpdateReasonsIsolationPiercing(
@@ -2956,6 +2972,11 @@
   if (object_.CanContainFixedPositionObjects())
     context_.container_for_fixed_position = &object_;
 
+  if (is_isolated) {
+    context_.force_subtree_update_reasons &=
+        ~PaintPropertyTreeBuilderContext::kSubtreeUpdateIsolationBlocked;
+  }
+
   // We need to update property tree states of paint chunks.
   if (property_added_or_removed)
     context_.painting_layer->SetNeedsRepaint();
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 ddf64014..e0e178b 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
@@ -266,9 +266,6 @@
   paint_layer.SetPreviousPaintPhaseDescendantOutlinesEmpty(false);
   paint_layer.SetPreviousPaintPhaseFloatEmpty(false);
   paint_layer.SetPreviousPaintPhaseDescendantBlockBackgroundsEmpty(false);
-  // TODO(vmpstr): Investigate if this is required across an isolation boundary.
-  context.paint_invalidator_context.subtree_flags |=
-      PaintInvalidatorContext::kSubtreeVisualRectUpdate;
 }
 
 bool PrePaintTreeWalk::NeedsTreeBuilderContextUpdate(
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
index 360d4d4..5c7404e 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
@@ -4,6 +4,11 @@
 
 #include "third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h"
 
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_trusted_html.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_trusted_script.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_trusted_script_url.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_trusted_url.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -59,6 +64,51 @@
   return policyNames;
 }
 
+const WrapperTypeInfo*
+TrustedTypePolicyFactory::GetWrapperTypeInfoFromScriptValue(
+    ScriptState* script_state,
+    const ScriptValue& script_value) {
+  v8::Local<v8::Value> value = script_value.V8Value();
+  v8::Isolate* isolate = script_state->GetIsolate();
+  if (value.IsEmpty() || !value->IsObject() ||
+      !V8DOMWrapper::IsWrapper(isolate, value))
+    return nullptr;
+  v8::Local<v8::Object> object = script_value.V8Value()->ToObject(isolate);
+  return ToWrapperTypeInfo(object);
+}
+
+bool TrustedTypePolicyFactory::isHTML(ScriptState* script_state,
+                                      const ScriptValue& script_value) {
+  const WrapperTypeInfo* wrapper_type_info =
+      GetWrapperTypeInfoFromScriptValue(script_state, script_value);
+  return wrapper_type_info &&
+         wrapper_type_info->Equals(&V8TrustedHTML::wrapperTypeInfo);
+}
+
+bool TrustedTypePolicyFactory::isScript(ScriptState* script_state,
+                                        const ScriptValue& script_value) {
+  const WrapperTypeInfo* wrapper_type_info =
+      GetWrapperTypeInfoFromScriptValue(script_state, script_value);
+  return wrapper_type_info &&
+         wrapper_type_info->Equals(&V8TrustedScript::wrapperTypeInfo);
+}
+
+bool TrustedTypePolicyFactory::isScriptURL(ScriptState* script_state,
+                                           const ScriptValue& script_value) {
+  const WrapperTypeInfo* wrapper_type_info =
+      GetWrapperTypeInfoFromScriptValue(script_state, script_value);
+  return wrapper_type_info &&
+         wrapper_type_info->Equals(&V8TrustedScriptURL::wrapperTypeInfo);
+}
+
+bool TrustedTypePolicyFactory::isURL(ScriptState* script_state,
+                                     const ScriptValue& script_value) {
+  const WrapperTypeInfo* wrapper_type_info =
+      GetWrapperTypeInfoFromScriptValue(script_state, script_value);
+  return wrapper_type_info &&
+         wrapper_type_info->Equals(&V8TrustedURL::wrapperTypeInfo);
+}
+
 void TrustedTypePolicyFactory::Trace(blink::Visitor* visitor) {
   ScriptWrappable::Trace(visitor);
   DOMWindowClient::Trace(visitor);
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h
index 35a63133..7ac03691 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h
@@ -16,6 +16,8 @@
 
 class ExceptionState;
 class LocalFrame;
+class ScriptState;
+class ScriptValue;
 class TrustedTypePolicy;
 
 class CORE_EXPORT TrustedTypePolicyFactory final : public ScriptWrappable,
@@ -28,6 +30,7 @@
     return new TrustedTypePolicyFactory(frame);
   }
 
+  // TrustedTypePolicyFactory.idl
   TrustedTypePolicy* createPolicy(const String&,
                                   const TrustedTypePolicyOptions&,
                                   bool exposed,
@@ -37,11 +40,19 @@
 
   Vector<String> getPolicyNames() const;
 
+  bool isHTML(ScriptState*, const ScriptValue&);
+  bool isScript(ScriptState*, const ScriptValue&);
+  bool isScriptURL(ScriptState*, const ScriptValue&);
+  bool isURL(ScriptState*, const ScriptValue&);
+
   void Trace(blink::Visitor*) override;
 
  private:
   explicit TrustedTypePolicyFactory(LocalFrame*);
 
+  const WrapperTypeInfo* GetWrapperTypeInfoFromScriptValue(ScriptState*,
+                                                           const ScriptValue&);
+
   HeapHashMap<String, Member<TrustedTypePolicy>> policy_map_;
 };
 
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl
index 8b6a3db..df5289d 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl
@@ -12,4 +12,8 @@
     TrustedTypePolicy getExposedPolicy(DOMString policyName);
     // All the policy object names that have been created
     [Affects=Nothing] sequence<DOMString> getPolicyNames();
+    [CallWith=ScriptState] boolean isHTML(object? checkedObject);
+    [CallWith=ScriptState] boolean isScript(object? checkedObject);
+    [CallWith=ScriptState] boolean isScriptURL(object? checkedObject);
+    [CallWith=ScriptState] boolean isURL(object? checkedObject);
 };
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js b/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js
index 5ad5178..9b28ddf 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js
@@ -780,7 +780,8 @@
    * @return {boolean}
    */
   _isFailed() {
-    return (this._request.failed && !this._request.statusCode) || (this._request.statusCode >= 400);
+    return (this._request.failed && !this._request.statusCode) || (this._request.statusCode >= 400) ||
+        (!!this._request.signedExchangeInfo() && !!this._request.signedExchangeInfo().errors);
   }
 
   /**
@@ -957,7 +958,9 @@
     } else if (this._request.fetchedViaServiceWorker) {
       this._setTextAndTitle(cell, Common.UIString('(from ServiceWorker)'));
       cell.classList.add('network-dim-cell');
-    } else if (this._request.redirectSource() && this._request.redirectSource().signedExchangeInfo()) {
+    } else if (
+        this._request.redirectSource() && this._request.redirectSource().signedExchangeInfo() &&
+        !this._request.redirectSource().signedExchangeInfo().errors) {
       this._setTextAndTitle(cell, Common.UIString('(from signed-exchange)'));
       cell.classList.add('network-dim-cell');
     } else if (this._request.cached()) {
diff --git a/third_party/blink/renderer/devtools/front_end/network/RequestHeadersView.js b/third_party/blink/renderer/devtools/front_end/network/RequestHeadersView.js
index 520161f9..b69345c7 100644
--- a/third_party/blink/renderer/devtools/front_end/network/RequestHeadersView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/RequestHeadersView.js
@@ -399,7 +399,9 @@
       } else if (this._request.fetchedViaServiceWorker) {
         statusText += ' ' + Common.UIString('(from ServiceWorker)');
         statusTextElement.classList.add('status-from-cache');
-      } else if (this._request.redirectSource() && this._request.redirectSource().signedExchangeInfo()) {
+      } else if (
+          this._request.redirectSource() && this._request.redirectSource().signedExchangeInfo() &&
+          !this._request.redirectSource().signedExchangeInfo().errors) {
         statusText += ' ' + Common.UIString('(from signed-exchange)');
         statusTextElement.classList.add('status-from-cache');
       } else if (this._request.cached()) {
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/CPUProfileDataModel.js b/third_party/blink/renderer/devtools/front_end/sdk/CPUProfileDataModel.js
index adea776..27d4b72 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/CPUProfileDataModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/CPUProfileDataModel.js
@@ -114,6 +114,7 @@
         return !!node.callFrame.url && node.callFrame.url.startsWith('native ');
       return !!node['url'] && node['url'].startsWith('native ');
     }
+
     /**
      * @param {!Array<!Protocol.Profiler.ProfileNode>} nodes
      */
@@ -130,12 +131,29 @@
           parentNode.children = [node.id];
       }
     }
+
+    /**
+     * @param {!Array<!Protocol.Profiler.ProfileNode>} nodes
+     * @param {!Array<number>|undefined} samples
+     */
+    function buildHitCountFromSamples(nodes, samples) {
+      if (typeof(nodes[0].hitCount) === 'number')
+        return;
+      console.assert(samples, 'Error: Neither hitCount nor samples are present in profile.');
+      for (let i = 0; i < nodes.length; ++i)
+        nodes[i].hitCount = 0;
+      for (let i = 0; i < samples.length; ++i)
+        ++nodeByIdMap.get(samples[i]).hitCount;
+    }
+
     /** @type {!Map<number, !Protocol.Profiler.ProfileNode>} */
     const nodeByIdMap = new Map();
     for (let i = 0; i < nodes.length; ++i) {
       const node = nodes[i];
       nodeByIdMap.set(node.id, node);
     }
+
+    buildHitCountFromSamples(nodes, this.samples);
     buildChildrenFromParents(nodes);
     this.totalHitCount = nodes.reduce((acc, node) => acc + node.hitCount, 0);
     const sampleTime = (this.profileEndTime - this.profileStartTime) / this.totalHitCount;
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index a45ad319..20c632ba 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -94,6 +94,7 @@
     "//third_party/blink/renderer/modules/audio_output_devices",
     "//third_party/blink/renderer/modules/background_fetch",
     "//third_party/blink/renderer/modules/background_sync",
+    "//third_party/blink/renderer/modules/badging",
     "//third_party/blink/renderer/modules/battery",
     "//third_party/blink/renderer/modules/beacon",
     "//third_party/blink/renderer/modules/bluetooth",
diff --git a/third_party/blink/renderer/modules/badging/BUILD.gn b/third_party/blink/renderer/modules/badging/BUILD.gn
new file mode 100644
index 0000000..8af60813
--- /dev/null
+++ b/third_party/blink/renderer/modules/badging/BUILD.gn
@@ -0,0 +1,12 @@
+# 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("//third_party/blink/renderer/modules/modules.gni")
+
+blink_modules_sources("badging") {
+  sources = [
+    "badge.cc",
+    "badge.h",
+  ]
+}
diff --git a/third_party/blink/renderer/modules/badging/OWNERS b/third_party/blink/renderer/modules/badging/OWNERS
new file mode 100644
index 0000000..c8216ae
--- /dev/null
+++ b/third_party/blink/renderer/modules/badging/OWNERS
@@ -0,0 +1,5 @@
+estevenson@chromium.org
+mgiuca@chromium.org
+
+# TEAM: apps-dev@chromium.org
+# COMPONENT: Platform>Apps
diff --git a/third_party/blink/renderer/modules/badging/README.md b/third_party/blink/renderer/modules/badging/README.md
new file mode 100644
index 0000000..ad293263
--- /dev/null
+++ b/third_party/blink/renderer/modules/badging/README.md
@@ -0,0 +1,24 @@
+# Badging
+
+This module contains the implementation of the [Badging API]. The implementation
+is under [active development].
+
+[Badging API]: https://github.com/WICG/badging
+[active development]: https://crbug.com/719176
+
+### API
+
+See the [explainer] for details. The Badge interface is a member on Window
+and exposes two static methods:
+
+[explainer]: https://github.com/WICG/badging/blob/master/explainer.md
+
+* `set(contents)`: Sets the associated app's badge as a "flag" (the argument
+  is ignored).
+* `clear()`: Sets the associated app's badge to nothing.
+
+### Testing
+
+`LayoutTests/badging/*.html` tests that the API accepts/rejects the appropriate
+inputs (with a mock Mojo service). Testing at other layers will be added
+during implementation.
diff --git a/third_party/blink/renderer/modules/badging/badge.cc b/third_party/blink/renderer/modules/badging/badge.cc
new file mode 100644
index 0000000..e751f41
--- /dev/null
+++ b/third_party/blink/renderer/modules/badging/badge.cc
@@ -0,0 +1,84 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file
+
+#include "third_party/blink/renderer/modules/badging/badge.h"
+
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/renderer/bindings/modules/v8/usv_string_or_long.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+
+namespace blink {
+
+const char Badge::kSupplementName[] = "Badge";
+
+Badge::~Badge() = default;
+
+// static
+Badge* Badge::From(ExecutionContext* context) {
+  Badge* supplement = Supplement<ExecutionContext>::From<Badge>(context);
+  if (!supplement) {
+    supplement = new Badge(context);
+    ProvideTo(*context, supplement);
+  }
+  return supplement;
+}
+
+// static
+void Badge::set(ScriptState* script_state, ExceptionState& exception_state) {
+  BadgeFromState(script_state)->Set(nullptr, exception_state);
+}
+
+// static
+void Badge::set(ScriptState* script_state,
+                USVStringOrLong& contents,
+                ExceptionState& exception_state) {
+  BadgeFromState(script_state)->Set(&contents, exception_state);
+}
+
+// static
+void Badge::clear(ScriptState* script_state) {
+  BadgeFromState(script_state)->Clear();
+}
+
+void Badge::Set(USVStringOrLong* contents, ExceptionState& exception_state) {
+  if (contents) {
+    if (contents->IsLong() && contents->GetAsLong() <= 0) {
+      exception_state.ThrowTypeError("Badge contents should be > 0");
+      return;
+    }
+    if (contents->IsUSVString() && contents->GetAsUSVString() == "") {
+      exception_state.ThrowTypeError(
+          "Badge contents cannot be the empty string");
+      return;
+    }
+  }
+  // TODO(estevenson): Add support for sending badge contents to the browser.
+  // TODO(estevenson): Verify that contents is a single grapheme cluster.
+  badge_service_->SetBadge();
+}
+
+void Badge::Clear() {
+  badge_service_->ClearBadge();
+}
+
+void Badge::Trace(blink::Visitor* visitor) {
+  Supplement<ExecutionContext>::Trace(visitor);
+  ScriptWrappable::Trace(visitor);
+}
+
+Badge::Badge(ExecutionContext* context) {
+  context->GetInterfaceProvider()->GetInterface(
+      mojo::MakeRequest(&badge_service_));
+  DCHECK(badge_service_);
+}
+
+// static
+Badge* Badge::BadgeFromState(ScriptState* script_state) {
+  return Badge::From(ExecutionContext::From(script_state));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/badging/badge.h b/third_party/blink/renderer/modules/badging/badge.h
new file mode 100644
index 0000000..c6a742a
--- /dev/null
+++ b/third_party/blink/renderer/modules/badging/badge.h
@@ -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
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BADGING_BADGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_BADGING_BADGE_H_
+
+#include "third_party/blink/public/platform/modules/badging/badging.mojom-blink.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/supplementable.h"
+
+namespace blink {
+
+class ExceptionState;
+class ExecutionContext;
+class ScriptState;
+class USVStringOrLong;
+
+class Badge final : public ScriptWrappable,
+                    public Supplement<ExecutionContext> {
+  DEFINE_WRAPPERTYPEINFO();
+  USING_GARBAGE_COLLECTED_MIXIN(Badge);
+
+ public:
+  static const char kSupplementName[];
+
+  static Badge* From(ExecutionContext*);
+
+  ~Badge() override;
+
+  // Badge IDL interface.
+  static void set(ScriptState*, ExceptionState&);
+  static void set(ScriptState*, USVStringOrLong&, ExceptionState&);
+  static void clear(ScriptState*);
+
+  void Set(USVStringOrLong*, ExceptionState&);
+  void Clear();
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  explicit Badge(ExecutionContext*);
+
+  static Badge* BadgeFromState(ScriptState* script_state);
+
+  blink::mojom::blink::BadgeServicePtr badge_service_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_BADGING_BADGE_H_
diff --git a/third_party/blink/renderer/modules/badging/badge.idl b/third_party/blink/renderer/modules/badging/badge.idl
new file mode 100644
index 0000000..3c5ec519
--- /dev/null
+++ b/third_party/blink/renderer/modules/badging/badge.idl
@@ -0,0 +1,16 @@
+// 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.
+
+// TODO(estevenson): Add link to spec once complete.
+// https://github.com/WICG/badging/blob/master/explainer.md
+
+[
+    RuntimeEnabled=Badging,
+    // TODO(estevenson): Expose the Badge interface to Worker.
+    Exposed=Window
+] interface Badge {
+  [CallWith=ScriptState, RaisesException]
+  static void set(optional (USVString or long) contents);
+  [CallWith=ScriptState] static void clear();
+};
diff --git a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
index a70c65d..68cb989 100644
--- a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
+++ b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
@@ -601,7 +601,7 @@
   width: 100px;
   transition: width 0.3s;
   margin: 0;
-  padding: 22px 0 22px 0; /* (48px button panel height - 4px slider height) / 2 for top/bottom */
+  padding: 22px 0; /* (48px button panel height - 4px slider height) / 2  */
   background: transparent;
   /* This prevents layout issues in quirks mode. */
   box-sizing: unset !important;
@@ -615,6 +615,11 @@
   transition: width 0.3s ease, opacity 0.28s step-end;
 }
 
+video::-webkit-media-controls.sizing-medium input[pseudo="-webkit-media-controls-volume-slider" i],
+video::-webkit-media-controls.sizing-large input[pseudo="-webkit-media-controls-volume-slider" i] {
+  padding: 30px 0; /* (64px button panel height - 4px slider height) / 2  */
+}
+
 /**
  * Time Display
  */
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index c8c2caa..caa18af 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -73,6 +73,7 @@
           "background_fetch/background_fetch_update_ui_event.idl",
           "background_sync/sync_event.idl",
           "background_sync/sync_manager.idl",
+          "badging/badge.idl",
           "battery/battery_manager.idl",
           "bluetooth/bluetooth.idl",
           "bluetooth/bluetooth_characteristic_properties.idl",
diff --git a/third_party/blink/renderer/modules/speech/speech_synthesis_error_event.idl b/third_party/blink/renderer/modules/speech/speech_synthesis_error_event.idl
index 03ba7d7..ec5d780 100644
--- a/third_party/blink/renderer/modules/speech/speech_synthesis_error_event.idl
+++ b/third_party/blink/renderer/modules/speech/speech_synthesis_error_event.idl
@@ -18,7 +18,7 @@
     "not-allowed",
 };
 
-// https://w3c.github.io/speech-api/#enumdef-speechsynthesiserrorcode
+// https://w3c.github.io/speech-api/#speechsynthesiserrorevent
 [
     Exposed=Window,
     Constructor(DOMString type, SpeechSynthesisErrorEventInit eventInitDict),
diff --git a/third_party/blink/renderer/modules/speech/speech_synthesis_error_event_init.idl b/third_party/blink/renderer/modules/speech/speech_synthesis_error_event_init.idl
index bb22c8c7..d6d201e6 100644
--- a/third_party/blink/renderer/modules/speech/speech_synthesis_error_event_init.idl
+++ b/third_party/blink/renderer/modules/speech/speech_synthesis_error_event_init.idl
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// https://w3c.github.io/speech-api/#dictdef-speechsynthesiseventinit
 dictionary SpeechSynthesisErrorEventInit : SpeechSynthesisEventInit {
     required SpeechSynthesisErrorCode error;
 };
diff --git a/third_party/blink/renderer/modules/speech/speech_synthesis_event_init.idl b/third_party/blink/renderer/modules/speech/speech_synthesis_event_init.idl
index 741c408c..0c1386f 100644
--- a/third_party/blink/renderer/modules/speech/speech_synthesis_event_init.idl
+++ b/third_party/blink/renderer/modules/speech/speech_synthesis_event_init.idl
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// https://w3c.github.io/speech-api/#dictdef-speechsynthesiseventinit
 dictionary SpeechSynthesisEventInit : EventInit {
     required SpeechSynthesisUtterance utterance;
     unsigned long charIndex = 0;
diff --git a/third_party/blink/renderer/modules/webaudio/convolver_node.cc b/third_party/blink/renderer/modules/webaudio/convolver_node.cc
index 372008b..3e6b0b4 100644
--- a/third_party/blink/renderer/modules/webaudio/convolver_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/convolver_node.cc
@@ -44,9 +44,7 @@
 namespace blink {
 
 ConvolverHandler::ConvolverHandler(AudioNode& node, float sample_rate)
-    : AudioHandler(kNodeTypeConvolver, node, sample_rate),
-      normalize_(true),
-      buffer_has_been_set_(false) {
+    : AudioHandler(kNodeTypeConvolver, node, sample_rate), normalize_(true) {
   AddInput();
   AddOutput(1);
 
@@ -102,15 +100,6 @@
     return;
   }
 
-  if (buffer && buffer_has_been_set_) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Cannot set buffer to non-null after it "
-                                      "has been already been set to a non-null "
-                                      "buffer");
-    return;
-  }
-
-  buffer_has_been_set_ = true;
   if (buffer->sampleRate() != Context()->sampleRate()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kNotSupportedError,
diff --git a/third_party/blink/renderer/modules/webaudio/convolver_node.h b/third_party/blink/renderer/modules/webaudio/convolver_node.h
index fa8659a..fc11d61 100644
--- a/third_party/blink/renderer/modules/webaudio/convolver_node.h
+++ b/third_party/blink/renderer/modules/webaudio/convolver_node.h
@@ -86,10 +86,6 @@
   // Normalize the impulse response or not. Must default to true.
   bool normalize_;
 
-  // True if the |buffer| attribute has ever been set to a non-null
-  // value.  Defaults to false.
-  bool buffer_has_been_set_;
-
   FRIEND_TEST_ALL_PREFIXES(ConvolverNodeTest, ReverbLifetime);
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
index 1a4002c..ba64fc08 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
@@ -79,6 +79,13 @@
       static_cast<int>(ColorSpaceUtilities::GetColorSpaceGamut(color_profile)));
 }
 
+void BitmapImageMetrics::CountJpegColorSpace(JpegColorSpace color_space) {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(
+      EnumerationHistogram, color_space_histogram,
+      ("Blink.ImageDecoders.Jpeg.ColorSpace", JpegColorSpace::kMaxValue));
+  color_space_histogram.Count(color_space);
+}
+
 BitmapImageMetrics::Gamma BitmapImageMetrics::GetColorSpaceGamma(
     const skcms_ICCProfile* color_profile) {
   Gamma gamma = kGammaNull;
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
index dbd9b0b..09833bd 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
@@ -19,7 +19,8 @@
 
  public:
   // Values synced with 'DecodedImageType' in
-  // src/tools/metrics/histograms/histograms.xml
+  // src/tools/metrics/histograms/enums.xml. These values are persisted to logs.
+  // Entries should not be renumbered and numeric values should never be reused.
   enum DecodedImageType {
     kImageUnknown = 0,
     kImageJPEG = 1,
@@ -31,8 +32,10 @@
     kDecodedImageTypeEnumEnd = kImageBMP + 1
   };
 
+  // Values synced with 'Gamma' in src/tools/metrics/histograms/enums.xml. These
+  // values are persisted to logs. Entries should not be renumbered and numeric
+  // values should never be reused.
   enum Gamma {
-    // Values synced with 'Gamma' in src/tools/metrics/histograms/histograms.xml
     kGammaLinear = 0,
     kGammaSRGB = 1,
     kGamma2Dot2 = 2,
@@ -47,6 +50,26 @@
     kGammaEnd = kGammaNamed + 1,
   };
 
+  // Categories for the JPEG color space histogram. Synced with 'JpegColorSpace'
+  // in src/tools/metrics/histograms/enums.xml. These values are persisted to
+  // logs. Entries should not be renumbered and numeric values should never be
+  // reused.
+  enum JpegColorSpace {
+    kUnknown = 0,
+    kGrayscale = 1,
+    kRGB = 2,
+    kCMYK = 3,
+    kYCCK = 4,
+    kYCbCr410 = 5,
+    kYCbCr411 = 6,
+    kYCbCr420 = 7,
+    kYCbCr422 = 8,
+    kYCbCr440 = 9,
+    kYCbCr444 = 10,
+    kYCbCrOther = 11,
+    kMaxValue = kYCbCrOther,
+  };
+
   static void CountDecodedImageType(const String& type);
   static void CountImageOrientation(const ImageOrientationEnum);
   // Report the JPEG compression density in 0.01 bits per pixel for an image
@@ -54,6 +77,7 @@
   static void CountImageJpegDensity(int image_min_side,
                                     int64_t density_centi_bpp);
   static void CountImageGammaAndGamut(const skcms_ICCProfile*);
+  static void CountJpegColorSpace(JpegColorSpace color_space);
 
  private:
   static Gamma GetColorSpaceGamma(const skcms_ICCProfile*);
diff --git a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
index 5792b23..e2287ac 100644
--- a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
@@ -41,6 +41,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "build/build_config.h"
 #include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/platform_instrumentation.h"
 
@@ -100,6 +101,64 @@
       base::saturated_cast<base::HistogramBase::Sample>(size.Area()));
 }
 
+// Extracts the JPEG color space of an image for UMA purposes given |info| which
+// is assumed to have gone through a jpeg_read_header(). When the color space is
+// YCbCr, we also extract the chroma subsampling. The caveat is that the
+// extracted color space is really libjpeg_turbo's guess. According to
+// libjpeg.txt, "[t]he JPEG color space, unfortunately, is something of a guess
+// since the JPEG standard proper does not provide a way to record it. In
+// practice most files adhere to the JFIF or Adobe conventions, and the decoder
+// will recognize these correctly."
+blink::BitmapImageMetrics::JpegColorSpace ExtractUMAJpegColorSpace(
+    const jpeg_decompress_struct& info) {
+  switch (info.jpeg_color_space) {
+    case JCS_GRAYSCALE:
+      return blink::BitmapImageMetrics::JpegColorSpace::kGrayscale;
+    case JCS_RGB:
+      return blink::BitmapImageMetrics::JpegColorSpace::kRGB;
+    case JCS_CMYK:
+      return blink::BitmapImageMetrics::JpegColorSpace::kCMYK;
+    case JCS_YCCK:
+      return blink::BitmapImageMetrics::JpegColorSpace::kYCCK;
+    case JCS_YCbCr:
+      // The following logic is mostly reused from YuvSubsampling(). However,
+      // here we use |info.comp_info| instead of |info.cur_comp_info| to read
+      // the components from the SOF instead of the first scan. We also don't
+      // care about |info.scale_denom|.
+      // TODO: can we use this same logic in YuvSubsampling()?
+      if (info.num_components == 3 && info.comp_info &&
+          info.comp_info[1].h_samp_factor == 1 &&
+          info.comp_info[1].v_samp_factor == 1 &&
+          info.comp_info[2].h_samp_factor == 1 &&
+          info.comp_info[2].v_samp_factor == 1) {
+        const int h = info.comp_info[0].h_samp_factor;
+        const int v = info.comp_info[0].v_samp_factor;
+        if (v == 1) {
+          switch (h) {
+            case 1:
+              return blink::BitmapImageMetrics::JpegColorSpace::kYCbCr444;
+            case 2:
+              return blink::BitmapImageMetrics::JpegColorSpace::kYCbCr422;
+            case 4:
+              return blink::BitmapImageMetrics::JpegColorSpace::kYCbCr411;
+          }
+        } else if (v == 2) {
+          switch (h) {
+            case 1:
+              return blink::BitmapImageMetrics::JpegColorSpace::kYCbCr440;
+            case 2:
+              return blink::BitmapImageMetrics::JpegColorSpace::kYCbCr420;
+            case 4:
+              return blink::BitmapImageMetrics::JpegColorSpace::kYCbCr410;
+          }
+        }
+      }
+      return blink::BitmapImageMetrics::JpegColorSpace::kYCbCrOther;
+    default:
+      return blink::BitmapImageMetrics::JpegColorSpace::kUnknown;
+  }
+}
+
 }  // namespace
 
 namespace blink {
@@ -653,6 +712,8 @@
       case JPEG_DONE:
         // Finish decompression.
         RecordJpegImageArea(decoder_->Size());
+        BitmapImageMetrics::CountJpegColorSpace(
+            ExtractUMAJpegColorSpace(info_));
         return jpeg_finish_decompress(&info_);
     }
 
diff --git a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
index bbc8ebf1..899618c 100644
--- a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
@@ -38,9 +38,11 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_data.h"
 #include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
 #include "third_party/blink/renderer/platform/image-decoders/image_animation.h"
 #include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
 #include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
 #include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h"
 
 namespace blink {
@@ -384,4 +386,90 @@
   }
 }
 
+struct ColorSpaceUMATestParam {
+  std::string file;
+  bool expected_success;
+  BitmapImageMetrics::JpegColorSpace expected_color_space;
+};
+
+class ColorSpaceUMATest
+    : public ::testing::TestWithParam<ColorSpaceUMATestParam> {};
+
+// Tests that the JPEG color space/subsampling is recorded correctly as a UMA
+// for a variety of images. When the decode fails, no UMA should be recorded.
+TEST_P(ColorSpaceUMATest, CorrectColorSpaceRecorded) {
+  HistogramTester histogram_tester;
+  scoped_refptr<SharedBuffer> data =
+      ReadFile(("/images/resources/" + GetParam().file).c_str());
+  ASSERT_TRUE(data);
+
+  std::unique_ptr<ImageDecoder> decoder = CreateJPEGDecoder();
+  decoder->SetData(data.get(), true);
+
+  ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+  ASSERT_TRUE(frame);
+
+  if (GetParam().expected_success) {
+    ASSERT_FALSE(decoder->Failed());
+    histogram_tester.ExpectUniqueSample("Blink.ImageDecoders.Jpeg.ColorSpace",
+                                        GetParam().expected_color_space, 1);
+  } else {
+    ASSERT_TRUE(decoder->Failed());
+    histogram_tester.ExpectTotalCount("Blink.ImageDecoders.Jpeg.ColorSpace", 0);
+  }
+}
+
+const ColorSpaceUMATest::ParamType kColorSpaceUMATestParams[] = {
+    {"cs-uma-grayscale.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kGrayscale},
+    {"cs-uma-rgb.jpg", true, BitmapImageMetrics::JpegColorSpace::kRGB},
+    // Each component is in a separate plane. Should not make a difference.
+    {"cs-uma-rgb-non-interleaved.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kRGB},
+    {"cs-uma-cmyk.jpg", true, BitmapImageMetrics::JpegColorSpace::kCMYK},
+    // 4 components/no markers, so we expect libjpeg_turbo to guess CMYK.
+    {"cs-uma-cmyk-no-jfif-or-adobe-markers.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kCMYK},
+    // 4 components are not legal in JFIF, but we expect libjpeg_turbo to guess
+    // CMYK.
+    {"cs-uma-cmyk-jfif-marker.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kCMYK},
+    {"cs-uma-ycck.jpg", true, BitmapImageMetrics::JpegColorSpace::kYCCK},
+    // Contains CMYK data but uses a bad Adobe color transform, so libjpeg_turbo
+    // will guess YCCK.
+    {"cs-uma-cmyk-unknown-transform.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kYCCK},
+    {"cs-uma-ycbcr-410.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kYCbCr410},
+    {"cs-uma-ycbcr-411.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kYCbCr411},
+    {"cs-uma-ycbcr-420.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kYCbCr420},
+    // Each component is in a separate plane. Should not make a difference.
+    {"cs-uma-ycbcr-420-non-interleaved.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kYCbCr420},
+    // 3 components/both JFIF and Adobe markers, so we expect libjpeg_turbo to
+    // guess YCbCr.
+    {"cs-uma-ycbcr-420-both-jfif-adobe.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kYCbCr420},
+    {"cs-uma-ycbcr-422.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kYCbCr422},
+    {"cs-uma-ycbcr-440.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kYCbCr440},
+    {"cs-uma-ycbcr-444.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kYCbCr444},
+    // Contains RGB data but uses a bad Adobe color transform, so libjpeg_turbo
+    // will guess YCbCr.
+    {"cs-uma-rgb-unknown-transform.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kYCbCr444},
+    {"cs-uma-ycbcr-other.jpg", true,
+     BitmapImageMetrics::JpegColorSpace::kYCbCrOther},
+    // Contains only 2 components. We expect the decode to fail and not produce
+    // any samples.
+    {"cs-uma-two-channels-jfif-marker.jpg", false}};
+
+INSTANTIATE_TEST_CASE_P(JPEGImageDecoderTest,
+                        ColorSpaceUMATest,
+                        ::testing::ValuesIn(kColorSpaceUMATestParams));
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 07e6481..a09f0034 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -121,6 +121,10 @@
       status: "stable",
     },
     {
+      name: "Badging",
+      status: "test",
+    },
+    {
       name: "BlinkGenPropertyTrees",
     },
     {
diff --git a/third_party/fuchsia-sdk/BUILD.gn b/third_party/fuchsia-sdk/BUILD.gn
index 704e8f4..dd04e9f 100644
--- a/third_party/fuchsia-sdk/BUILD.gn
+++ b/third_party/fuchsia-sdk/BUILD.gn
@@ -229,9 +229,9 @@
 
   sources = [
     "audio.fidl",
+    "audio_capturer.fidl",
     "audio_device_enumerator.fidl",
-    "audio_in.fidl",
-    "audio_out.fidl",
+    "audio_renderer.fidl",
     "gain_control.fidl",
     "stream.fidl",
     "stream_type.fidl",
@@ -255,6 +255,19 @@
     "net_address.fidl",
     "netstack.fidl",
   ]
+
+  deps = [
+    ":ethernet",
+  ]
+}
+
+fuchsia_sdk_fidl_pkg("ethernet") {
+  namespace = "zircon"
+  namespace_path = "zircon"
+
+  sources = [
+    "ethernet.fidl",
+  ]
 }
 
 fuchsia_sdk_fidl_pkg("oldhttp") {
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index 44d0f3f..ce8f826 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -1105,7 +1105,6 @@
   for name in object_paths_by_name:
     if name.startswith(PREFIX):
       pak_id = int(name[id_start_idx:id_end_idx])
-      logging.info('PAK ID: %d', pak_id)
       object_paths_by_pak_id[pak_id] = object_paths_by_name[name]
   return object_paths_by_pak_id
 
diff --git a/tools/gdb/gdb_chrome.py b/tools/gdb/gdb_chrome.py
index 4df02d6..fdbf6ff 100644
--- a/tools/gdb/gdb_chrome.py
+++ b/tools/gdb/gdb_chrome.py
@@ -88,7 +88,7 @@
 
 class String16Printer(StringPrinter):
     def to_string(self):
-        return webkit.ustring_to_string(self.val['_M_dataplus']['_M_p'])
+        return blink.ustring_to_string(self.val['_M_dataplus']['_M_p'])
 pp_set.add_printer(
     'string16',
     '^string16|std::basic_string<(unsigned short|base::char16).*>$',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 86eaae3..6d3c34fb 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -1419,6 +1419,13 @@
   <int value="17" label="Window Exit Presentation Mode"/>
 </enum>
 
+<enum name="AppLifecycleEvent">
+  <int value="0" label="Enter Foreground"/>
+  <int value="1" label="Enter Background"/>
+  <int value="2" label="Clear All"/>
+  <int value="3" label="Initialize"/>
+</enum>
+
 <enum name="AppListAppMovingType">
   <int value="0" label="MOVE_INTO_FOLDER"/>
   <int value="1" label="MOVE_OUT_OF_FOLDER"/>
@@ -26648,6 +26655,24 @@
   </int>
 </enum>
 
+<enum name="JpegColorSpace">
+  <int value="0" label="Unknown color space">
+    This is the bucket that counts the images that did not fall under any of the
+    other categories.
+  </int>
+  <int value="1" label="Grayscale"/>
+  <int value="2" label="RGB"/>
+  <int value="3" label="CMYK"/>
+  <int value="4" label="YCCK"/>
+  <int value="5" label="YCbCr 4:1:0"/>
+  <int value="6" label="YCbCr 4:1:1"/>
+  <int value="7" label="YCbCr 4:2:0"/>
+  <int value="8" label="YCbCr 4:2:2"/>
+  <int value="9" label="YCbCr 4:4:0"/>
+  <int value="10" label="YCbCr 4:4:4"/>
+  <int value="11" label="YCbCr with other subsampling"/>
+</enum>
+
 <enum name="JumplisticonsDeleteCategory">
   <int value="0" label="Success"/>
   <int value="1" label="Fail as directory name length exceeds MAX_PATH"/>
@@ -49100,6 +49125,7 @@
   <int value="16" label="Decision made to create infobar on Android"/>
   <int value="17" label="Show omnibar icon to the user"/>
   <int value="18" label="Suppress infobar but show omnibar icon"/>
+  <int value="19" label="Aborted by CLD and HTML/content language conflict"/>
 </enum>
 
 <enum name="TranslateLanguage">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 56d069a..d696a6d 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -9091,12 +9091,24 @@
 <histogram name="Blink.ImageDecoders.Jpeg.Area" units="pixels">
   <owner>andrescj@chromium.org</owner>
   <summary>
-    Number of pixels in a decoded JPEG image. Recorded after decoding is done.
-    Tracking the image area may include degenerate cases, e.g., an image with
-    area = 10000 and dimensions = 1x10000. However, it is reasonable to assume
-    these cases are relatively rare and the area of the image can serve as a
-    proxy of how useful a decode accelerator can be (the larger the area, the
-    better).
+    Number of pixels in a decoded JPEG image. Recorded after decoding is done by
+    Blink's JPEG decoder (as opposed to, possibly, a hardware decode
+    accelerator). Tracking the image area may include degenerate cases, e.g., an
+    image with area = 10000 and dimensions = 1x10000. However, it is reasonable
+    to assume these cases are relatively rare and the area of the image can
+    serve as a proxy of how useful a decode accelerator can be (the larger the
+    area, the better).
+  </summary>
+</histogram>
+
+<histogram name="Blink.ImageDecoders.Jpeg.ColorSpace" enum="JpegColorSpace">
+  <owner>andrescj@chromium.org</owner>
+  <owner>mcasas@chromium.org</owner>
+  <summary>
+    JPEG color space of a decoded image as guessed by libjpeg_turbo. Recorded
+    after decoding is done by Blink's JPEG decoder (as opposed to, possibly, a
+    hardware decode accelerator). When the color space is YCbCr, the chroma
+    subsampling is also recorded.
   </summary>
 </histogram>
 
@@ -14862,6 +14874,17 @@
   </summary>
 </histogram>
 
+<histogram name="ContentSuggestions.Feed.AppLifecycleEvents"
+    enum="AppLifecycleEvent" expires_after="2019-10-01">
+  <owner>pnoland@chromium.org</owner>
+  <owner>fgorski@chromium.org</owner>
+  <summary>
+    Android: count of app lifecycle events reported to the Feed library.
+    Recorded as these events occur; e.g. when Chrome is foregrounded,
+    backgrounded, or the user clears cached browsing data.
+  </summary>
+</histogram>
+
 <histogram name="ContentSuggestions.Feed.NetworkRequestStatusCode"
     enum="CombinedHttpResponseAndNetErrorCode">
   <owner>pnoland@chromium.org</owner>
@@ -35931,6 +35954,14 @@
   </summary>
 </histogram>
 
+<histogram name="History.DatabaseMonthlyHostCountTime" units="ms">
+  <owner>dimich@chromium.org</owner>
+  <summary>
+    Time spent on computing the monthly count of hosts visited. Reported every
+    time computation is performed, which in turn is done approximately weekly.
+  </summary>
+</histogram>
+
 <histogram name="History.DeleteFTSIndexDatabases" expires_after="2018-08-30">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
diff --git a/tools/perf/page_sets/data/system_health_desktop.json b/tools/perf/page_sets/data/system_health_desktop.json
index 217de0f1..4f20030 100644
--- a/tools/perf/page_sets/data/system_health_desktop.json
+++ b/tools/perf/page_sets/data/system_health_desktop.json
@@ -234,6 +234,9 @@
         "multitab:misc:typical24": {
             "DEFAULT": "system_health_desktop_049.wprgo"
         },
+        "multitab:misc:typical24:2018": {
+            "DEFAULT": "system_health_desktop_37bd086ce0.wprgo"
+        },
         "play:media:google_play_music": {
             "DEFAULT": "system_health_desktop_030.wprgo"
         },
diff --git a/tools/perf/page_sets/data/system_health_desktop_37bd086ce0.wprgo.sha1 b/tools/perf/page_sets/data/system_health_desktop_37bd086ce0.wprgo.sha1
new file mode 100644
index 0000000..8fa8bb3
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_desktop_37bd086ce0.wprgo.sha1
@@ -0,0 +1 @@
+37bd086ce08318237ccbea5e7b64bbb0a9d3657e
\ No newline at end of file
diff --git a/tools/perf/page_sets/system_health/multi_tab_stories.py b/tools/perf/page_sets/system_health/multi_tab_stories.py
index fc9a39934..6d71151 100644
--- a/tools/perf/page_sets/system_health/multi_tab_stories.py
+++ b/tools/perf/page_sets/system_health/multi_tab_stories.py
@@ -86,3 +86,60 @@
   ]
   URL = URL_LIST[0]
   SUPPORTED_PLATFORMS = platforms.DESKTOP_ONLY
+
+
+class MultiTabTypical24Story2018(MultiTabStory):
+  """Load 24 different web sites in 24 tabs, then cycle through each tab."""
+  NAME = 'multitab:misc:typical24:2018'
+  TAGS = [story_tags.TABS_SWITCHING, story_tags.YEAR_2018,
+          story_tags.INTERNATIONAL]
+  URL_LIST = [
+      # Why: Top Site Africa
+      'https://www.nairaland.com/?',
+      'https://www.jumia.com.ng',
+
+      # Why: Top Site Asia
+      ('https://activity.alibaba.com/sale/Super-September/'
+       'machinery.html?spm=a2700.8293689.procates.4.46ce65aa0eZF5r'),
+      'https://www.flipkart.com/',
+      ('https://www.indiatimes.com/technology/science-and-future/'
+       'how-spacex-s-trip-around-the-moon-can-make-the-tourists-sick-or-even'
+       '-give-them-a-heart-attack-353365.html'),
+
+      # Why: Top Site Caribbean
+      'https://www.clasificadosonline.com/Miscellaneous.asp',
+      'http://guardian.co.tt/',
+
+      # Why: Top Site Central America
+      'https://www.copaair.com/en/web/us',
+      'http://www.ticotimes.net',
+
+      # Why: Top Site Europe
+      'https://poker.bet365.com/home/ro/',
+      'https://www.asos.com/se/kvinna/?r=1',
+      'https://www.thesun.co.uk/',
+
+      # Why: Top Site Middle East
+      'https://www.irib.ir/',
+      'https://www.qatarliving.com',
+      'https://www.aljazeera.com/',
+
+      # Why: Top Site North America
+      'https://www.nih.gov/',
+      ('https://www.walmart.com/browse/2637_615760?cat_id=2637_615760_1088766_'
+       '1054039&povid=615760+%7C+2018-08-31+%7C+Kids%20Costumes%20POV'),
+      'https://weather.com/',
+
+      # Why: Top Site Oceania
+      'http://www.abc.net.au/',
+      ('https://www.seek.com.au/jobs-in-consulting-strategy?highpay=True&'
+       'salaryrange=150000-999999&salarytype=annual'),
+      'https://www.westpac.com.au/',
+
+      # Why: Top Site South America
+      'http://brasil.gov.br',
+      'http://www.b3.com.br/pt_br/',
+      'https://www.visitchile.com/es/circuitos/'
+  ]
+  URL = URL_LIST[0]
+  SUPPORTED_PLATFORMS = platforms.DESKTOP_ONLY
diff --git a/ui/aura/mus/client_surface_embedder.cc b/ui/aura/mus/client_surface_embedder.cc
index 21913636..6ce87d0 100644
--- a/ui/aura/mus/client_surface_embedder.cc
+++ b/ui/aura/mus/client_surface_embedder.cc
@@ -50,6 +50,16 @@
   UpdateSizeAndGutters();
 }
 
+void ClientSurfaceEmbedder::SetClientAreaInsets(
+    const gfx::Insets& client_area_insets) {
+  if (client_area_insets_ == client_area_insets)
+    return;
+
+  client_area_insets_ = client_area_insets;
+  if (inject_gutter_)
+    UpdateSizeAndGutters();
+}
+
 void ClientSurfaceEmbedder::UpdateSizeAndGutters() {
   surface_layer_owner_->layer()->SetBounds(gfx::Rect(window_->bounds().size()));
   if (!inject_gutter_)
diff --git a/ui/aura/mus/client_surface_embedder.h b/ui/aura/mus/client_surface_embedder.h
index cec86d00..724a0825 100644
--- a/ui/aura/mus/client_surface_embedder.h
+++ b/ui/aura/mus/client_surface_embedder.h
@@ -41,6 +41,9 @@
   // updated.
   void SetFallbackSurfaceInfo(const viz::SurfaceInfo& surface_info);
 
+  void SetClientAreaInsets(const gfx::Insets& client_area_insets);
+  const gfx::Insets& client_area_insets() const { return client_area_insets_; }
+
   // Update the surface layer size and the right and bottom gutter layers for
   // the current window size.
   void UpdateSizeAndGutters();
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 2466fc7..7244b68 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -89,7 +89,6 @@
     "clipboard/clipboard_win.cc",
     "clipboard/clipboard_win.h",
     "clipboard/custom_data_helper_mac.mm",
-    "cocoa/accessibility_hostable.h",
     "cocoa/animation_utils.h",
     "cocoa/appkit_utils.h",
     "cocoa/appkit_utils.mm",
@@ -152,6 +151,7 @@
     "cocoa/user_interface_item_command_handler.h",
     "cocoa/view_description.h",
     "cocoa/view_description.mm",
+    "cocoa/views_hostable.h",
     "cocoa/weak_ptr_nsobject.h",
     "cocoa/weak_ptr_nsobject.mm",
     "cocoa/window_size_constants.h",
diff --git a/ui/base/cocoa/accessibility_hostable.h b/ui/base/cocoa/accessibility_hostable.h
deleted file mode 100644
index 63a4a24..0000000
--- a/ui/base/cocoa/accessibility_hostable.h
+++ /dev/null
@@ -1,25 +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_BASE_COCOA_ACCESSIBILITY_HOSTABLE_H_
-#define UI_BASE_COCOA_ACCESSIBILITY_HOSTABLE_H_
-
-#import <objc/objc.h>
-
-// An object that can be hosted in another accessibility hierarchy.
-// This allows for stitching together heterogenous accessibility
-// hierarchies, for example the AXPlatformNodeCocoa-based views
-// toolkit hierarchy and the BrowserAccessibilityCocoa-based
-// web content hierarchy.
-@protocol AccessibilityHostable
-
-// Sets |accessibilityParent| as the object returned when the
-// receiver is queried for its accessibility parent.
-// TODO(lgrey/ellyjones): Remove this in favor of setAccessibilityParent:
-// when we switch to the new accessibility API.
-- (void)setAccessibilityParentElement:(id)accessibilityParent;
-
-@end
-
-#endif  // UI_BASE_COCOA_ACCESSIBILITY_HOSTABLE_H_
diff --git a/ui/base/cocoa/views_hostable.h b/ui/base/cocoa/views_hostable.h
new file mode 100644
index 0000000..c548b97
--- /dev/null
+++ b/ui/base/cocoa/views_hostable.h
@@ -0,0 +1,59 @@
+// 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_BASE_COCOA_VIEWS_HOSTABLE_H_
+#define UI_BASE_COCOA_VIEWS_HOSTABLE_H_
+
+#import <objc/objc.h>
+
+#include "ui/base/ui_base_export.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace ui {
+
+class Layer;
+
+// Interface that it used to stitch a content::WebContentsView into a
+// views::View.
+class ViewsHostableView {
+ public:
+  // Host interface through which the WebContentsView may indicate that its C++
+  // object is destroying.
+  class Host {
+   public:
+    // Query the ui::Layer of the host.
+    virtual ui::Layer* GetUiLayer() const = 0;
+
+    // Query the parent accessibility element of the host.
+    virtual id GetAccessibilityElement() const = 0;
+
+    // Called when the hostable view will be destroyed.
+    virtual void OnHostableViewDestroying() = 0;
+  };
+
+  // Called when the content::WebContentsView's NSView is added as a subview of
+  // the views::View's NSView (note that these are the browser-side NSViews).
+  // This is responsible for:
+  // - Adding the WebContentsView's ui::Layer to the parent's ui::Layer tree.
+  // - Stitching together the accessibility tree between the views::View and
+  //   the WebContentsView.
+  // - Stitching together any app-shim-side NSViews.
+  virtual void OnViewsHostableAttached(Host* host) = 0;
+  // Called when the WebContentsView's NSView has been removed from the
+  // views::View's NSView. This is responsible for un-doing all of the actions
+  // taken when attaching.
+  virtual void OnViewsHostableDetached() = 0;
+};
+
+}  // namespace ui
+
+// The protocol through which an NSView indicates support for the
+// ViewsHostableView interface.
+@protocol ViewsHostable
+
+- (ui::ViewsHostableView*)viewsHostableView;
+
+@end
+
+#endif  // UI_BASE_COCOA_VIEWS_HOSTABLE_H_
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index a6e11f37..4a799dda3 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -83,7 +83,7 @@
 UI_BASE_EXPORT bool IsOzoneDrmMojo();
 
 // Whether default UI should use a dark mode color scheme, if enabled on
-// macOS Mojave/Windows 10, or the --force-dark-mode flag is provided.
+// macOS Mojave/Windows 10.
 UI_BASE_EXPORT extern const base::Feature kDarkMode;
 
 }  // namespace features
diff --git a/ui/views/controls/native/native_view_host_mac.h b/ui/views/controls/native/native_view_host_mac.h
index de09f5b..06f3e9c 100644
--- a/ui/views/controls/native/native_view_host_mac.h
+++ b/ui/views/controls/native/native_view_host_mac.h
@@ -7,24 +7,32 @@
 
 #include "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
+#include "ui/base/cocoa/views_hostable.h"
 #include "ui/views/controls/native/native_view_host_wrapper.h"
 #include "ui/views/views_export.h"
 
 namespace ui {
 class LayerOwner;
-}
+class ViewsHostableView;
+}  // namespace ui
 
 namespace views {
 
 class NativeViewHost;
 
 // Mac implementation of NativeViewHostWrapper.
-class NativeViewHostMac : public NativeViewHostWrapper {
+class NativeViewHostMac : public NativeViewHostWrapper,
+                          public ui::ViewsHostableView::Host {
  public:
   explicit NativeViewHostMac(NativeViewHost* host);
   ~NativeViewHostMac() override;
 
-  // Overridden from NativeViewHostWrapper:
+  // ViewsHostableView::Host:
+  ui::Layer* GetUiLayer() const override;
+  id GetAccessibilityElement() const override;
+  void OnHostableViewDestroying() override;
+
+  // NativeViewHostWrapper:
   void AttachNativeView() override;
   void NativeViewDetaching(bool destroyed) override;
   void AddedToWidget() override;
@@ -48,6 +56,11 @@
   // Retain the native view as it may be destroyed at an unpredictable time.
   base::scoped_nsobject<NSView> native_view_;
 
+  // If |native_view| supports the ViewsHostable protocol, then this is the
+  // the corresponding ViewsHostableView interface (which is implemeted only
+  // by WebContents and tests).
+  ui::ViewsHostableView* native_view_hostable_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(NativeViewHostMac);
 };
 
diff --git a/ui/views/controls/native/native_view_host_mac.mm b/ui/views/controls/native/native_view_host_mac.mm
index 20a2f81..47b68fd 100644
--- a/ui/views/controls/native/native_view_host_mac.mm
+++ b/ui/views/controls/native/native_view_host_mac.mm
@@ -8,19 +8,11 @@
 
 #include "base/mac/foundation_util.h"
 #import "ui/accessibility/platform/ax_platform_node_mac.h"
-#import "ui/base/cocoa/accessibility_hostable.h"
 #import "ui/views/cocoa/bridged_native_widget_host_impl.h"
 #include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/widget/native_widget_mac.h"
 #include "ui/views/widget/widget.h"
 
-// NSViews that can be drawn as a ui::Layer directly will implement this
-// interface. Calling cr_setParentLayer will embed the ui::Layer of the NSView
-// under |parentUiLayer|.
-@interface NSView (UICompositor)
-- (void)cr_setParentUiLayer:(ui::Layer*)parentUiLayer;
-@end
-
 namespace views {
 namespace {
 
@@ -60,52 +52,65 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// NativeViewHostMac, ViewsHostableView::Host implementation:
+
+ui::Layer* NativeViewHostMac::GetUiLayer() const {
+  return host_->layer();
+}
+
+id NativeViewHostMac::GetAccessibilityElement() const {
+  // Find the closest ancestor view that participates in the views toolkit
+  // accessibility hierarchy and set its element as the native view's parent.
+  // This is necessary because a closer ancestor might already be attaching
+  // to the NSView/content hierarchy.
+  // For example, web content is currently embedded into the views hierarchy
+  // roughly like this:
+  // BrowserView (views)
+  // |_  WebView (views)
+  //   |_  NativeViewHost (views)
+  //     |_  WebContentView (Cocoa, is |native_view_| in this scenario,
+  //         |               accessibility ignored).
+  //         |_ RenderWidgetHostView (Cocoa)
+  // WebView specifies either the RenderWidgetHostView or the native view as
+  // its accessibility element. That means that if we were to set it as
+  // |native_view_|'s parent, the RenderWidgetHostView would be its own
+  // accessibility parent! Instead, we want to find the browser view and
+  // attach to its node.
+  return ClosestPlatformAncestorNode(host_->parent());
+}
+
+void NativeViewHostMac::OnHostableViewDestroying() {
+  DCHECK(native_view_hostable_);
+  host_->NativeViewDestroyed();
+  DCHECK(!native_view_hostable_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // NativeViewHostMac, NativeViewHostWrapper implementation:
 
 void NativeViewHostMac::AttachNativeView() {
   DCHECK(host_->native_view());
   DCHECK(!native_view_);
   native_view_.reset([host_->native_view() retain]);
-
-  if ([native_view_ respondsToSelector:@selector(cr_setParentUiLayer:)])
-    [native_view_ cr_setParentUiLayer:host_->layer()];
-  if ([native_view_ conformsToProtocol:@protocol(AccessibilityHostable)]) {
-    // Find the closest ancestor view that participates in the views toolkit
-    // accessibility hierarchy and set its element as the native view's parent.
-    // This is necessary because a closer ancestor might already be attaching
-    // to the NSView/content hierarchy.
-    // For example, web content is currently embedded into the views hierarchy
-    // roughly like this:
-    // BrowserView (views)
-    // |_  WebView (views)
-    //   |_  NativeViewHost (views)
-    //     |_  WebContentView (Cocoa, is |native_view_| in this scenario,
-    //         |               accessibility ignored).
-    //         |_ RenderWidgetHostView (Cocoa)
-    // WebView specifies either the RenderWidgetHostView or the native view as
-    // its accessibility element. That means that if we were to set it as
-    // |native_view_|'s parent, the RenderWidgetHostView would be its own
-    // accessibility parent! Instead, we want to find the browser view and
-    // attach to its node.
-    id hostable = native_view_;
-    [hostable setAccessibilityParentElement:ClosestPlatformAncestorNode(
-                                                host_->parent())];
-  }
-
   EnsureNativeViewHasNoChildWidgets(native_view_);
   BridgedNativeWidgetHostImpl* bridge_host =
       BridgedNativeWidgetHostImpl::GetFromNativeWindow(
           host_->GetWidget()->GetNativeWindow());
   DCHECK(bridge_host);
   bridge_host->SetAssociationForView(host_, native_view_);
+
+  if ([native_view_ conformsToProtocol:@protocol(ViewsHostable)]) {
+    id hostable = native_view_;
+    native_view_hostable_ = [hostable viewsHostableView];
+    if (native_view_hostable_)
+      native_view_hostable_->OnViewsHostableAttached(this);
+  }
 }
 
 void NativeViewHostMac::NativeViewDetaching(bool destroyed) {
-  // |destroyed| is only true if this class calls host_->NativeViewDestroyed().
-  // Aura does this after observing an aura OnWindowDestroying, but NSViews
-  // are reference counted so there isn't a reliable signal. Instead, a
-  // reference is retained until the NativeViewHost is detached.
-  DCHECK(!destroyed);
+  // |destroyed| is only true if this class calls host_->NativeViewDestroyed(),
+  // which is called if a hosted WebContentsView about to be destroyed (note
+  // that its corresponding NSView may still exist).
 
   // |native_view_| can be nil here if RemovedFromWidget() is called before
   // NativeViewHost::Detach().
@@ -118,11 +123,9 @@
   [host_->native_view() setHidden:YES];
   [host_->native_view() removeFromSuperview];
 
-  if ([native_view_ respondsToSelector:@selector(cr_setParentUiLayer:)])
-    [native_view_ cr_setParentUiLayer:nullptr];
-  if ([native_view_ conformsToProtocol:@protocol(AccessibilityHostable)]) {
-    id hostable = native_view_;
-    [hostable setAccessibilityParentElement:nil];
+  if (native_view_hostable_) {
+    native_view_hostable_->OnViewsHostableDetached();
+    native_view_hostable_ = nullptr;
   }
 
   EnsureNativeViewHasNoChildWidgets(host_->native_view());
diff --git a/ui/views/controls/native/native_view_host_mac_unittest.mm b/ui/views/controls/native/native_view_host_mac_unittest.mm
index 4a4fdca..5213360 100644
--- a/ui/views/controls/native/native_view_host_mac_unittest.mm
+++ b/ui/views/controls/native/native_view_host_mac_unittest.mm
@@ -12,17 +12,35 @@
 #import "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
 #import "testing/gtest_mac.h"
-#import "ui/base/cocoa/accessibility_hostable.h"
+#import "ui/base/cocoa/views_hostable.h"
 #include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/controls/native/native_view_host_test_base.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
-@interface TestAccessibilityHostableView : NSView<AccessibilityHostable>
-@property(nonatomic, assign) id accessibilityParentElement;
+class TestViewsHostable : public ui::ViewsHostableView {
+ public:
+  id parent_accessibility_element() const {
+    return parent_accessibility_element_;
+  }
+
+ private:
+  // ui::ViewsHostableView:
+  void OnViewsHostableAttached(ui::ViewsHostableView::Host* host) override {
+    parent_accessibility_element_ = host->GetAccessibilityElement();
+  }
+  void OnViewsHostableDetached() override {
+    parent_accessibility_element_ = nil;
+  }
+
+  id parent_accessibility_element_ = nil;
+};
+
+@interface TestViewsHostableView : NSView<ViewsHostable>
+@property(nonatomic, assign) ui::ViewsHostableView* viewsHostableView;
 @end
-@implementation TestAccessibilityHostableView
-@synthesize accessibilityParentElement = accessibilityParentElement_;
+@implementation TestViewsHostableView
+@synthesize viewsHostableView = viewsHostableView_;
 @end
 
 namespace views {
@@ -115,15 +133,18 @@
   CreateHost();
   host()->Detach();
 
-  base::scoped_nsobject<TestAccessibilityHostableView> view(
-      [[TestAccessibilityHostableView alloc] init]);
+  base::scoped_nsobject<TestViewsHostableView> view(
+      [[TestViewsHostableView alloc] init]);
+  TestViewsHostable views_hostable;
+  [view setViewsHostableView:&views_hostable];
+
   host()->Attach(view);
-  EXPECT_NSEQ([view accessibilityParentElement],
+  EXPECT_NSEQ(views_hostable.parent_accessibility_element(),
               toplevel()->GetRootView()->GetNativeViewAccessible());
 
   host()->Detach();
   DestroyHost();
-  EXPECT_FALSE([view accessibilityParentElement]);
+  EXPECT_FALSE(views_hostable.parent_accessibility_element());
 }
 
 // Test that the content windows' bounds are set to the correct values while the