diff --git a/.gitignore b/.gitignore
index 93b62c81..99a2ddfa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -166,7 +166,8 @@
 /chromecast/internal
 /chromeos/assistant/internal
 /chromeos/profiles/chromeos.orderfile.txt
-/chromeos/profiles/orderfile.local.txt
+/chromeos/profiles/*.local.txt
+/chromeos/profiles/*.afdo.prof
 /cipd_cache/
 /clank
 /cloud_print/cloud_print_version_resources.xml
diff --git a/BUILD.gn b/BUILD.gn
index fd1c723..9a9ef733 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -321,6 +321,7 @@
       "//content/public/android:content_junit_tests",
       "//content/shell/android:content_shell_apk",
       "//device:device_junit_tests",
+      "//weblayer/shell/android:weblayer_demo_apk",
       "//weblayer/shell/android:weblayer_shell_apk",
 
       # TODO(https://crbug.com/879065): remove once tests have been migrated to
diff --git a/DEPS b/DEPS
index a36f258..849c868b 100644
--- a/DEPS
+++ b/DEPS
@@ -162,7 +162,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '932a2c0e3bb614c77bd4293f56bb5619f902e507',
+  'skia_revision': '0033008d95ac6deb3cc43203fda5313b30ae1cf1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -178,7 +178,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '860369fc431e8510f974799961083b42d01cfa8b',
+  'swiftshader_revision': '61a2765940a72891baa108e2815da6c21f7f3218',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # 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 freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '99f23d6ff2203966d210bccd49eacc62a20328f9',
+  'freetype_revision': '04ebb2a000ee40df2a9900198ec62d79af745b1f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
@@ -853,7 +853,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '818d963727de2aa9d1b7252e9657d425ec856001',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7fd753567d3cac55292131dcf0c3094f0532dcda',
       'condition': 'checkout_linux',
   },
 
@@ -878,7 +878,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '2c210a490857380a93f8308dd504aeb1ef759d38',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6f9a0238ce40b6af186aac520c3b87ffa9a8be3b',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1257,7 +1257,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'e7a75776335a1735f41268a150193c67fc964bcd',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '5b12fdd2a7ac6d8a9a61013b855ace3766393934',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1425,7 +1425,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'abaae129d9a0c6e1e092067e0b105475df43352e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '809198edfff416fce8d75b574a43afab5e67b1cd',
+    Var('webrtc_git') + '/src.git' + '@' + '86314cfb5dc09bba15a1607585e9ddd544078ac5',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1487,7 +1487,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ea5731baa00cf8b8e3041795ccaa13f419b41699',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@95dfac5bc3249a5a17961b41c3082cb30b9f469b',
     'condition': 'checkout_src_internal',
   },
 
@@ -3681,6 +3681,43 @@
                 '--gs_url_base=chromeos-prebuilt/afdo-job/orderfiles/vetted',
     ],
   },
+  # Download AFDO profiles for Chrome OS for each architecture.
+  {
+    'name': 'Fetch Chrome OS AFDO profiles (silvermont)',
+    'pattern': '.',
+    'condition': 'checkout_chromeos or checkout_simplechrome',
+    'action': [ 'vpython',
+                'src/tools/download_cros_provided_profile.py',
+                '--newest_state=src/chromeos/profiles/silvermont.afdo.newest.txt',
+                '--local_state=src/chromeos/profiles/silvermont.afdo.local.txt',
+                '--output_name=src/chromeos/profiles/silvermont.afdo.prof',
+                '--gs_url_base=chromeos-prebuilt/afdo-job/vetted/release',
+    ],
+  },
+  {
+    'name': 'Fetch Chrome OS AFDO profiles (airmont)',
+    'pattern': '.',
+    'condition': 'checkout_chromeos or checkout_simplechrome',
+    'action': [ 'vpython',
+                'src/tools/download_cros_provided_profile.py',
+                '--newest_state=src/chromeos/profiles/airmont.afdo.newest.txt',
+                '--local_state=src/chromeos/profiles/airmont.afdo.local.txt',
+                '--output_name=src/chromeos/profiles/airmont.afdo.prof',
+                '--gs_url_base=chromeos-prebuilt/afdo-job/vetted/release',
+    ],
+  },
+  {
+    'name': 'Fetch Chrome OS AFDO profiles (broadwell)',
+    'pattern': '.',
+    'condition': 'checkout_chromeos or checkout_simplechrome',
+    'action': [ 'vpython',
+                'src/tools/download_cros_provided_profile.py',
+                '--newest_state=src/chromeos/profiles/broadwell.afdo.newest.txt',
+                '--local_state=src/chromeos/profiles/broadwell.afdo.local.txt',
+                '--output_name=src/chromeos/profiles/broadwell.afdo.prof',
+                '--gs_url_base=chromeos-prebuilt/afdo-job/vetted/release',
+    ],
+  },
   {
     # Pull doclava binaries if building for Android.
     'name': 'doclava',
diff --git a/ash/app_list/views/search_result_tile_item_list_view.cc b/ash/app_list/views/search_result_tile_item_list_view.cc
index 9103523..1efe799 100644
--- a/ash/app_list/views/search_result_tile_item_list_view.cc
+++ b/ash/app_list/views/search_result_tile_item_list_view.cc
@@ -212,7 +212,7 @@
         FROM_HERE,
         base::TimeDelta::FromMilliseconds(kPlayStoreImpressionDelayInMs), this,
         &SearchResultTileItemListView::OnPlayStoreImpressionTimer);
-  } else {
+  } else if (!found_playstore_results) {
     playstore_impression_timer_.Stop();
   }
 
@@ -347,6 +347,11 @@
                              playstore_app_num, max_search_result_tiles_);
 }
 
+void SearchResultTileItemListView::CleanUpOnViewHide() {
+  playstore_impression_timer_.Stop();
+  recent_playstore_query_.clear();
+}
+
 bool SearchResultTileItemListView::OnKeyPressed(const ui::KeyEvent& event) {
   // Let the FocusManager handle Left/Right keys.
   if (!IsUnhandledUpDownKeyEvent(event))
@@ -397,8 +402,6 @@
       view_delegate()->OnSearchResultVisibilityChanged(result->id(), shown());
     }
   }
-  if (!shown())
-    playstore_impression_timer_.Stop();
 }
 
 void SearchResultTileItemListView::VisibilityChanged(View* starting_from,
@@ -410,7 +413,7 @@
     return;
   }
 
-  playstore_impression_timer_.Stop();
+  CleanUpOnViewHide();
 
   for (const auto* tile_view : tile_views_) {
     SearchResult* result = tile_view->result();
diff --git a/ash/app_list/views/search_result_tile_item_list_view.h b/ash/app_list/views/search_result_tile_item_list_view.h
index 4c1cb59..27f380e 100644
--- a/ash/app_list/views/search_result_tile_item_list_view.h
+++ b/ash/app_list/views/search_result_tile_item_list_view.h
@@ -62,6 +62,10 @@
 
   void OnPlayStoreImpressionTimer();
 
+  // Cleans up when the view is hid due to closing the suggestion widow
+  // or closing the launcher.
+  void CleanUpOnViewHide();
+
   std::vector<SearchResultTileItemView*> tile_views_;
 
   std::vector<views::Separator*> separator_views_;
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index d3867a4..09513a99 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -90,6 +90,9 @@
 const base::Feature kEnableBackgroundBlur{"EnableBackgroundBlur",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kSwipingFromLeftEdgeToGoBack{
+    "SwipingFromLeftEdgeToGoBack", base::FEATURE_DISABLED_BY_DEFAULT};
+
 bool IsHideArcMediaNotificationsEnabled() {
   return base::FeatureList::IsEnabled(kMediaSessionNotification) &&
          base::FeatureList::IsEnabled(kHideArcMediaNotifications);
@@ -167,5 +170,9 @@
   return base::FeatureList::IsEnabled(kEnableBackgroundBlur);
 }
 
+bool IsSwipingFromLeftEdgeToGoBackEnabled() {
+  return base::FeatureList::IsEnabled(kSwipingFromLeftEdgeToGoBack);
+}
+
 }  // namespace features
 }  // namespace ash
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index 1b3d57b..50ab4d6 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -115,6 +115,10 @@
 // the UnifiedSystemTrayView.
 ASH_PUBLIC_EXPORT extern const base::Feature kUnifiedMessageCenterRefactor;
 
+// Enables going back to previous page while swiping from the left edge of the
+// display. Only for tablet mode.
+ASH_PUBLIC_EXPORT extern const base::Feature kSwipingFromLeftEdgeToGoBack;
+
 ASH_PUBLIC_EXPORT bool IsHideArcMediaNotificationsEnabled();
 
 ASH_PUBLIC_EXPORT bool IsKeyboardShortcutViewerAppEnabled();
@@ -151,6 +155,8 @@
 
 ASH_PUBLIC_EXPORT bool IsBackgroundBlurEnabled();
 
+ASH_PUBLIC_EXPORT bool IsSwipingFromLeftEdgeToGoBackEnabled();
+
 }  // namespace features
 }  // namespace ash
 
diff --git a/ash/shelf/shelf_tooltip_manager_unittest.cc b/ash/shelf/shelf_tooltip_manager_unittest.cc
index af60328..bc0d0d47 100644
--- a/ash/shelf/shelf_tooltip_manager_unittest.cc
+++ b/ash/shelf/shelf_tooltip_manager_unittest.cc
@@ -183,6 +183,7 @@
   generator->MoveMouseTo(shelf_bounds.CenterPoint());
   generator->PressLeftButton();
   EXPECT_FALSE(tooltip_manager_->IsVisible());
+  generator->ReleaseLeftButton();
 
   // Should hide for touch events in the shelf.
   ShowTooltipForFirstAppIcon();
diff --git a/ash/shelf/shelf_widget_unittest.cc b/ash/shelf/shelf_widget_unittest.cc
index 1d5729f..bf7e41b 100644
--- a/ash/shelf/shelf_widget_unittest.cc
+++ b/ash/shelf/shelf_widget_unittest.cc
@@ -152,7 +152,7 @@
   const int nav_width =
       shelf_widget->navigation_widget()->GetWindowBoundsInScreen().width();
   const int hotseat_width =
-      GetPrimaryShelf()->GetShelfViewForTesting()->width();
+      shelf_widget->hotseat_widget()->GetWindowBoundsInScreen().width();
   const int margins = ShelfConfig::Get()->home_button_edge_spacing() +
                       ShelfConfig::Get()->app_icon_group_margin();
   EXPECT_EQ(status_width, total_width - nav_width - hotseat_width - margins);
diff --git a/ash/shell/content/test/ash_content_perf_test_launcher.cc b/ash/shell/content/test/ash_content_perf_test_launcher.cc
index fb68206..947b77ff 100644
--- a/ash/shell/content/test/ash_content_perf_test_launcher.cc
+++ b/ash/shell/content/test/ash_content_perf_test_launcher.cc
@@ -34,10 +34,9 @@
  protected:
   // content::ContentTestSuiteBase:
   void Initialize() override {
-    // Browser tests are expected not to tear-down various globals and may
-    // complete with the thread priority being above NORMAL.
+    // Browser tests are expected not to tear-down various globals. (Must run
+    // before the base class is initialized.)
     base::TestSuite::DisableCheckForLeakedGlobals();
-    base::TestSuite::DisableCheckForThreadPriorityAtTestEnd();
     ContentTestSuiteBase::Initialize();
     ui_controls::InstallUIControlsAura(ash::test::CreateAshUIControls());
   }
diff --git a/ash/wm/overview/overview_constants.h b/ash/wm/overview/overview_constants.h
index 0ea40491..e9f4d1f 100644
--- a/ash/wm/overview/overview_constants.h
+++ b/ash/wm/overview/overview_constants.h
@@ -37,7 +37,8 @@
 
 // Amount of time we wait to unpause the occlusion tracker after a overview item
 // is finished dragging. Waits a bit longer than the overview item animation.
-constexpr int kOcclusionPauseDurationForDragMs = 300;
+constexpr base::TimeDelta kOcclusionPauseDurationForDrag =
+    base::TimeDelta::FromMilliseconds(300);
 
 }  // namespace ash
 
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc
index f68f158..0287b8cb 100644
--- a/ash/wm/overview/overview_controller.cc
+++ b/ash/wm/overview/overview_controller.cc
@@ -54,11 +54,13 @@
 // It can take up to two frames until the frame created in the UI thread that
 // triggered animation observer is drawn. Wait 50ms in attempt to let its draw
 // and swap finish.
-constexpr int kOcclusionPauseDurationForStartMs = 50;
+constexpr base::TimeDelta kOcclusionPauseDurationForStart =
+    base::TimeDelta::FromMilliseconds(50);
 
 // Wait longer when exiting overview mode in case when a user may re-enter
 // overview mode immediately, contents are ready.
-constexpr int kOcclusionPauseDurationForEndMs = 500;
+constexpr base::TimeDelta kOcclusionPauseDurationForEnd =
+    base::TimeDelta::FromMilliseconds(500);
 
 bool IsWallpaperChangeAllowed() {
   return !g_disable_wallpaper_change_for_tests &&
@@ -255,7 +257,7 @@
 };
 
 OverviewController::OverviewController()
-    : occlusion_pause_duration_for_end_ms_(kOcclusionPauseDurationForEndMs),
+    : occlusion_pause_duration_for_end_(kOcclusionPauseDurationForEnd),
       overview_wallpaper_controller_(
           std::make_unique<OverviewWallpaperController>()),
       delayed_animation_task_delay_(kTransition) {
@@ -426,12 +428,11 @@
       std::make_unique<aura::WindowOcclusionTracker::ScopedPause>();
 }
 
-void OverviewController::UnpauseOcclusionTracker(int delay) {
+void OverviewController::UnpauseOcclusionTracker(base::TimeDelta delay) {
   reset_pauser_task_.Reset(base::BindOnce(&OverviewController::ResetPauser,
                                           weak_ptr_factory_.GetWeakPtr()));
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, reset_pauser_task_.callback(),
-      base::TimeDelta::FromMilliseconds(delay));
+      FROM_HERE, reset_pauser_task_.callback(), delay);
 }
 
 void OverviewController::AddObserver(OverviewObserver* observer) {
@@ -738,7 +739,7 @@
     overview_session_->OnStartingAnimationComplete(canceled,
                                                    should_focus_overview_);
   }
-  UnpauseOcclusionTracker(kOcclusionPauseDurationForStartMs);
+  UnpauseOcclusionTracker(kOcclusionPauseDurationForStart);
   TRACE_EVENT_ASYNC_END1("ui", "OverviewController::EnterOverview", this,
                          "canceled", canceled);
 }
@@ -752,7 +753,7 @@
 
   for (auto& observer : observers_)
     observer.OnOverviewModeEndingAnimationComplete(canceled);
-  UnpauseOcclusionTracker(occlusion_pause_duration_for_end_ms_);
+  UnpauseOcclusionTracker(occlusion_pause_duration_for_end_);
   TRACE_EVENT_ASYNC_END1("ui", "OverviewController::ExitOverview", this,
                          "canceled", canceled);
 }
diff --git a/ash/wm/overview/overview_controller.h b/ash/wm/overview/overview_controller.h
index 05e5aed8..8b60f57 100644
--- a/ash/wm/overview/overview_controller.h
+++ b/ash/wm/overview/overview_controller.h
@@ -63,7 +63,7 @@
   // Pause or unpause the occlusion tracker. Resets the unpause delay if we were
   // already in the process of unpausing.
   void PauseOcclusionTracker();
-  void UnpauseOcclusionTracker(int delay);
+  void UnpauseOcclusionTracker(base::TimeDelta delay);
 
   void AddObserver(OverviewObserver* observer);
   void RemoveObserver(OverviewObserver* observer);
@@ -93,8 +93,8 @@
 
   OverviewSession* overview_session() { return overview_session_.get(); }
 
-  void set_occlusion_pause_duration_for_end_ms_for_test(int duration) {
-    occlusion_pause_duration_for_end_ms_ = duration;
+  void set_occlusion_pause_duration_for_end_for_test(base::TimeDelta duration) {
+    occlusion_pause_duration_for_end_ = duration;
   }
   void set_delayed_animation_task_delay_for_test(base::TimeDelta delta) {
     delayed_animation_task_delay_ = delta;
@@ -154,7 +154,7 @@
   std::unique_ptr<OverviewSession> overview_session_;
   base::Time last_overview_session_time_;
 
-  int occlusion_pause_duration_for_end_ms_;
+  base::TimeDelta occlusion_pause_duration_for_end_;
 
   // Handles blurring and dimming of the wallpaper when entering or exiting
   // overview mode. Animates the blurring and dimming if necessary.
diff --git a/ash/wm/overview/overview_controller_unittest.cc b/ash/wm/overview/overview_controller_unittest.cc
index 0447249..9765480 100644
--- a/ash/wm/overview/overview_controller_unittest.cc
+++ b/ash/wm/overview/overview_controller_unittest.cc
@@ -293,7 +293,8 @@
 
   Shell::Get()
       ->overview_controller()
-      ->set_occlusion_pause_duration_for_end_ms_for_test(100);
+      ->set_occlusion_pause_duration_for_end_for_test(
+          base::TimeDelta::FromMilliseconds(100));
   TestOverviewObserver observer(/*should_monitor_animation_state = */ true);
   ui::ScopedAnimationDurationScaleMode non_zero(
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index eed44aa..a079bfb 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -80,7 +80,11 @@
 
 // Wait a while before unpausing the occlusion tracker after a scroll has
 // completed as the user may start another scroll.
-constexpr int kOcclusionUnpauseDurationForScrollMs = 500;
+constexpr base::TimeDelta kOcclusionUnpauseDurationForScroll =
+    base::TimeDelta::FromMilliseconds(500);
+
+constexpr base::TimeDelta kOcclusionUnpauseDurationForRotation =
+    base::TimeDelta::FromMilliseconds(300);
 
 // Histogram names for overview enter/exit smoothness in clamshell,
 // tablet mode and splitview.
@@ -630,10 +634,11 @@
 
 void OverviewGrid::SetBoundsAndUpdatePositions(
     const gfx::Rect& bounds_in_screen,
-    const base::flat_set<OverviewItem*>& ignored_items) {
+    const base::flat_set<OverviewItem*>& ignored_items,
+    bool animate) {
   bounds_ = bounds_in_screen;
   MaybeUpdateDesksWidgetBounds();
-  PositionWindows(/*animate=*/true, ignored_items);
+  PositionWindows(animate, ignored_items);
 }
 
 void OverviewGrid::RearrangeDuringDrag(aura::Window* dragged_window,
@@ -662,7 +667,8 @@
       ignored_items.insert(dragged_item);
     if (drop_target && !wanted_drop_target_visibility)
       ignored_items.insert(drop_target);
-    SetBoundsAndUpdatePositions(wanted_grid_bounds, ignored_items);
+    SetBoundsAndUpdatePositions(wanted_grid_bounds, ignored_items,
+                                /*animate=*/true);
   }
 }
 
@@ -803,7 +809,8 @@
   // be updated based on the preview area during drag, but the window finally
   // didn't be snapped to the preview area.
   SetBoundsAndUpdatePositions(
-      GetGridBoundsInScreenAfterDragging(dragged_window), /*ignored_items=*/{});
+      GetGridBoundsInScreenAfterDragging(dragged_window), /*ignored_items=*/{},
+      /*animate=*/true);
 }
 
 bool OverviewGrid::IsDropTargetWindow(aura::Window* window) const {
@@ -914,6 +921,8 @@
 }
 
 void OverviewGrid::OnScreenCopiedBeforeRotation() {
+  Shell::Get()->overview_controller()->PauseOcclusionTracker();
+
   for (auto& window : window_list()) {
     window->set_disable_mask(true);
     window->UpdateRoundedCornersAndShadow();
@@ -927,6 +936,8 @@
   for (auto& window : window_list())
     window->set_disable_mask(false);
   Shell::Get()->overview_controller()->DelayedUpdateRoundedCornersAndShadow();
+  Shell::Get()->overview_controller()->UnpauseOcclusionTracker(
+      kOcclusionUnpauseDurationForRotation);
 }
 
 void OverviewGrid::OnWallpaperChanging() {
@@ -1417,7 +1428,7 @@
 
 void OverviewGrid::EndScroll() {
   Shell::Get()->overview_controller()->UnpauseOcclusionTracker(
-      kOcclusionUnpauseDurationForScrollMs);
+      kOcclusionUnpauseDurationForScroll);
   items_scrolling_bounds_.clear();
   presentation_time_recorder_.reset();
 
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h
index a23b276..69ec63d 100644
--- a/ash/wm/overview/overview_grid.h
+++ b/ash/wm/overview/overview_grid.h
@@ -126,7 +126,8 @@
   // except windows in |ignored_items|.
   void SetBoundsAndUpdatePositions(
       const gfx::Rect& bounds_in_screen,
-      const base::flat_set<OverviewItem*>& ignored_items);
+      const base::flat_set<OverviewItem*>& ignored_items,
+      bool animate);
 
   // Updates overview bounds and hides the drop target when a preview area is
   // shown.
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index 80ef5576e4..cc93b46 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -920,7 +920,7 @@
 
   if (is_being_dragged_) {
     Shell::Get()->overview_controller()->UnpauseOcclusionTracker(
-        kOcclusionPauseDurationForDragMs);
+        kOcclusionPauseDurationForDrag);
   }
 }
 
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 5d7dfc0b..54fd8ec 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -977,7 +977,7 @@
     grid->SetBoundsAndUpdatePositions(
         GetGridBoundsInScreen(const_cast<aura::Window*>(grid->root_window()),
                               /*divider_changed=*/true),
-        /*ignored_items=*/{});
+        /*ignored_items=*/{}, /*animate=*/true);
   }
   PositionWindows(/*animate=*/false);
   UpdateNoWindowsWidget();
@@ -1063,9 +1063,8 @@
     grid->SetBoundsAndUpdatePositions(
         GetGridBoundsInScreen(const_cast<aura::Window*>(grid->root_window()),
                               /*divider_changed=*/false),
-        /*ignored_items=*/{});
+        /*ignored_items=*/{}, /*animate=*/false);
   }
-  PositionWindows(/*animate=*/false);
   UpdateNoWindowsWidget();
   if (split_view_drag_indicators_)
     split_view_drag_indicators_->OnDisplayBoundsChanged();
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index 8741b97..6ab063bc 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -65,7 +65,7 @@
 
 void UnpauseOcclusionTracker() {
   Shell::Get()->overview_controller()->UnpauseOcclusionTracker(
-      kOcclusionPauseDurationForDragMs);
+      kOcclusionPauseDurationForDrag);
 }
 
 // Returns the scaled-down size of the dragged item that should be used when
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index 4e88482..746872a 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -15,6 +15,7 @@
 #include "ash/display/screen_orientation_controller_test_api.h"
 #include "ash/magnifier/docked_magnifier_controller_impl.h"
 #include "ash/public/cpp/app_types.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/fps_counter.h"
 #include "ash/public/cpp/presentation_time_recorder.h"
 #include "ash/public/cpp/window_properties.h"
@@ -49,6 +50,7 @@
 #include "ash/wm/wm_event.h"
 #include "base/stl_util.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/test/test_windows.h"
@@ -4141,6 +4143,57 @@
   EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
 }
 
+class SplitViewTabDraggingTestWithClamshellSupport
+    : public SplitViewTabDraggingTest {
+ public:
+  SplitViewTabDraggingTestWithClamshellSupport() = default;
+  ~SplitViewTabDraggingTestWithClamshellSupport() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        ash::features::kDragToSnapInClamshellMode);
+    SplitViewTabDraggingTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(SplitViewTabDraggingTestWithClamshellSupport);
+};
+
+TEST_F(SplitViewTabDraggingTestWithClamshellSupport,
+       DragTabFromSnappedWindowToOverviewAndThenExitTablet) {
+  // Snap a browser window in split view.
+  std::unique_ptr<aura::Window> snapped_window(
+      CreateWindowWithType(gfx::Rect(), AppType::BROWSER));
+  ToggleOverview();
+  split_view_controller()->SnapWindow(snapped_window.get(),
+                                      SplitViewController::LEFT);
+
+  // Drag a tab out of the browser window and into overview.
+  std::unique_ptr<aura::Window> dragged_tab(
+      CreateWindowWithType(gfx::Rect(), AppType::BROWSER));
+  std::unique_ptr<WindowResizer> resizer =
+      StartDrag(dragged_tab.get(), snapped_window.get());
+  DragWindowTo(resizer.get(),
+               gfx::ToEnclosingRect(
+                   Shell::Get()
+                       ->overview_controller()
+                       ->overview_session()
+                       ->GetGridWithRootWindow(snapped_window->GetRootWindow())
+                       ->GetDropTarget()
+                       ->target_bounds())
+                   .CenterPoint());
+  CompleteDrag(std::move(resizer));
+  EXPECT_TRUE(dragged_tab->GetProperty(kIsShowingInOverviewKey));
+
+  // Switch to clamshell mode and check that |snapped_window| keeps its snapped
+  // window state.
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
+  EXPECT_EQ(WindowStateType::kLeftSnapped,
+            WindowState::Get(snapped_window.get())->GetStateType());
+}
+
 class TestWindowDelegateWithWidget : public views::WidgetDelegate {
  public:
   TestWindowDelegateWithWidget(bool can_activate)
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.cc b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
index 7d2ad60..8e5a3a5 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
@@ -42,7 +42,7 @@
 // This function is called to check if window[i] is eligible to be carried over
 // to split view mode during clamshell <-> tablet mode transition or multi-user
 // switch transition. Returns true if windows[i] exists, can snap in split view,
-// is not showing in overview, and is not ARC window.
+// and is not an ARC window.
 // TODO(xdai): Make it work for ARC windows. (see
 // https://crbug.com/922282 and
 // https://buganizer.corp.google.com/issues/123432223).
@@ -50,7 +50,6 @@
     const MruWindowTracker::WindowList& windows,
     size_t i) {
   return windows.size() > i && CanSnapInSplitview(windows[i]) &&
-         !windows[i]->GetProperty(kIsShowingInOverviewKey) &&
          static_cast<ash::AppType>(windows[i]->GetProperty(
              aura::client::kAppType)) != AppType::ARC_APP;
 }
@@ -61,11 +60,17 @@
 base::flat_map<aura::Window*, WindowStateType>
 GetCarryOverWindowsInSplitView() {
   base::flat_map<aura::Window*, WindowStateType> windows;
-  // Check the topmost window and the second topmost's window state to see if
-  // they are eligible to be carried over to splitscreen. A window must meet
+  // Check the states of the topmost two non-overview windows to see if they are
+  // eligible to be carried over to splitscreen. A window must meet
   // IsCarryOverCandidateForSplitView() to be carried over to splitscreen.
   MruWindowTracker::WindowList mru_windows =
       Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kActiveDesk);
+  mru_windows.erase(
+      std::remove_if(mru_windows.begin(), mru_windows.end(),
+                     [](aura::Window* window) {
+                       return window->GetProperty(kIsShowingInOverviewKey);
+                     }),
+      mru_windows.end());
   if (IsCarryOverCandidateForSplitView(mru_windows, 0u)) {
     if (WindowState::Get(mru_windows[0])->GetStateType() ==
         WindowStateType::kLeftSnapped) {
diff --git a/ash/wm/toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc
index 30292a1..b690b54 100644
--- a/ash/wm/toplevel_window_event_handler.cc
+++ b/ash/wm/toplevel_window_event_handler.cc
@@ -5,6 +5,7 @@
 #include "ash/wm/toplevel_window_event_handler.h"
 
 #include "ash/public/cpp/app_types.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/shell.h"
 #include "ash/wm/resize_shadow_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
@@ -37,6 +38,10 @@
 // window from the top of the screen in tablet mode.
 constexpr int kDragStartTopEdgeInset = 8;
 
+// How many dips are reserved for gesture events to start swiping to previous
+// page from the left edge of the screen in tablet mode.
+constexpr int kStartGoingBackLeftEdgeInset = 16;
+
 // Returns whether |window| can be moved via a two finger drag given
 // the hittest results of the two fingers.
 bool CanStartTwoFingerMove(aura::Window* window,
@@ -94,6 +99,35 @@
   run_loop->Quit();
 }
 
+// True if we can start swiping from left edge to go to previous page.
+bool CanStartGoingBack() {
+  if (!features::IsSwipingFromLeftEdgeToGoBackEnabled())
+    return false;
+
+  if (!Shell::Get()->tablet_mode_controller()->InTabletMode())
+    return false;
+
+  return true;
+}
+
+// True if |event| is scrolling away from the restricted left area of the
+// display.
+bool StartedAwayFromLeftArea(ui::GestureEvent* event) {
+  if (event->details().scroll_x_hint() < 0)
+    return false;
+
+  const gfx::Point location_in_screen =
+      event->target()->GetScreenLocation(*event);
+  const gfx::Rect work_area_bounds =
+      display::Screen::GetScreen()
+          ->GetDisplayNearestWindow(static_cast<aura::Window*>(event->target()))
+          .work_area();
+
+  gfx::Rect hit_bounds_in_screen(work_area_bounds);
+  hit_bounds_in_screen.set_width(kStartGoingBackLeftEdgeInset);
+  return hit_bounds_in_screen.Contains(location_in_screen);
+}
+
 }  // namespace
 
 // ScopedWindowResizer ---------------------------------------------------------
@@ -264,6 +298,11 @@
 }
 
 void ToplevelWindowEventHandler::OnGestureEvent(ui::GestureEvent* event) {
+  if (HandleGoingBackFromLeftEdge(event)) {
+    event->StopPropagation();
+    return;
+  }
+
   aura::Window* target = static_cast<aura::Window*>(event->target());
   int component = window_util::GetNonClientComponent(target, event->location());
   gfx::Point event_location = event->location();
@@ -796,4 +835,47 @@
     gesture_target_->AddObserver(this);
 }
 
+bool ToplevelWindowEventHandler::HandleGoingBackFromLeftEdge(
+    ui::GestureEvent* event) {
+  if (!CanStartGoingBack())
+    return false;
+
+  switch (event->type()) {
+    case ui::ET_GESTURE_SCROLL_BEGIN:
+      going_back_started_ = StartedAwayFromLeftArea(event);
+      if (going_back_started_)
+        return true;
+      break;
+    case ui::ET_GESTURE_SCROLL_UPDATE:
+      if (!going_back_started_)
+        break;
+      // TODO(crbug.com/1002733): Update the arrow animation.
+      return true;
+    case ui::ET_GESTURE_SCROLL_END:
+      if (!going_back_started_)
+        break;
+      if (event->location().x() >= kSwipingDistanceForGoingBack) {
+        aura::Window* root_window =
+            window_util::GetRootWindowAt(event->location());
+        ui::KeyEvent press_key_event(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK,
+                                     ui::EF_NONE);
+        ignore_result(
+            root_window->GetHost()->SendEventToSink(&press_key_event));
+        ui::KeyEvent release_key_event(ui::ET_KEY_RELEASED,
+                                       ui::VKEY_BROWSER_BACK, ui::EF_NONE);
+        ignore_result(
+            root_window->GetHost()->SendEventToSink(&release_key_event));
+      } else {
+        // TODO(crbug.com/1002733): Revert going back animation.
+      }
+      going_back_started_ = false;
+      return true;
+    // TODO(crbug.com/1002733): Add support for fling event.
+    default:
+      break;
+  }
+
+  return false;
+}
+
 }  // namespace ash
diff --git a/ash/wm/toplevel_window_event_handler.h b/ash/wm/toplevel_window_event_handler.h
index b3a85f0..d15e39e 100644
--- a/ash/wm/toplevel_window_event_handler.h
+++ b/ash/wm/toplevel_window_event_handler.h
@@ -43,6 +43,9 @@
       public ui::EventHandler,
       public ::wm::WindowMoveClient {
  public:
+  // The distance for swiping from left edge to go previous page.
+  static constexpr int kSwipingDistanceForGoingBack = 80;
+
   // Describes what triggered ending the drag.
   enum class DragResult {
     // The drag successfully completed.
@@ -157,6 +160,9 @@
   void UpdateGestureTarget(aura::Window* window,
                            const gfx::Point& location = gfx::Point());
 
+  // True if the event is handled for swiping to previous page.
+  bool HandleGoingBackFromLeftEdge(ui::GestureEvent* event);
+
   // The hittest result for the first finger at the time that it initially
   // touched the screen. |first_finger_hittest_| is one of ui/base/hit_test.h
   int first_finger_hittest_;
@@ -178,6 +184,9 @@
   // Are we running a nested run loop from RunMoveLoop().
   bool in_move_loop_ = false;
 
+  // True if swiping from left edge to go to previous page is in progress.
+  bool going_back_started_ = false;
+
   base::WeakPtrFactory<ToplevelWindowEventHandler> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ToplevelWindowEventHandler);
diff --git a/ash/wm/toplevel_window_event_handler_unittest.cc b/ash/wm/toplevel_window_event_handler_unittest.cc
index 2044b5b..334611c 100644
--- a/ash/wm/toplevel_window_event_handler_unittest.cc
+++ b/ash/wm/toplevel_window_event_handler_unittest.cc
@@ -4,9 +4,11 @@
 
 #include "ash/wm/toplevel_window_event_handler.h"
 
+#include "ash/accelerators/accelerator_controller_impl.h"
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
 #include "ash/public/cpp/app_types.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
@@ -29,6 +31,7 @@
 #include "base/bind.h"
 #include "base/compiler_specific.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
@@ -37,6 +40,8 @@
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_observer.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/accelerators/test_accelerator_target.h"
 #include "ui/base/hit_test.h"
 #include "ui/display/display_layout_builder.h"
 #include "ui/display/manager/display_manager.h"
@@ -1006,6 +1011,51 @@
   EXPECT_EQ("10,11 100x100", window1->bounds().ToString());
 }
 
+TEST_F(ToplevelWindowEventHandlerTest, SwipingFromLeftEdgeToGoBack) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      ash::features::kSwipingFromLeftEdgeToGoBack);
+  std::unique_ptr<aura::Window> w1 = CreateTestWindow();
+  TabletModeControllerTestApi().EnterTabletMode();
+
+  AcceleratorControllerImpl* controller =
+      Shell::Get()->accelerator_controller();
+
+  // Register an accelerator that looks for back presses.
+  ui::Accelerator accelerator_back_press(ui::VKEY_BROWSER_BACK, ui::EF_NONE);
+  accelerator_back_press.set_key_state(ui::Accelerator::KeyState::PRESSED);
+  ui::TestAcceleratorTarget target_back_press;
+  controller->Register({accelerator_back_press}, &target_back_press);
+
+  // Register an accelerator that looks for back releases.
+  ui::Accelerator accelerator_back_release(ui::VKEY_BROWSER_BACK, ui::EF_NONE);
+  accelerator_back_release.set_key_state(ui::Accelerator::KeyState::RELEASED);
+  ui::TestAcceleratorTarget target_back_release;
+  controller->Register({accelerator_back_release}, &target_back_release);
+
+  // Tests that swiping from the left less than |kSwipingDistanceForGoingBack|
+  // should not go to previous page.
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  const gfx::Point start(0, 100);
+  generator->GestureScrollSequence(
+      start,
+      gfx::Point(ToplevelWindowEventHandler::kSwipingDistanceForGoingBack - 10,
+                 100),
+      base::TimeDelta::FromMilliseconds(100), 3);
+  EXPECT_EQ(0, target_back_press.accelerator_count());
+  EXPECT_EQ(0, target_back_release.accelerator_count());
+
+  // Tests that swiping from the left more than |kSwipingDistanceForGoingBack|
+  // should go to previous page.
+  generator->GestureScrollSequence(
+      start,
+      gfx::Point(ToplevelWindowEventHandler::kSwipingDistanceForGoingBack + 10,
+                 100),
+      base::TimeDelta::FromMilliseconds(100), 3);
+  EXPECT_EQ(1, target_back_press.accelerator_count());
+  EXPECT_EQ(1, target_back_release.accelerator_count());
+}
+
 namespace {
 
 void SendMouseReleaseAndReleaseCapture(ui::test::EventGenerator* generator,
diff --git a/base/android/java/src/org/chromium/base/LifetimeAssert.java b/base/android/java/src/org/chromium/base/LifetimeAssert.java
index 61bd17f5..c85d22be 100644
--- a/base/android/java/src/org/chromium/base/LifetimeAssert.java
+++ b/base/android/java/src/org/chromium/base/LifetimeAssert.java
@@ -6,6 +6,8 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.annotations.CheckDiscard;
+
 import java.lang.ref.PhantomReference;
 import java.lang.ref.ReferenceQueue;
 import java.util.Collections;
@@ -26,6 +28,7 @@
  *     }
  * }
  */
+@CheckDiscard
 public class LifetimeAssert {
     interface TestHook {
         void onCleaned(WrappedReference ref, String msg);
diff --git a/base/task/promise/dependent_list.h b/base/task/promise/dependent_list.h
index 020bdbfc..3245c1c 100644
--- a/base/task/promise/dependent_list.h
+++ b/base/task/promise/dependent_list.h
@@ -59,7 +59,7 @@
 
   // Align Node on an 8-byte boundary to ensure the first 3 bits are 0 and can
   // be used to store additional state (see static_asserts below).
-  class BASE_EXPORT alignas(8) Node {
+  class BASE_EXPORT ALIGNAS(8) Node {
    public:
     Node();
     explicit Node(Node&& other) noexcept;
diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc
index fb9a444..3b1de80 100644
--- a/base/test/test_suite.cc
+++ b/base/test/test_suite.cc
@@ -37,7 +37,6 @@
 #include "base/test/multiprocess_test.h"
 #include "base/test/test_switches.h"
 #include "base/test/test_timeouts.h"
-#include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -189,31 +188,6 @@
 };
 #endif  // !defined(OS_IOS)
 
-class CheckThreadPriority : public testing::EmptyTestEventListener {
- public:
-  CheckThreadPriority(bool check_thread_priority_at_test_end)
-      : check_thread_priority_at_test_end_(check_thread_priority_at_test_end) {
-    CHECK_EQ(base::PlatformThread::GetCurrentThreadPriority(),
-             base::ThreadPriority::NORMAL);
-  }
-
-  void OnTestStart(const testing::TestInfo& test) override {
-    EXPECT_EQ(base::PlatformThread::GetCurrentThreadPriority(),
-              base::ThreadPriority::NORMAL);
-  }
-  void OnTestEnd(const testing::TestInfo& test) override {
-    if (check_thread_priority_at_test_end_) {
-      EXPECT_EQ(base::PlatformThread::GetCurrentThreadPriority(),
-                base::ThreadPriority::NORMAL);
-    }
-  }
-
- private:
-  const bool check_thread_priority_at_test_end_;
-
-  DISALLOW_COPY_AND_ASSIGN(CheckThreadPriority);
-};
-
 const std::string& GetProfileName() {
   static const NoDestructor<std::string> profile_name([]() {
     const CommandLine& command_line = *CommandLine::ForCurrentProcess();
@@ -407,11 +381,6 @@
   check_for_process_priority_ = false;
 }
 
-void TestSuite::DisableCheckForThreadPriorityAtTestEnd() {
-  DCHECK(!is_initialized_);
-  check_for_thread_priority_at_test_end_ = false;
-}
-
 void TestSuite::UnitTestAssertHandler(const char* file,
                                       int line,
                                       const StringPiece summary,
@@ -602,8 +571,6 @@
   if (check_for_process_priority_)
     listeners.Append(new CheckProcessPriority);
 #endif
-  listeners.Append(
-      new CheckThreadPriority(check_for_thread_priority_at_test_end_));
 
   AddTestLauncherResultPrinter();
 
diff --git a/base/test/test_suite.h b/base/test/test_suite.h
index 0565c5e..6e1e750 100644
--- a/base/test/test_suite.h
+++ b/base/test/test_suite.h
@@ -46,10 +46,6 @@
   // Disables checks for process priority. Most tests should not use this.
   void DisableCheckForProcessPriority();
 
-  // Disables checks for thread priority at test end. This may be used for tests
-  // that each run in their own process.
-  void DisableCheckForThreadPriorityAtTestEnd();
-
   // Disables checks for certain global objects being leaked across tests.
   void DisableCheckForLeakedGlobals();
 
@@ -98,7 +94,6 @@
 
   bool check_for_leaked_globals_ = true;
   bool check_for_process_priority_ = true;
-  bool check_for_thread_priority_at_test_end_ = true;
 
   bool is_initialized_ = false;
 
diff --git a/base/unguessable_token.h b/base/unguessable_token.h
index 222177a9..895dbc46 100644
--- a/base/unguessable_token.h
+++ b/base/unguessable_token.h
@@ -20,26 +20,30 @@
 struct UnguessableTokenHash;
 
 // UnguessableToken is, like Token, a randomly chosen 128-bit value. Unlike
-// Token however, a new UnguessableToken must always be generated at runtime
-// from a cryptographically strong random source (or copied or serialized and
+// Token, a new UnguessableToken is always generated at runtime from a
+// cryptographically strong random source (or copied or serialized and
 // deserialized from another such UnguessableToken). It can be used as part of a
 // larger aggregate type, or as an ID in and of itself.
 //
-// UnguessableToken can be used to implement "Capability-Based Security".
-// In other words, UnguessableToken can be used when the resource associated
-// with the ID needs to be protected against manipulation by other untrusted
-// agents in the system, and there is no other convenient way to verify the
-// authority of the agent to do so (because the resource is part of a table
-// shared across processes, for instance). In such a scheme, knowledge of the
-// token value in and of itself is sufficient proof of authority to carry out
-// an operation against the associated resource.
+// An UnguessableToken is a strong *bearer token*. Bearer tokens are like HTTP
+// cookies: if a caller has the token, the callee thereby considers the caller
+// authorized to request the operation the callee performs.
+//
+// UnguessableToken can be used when the resource associated with the ID needs
+// to be protected against manipulation by other untrusted agents in the system,
+// and there is no other convenient way to verify the authority of the agent to
+// do so (because the resource is part of a table shared across processes, for
+// instance). In such a scheme, knowledge of the token value in and of itself is
+// sufficient proof of authority to carry out an operation on the associated
+// resource.
 //
 // Use Create() for creating new UnguessableTokens.
 //
 // NOTE: It is illegal to send empty UnguessableTokens across processes, and
-// sending/receiving empty tokens should be treated as a security issue.
-// If there is a valid scenario for sending "no token" across processes,
-// base::Optional should be used instead of an empty token.
+// sending/receiving empty tokens should be treated as a security issue. If
+// there is a valid scenario for sending "no token" across processes, use
+// base::Optional instead of an empty token.
+
 class BASE_EXPORT UnguessableToken {
  public:
   // Create a unique UnguessableToken.
diff --git a/cc/benchmarks/invalidation_benchmark.cc b/cc/benchmarks/invalidation_benchmark.cc
index 8df14b6..39309d3a 100644
--- a/cc/benchmarks/invalidation_benchmark.cc
+++ b/cc/benchmarks/invalidation_benchmark.cc
@@ -63,9 +63,8 @@
 InvalidationBenchmark::~InvalidationBenchmark() = default;
 
 void InvalidationBenchmark::DidUpdateLayers(LayerTreeHost* layer_tree_host) {
-  LayerTreeHostCommon::CallFunctionForEveryLayer(
-      layer_tree_host,
-      [this](Layer* layer) { layer->RunMicroBenchmark(this); });
+  for (auto* layer : *layer_tree_host)
+    layer->RunMicroBenchmark(this);
 }
 
 void InvalidationBenchmark::RunOnLayer(PictureLayer* layer) {
diff --git a/cc/benchmarks/rasterize_and_record_benchmark.cc b/cc/benchmarks/rasterize_and_record_benchmark.cc
index 61bc655..2fa6600 100644
--- a/cc/benchmarks/rasterize_and_record_benchmark.cc
+++ b/cc/benchmarks/rasterize_and_record_benchmark.cc
@@ -86,9 +86,8 @@
 void RasterizeAndRecordBenchmark::DidUpdateLayers(
     LayerTreeHost* layer_tree_host) {
   layer_tree_host_ = layer_tree_host;
-  LayerTreeHostCommon::CallFunctionForEveryLayer(
-      layer_tree_host_,
-      [this](Layer* layer) { layer->RunMicroBenchmark(this); });
+  for (auto* layer : *layer_tree_host)
+    layer->RunMicroBenchmark(this);
 
   DCHECK(!results_.get());
   results_ = base::WrapUnique(new base::DictionaryValue);
diff --git a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
index 93b5d5d..89946a0 100644
--- a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
+++ b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
@@ -146,11 +146,10 @@
 
 void RasterizeAndRecordBenchmarkImpl::DidCompleteCommit(
     LayerTreeHostImpl* host) {
-  LayerTreeHostCommon::CallFunctionForEveryLayer(
-      host->active_tree(), [this](LayerImpl* layer) {
-        rasterize_results_.total_layers++;
-        layer->RunMicroBenchmark(this);
-      });
+  for (auto* layer : *host->active_tree()) {
+    rasterize_results_.total_layers++;
+    layer->RunMicroBenchmark(this);
+  }
 
   std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
   result->SetDouble("rasterize_time_ms",
diff --git a/cc/layers/effect_tree_layer_list_iterator.cc b/cc/layers/effect_tree_layer_list_iterator.cc
index dad09611..9270f7d 100644
--- a/cc/layers/effect_tree_layer_list_iterator.cc
+++ b/cc/layers/effect_tree_layer_list_iterator.cc
@@ -9,17 +9,16 @@
 EffectTreeLayerListIterator::EffectTreeLayerListIterator(
     LayerTreeImpl* layer_tree_impl)
     : state_(EffectTreeLayerListIterator::State::END),
+      layer_list_iterator_(layer_tree_impl->rbegin()),
       current_effect_tree_index_(EffectTree::kInvalidNodeId),
       next_effect_tree_index_(EffectTree::kInvalidNodeId),
       lowest_common_effect_tree_ancestor_index_(EffectTree::kInvalidNodeId),
       layer_tree_impl_(layer_tree_impl),
       effect_tree_(&layer_tree_impl->property_trees()->effect_tree) {
-  layer_list_iterator_ = layer_tree_impl->rbegin();
-
   // Find the front-most drawn layer.
   while (layer_list_iterator_ != layer_tree_impl->rend() &&
          !(*layer_list_iterator_)->contributes_to_drawn_render_surface()) {
-    layer_list_iterator_++;
+    ++layer_list_iterator_;
   }
 
   // If there are no drawn layers, start at the root render surface, if it
@@ -46,10 +45,10 @@
   switch (state_) {
     case State::LAYER:
       // Find the next drawn layer.
-      layer_list_iterator_++;
+      ++layer_list_iterator_;
       while (layer_list_iterator_ != layer_tree_impl_->rend() &&
              !(*layer_list_iterator_)->contributes_to_drawn_render_surface()) {
-        layer_list_iterator_++;
+        ++layer_list_iterator_;
       }
       if (layer_list_iterator_ == layer_tree_impl_->rend()) {
         next_effect_tree_index_ = EffectTree::kInvalidNodeId;
diff --git a/cc/layers/effect_tree_layer_list_iterator.h b/cc/layers/effect_tree_layer_list_iterator.h
index c9991638..577e14b5 100644
--- a/cc/layers/effect_tree_layer_list_iterator.h
+++ b/cc/layers/effect_tree_layer_list_iterator.h
@@ -99,7 +99,7 @@
   // When in state LAYER, this is the layer that's currently being visited.
   // Otherwise, this is the layer that will be visited the next time we're in
   // state LAYER.
-  LayerImplList::reverse_iterator layer_list_iterator_;
+  LayerTreeImpl::const_reverse_iterator layer_list_iterator_;
 
   // When in state LAYER, this is the render target effect tree index for the
   // currently visited layer. Otherwise, this is the the effect tree index of
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 5e5374e..f92363d 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -62,7 +62,6 @@
     SkColor background_color;
     FilterOperations filters[2];
     base::Optional<gfx::RRectF> backdrop_filter_bounds;
-    ElementId backdrop_mask_element_id;
     gfx::PointF filters_origin;
     float backdrop_filter_quality;
     gfx::RoundedCornersF corner_radii;
@@ -164,10 +163,6 @@
 
   // Remove the parent reference from all children and dependents.
   RemoveAllChildren();
-  if (inputs_.mask_layer.get()) {
-    DCHECK_EQ(this, inputs_.mask_layer->parent());
-    inputs_.mask_layer->RemoveFromParent();
-  }
 }
 
 void Layer::SetLayerTreeHost(LayerTreeHost* host) {
@@ -212,9 +207,6 @@
   for (size_t i = 0; i < inputs_.children.size(); ++i)
     inputs_.children[i]->SetLayerTreeHost(host);
 
-  if (inputs_.mask_layer.get())
-    inputs_.mask_layer->SetLayerTreeHost(host);
-
   if (host && !host->IsUsingLayerLists() &&
       host->mutator_host()->IsElementAnimating(element_id())) {
     host->SetNeedsCommit();
@@ -288,6 +280,11 @@
   child->SetSubtreePropertyChanged();
 
   index = std::min(index, inputs_.children.size());
+  if (inputs_.mask_layer && index && index == inputs_.children.size()) {
+    // Ensure that the mask layer is always the last child.
+    DCHECK_EQ(inputs_.mask_layer, inputs_.children.back().get());
+    index--;
+  }
   inputs_.children.insert(inputs_.children.begin() + index, child);
   SetNeedsFullTreeSync();
 }
@@ -295,16 +292,12 @@
 void Layer::RemoveFromParent() {
   DCHECK(IsPropertyChangeAllowed());
   if (parent_)
-    parent_->RemoveChildOrDependent(this);
+    parent_->RemoveChild(this);
 }
 
-void Layer::RemoveChildOrDependent(Layer* child) {
-  if (inputs_.mask_layer.get() == child) {
-    inputs_.mask_layer->SetParent(nullptr);
+void Layer::RemoveChild(Layer* child) {
+  if (child == inputs_.mask_layer)
     inputs_.mask_layer = nullptr;
-    SetNeedsFullTreeSync();
-    return;
-  }
 
   for (auto iter = inputs_.children.begin(); iter != inputs_.children.end();
        ++iter) {
@@ -377,7 +370,7 @@
   // Rounded corner clipping, bounds clipping and mask clipping can result in
   // new areas of subtrees being exposed on a bounds change. Ensure the damaged
   // areas are updated.
-  if (masks_to_bounds() || inputs_.mask_layer.get() || HasRoundedCorner()) {
+  if (masks_to_bounds() || IsMaskedByChild() || HasRoundedCorner()) {
     SetSubtreePropertyChanged();
     SetPropertyTreesNeedRebuild();
   }
@@ -588,7 +581,8 @@
 
   // Layer needs to clip to its bounds as well apply a clip rect. Intersect the
   // two to get the effective clip.
-  if (masks_to_bounds() || mask_layer() || filters().HasFilterThatMovesPixels())
+  if (masks_to_bounds() || IsMaskedByChild() ||
+      filters().HasFilterThatMovesPixels())
     return gfx::IntersectRects(layer_bounds, clip_rect_f);
 
   // Clip rect is the only clip effecting the layer.
@@ -597,25 +591,27 @@
 
 void Layer::SetMaskLayer(scoped_refptr<PictureLayer> mask_layer) {
   DCHECK(IsPropertyChangeAllowed());
-  if (inputs_.mask_layer.get() == mask_layer)
-    return;
   DCHECK(!layer_tree_host_ || !layer_tree_host_->IsUsingLayerLists());
-  if (inputs_.mask_layer.get()) {
+  if (inputs_.mask_layer == mask_layer)
+    return;
+  if (inputs_.mask_layer) {
     DCHECK_EQ(this, inputs_.mask_layer->parent());
     inputs_.mask_layer->RemoveFromParent();
   }
-  inputs_.mask_layer = mask_layer;
-  if (inputs_.mask_layer.get()) {
+  // Clear mask_layer first and set it later because InsertChild() checks it to
+  // ensure the mask layer is the last child.
+  inputs_.mask_layer = nullptr;
+  if (mask_layer) {
     // The mask layer should not have any children.
-    DCHECK(inputs_.mask_layer->children().empty());
+    DCHECK(mask_layer->children().empty());
 
-    inputs_.mask_layer->RemoveFromParent();
-    DCHECK(!inputs_.mask_layer->parent());
-    inputs_.mask_layer->SetParent(this);
-    inputs_.mask_layer->set_is_mask();
+    mask_layer->SetIsDrawable(true);
+    mask_layer->SetBlendMode(SkBlendMode::kDstIn);
+    mask_layer->SetIsMask(true);
+    AddChild(mask_layer);
   }
+  inputs_.mask_layer = mask_layer.get();
   SetSubtreePropertyChanged();
-  SetNeedsFullTreeSync();
 }
 
 void Layer::SetFilters(const FilterOperations& filters) {
@@ -1391,6 +1387,9 @@
                "Layer::PushPropertiesTo");
   DCHECK(layer_tree_host_);
 
+  if (inputs_.mask_layer)
+    DCHECK_EQ(bounds(), inputs_.mask_layer->bounds());
+
   // The element id should be set first because other setters may
   // depend on it. Referencing element id on a layer is
   // deprecated. http://crbug.com/709137
@@ -1469,9 +1468,6 @@
   needs_show_scrollbars_ = false;
   subtree_property_changed_ = false;
   inputs_.update_rect = gfx::Rect();
-
-  if (mask_layer())
-    DCHECK_EQ(bounds().ToString(), mask_layer()->bounds().ToString());
 }
 
 void Layer::TakeCopyRequests(
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 326e50c7..a9aaa92 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -203,7 +203,7 @@
   // for each matching pixel.
   // This is for layer tree mode only.
   void SetMaskLayer(scoped_refptr<PictureLayer> mask_layer);
-  PictureLayer* mask_layer() { return inputs_.mask_layer.get(); }
+  bool IsMaskedByChild() const { return !!inputs_.mask_layer; }
 
   // Marks the |dirty_rect| as being changed, which will cause a commit and
   // the compositor to submit a new frame with a damage rect that includes the
@@ -295,10 +295,6 @@
     return inputs_.backdrop_filter_bounds;
   }
 
-  const ElementId backdrop_mask_element_id() const {
-    return inputs_.backdrop_mask_element_id;
-  }
-
   void SetBackdropFilterQuality(const float quality);
   float backdrop_filter_quality() const {
     return inputs_.backdrop_filter_quality;
@@ -837,7 +833,7 @@
   void SetParent(Layer* layer);
 
   // This should only be called from RemoveFromParent().
-  void RemoveChildOrDependent(Layer* child);
+  void RemoveChild(Layer* child);
 
   // When we detach or attach layer to new LayerTreeHost, all property trees'
   // indices becomes invalid.
@@ -867,7 +863,9 @@
     gfx::Size bounds;
     gfx::Rect clip_rect;
 
-    scoped_refptr<PictureLayer> mask_layer;
+    // If not null, points to one of child layers which is set as mask layer
+    // by SetMaskLayer().
+    Layer* mask_layer;
 
     int layer_id;
 
@@ -925,7 +923,6 @@
     FilterOperations filters;
     FilterOperations backdrop_filters;
     base::Optional<gfx::RRectF> backdrop_filter_bounds;
-    ElementId backdrop_mask_element_id;
     gfx::PointF filters_origin;
     float backdrop_filter_quality;
 
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index 89c23a3..0f324e7 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -288,6 +288,7 @@
   EXPECT_SET_NEEDS_COMMIT(1, top->SetBounds(arbitrary_size));
   EXPECT_SET_NEEDS_COMMIT(0, mask_layer1->SetBounds(arbitrary_size));
   EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(1);
+  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
   EXECUTE_AND_VERIFY_SUBTREE_CHANGED(top->SetMaskLayer(mask_layer1));
 
   // Set up the impl layers after the full tree is constructed, including the
@@ -497,6 +498,69 @@
   EXPECT_SET_NEEDS_FULL_TREE_SYNC(AtLeast(1), child->RemoveFromParent());
 }
 
+TEST_F(LayerTest, SetMaskLayer) {
+  scoped_refptr<Layer> parent = Layer::Create();
+  FakeContentLayerClient client;
+  scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client);
+
+  parent->SetMaskLayer(mask);
+  ASSERT_EQ(1u, parent->children().size());
+  EXPECT_EQ(parent.get(), mask->parent());
+  EXPECT_EQ(mask.get(), parent->children()[0]);
+  EXPECT_TRUE(parent->IsMaskedByChild());
+
+  parent->SetMaskLayer(mask);
+  ASSERT_EQ(1u, parent->children().size());
+  EXPECT_EQ(parent.get(), mask->parent());
+  EXPECT_EQ(mask.get(), parent->children()[0]);
+  EXPECT_TRUE(parent->IsMaskedByChild());
+
+  scoped_refptr<PictureLayer> mask2 = PictureLayer::Create(&client);
+  parent->SetMaskLayer(mask2);
+  EXPECT_FALSE(mask->parent());
+  ASSERT_EQ(1u, parent->children().size());
+  EXPECT_EQ(parent.get(), mask2->parent());
+  EXPECT_EQ(mask2.get(), parent->children()[0]);
+  EXPECT_TRUE(parent->IsMaskedByChild());
+
+  parent->SetMaskLayer(nullptr);
+  EXPECT_EQ(0u, parent->children().size());
+  EXPECT_FALSE(mask2->parent());
+  EXPECT_FALSE(parent->IsMaskedByChild());
+}
+
+TEST_F(LayerTest, RemoveMaskLayerFromParent) {
+  scoped_refptr<Layer> parent = Layer::Create();
+  FakeContentLayerClient client;
+  scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client);
+
+  parent->SetMaskLayer(mask);
+  mask->RemoveFromParent();
+  EXPECT_EQ(0u, parent->children().size());
+  EXPECT_FALSE(mask->parent());
+  EXPECT_FALSE(parent->IsMaskedByChild());
+
+  scoped_refptr<PictureLayer> mask2 = PictureLayer::Create(&client);
+  parent->SetMaskLayer(mask2);
+  EXPECT_TRUE(parent->IsMaskedByChild());
+}
+
+TEST_F(LayerTest, AddChildAfterSetMaskLayer) {
+  scoped_refptr<Layer> parent = Layer::Create();
+  FakeContentLayerClient client;
+  scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client);
+  parent->SetMaskLayer(mask);
+  EXPECT_TRUE(parent->IsMaskedByChild());
+
+  parent->AddChild(Layer::Create());
+  EXPECT_EQ(mask.get(), parent->children().back().get());
+  EXPECT_TRUE(parent->IsMaskedByChild());
+
+  parent->InsertChild(Layer::Create(), parent->children().size());
+  EXPECT_EQ(mask.get(), parent->children().back().get());
+  EXPECT_TRUE(parent->IsMaskedByChild());
+}
+
 TEST_F(LayerTest, AddSameChildTwice) {
   EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(AtLeast(1));
 
@@ -948,6 +1012,7 @@
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetHideLayerAndSubtree(true));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetElementId(ElementId(2)));
 
+  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
   EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, test_layer->SetMaskLayer(mask_layer1));
 
   // The above tests should not have caused a change to the needs_display flag.
@@ -1096,9 +1161,6 @@
 
   for (size_t i = 0; i < layer->children().size(); ++i)
     AssertLayerTreeHostMatchesForSubtree(layer->children()[i].get(), host);
-
-  if (layer->mask_layer())
-    AssertLayerTreeHostMatchesForSubtree(layer->mask_layer(), host);
 }
 
 class LayerLayerTreeHostTest : public testing::Test {};
@@ -1941,9 +2003,5 @@
   EXPECT_EQ(nullptr, layer_tree_host_->LayerByElementId(element_id));
 }
 
-TEST(A, B) {
-  LOG(ERROR) << sizeof(Layer);
-}
-
 }  // namespace
 }  // namespace cc
diff --git a/cc/layers/mirror_layer_impl.cc b/cc/layers/mirror_layer_impl.cc
index 80363cd0..7a916bf 100644
--- a/cc/layers/mirror_layer_impl.cc
+++ b/cc/layers/mirror_layer_impl.cc
@@ -55,8 +55,7 @@
   auto* quad = render_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
   quad->SetNew(shared_quad_state, content_rect, unoccluded_content_rect,
                mirrored_layer_id_, mask_resource_id, mask_uv_rect,
-               mask_texture_size, /*mask_applies_to_backdrop=*/false,
-               mirrored_effect_node->surface_contents_scale,
+               mask_texture_size, mirrored_effect_node->surface_contents_scale,
                mirrored_effect_node->filters_origin,
                gfx::RectF(gfx::Rect(content_rect.size())),
                !layer_tree_impl()->settings().enable_edge_anti_aliasing, 0.f);
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index 7206fd0..5f08cf3 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -53,11 +53,6 @@
 
   PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer);
 
-  // This is before Layer::PushPropertiesTo() because PictureLayerImpl requires
-  // that is_mask flag can change only when the layer is just created before
-  // any property tree state is assigned.
-  layer_impl->SetIsMask(is_mask());
-
   Layer::PushPropertiesTo(base_layer);
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
                "PictureLayer::PushPropertiesTo");
@@ -69,6 +64,9 @@
   layer_impl->set_gpu_raster_max_texture_size(
       layer_tree_host()->device_viewport_rect().size());
 
+  layer_impl->SetIsMask(is_mask());
+  layer_impl->SetIsBackdropFilterMask(is_backdrop_filter_mask());
+
   // TODO(enne): http://crbug.com/918126 debugging
   CHECK(this);
   if (!recording_source_) {
@@ -234,6 +232,23 @@
   return picture_layer_inputs_.client && Layer::HasDrawableContent();
 }
 
+void PictureLayer::SetIsMask(bool is_mask) {
+  if (picture_layer_inputs_.is_mask == is_mask)
+    return;
+
+  picture_layer_inputs_.is_mask = is_mask;
+  SetNeedsCommit();
+}
+
+void PictureLayer::SetIsBackdropFilterMask(bool is_backdrop_filter_mask) {
+  if (picture_layer_inputs_.is_backdrop_filter_mask == is_backdrop_filter_mask)
+    return;
+
+  DCHECK(!is_backdrop_filter_mask || is_mask());
+  picture_layer_inputs_.is_backdrop_filter_mask = is_backdrop_filter_mask;
+  SetNeedsCommit();
+}
+
 void PictureLayer::RunMicroBenchmark(MicroBenchmark* benchmark) {
   benchmark->RunOnLayer(this);
 }
diff --git a/cc/layers/picture_layer.h b/cc/layers/picture_layer.h
index cb9d1bf..0838827 100644
--- a/cc/layers/picture_layer.h
+++ b/cc/layers/picture_layer.h
@@ -37,9 +37,16 @@
     return picture_layer_inputs_.transformed_rasterization_allowed;
   }
 
-  void set_is_mask() { picture_layer_inputs_.is_mask = true; }
+  // TODO(crbug.com/1003414): Remove this flag when we tile mask layers (except
+  // for backdrop filter masks) normally.
+  void SetIsMask(bool is_mask);
   bool is_mask() const { return picture_layer_inputs_.is_mask; }
 
+  void SetIsBackdropFilterMask(bool is_backdrop_filter_mask);
+  bool is_backdrop_filter_mask() const {
+    return picture_layer_inputs_.is_backdrop_filter_mask;
+  }
+
   // Layer interface.
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
@@ -71,6 +78,7 @@
     bool nearest_neighbor = false;
     bool transformed_rasterization_allowed = false;
     bool is_mask = false;
+    bool is_backdrop_filter_mask = false;
     gfx::Rect recorded_viewport;
     scoped_refptr<DisplayItemList> display_list;
     size_t painter_reported_memory_usage = 0;
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 7483549..0bb56d2 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -92,6 +92,7 @@
       raster_contents_scale_(0.f),
       low_res_raster_contents_scale_(0.f),
       is_mask_(false),
+      is_backdrop_filter_mask_(false),
       was_screen_space_transform_animating_(false),
       only_used_low_res_last_append_quads_(false),
       nearest_neighbor_(false),
@@ -125,15 +126,6 @@
   UnregisterAnimatedImages();
 }
 
-void PictureLayerImpl::SetIsMask(bool is_mask) {
-  if (is_mask_ == is_mask)
-    return;
-
-  // This flag can't be set after property trees have been updated.
-  DCHECK_EQ(effect_tree_index(), EffectTree::kInvalidNodeId);
-  is_mask_ = is_mask;
-}
-
 const char* PictureLayerImpl::LayerTypeAsString() const {
   return "cc::PictureLayerImpl";
 }
@@ -146,10 +138,6 @@
 void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) {
   PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer);
 
-  // This is before LayerImpl::PushPropertiesTo() because PictureLayerImpl
-  // requires that is_mask flag can change only when the layer is just created
-  // before any property tree state is assigned.
-  layer_impl->SetIsMask(is_mask());
 
   LayerImpl::PushPropertiesTo(base_layer);
 
@@ -165,6 +153,8 @@
 
   layer_impl->SetNearestNeighbor(nearest_neighbor_);
   layer_impl->SetUseTransformedRasterization(use_transformed_rasterization_);
+  layer_impl->SetIsMask(is_mask_);
+  layer_impl->SetIsBackdropFilterMask(is_backdrop_filter_mask_);
 
   // Solid color layers have no tilings.
   DCHECK(!raster_source_->IsSolidColor() || tilings_->num_tilings() == 0);
@@ -197,6 +187,13 @@
 
 void PictureLayerImpl::AppendQuads(viz::RenderPass* render_pass,
                                    AppendQuadsData* append_quads_data) {
+  // RenderSurfaceImpl::AppendQuads sets mask properties in the DrawQuad for
+  // the masked surface, which will apply to both the backdrop filter and the
+  // contents of the masked surface, so we should not quad of the mask layer
+  // in DstIn blend mode.
+  if (is_backdrop_filter_mask_)
+    return;
+
   // The bounds and the pile size may differ if the pile wasn't updated (ie.
   // PictureLayer::Update didn't happen). In that case the pile will be empty.
   DCHECK(raster_source_->GetSize().IsEmpty() ||
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index f76ff2cf..aafc8dd6 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -43,9 +43,17 @@
 
   PictureLayerImpl& operator=(const PictureLayerImpl&) = delete;
 
-  void SetIsMask(bool is_mask);
+  // TODO(crbug.com/1003414): Remove this flag when we tile mask layers (except
+  // for backdrop filter masks) normally.
+  void SetIsMask(bool is_mask) { is_mask_ = is_mask; }
   bool is_mask() const { return is_mask_; }
 
+  void SetIsBackdropFilterMask(bool is_backdrop_filter_mask) {
+    DCHECK(!is_backdrop_filter_mask || is_mask_);
+    is_backdrop_filter_mask_ = is_backdrop_filter_mask;
+  }
+  bool is_backdrop_filter_mask() const { return is_backdrop_filter_mask_; }
+
   // LayerImpl overrides.
   const char* LayerTypeAsString() const override;
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
@@ -222,6 +230,7 @@
   float low_res_raster_contents_scale_;
 
   bool is_mask_ : 1;
+  bool is_backdrop_filter_mask_ : 1;
 
   bool was_screen_space_transform_animating_ : 1;
   bool only_used_low_res_last_append_quads_ : 1;
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 8ea26c90..4afc2f7 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -1159,10 +1159,9 @@
 
   // Mask layers dont create low res since they always fit on one tile.
   CreateEffectNode(pending_layer());
-  auto* mask = AddMaskLayer<FakePictureLayerImpl>(
-      host_impl()->pending_tree(), pending_layer(), pending_raster_source);
-  mask->SetBounds(layer_bounds);
-  mask->SetDrawsContent(true);
+  auto* mask = AddLayer<FakePictureLayerImpl>(host_impl()->pending_tree(),
+                                              pending_raster_source);
+  SetupMaskProperties(pending_layer(), mask);
 
   UpdateDrawProperties(host_impl()->pending_tree());
 
@@ -1188,10 +1187,9 @@
   SetupPendingTree(valid_raster_source);
 
   CreateEffectNode(pending_layer());
-  auto* pending_mask = AddMaskLayer<FakePictureLayerImpl>(
-      host_impl()->pending_tree(), pending_layer(), valid_raster_source);
-  pending_mask->SetBounds(layer_bounds);
-  pending_mask->SetDrawsContent(true);
+  auto* pending_mask = AddLayer<FakePictureLayerImpl>(
+      host_impl()->pending_tree(), valid_raster_source);
+  SetupMaskProperties(pending_layer(), pending_mask);
 
   host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1));
   UpdateDrawProperties(host_impl()->pending_tree());
@@ -1315,10 +1313,9 @@
   SetupPendingTree(valid_raster_source);
 
   CreateEffectNode(pending_layer());
-  auto* pending_mask = AddMaskLayer<FakePictureLayerImpl>(
-      host_impl()->pending_tree(), pending_layer(), valid_raster_source);
-  pending_mask->SetBounds(layer_bounds);
-  pending_mask->SetDrawsContent(true);
+  auto* pending_mask = AddLayer<FakePictureLayerImpl>(
+      host_impl()->pending_tree(), valid_raster_source);
+  SetupMaskProperties(pending_layer(), pending_mask);
 
   host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1));
   UpdateDrawProperties(host_impl()->pending_tree());
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
index 0e81947..464eaf40 100644
--- a/cc/layers/render_surface_impl.cc
+++ b/cc/layers/render_surface_impl.cc
@@ -121,11 +121,6 @@
       layer_tree_impl_ ? layer_tree_impl_->device_scale_factor() : 1);
 }
 
-LayerImpl* RenderSurfaceImpl::MaskLayer() {
-  int mask_layer_id = OwningEffectNode()->mask_layer_id;
-  return layer_tree_impl_->LayerById(mask_layer_id);
-}
-
 LayerImpl* RenderSurfaceImpl::BackdropMaskLayer() const {
   ElementId mask_element_id = OwningEffectNode()->backdrop_mask_element_id;
   if (!mask_element_id)
@@ -133,10 +128,6 @@
   return layer_tree_impl_->LayerByElementId(mask_element_id);
 }
 
-bool RenderSurfaceImpl::HasMask() const {
-  return OwningEffectNode()->mask_layer_id != Layer::INVALID_ID;
-}
-
 bool RenderSurfaceImpl::HasMaskingContributingSurface() const {
   return OwningEffectNode()->has_masking_child;
 }
@@ -423,14 +414,7 @@
                               GetDebugBorderWidth());
   }
 
-  DCHECK(!(MaskLayer() && BackdropMaskLayer()))
-      << "Can't support both a mask_layer and a backdrop_mask_layer";
-  bool mask_applies_to_backdrop = BackdropMaskLayer();
-  PictureLayerImpl* mask_layer =
-      mask_applies_to_backdrop
-          ? static_cast<PictureLayerImpl*>(BackdropMaskLayer())
-          : static_cast<PictureLayerImpl*>(MaskLayer());
-
+  LayerImpl* mask_layer = BackdropMaskLayer();
   viz::ResourceId mask_resource_id = 0;
   gfx::Size mask_texture_size;
   gfx::RectF mask_uv_rect;
@@ -455,10 +439,14 @@
     gfx::SizeF unclipped_mask_target_size =
         gfx::ScaleSize(gfx::SizeF(mask_layer->bounds()),
                        surface_contents_scale.x(), surface_contents_scale.y());
+    gfx::Vector2dF mask_offset = gfx::ScaleVector2d(
+        mask_layer->offset_to_transform_parent(), surface_contents_scale.x(),
+        surface_contents_scale.y());
     // Convert content_rect from target space to normalized mask UV space.
     // Where |unclipped_mask_target_size| maps to |mask_uv_size|.
     mask_uv_rect = gfx::ScaleRect(
-        gfx::RectF(content_rect()),
+        // Translate content_rect into mask resource's space.
+        gfx::RectF(content_rect()) - mask_offset,
         mask_uv_size.width() / unclipped_mask_target_size.width(),
         mask_uv_size.height() / unclipped_mask_target_size.height());
   }
@@ -467,8 +455,7 @@
   auto* quad = render_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
   quad->SetNew(shared_quad_state, content_rect(), unoccluded_content_rect, id(),
                mask_resource_id, mask_uv_rect, mask_texture_size,
-               mask_applies_to_backdrop, surface_contents_scale,
-               FiltersOrigin(), tex_coord_rect,
+               surface_contents_scale, FiltersOrigin(), tex_coord_rect,
                !layer_tree_impl_->settings().enable_edge_anti_aliasing,
                OwningEffectNode()->backdrop_filter_quality);
 }
diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h
index 38fcaea8..545c840 100644
--- a/cc/layers/render_surface_impl.h
+++ b/cc/layers/render_surface_impl.h
@@ -154,8 +154,6 @@
 
   uint64_t id() const { return stable_id_; }
 
-  LayerImpl* MaskLayer();
-  bool HasMask() const;
   bool HasMaskingContributingSurface() const;
 
   const FilterOperations& Filters() const;
diff --git a/cc/layers/render_surface_impl_unittest.cc b/cc/layers/render_surface_impl_unittest.cc
index 0e69bc4d..8bcdb89 100644
--- a/cc/layers/render_surface_impl_unittest.cc
+++ b/cc/layers/render_surface_impl_unittest.cc
@@ -87,12 +87,10 @@
   CreateTransformNode(surface).local = scale;
   CreateEffectNode(surface).render_surface_reason = RenderSurfaceReason::kTest;
 
-  auto* mask_layer =
-      impl.AddMaskLayer<FakeMaskLayerImpl>(surface, raster_source);
+  auto* mask_layer = impl.AddLayer<FakeMaskLayerImpl>(raster_source);
   mask_layer->set_resource_size(
       gfx::ScaleToCeiledSize(layer_size, scale_factor));
-  mask_layer->SetDrawsContent(true);
-  mask_layer->SetBounds(layer_size);
+  SetupMaskProperties(surface, mask_layer);
 
   auto* child = impl.AddLayer<LayerImpl>();
   child->SetDrawsContent(true);
@@ -118,8 +116,10 @@
   DCHECK(render_pass->quad_list.front());
   const viz::RenderPassDrawQuad* quad =
       viz::RenderPassDrawQuad::MaterialCast(render_pass->quad_list.front());
-  EXPECT_EQ(gfx::RectF(0, 0, 1, 1), quad->mask_uv_rect);
+  // Mask layers don't use quad's mask functionality.
+  EXPECT_EQ(gfx::RectF(), quad->mask_uv_rect);
   EXPECT_EQ(gfx::Vector2dF(2.f, 2.f), quad->filters_scale);
+  EXPECT_EQ(0u, quad->mask_resource_id());
 }
 
 TEST(RenderSurfaceLayerImplTest, ResourcelessAppendQuadsSkipMask) {
diff --git a/cc/layers/render_surface_unittest.cc b/cc/layers/render_surface_unittest.cc
index 3544436..723d930 100644
--- a/cc/layers/render_surface_unittest.cc
+++ b/cc/layers/render_surface_unittest.cc
@@ -221,16 +221,15 @@
   CopyProperties(root, layer);
   CreateEffectNode(layer);
 
-  auto* mask_layer =
-      impl.AddMaskLayer<FakePictureLayerImplForRenderSurfaceTest>(layer);
+  auto* mask_layer = impl.AddLayer<FakePictureLayerImplForRenderSurfaceTest>();
   scoped_refptr<FakeRasterSource> raster_source(
       FakeRasterSource::CreateFilled(mask_layer->bounds()));
   mask_layer->SetRasterSourceOnActive(raster_source, Region());
-  mask_layer->SetDrawsContent(true);
   std::vector<gfx::Rect> quad_rects;
   quad_rects.push_back(gfx::Rect(0, 0, 100, 100));
   quad_rects.push_back(gfx::Rect(100, 0, 100, 100));
   mask_layer->SetQuadRectsForTesting(quad_rects);
+  SetupMaskProperties(layer, mask_layer);
 
   UpdateDrawProperties(impl.host_impl()->active_tree());
 
diff --git a/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc b/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc
index 48ec0281..b80ed04 100644
--- a/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc
+++ b/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc
@@ -289,15 +289,15 @@
   quad3_root_1->SetNew(shared_quad_state3_root, /*rect=*/rect3_root,
                        /*visible_rect=*/rect3_root, /*render_pass_id=*/3,
                        /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                       /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1),
-                       gfx::PointF(), gfx::RectF(), false, 1.0f);
+                       gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                       1.0f);
   auto* quad3_root_2 =
       pass3_root->quad_list.AllocateAndConstruct<viz::RenderPassDrawQuad>();
   quad3_root_2->SetNew(shared_quad_state3_root, /*rect=*/rect3_root,
                        /*visible_rect=*/rect3_root, /*render_pass_id=*/4,
                        /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                       /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1),
-                       gfx::PointF(), gfx::RectF(), false, 1.0f);
+                       gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                       1.0f);
   pass_list.push_back(std::move(pass3_root));
 
   SendRenderPassList(&pass_list, /*hit_test_data_changed=*/false);
@@ -393,15 +393,15 @@
   quad2_root_1->SetNew(shared_quad_state2_root, /*rect=*/rect2_root,
                        /*visible_rect=*/rect2_root, /*render_pass_id=*/2,
                        /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                       /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1),
-                       gfx::PointF(), gfx::RectF(), false, 1.0f);
+                       gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                       1.0f);
   auto* quad2_root_2 =
       pass2_root->quad_list.AllocateAndConstruct<viz::RenderPassDrawQuad>();
   quad2_root_2->SetNew(shared_quad_state2_root, /*rect=*/rect2_root,
                        /*visible_rect=*/rect2_root, /*render_pass_id=*/3,
                        /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                       /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1),
-                       gfx::PointF(), gfx::RectF(), false, 1.0f);
+                       gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                       1.0f);
   pass_list.push_back(std::move(pass2_root));
 
   SendRenderPassList(&pass_list, /*hit_test_data_changed=*/true);
diff --git a/cc/test/layer_test_common.h b/cc/test/layer_test_common.h
index 38ca9ec9..a538cc01 100644
--- a/cc/test/layer_test_common.h
+++ b/cc/test/layer_test_common.h
@@ -85,27 +85,6 @@
                                  std::forward<Args>(args)...);
     }
 
-    template <typename T, typename... Args>
-    T* AddMaskLayer(LayerImpl* origin, Args&&... args) {
-      static_assert(std::is_base_of<PictureLayerImpl, T>::value, "");
-      std::unique_ptr<T> layer =
-          T::Create(host_impl()->active_tree(), layer_impl_id_++,
-                    std::forward<Args>(args)...);
-      layer->SetBounds(origin->bounds());
-      layer->SetIsMask(true);
-      T* ptr = layer.get();
-      host_impl()->active_tree()->AddMaskLayer(std::move(layer));
-      if (host_impl()->active_tree()->settings().use_layer_lists) {
-        auto* origin_effect = GetEffectNode(origin);
-        origin_effect->render_surface_reason = RenderSurfaceReason::kMask;
-        origin_effect->is_masked = true;
-        origin_effect->mask_layer_id = ptr->id();
-        ptr->SetOffsetToTransformParent(origin->offset_to_transform_parent());
-        CopyProperties(origin, ptr);
-      }
-      return ptr;
-    }
-
     void CalcDrawProps(const gfx::Size& viewport_size);
     void AppendQuadsWithOcclusion(LayerImpl* layer_impl,
                                   const gfx::Rect& occluded);
diff --git a/cc/test/property_tree_test_utils.cc b/cc/test/property_tree_test_utils.cc
index 0d5159a..293cfb75 100644
--- a/cc/test/property_tree_test_utils.cc
+++ b/cc/test/property_tree_test_utils.cc
@@ -146,6 +146,28 @@
   return *node;
 }
 
+template <typename LayerType, typename MaskLayerType>
+void SetupMaskPropertiesInternal(LayerType* masked_layer,
+                                 MaskLayerType* mask_layer) {
+  mask_layer->SetIsMask(true);
+  if (!GetEffectNode(masked_layer)->backdrop_filters.IsEmpty())
+    mask_layer->SetIsBackdropFilterMask(true);
+  mask_layer->SetBounds(masked_layer->bounds());
+  auto* masked_effect = GetEffectNode(masked_layer);
+  masked_effect->render_surface_reason = RenderSurfaceReason::kMask;
+  masked_effect->has_masking_child = true;
+  if (!masked_effect->backdrop_filters.IsEmpty()) {
+    if (!mask_layer->element_id())
+      mask_layer->SetElementId(LayerIdToElementIdForTesting(mask_layer->id()));
+    masked_effect->backdrop_mask_element_id = mask_layer->element_id();
+  }
+
+  CopyProperties(masked_layer, mask_layer);
+  mask_layer->SetOffsetToTransformParent(
+      masked_layer->offset_to_transform_parent());
+  CreateEffectNode(mask_layer).blend_mode = SkBlendMode::kDstIn;
+}
+
 template <typename LayerType>
 void SetScrollOffsetInternal(LayerType* layer,
                              const gfx::ScrollOffset& scroll_offset) {
@@ -253,6 +275,17 @@
   return CreateScrollNodeInternal(layer, parent_id);
 }
 
+void SetupMaskProperties(Layer* masked_layer, PictureLayer* mask_layer) {
+  mask_layer->SetIsDrawable(true);
+  SetupMaskPropertiesInternal(masked_layer, mask_layer);
+}
+
+void SetupMaskProperties(LayerImpl* masked_layer,
+                         PictureLayerImpl* mask_layer) {
+  mask_layer->SetDrawsContent(true);
+  SetupMaskPropertiesInternal(masked_layer, mask_layer);
+}
+
 void SetScrollOffset(Layer* layer, const gfx::ScrollOffset& scroll_offset) {
   layer->SetScrollOffset(scroll_offset);
   SetScrollOffsetInternal(layer, scroll_offset);
diff --git a/cc/test/property_tree_test_utils.h b/cc/test/property_tree_test_utils.h
index 295d5c3..1f803c4 100644
--- a/cc/test/property_tree_test_utils.h
+++ b/cc/test/property_tree_test_utils.h
@@ -17,6 +17,8 @@
 
 class Layer;
 class LayerImpl;
+class PictureLayer;
+class PictureLayerImpl;
 
 // Sets up properties that apply to the root layer.
 void SetupRootProperties(Layer* root);
@@ -50,6 +52,9 @@
 ScrollNode& CreateScrollNode(LayerImpl*,
                              int parent_id = ScrollTree::kInvalidNodeId);
 
+void SetupMaskProperties(LayerImpl* masked_layer, PictureLayerImpl* mask_layer);
+void SetupMaskProperties(Layer* masked_layer, PictureLayer* mask_layer);
+
 template <typename LayerType>
 TransformNode* GetTransformNode(const LayerType* layer) {
   return GetPropertyTrees(layer)->transform_tree.Node(
diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc
index 6ff1bf3..924bc79a 100644
--- a/cc/test/render_pass_test_utils.cc
+++ b/cc/test/render_pass_test_utils.cc
@@ -117,8 +117,8 @@
                        SkBlendMode::kSrcOver, 0);
   auto* quad = to_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
   quad->SetNew(shared_state, output_rect, output_rect, contributing_pass->id, 0,
-               gfx::RectF(), gfx::Size(), false, gfx::Vector2dF(),
-               gfx::PointF(), gfx::RectF(), false, 1.0f);
+               gfx::RectF(), gfx::Size(), gfx::Vector2dF(), gfx::PointF(),
+               gfx::RectF(), false, 1.0f);
   return quad;
 }
 
@@ -136,7 +136,7 @@
   gfx::Size arbitrary_nonzero_size(1, 1);
   quad->SetNew(shared_state, output_rect, output_rect, contributing_pass->id,
                mask_resource_id, gfx::RectF(output_rect),
-               arbitrary_nonzero_size, false, gfx::Vector2dF(), gfx::PointF(),
+               arbitrary_nonzero_size, gfx::Vector2dF(), gfx::PointF(),
                gfx::RectF(), false, 1.0f);
 }
 
@@ -188,8 +188,8 @@
         to_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
     render_pass_quad->SetNew(shared_state, rect, visible_rect, child_pass_id,
                              resource5, gfx::RectF(rect), gfx::Size(73, 26),
-                             false, gfx::Vector2dF(), gfx::PointF(),
-                             gfx::RectF(), false, 1.0f);
+                             gfx::Vector2dF(), gfx::PointF(), gfx::RectF(),
+                             false, 1.0f);
   }
 
   auto* solid_color_quad =
@@ -364,8 +364,8 @@
         to_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
     render_pass_quad->SetNew(shared_state, rect, visible_rect, child_pass_id,
                              mapped_resource5, gfx::RectF(rect),
-                             gfx::Size(73, 26), false, gfx::Vector2dF(),
-                             gfx::PointF(), gfx::RectF(), false, 1.0f);
+                             gfx::Size(73, 26), gfx::Vector2dF(), gfx::PointF(),
+                             gfx::RectF(), false, 1.0f);
   }
 
   viz::SolidColorDrawQuad* solid_color_quad =
diff --git a/cc/test/test_layer_tree_host_base.h b/cc/test/test_layer_tree_host_base.h
index 2e8e086..3e76715 100644
--- a/cc/test/test_layer_tree_host_base.h
+++ b/cc/test/test_layer_tree_host_base.h
@@ -72,25 +72,6 @@
     return result;
   }
 
-  template <typename T, typename... Args>
-  T* AddMaskLayer(LayerTreeImpl* layer_tree_impl,
-                  LayerImpl* masked_layer,
-                  Args&&... args) {
-    static_assert(std::is_base_of<PictureLayerImpl, T>::value, "");
-    std::unique_ptr<T> mask = T::Create(layer_tree_impl, next_layer_id_++,
-                                        std::forward<Args>(args)...);
-    T* result = mask.get();
-    layer_tree_impl->AddMaskLayer(std::move(mask));
-    result->SetIsMask(true);
-    CopyProperties(masked_layer, result);
-    auto* masked_effect = GetEffectNode(masked_layer);
-    masked_effect->render_surface_reason = RenderSurfaceReason::kMask;
-    masked_effect->mask_layer_id = result->id();
-    masked_effect->is_masked = true;
-    GetPropertyTrees(masked_layer)->effect_tree.AddMaskLayerId(result->id());
-    return result;
-  }
-
   int root_id() const { return root_id_; }
 
  private:
diff --git a/cc/trees/damage_tracker.cc b/cc/trees/damage_tracker.cc
index 85e9cd7..5f8642e7 100644
--- a/cc/trees/damage_tracker.cc
+++ b/cc/trees/damage_tracker.cc
@@ -170,8 +170,6 @@
   // These functions cannot be bypassed with early-exits, even if we know what
   // the damage will be for this frame, because we need to update the damage
   // tracker state to correctly track the next frame.
-  DamageAccumulator damage_from_surface_mask =
-      TrackDamageFromSurfaceMask(render_surface->MaskLayer());
   DamageAccumulator damage_from_leftover_rects = TrackDamageFromLeftoverRects();
   // True if any layer is removed.
   has_damage_from_contributing_content_ |=
@@ -185,7 +183,6 @@
   } else {
     // TODO(shawnsingh): can we clamp this damage to the surface's content rect?
     // (affects performance, but not correctness)
-    damage_for_this_update_.Union(damage_from_surface_mask);
     damage_for_this_update_.Union(damage_from_leftover_rects);
 
     gfx::Rect damage_rect;
@@ -239,24 +236,6 @@
   return *it;
 }
 
-DamageTracker::DamageAccumulator DamageTracker::TrackDamageFromSurfaceMask(
-    LayerImpl* target_surface_mask_layer) {
-  DamageAccumulator damage;
-
-  if (!target_surface_mask_layer)
-    return damage;
-
-  // Currently, if there is any change to the mask, we choose to damage the
-  // entire surface. This could potentially be optimized later, but it is not
-  // expected to be a common case.
-  if (target_surface_mask_layer->LayerPropertyChanged() ||
-      !target_surface_mask_layer->update_rect().IsEmpty()) {
-    damage.Union(gfx::Rect(target_surface_mask_layer->bounds()));
-  }
-
-  return damage;
-}
-
 void DamageTracker::PrepareForUpdate() {
   mailboxId_++;
   damage_for_this_update_ = DamageAccumulator();
diff --git a/cc/trees/damage_tracker.h b/cc/trees/damage_tracker.h
index 1851a09..7edeac4 100644
--- a/cc/trees/damage_tracker.h
+++ b/cc/trees/damage_tracker.h
@@ -91,8 +91,6 @@
     int bottom_ = 0;
   };
 
-  DamageAccumulator TrackDamageFromSurfaceMask(
-      LayerImpl* target_surface_mask_layer);
   DamageAccumulator TrackDamageFromLeftoverRects();
 
   // These helper functions are used only during UpdateDamageTracking().
diff --git a/cc/trees/damage_tracker_unittest.cc b/cc/trees/damage_tracker_unittest.cc
index 4914301..b65674e 100644
--- a/cc/trees/damage_tracker_unittest.cc
+++ b/cc/trees/damage_tracker_unittest.cc
@@ -10,6 +10,8 @@
 #include "cc/layers/layer_impl.h"
 #include "cc/paint/filter_operation.h"
 #include "cc/paint/filter_operations.h"
+#include "cc/test/fake_picture_layer_impl.h"
+#include "cc/test/fake_raster_source.h"
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/layer_test_common.h"
 #include "cc/trees/layer_tree_host_common.h"
@@ -73,6 +75,7 @@
 
     LayerImpl* root = root_layer();
     root->SetBounds(gfx::Size(500, 500));
+    root->layer_tree_impl()->SetDeviceViewportRect(gfx::Rect(root->bounds()));
     root->SetDrawsContent(true);
     SetupRootProperties(root);
 
@@ -99,6 +102,7 @@
 
     LayerImpl* root = root_layer();
     root->SetBounds(gfx::Size(500, 500));
+    root->layer_tree_impl()->SetDeviceViewportRect(gfx::Rect(root->bounds()));
     root->SetDrawsContent(true);
     SetupRootProperties(root);
 
@@ -165,10 +169,13 @@
     //   3. resetting all update_rects and property_changed flags for all layers
     //      and surfaces.
 
-    ExecuteCalculateDrawProperties(root, device_scale_factor);
+    root->layer_tree_impl()->SetDeviceScaleFactor(device_scale_factor);
+    root->layer_tree_impl()->set_needs_update_draw_properties();
+    UpdateDrawProperties(root->layer_tree_impl());
 
-    DamageTracker::UpdateDamageTracking(root->layer_tree_impl(),
-                                        *render_surface_list_impl());
+    DamageTracker::UpdateDamageTracking(
+        root->layer_tree_impl(),
+        root->layer_tree_impl()->GetRenderSurfaceList());
 
     root->layer_tree_impl()->ResetAllChangeTracking();
   }
@@ -1561,7 +1568,12 @@
 
   // Set up the mask layer.
   CreateEffectNode(child);
-  LayerImpl* mask_layer = AddMaskLayer<PictureLayerImpl>(child);
+  auto* mask_layer = AddLayer<FakePictureLayerImpl>();
+  SetupMaskProperties(child, mask_layer);
+  Region empty_invalidation;
+  mask_layer->UpdateRasterSource(
+      FakeRasterSource::CreateFilled(child->bounds()), &empty_invalidation,
+      nullptr, nullptr);
 
   // Add opacity and a grand_child so that the render surface persists even
   // after we remove the mask.
@@ -1572,22 +1584,28 @@
   grand_child->SetOffsetToTransformParent(gfx::Vector2dF(2.f, 2.f));
   EmulateDrawingOneFrame(root);
 
-  // CASE 1: the update_rect on a mask layer should damage the entire target
-  //         surface.
+  EXPECT_EQ(2, GetRenderSurface(root)->num_contributors());
+  EXPECT_TRUE(root->contributes_to_drawn_render_surface());
+  EXPECT_EQ(3, GetRenderSurface(child)->num_contributors());
+  EXPECT_TRUE(child->contributes_to_drawn_render_surface());
+  EXPECT_EQ(GetRenderSurface(child), GetRenderSurface(mask_layer));
+  EXPECT_TRUE(mask_layer->contributes_to_drawn_render_surface());
+
+  // CASE 1: the update_rect on a mask layer should damage the rect.
   ClearDamageForAllSurfaces(root);
   mask_layer->SetUpdateRect(gfx::Rect(1, 2, 3, 4));
   EmulateDrawingOneFrame(root);
   gfx::Rect child_damage_rect;
   EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid(
       &child_damage_rect));
-  EXPECT_EQ(gfx::Rect(30, 30).ToString(), child_damage_rect.ToString());
+  EXPECT_EQ(gfx::Rect(1, 2, 3, 4), child_damage_rect);
 
   EXPECT_TRUE(GetRenderSurface(root)
                   ->damage_tracker()
                   ->has_damage_from_contributing_content());
-  EXPECT_FALSE(GetRenderSurface(child)
-                   ->damage_tracker()
-                   ->has_damage_from_contributing_content());
+  EXPECT_TRUE(GetRenderSurface(child)
+                  ->damage_tracker()
+                  ->has_damage_from_contributing_content());
 
   // CASE 2: a property change on the mask layer should damage the entire
   //         target surface.
@@ -1607,14 +1625,14 @@
   EmulateDrawingOneFrame(root);
   EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid(
       &child_damage_rect));
-  EXPECT_EQ(gfx::Rect(30, 30).ToString(), child_damage_rect.ToString());
+  EXPECT_EQ(gfx::Rect(30, 30), child_damage_rect);
 
   EXPECT_TRUE(GetRenderSurface(root)
                   ->damage_tracker()
                   ->has_damage_from_contributing_content());
-  EXPECT_FALSE(GetRenderSurface(child)
-                   ->damage_tracker()
-                   ->has_damage_from_contributing_content());
+  EXPECT_TRUE(GetRenderSurface(child)
+                  ->damage_tracker()
+                  ->has_damage_from_contributing_content());
 
   // CASE 3: removing the mask also damages the entire target surface.
   //
@@ -1629,9 +1647,17 @@
 
   // Then test mask removal.
   ClearDamageForAllSurfaces(root);
-  GetEffectNode(child)->is_masked = false;
-  GetEffectNode(child)->mask_layer_id = -1;
-  child->NoteLayerPropertyChanged();
+  auto layers =
+      root->layer_tree_impl()->DetachLayersKeepingRootLayerForTesting();
+  ASSERT_EQ(layers[1].get(), child);
+  root->layer_tree_impl()->AddLayer(std::move(layers[1]));
+  ASSERT_EQ(layers[2].get(), mask_layer);
+  ASSERT_EQ(layers[3].get(), grand_child);
+  root->layer_tree_impl()->AddLayer(std::move(layers[3]));
+  CopyProperties(root, child);
+  CreateEffectNode(child).render_surface_reason = RenderSurfaceReason::kTest;
+  CopyProperties(child, grand_child);
+
   EmulateDrawingOneFrame(root);
 
   // Sanity check that a render surface still exists.
@@ -1639,7 +1665,7 @@
 
   EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid(
       &child_damage_rect));
-  EXPECT_EQ(gfx::Rect(30, 30).ToString(), child_damage_rect.ToString());
+  EXPECT_EQ(gfx::Rect(30, 30), child_damage_rect);
 
   EXPECT_TRUE(GetRenderSurface(root)
                   ->damage_tracker()
diff --git a/cc/trees/debug_rect_history.cc b/cc/trees/debug_rect_history.cc
index 07e6bd17..77917aa 100644
--- a/cc/trees/debug_rect_history.cc
+++ b/cc/trees/debug_rect_history.cc
@@ -137,9 +137,8 @@
 }
 
 void DebugRectHistory::SaveTouchEventHandlerRects(LayerTreeImpl* tree_impl) {
-  LayerTreeHostCommon::CallFunctionForEveryLayer(
-      tree_impl,
-      [this](LayerImpl* layer) { SaveTouchEventHandlerRectsCallback(layer); });
+  for (auto* layer : *tree_impl)
+    SaveTouchEventHandlerRectsCallback(layer);
 }
 
 void DebugRectHistory::SaveTouchEventHandlerRectsCallback(LayerImpl* layer) {
@@ -177,9 +176,8 @@
 }
 
 void DebugRectHistory::SaveScrollEventHandlerRects(LayerTreeImpl* tree_impl) {
-  LayerTreeHostCommon::CallFunctionForEveryLayer(
-      tree_impl,
-      [this](LayerImpl* layer) { SaveScrollEventHandlerRectsCallback(layer); });
+  for (auto* layer : *tree_impl)
+    SaveScrollEventHandlerRectsCallback(layer);
 }
 
 void DebugRectHistory::SaveScrollEventHandlerRectsCallback(LayerImpl* layer) {
@@ -193,9 +191,8 @@
 }
 
 void DebugRectHistory::SaveNonFastScrollableRects(LayerTreeImpl* tree_impl) {
-  LayerTreeHostCommon::CallFunctionForEveryLayer(
-      tree_impl,
-      [this](LayerImpl* layer) { SaveNonFastScrollableRectsCallback(layer); });
+  for (auto* layer : *tree_impl)
+    SaveNonFastScrollableRectsCallback(layer);
 }
 
 void DebugRectHistory::SaveNonFastScrollableRectsCallback(LayerImpl* layer) {
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index a2dc10f8..aede10a6c 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -798,14 +798,6 @@
     if (LayerNeedsUpdate(layer, layer_is_drawn, property_trees)) {
       update_layer_list->push_back(layer);
     }
-
-    // Append mask layers to the update layer list. They don't have valid
-    // visible rects, so need to get added after the above calculation.
-    if (PictureLayer* mask_layer = layer->mask_layer()) {
-      // Layers with empty bounds should never be painted, including masks.
-      if (!mask_layer->bounds().IsEmpty())
-        update_layer_list->push_back(mask_layer);
-    }
   }
 }
 
diff --git a/cc/trees/effect_node.cc b/cc/trees/effect_node.cc
index 0b0a525..0336613 100644
--- a/cc/trees/effect_node.cc
+++ b/cc/trees/effect_node.cc
@@ -31,7 +31,6 @@
       is_currently_animating_backdrop_filter(false),
       is_currently_animating_opacity(false),
       has_masking_child(false),
-      is_masked(false),
       effect_changed(false),
       subtree_has_copy_request(false),
       is_fast_rounded_corner(false),
@@ -39,7 +38,6 @@
       transform_id(0),
       clip_id(0),
       target_id(1),
-      mask_layer_id(Layer::INVALID_ID),
       closest_ancestor_with_cached_render_surface_id(-1),
       closest_ancestor_with_copy_request_id(-1) {}
 
@@ -82,11 +80,10 @@
          is_currently_animating_opacity ==
              other.is_currently_animating_opacity &&
          has_masking_child == other.has_masking_child &&
-         is_masked == other.is_masked &&
          effect_changed == other.effect_changed &&
          subtree_has_copy_request == other.subtree_has_copy_request &&
          transform_id == other.transform_id && clip_id == other.clip_id &&
-         target_id == other.target_id && mask_layer_id == other.mask_layer_id &&
+         target_id == other.target_id &&
          closest_ancestor_with_cached_render_surface_id ==
              other.closest_ancestor_with_cached_render_surface_id &&
          closest_ancestor_with_copy_request_id ==
@@ -170,7 +167,6 @@
   value->SetBoolean("has_potential_opacity_animation",
                     has_potential_opacity_animation);
   value->SetBoolean("has_masking_child", has_masking_child);
-  value->SetBoolean("is_masked", is_masked);
   value->SetBoolean("effect_changed", effect_changed);
   value->SetBoolean("subtree_has_copy_request", subtree_has_copy_request);
   value->SetString("render_surface_reason",
@@ -178,7 +174,6 @@
   value->SetInteger("transform_id", transform_id);
   value->SetInteger("clip_id", clip_id);
   value->SetInteger("target_id", target_id);
-  value->SetInteger("mask_layer_id", mask_layer_id);
   value->SetInteger("closest_ancestor_with_cached_render_surface_id",
                     closest_ancestor_with_cached_render_surface_id);
   value->SetInteger("closest_ancestor_with_copy_request_id",
diff --git a/cc/trees/effect_node.h b/cc/trees/effect_node.h
index 51cf4d34..1b5df24 100644
--- a/cc/trees/effect_node.h
+++ b/cc/trees/effect_node.h
@@ -116,8 +116,6 @@
   bool is_currently_animating_opacity : 1;
   // Whether this node has a child node with kDstIn blend mode.
   bool has_masking_child : 1;
-  // Whether this node has a mask. This bit is not used when using layer lists.
-  bool is_masked : 1;
   // Whether this node's effect has been changed since the last
   // frame. Needed in order to compute damage rect.
   bool effect_changed : 1;
@@ -138,9 +136,6 @@
   // This is the id of the ancestor effect node that induces a
   // RenderSurfaceImpl.
   int target_id;
-  // The layer id of the mask layer, if any, to apply to this effect
-  // node's content when rendering to a surface.
-  int mask_layer_id;
   int closest_ancestor_with_cached_render_surface_id;
   int closest_ancestor_with_copy_request_id;
 
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index a480efec..a42af87b 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -443,8 +443,8 @@
       // PushPropertiesTo() propagates layer debug info to the impl side --
       // otherwise this won't happen for the layers that remain unchanged since
       // tracing started.
-      LayerTreeHostCommon::CallFunctionForEveryLayer(
-          this, [](Layer* layer) { layer->SetNeedsPushProperties(); });
+      for (auto* layer : *this)
+        layer->SetNeedsPushProperties();
     }
 
     for (Layer* layer : LayersThatShouldPushProperties())
@@ -1162,8 +1162,8 @@
         old_properties == EventListenerProperties::kBlockingAndPassive;
 
     if (old_property_is_blocking != new_property_is_blocking) {
-      LayerTreeHostCommon::CallFunctionForEveryLayer(
-          this, [](Layer* layer) { layer->SetNeedsPushProperties(); });
+      for (auto* layer : *this)
+        layer->SetNeedsPushProperties();
     }
   }
 
@@ -1311,8 +1311,8 @@
     return;
   raster_color_space_id_ = gfx::ColorSpace::GetNextId();
   raster_color_space_ = raster_color_space;
-  LayerTreeHostCommon::CallFunctionForEveryLayer(
-      this, [](Layer* layer) { layer->SetNeedsDisplay(); });
+  for (auto* layer : *this)
+    layer->SetNeedsDisplay();
 }
 
 void LayerTreeHost::SetExternalPageScaleFactor(
@@ -1674,12 +1674,9 @@
   mutator_host_->UpdateRegisteredElementIds(ElementListType::ACTIVE);
 }
 
-static void SetElementIdForTesting(Layer* layer) {
-  layer->SetElementId(LayerIdToElementIdForTesting(layer->id()));
-}
-
 void LayerTreeHost::SetElementIdsForTesting() {
-  LayerTreeHostCommon::CallFunctionForEveryLayer(this, SetElementIdForTesting);
+  for (auto* layer : *this)
+    layer->SetElementId(LayerIdToElementIdForTesting(layer->id()));
 }
 
 void LayerTreeHost::BuildPropertyTreesForTesting() {
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc
index 86e02f9..89f3bf88 100644
--- a/cc/trees/layer_tree_host_common.cc
+++ b/cc/trees/layer_tree_host_common.cc
@@ -169,23 +169,6 @@
 
 ScrollAndScaleSet::~ScrollAndScaleSet() = default;
 
-static inline void SetMaskLayersContributeToDrawnRenderSurface(
-    RenderSurfaceImpl* surface,
-    PropertyTrees* property_trees) {
-  LayerImpl* mask_layer = surface->MaskLayer();
-  if (mask_layer) {
-    mask_layer->set_contributes_to_drawn_render_surface(true);
-    draw_property_utils::ComputeMaskDrawProperties(mask_layer, property_trees);
-  }
-}
-
-static inline void ClearMaskLayersContributeToDrawnRenderSurface(
-    RenderSurfaceImpl* surface) {
-  LayerImpl* mask_layer = surface->MaskLayer();
-  if (mask_layer)
-    mask_layer->set_contributes_to_drawn_render_surface(false);
-}
-
 static float TranslationFromActiveTreeLayerScreenSpaceTransform(
     LayerImpl* pending_tree_layer) {
   LayerTreeImpl* layer_tree_impl = pending_tree_layer->layer_tree_impl();
@@ -329,7 +312,6 @@
     if (RenderSurfaceImpl* render_surface = effect_tree.GetRenderSurface(i)) {
       render_surface->set_is_render_surface_list_member(false);
       render_surface->reset_num_contributors();
-      ClearMaskLayersContributeToDrawnRenderSurface(render_surface);
     }
   }
 
@@ -434,7 +416,6 @@
       target_surface->decrement_num_contributors();
       continue;
     }
-    SetMaskLayersContributeToDrawnRenderSurface(surface, property_trees);
     final_surface_list->push_back(surface);
   }
   if (removed_surface) {
diff --git a/cc/trees/layer_tree_host_common.h b/cc/trees/layer_tree_host_common.h
index 9657c46..a12c014 100644
--- a/cc/trees/layer_tree_host_common.h
+++ b/cc/trees/layer_tree_host_common.h
@@ -123,14 +123,6 @@
   static void CalculateDrawPropertiesForTesting(
       CalcDrawPropsImplInputsForTesting* inputs);
 
-  template <typename Function>
-  static void CallFunctionForEveryLayer(LayerTreeHost* host,
-                                        const Function& function);
-
-  template <typename Function>
-  static void CallFunctionForEveryLayer(LayerTreeImpl* layer,
-                                        const Function& function);
-
   struct CC_EXPORT ScrollUpdateInfo {
     ElementId element_id;
     gfx::ScrollOffset scroll_delta;
@@ -198,27 +190,6 @@
   ManipulationInfo manipulation_info;
 };
 
-template <typename Function>
-void LayerTreeHostCommon::CallFunctionForEveryLayer(LayerTreeHost* host,
-                                                    const Function& function) {
-  for (auto* layer : *host) {
-    function(layer);
-    if (PictureLayer* mask_layer = layer->mask_layer())
-      function(mask_layer);
-  }
-}
-
-template <typename Function>
-void LayerTreeHostCommon::CallFunctionForEveryLayer(LayerTreeImpl* tree_impl,
-                                                    const Function& function) {
-  for (auto* layer : *tree_impl)
-    function(layer);
-
-  for (int id : tree_impl->property_trees()->effect_tree.mask_layer_ids()) {
-    function(tree_impl->LayerById(id));
-  }
-}
-
 CC_EXPORT PropertyTrees* GetPropertyTrees(const Layer* layer);
 CC_EXPORT PropertyTrees* GetPropertyTrees(const LayerImpl* layer);
 
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index b34f461..98e11ff2 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -3043,13 +3043,17 @@
   // surface_child's contents.
   Occlusion actual_occlusion =
       GetRenderSurfaceImpl(surface_child)->occlusion_in_content_space();
-  Occlusion expected_occlusion(translate, SimpleEnclosedRegion(gfx::Rect()),
+  Occlusion expected_occlusion(translate, SimpleEnclosedRegion(),
                                SimpleEnclosedRegion(gfx::Rect(200, 200)));
   EXPECT_TRUE(expected_occlusion.IsEqual(actual_occlusion));
 
-  // Mask layer should have the same occlusion.
+  // Mask layer's occlusion is different because we create transform and render
+  // surface for it in layer tree mode.
   actual_occlusion =
       ImplOf(surface_child_mask)->draw_properties().occlusion_in_content_space;
+  expected_occlusion = Occlusion(
+      gfx::Transform(), SimpleEnclosedRegion(gfx::Rect(-20, -20, 200, 200)),
+      SimpleEnclosedRegion());
   EXPECT_TRUE(expected_occlusion.IsEqual(actual_occlusion));
 }
 
@@ -3795,10 +3799,10 @@
   // the preserve-3d transform style. Instead, an example of when a surface
   // would be created with preserve-3d is when there is a mask layer.
   FakeContentLayerClient client;
-  auto dummy_mask_layer1 = PictureLayer::Create(&client);
-  front_facing_surface->SetMaskLayer(dummy_mask_layer1);
-  auto dummy_mask_layer2 = PictureLayer::Create(&client);
-  back_facing_surface->SetMaskLayer(dummy_mask_layer2);
+  auto front_facing_mask = PictureLayer::Create(&client);
+  front_facing_surface->SetMaskLayer(front_facing_mask);
+  auto back_facing_mask = PictureLayer::Create(&client);
+  back_facing_surface->SetMaskLayer(back_facing_mask);
 
   // Nothing is double-sided
   front_facing_child->SetDoubleSided(false);
@@ -3819,8 +3823,8 @@
   back_facing_child_of_front_facing_surface->SetIsDrawable(true);
   front_facing_child_of_back_facing_surface->SetIsDrawable(true);
   back_facing_child_of_back_facing_surface->SetIsDrawable(true);
-  dummy_mask_layer1->SetIsDrawable(true);
-  dummy_mask_layer2->SetIsDrawable(true);
+  front_facing_mask->SetIsDrawable(true);
+  back_facing_mask->SetIsDrawable(true);
 
   gfx::Transform backface_matrix;
   backface_matrix.Translate(50.0, 50.0);
@@ -3836,8 +3840,8 @@
   back_facing_child_of_front_facing_surface->SetBounds(gfx::Size(100, 100));
   front_facing_child_of_back_facing_surface->SetBounds(gfx::Size(100, 100));
   back_facing_child_of_back_facing_surface->SetBounds(gfx::Size(100, 100));
-  dummy_mask_layer1->SetBounds(gfx::Size(100, 100));
-  dummy_mask_layer2->SetBounds(gfx::Size(100, 100));
+  front_facing_mask->SetBounds(gfx::Size(100, 100));
+  back_facing_mask->SetBounds(gfx::Size(100, 100));
 
   back_facing_child->SetTransform(backface_matrix);
   back_facing_surface->SetTransform(backface_matrix);
@@ -3881,10 +3885,11 @@
   EXPECT_EQ(GetRenderSurfaceImpl(back_facing_child_of_back_facing_surface),
             GetRenderSurfaceImpl(back_facing_surface));
 
-  EXPECT_EQ(3u, update_layer_impl_list().size());
+  EXPECT_EQ(4u, update_layer_impl_list().size());
 
   EXPECT_TRUE(UpdateLayerImplListContains(front_facing_child->id()));
   EXPECT_TRUE(UpdateLayerImplListContains(front_facing_surface->id()));
+  EXPECT_TRUE(UpdateLayerImplListContains(front_facing_mask->id()));
   EXPECT_TRUE(UpdateLayerImplListContains(
       front_facing_child_of_front_facing_surface->id()));
 }
@@ -6826,9 +6831,6 @@
 
     if (it.state() != EffectTreeLayerListIterator::State::CONTRIBUTING_SURFACE)
       continue;
-
-    if (it.current_render_surface()->MaskLayer())
-      drawn_layers->insert(it.current_render_surface()->MaskLayer());
   }
 }
 
@@ -6975,11 +6977,13 @@
   EXPECT_FALSE(ImplOf(grand_parent)->contributes_to_drawn_render_surface());
   EXPECT_FALSE(ImplOf(parent)->contributes_to_drawn_render_surface());
   EXPECT_FALSE(ImplOf(child)->contributes_to_drawn_render_surface());
-  EXPECT_FALSE(ImplOf(mask)->contributes_to_drawn_render_surface());
+  // Mask layer has its own render surface in layer tree mode.
+  EXPECT_TRUE(ImplOf(mask)->contributes_to_drawn_render_surface());
   EXPECT_FALSE(ImplOf(grand_child1)->contributes_to_drawn_render_surface());
   EXPECT_FALSE(ImplOf(grand_child2)->contributes_to_drawn_render_surface());
 
   expected.clear();
+  expected.insert(ImplOf(mask));
   actual.clear();
   GatherDrawnLayers(host_impl()->active_tree(), &actual);
   EXPECT_EQ(expected, actual);
@@ -8643,7 +8647,8 @@
   root->SetIsDrawable(true);
   child->SetTransform(transform);
   child->SetBounds(gfx::Size(30, 30));
-  child->SetIsDrawable(false);
+  child->SetIsDrawable(true);
+  child->SetOpacity(0.f);
   mask->SetBounds(gfx::Size(30, 30));
 
   CommitAndActivate();
@@ -8652,13 +8657,14 @@
   // mask doesn't contribute to a drawn render surface. This means it has an
   // empty visible rect, but its screen space transform can still be computed
   // correctly on-demand.
+  EXPECT_FALSE(ImplOf(child)->contributes_to_drawn_render_surface());
   EXPECT_FALSE(ImplOf(mask)->contributes_to_drawn_render_surface());
   EXPECT_EQ(gfx::Rect(), ImplOf(mask)->visible_layer_rect());
   EXPECT_TRANSFORMATION_MATRIX_EQ(transform,
                                   ImplOf(mask)->ScreenSpaceTransform());
 
   // Make the child's render surface have contributing content.
-  child->SetIsDrawable(true);
+  child->SetOpacity(1.f);
   CommitAndActivate();
   EXPECT_TRUE(ImplOf(mask)->contributes_to_drawn_render_surface());
   EXPECT_EQ(gfx::Rect(30, 30), ImplOf(mask)->visible_layer_rect());
@@ -8671,6 +8677,17 @@
   EXPECT_TRANSFORMATION_MATRIX_EQ(transform,
                                   ImplOf(mask)->ScreenSpaceTransform());
   EXPECT_EQ(gfx::Rect(20, 20), ImplOf(mask)->visible_layer_rect());
+
+  // For now SetIsDrawable of masked layer doesn't affect draw properties of
+  // the mask layer because it doesn't affect property trees.
+  child->SetIsDrawable(false);
+  CommitAndActivate();
+  EXPECT_TRUE(ImplOf(mask)->contributes_to_drawn_render_surface());
+
+  // SetIsDrawable of mask layer itself affects its draw properties.
+  mask->SetIsDrawable(false);
+  CommitAndActivate();
+  EXPECT_FALSE(ImplOf(mask)->contributes_to_drawn_render_surface());
 }
 
 TEST_F(LayerTreeHostCommonTest,
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 343e0ea..25ea491c 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2326,11 +2326,11 @@
   TRACE_EVENT_IS_NEW_TRACE(&is_new_trace);
   if (is_new_trace) {
     if (pending_tree_) {
-      LayerTreeHostCommon::CallFunctionForEveryLayer(
-          pending_tree(), [](LayerImpl* layer) { layer->DidBeginTracing(); });
+      for (auto* layer : *pending_tree_)
+        layer->DidBeginTracing();
     }
-    LayerTreeHostCommon::CallFunctionForEveryLayer(
-        active_tree(), [](LayerImpl* layer) { layer->DidBeginTracing(); });
+    for (auto* layer : *active_tree_)
+      layer->DidBeginTracing();
   }
 
   {
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 90bca1a0..58266ffa1 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -11722,11 +11722,8 @@
   std::unique_ptr<FakePictureLayerImpl> mask_layer =
       FakePictureLayerImpl::Create(pending_tree, next_layer_id_++);
   FakePictureLayerImpl* raw_mask_layer = mask_layer.get();
-  pending_tree->AddMaskLayer(std::move(mask_layer));
-  GetEffectNode(pending_layer)->mask_layer_id = raw_mask_layer->id();
-  GetEffectNode(pending_layer)->is_masked = true;
-  GetPropertyTrees(pending_layer)
-      ->effect_tree.AddMaskLayerId(raw_mask_layer->id());
+  SetupMaskProperties(pending_layer, raw_mask_layer);
+  pending_tree->AddLayer(std::move(mask_layer));
 
   EXPECT_EQ(1u, pending_layer->did_become_active_call_count());
   EXPECT_EQ(0u, raw_mask_layer->did_become_active_call_count());
diff --git a/cc/trees/layer_tree_host_pixeltest_masks.cc b/cc/trees/layer_tree_host_pixeltest_masks.cc
index c38c6c4..2f185f8 100644
--- a/cc/trees/layer_tree_host_pixeltest_masks.cc
+++ b/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -526,41 +526,59 @@
   gfx::Size bounds_;
 };
 
-using LayerTreeHostMasksForBackdropFiltersPixelTest =
-    ParameterizedPixelResourceTest;
+class LayerTreeHostMasksForBackdropFiltersPixelTest
+    : public ParameterizedPixelResourceTest {
+ protected:
+  LayerTreeHostMasksForBackdropFiltersPixelTest()
+      : bounds_(100, 100),
+        picture_client_(bounds_, SK_ColorGREEN, true),
+        mask_client_(bounds_) {
+    SetUseLayerLists();
+  }
+
+  // Setup three layers for testing masks: a white background, a green layer,
+  // and a mask layer with kDstIn blend mode.
+  void SetupTree() override {
+    SetInitialRootBounds(bounds_);
+    ParameterizedPixelResourceTest::SetupTree();
+
+    Layer* root = layer_tree_host()->root_layer();
+
+    scoped_refptr<SolidColorLayer> background =
+        CreateSolidColorLayer(gfx::Rect(bounds_), SK_ColorWHITE);
+    CopyProperties(root, background.get());
+    root->AddChild(background);
+
+    scoped_refptr<PictureLayer> picture =
+        PictureLayer::Create(&picture_client_);
+    picture->SetBounds(bounds_);
+    picture->SetIsDrawable(true);
+    CopyProperties(background.get(), picture.get());
+    root->AddChild(picture);
+
+    scoped_refptr<SolidColorLayer> blur =
+        CreateSolidColorLayer(gfx::Rect(bounds_), SK_ColorTRANSPARENT);
+    CopyProperties(background.get(), blur.get());
+    CreateEffectNode(blur.get())
+        .backdrop_filters.Append(FilterOperation::CreateGrayscaleFilter(1.0));
+    root->AddChild(blur);
+
+    scoped_refptr<PictureLayer> mask = PictureLayer::Create(&mask_client_);
+    SetupMaskProperties(blur.get(), mask.get());
+
+    root->AddChild(mask);
+  }
+
+  const gfx::Size bounds_;
+  CheckerContentLayerClient picture_client_;
+  CircleContentLayerClient mask_client_;
+};
 
 INSTANTIATE_TEST_SUITE_P(PixelResourceTest,
                          LayerTreeHostMasksForBackdropFiltersPixelTest,
                          ::testing::ValuesIn(kTestCases));
 
-TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTest,
-       MaskOfLayerWithBackdropFilter) {
-  scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
-      gfx::Rect(100, 100), SK_ColorWHITE);
-
-  gfx::Size picture_bounds(100, 100);
-  CheckerContentLayerClient picture_client(picture_bounds, SK_ColorGREEN, true);
-  scoped_refptr<PictureLayer> picture = PictureLayer::Create(&picture_client);
-  picture->SetBounds(picture_bounds);
-  picture->SetIsDrawable(true);
-
-  scoped_refptr<SolidColorLayer> blur = CreateSolidColorLayer(
-      gfx::Rect(100, 100), SK_ColorTRANSPARENT);
-  background->AddChild(picture);
-  background->AddChild(blur);
-
-  FilterOperations filters;
-  filters.Append(FilterOperation::CreateGrayscaleFilter(1.0));
-  blur->SetBackdropFilters(filters);
-  blur->ClearBackdropFilterBounds();
-
-  gfx::Size mask_bounds(100, 100);
-  CircleContentLayerClient mask_client(mask_bounds);
-  scoped_refptr<PictureLayer> mask = PictureLayer::Create(&mask_client);
-  mask->SetBounds(mask_bounds);
-  mask->SetIsDrawable(true);
-  blur->SetMaskLayer(mask);
-
+TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTest, Test) {
   base::FilePath image_name =
       (raster_type() == GPU)
           ? base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter_gpu.png"))
@@ -580,10 +598,10 @@
         large_error_allowed, small_error_allowed);
   }
 
-  RunPixelResourceTest(background, image_name);
+  RunPixelResourceTestWithLayerList(image_name);
 }
 
-TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTest, MaskOfLayerWithBlend) {
+TEST_P(LayerTreeHostMasksPixelTest, MaskOfLayerWithBlend) {
   scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
       gfx::Rect(128, 128), SK_ColorWHITE);
 
@@ -997,43 +1015,64 @@
   RunPixelResourceTest(root, image_name);
 }
 
-TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTest,
-       MaskOfLayerWithBackdropFilterAndBlend) {
-  scoped_refptr<SolidColorLayer> background =
-      CreateSolidColorLayer(gfx::Rect(128, 128), SK_ColorWHITE);
+class LayerTreeHostMasksForBackdropFiltersAndBlendPixelTest
+    : public ParameterizedPixelResourceTest {
+ protected:
+  LayerTreeHostMasksForBackdropFiltersAndBlendPixelTest()
+      : bounds_(128, 128),
+        picture_client_vertical_(bounds_, SK_ColorGREEN, true),
+        picture_client_horizontal_(bounds_, SK_ColorMAGENTA, false),
+        mask_client_(bounds_) {
+    SetUseLayerLists();
+  }
 
-  gfx::Size picture_bounds(128, 128);
-  CheckerContentLayerClient picture_client_vertical(picture_bounds,
-                                                    SK_ColorGREEN, true);
-  scoped_refptr<PictureLayer> picture_vertical =
-      PictureLayer::Create(&picture_client_vertical);
-  picture_vertical->SetBounds(picture_bounds);
-  picture_vertical->SetIsDrawable(true);
+  void SetupTree() override {
+    SetInitialRootBounds(bounds_);
+    ParameterizedPixelResourceTest::SetupTree();
 
-  CheckerContentLayerClient picture_client_horizontal(picture_bounds,
-                                                      SK_ColorMAGENTA, false);
-  scoped_refptr<PictureLayer> picture_horizontal =
-      PictureLayer::Create(&picture_client_horizontal);
-  picture_horizontal->SetBounds(picture_bounds);
-  picture_horizontal->SetIsDrawable(true);
-  picture_horizontal->SetContentsOpaque(false);
-  picture_horizontal->SetBlendMode(SkBlendMode::kMultiply);
+    Layer* root = layer_tree_host()->root_layer();
 
-  FilterOperations filters;
-  filters.Append(FilterOperation::CreateGrayscaleFilter(1.0));
-  picture_horizontal->SetBackdropFilters(filters);
-  picture_horizontal->ClearBackdropFilterBounds();
+    scoped_refptr<SolidColorLayer> background =
+        CreateSolidColorLayer(gfx::Rect(bounds_), SK_ColorWHITE);
+    CopyProperties(root, background.get());
+    root->AddChild(background);
 
-  background->AddChild(picture_vertical);
-  background->AddChild(picture_horizontal);
+    scoped_refptr<PictureLayer> picture_vertical =
+        PictureLayer::Create(&picture_client_vertical_);
+    picture_vertical->SetBounds(bounds_);
+    picture_vertical->SetIsDrawable(true);
+    CopyProperties(background.get(), picture_vertical.get());
+    root->AddChild(picture_vertical);
 
-  gfx::Size mask_bounds(128, 128);
-  CircleContentLayerClient mask_client(mask_bounds);
-  scoped_refptr<PictureLayer> mask = PictureLayer::Create(&mask_client);
-  mask->SetBounds(mask_bounds);
-  mask->SetIsDrawable(true);
-  picture_horizontal->SetMaskLayer(mask);
+    scoped_refptr<PictureLayer> picture_horizontal =
+        PictureLayer::Create(&picture_client_horizontal_);
+    picture_horizontal->SetBounds(bounds_);
+    picture_horizontal->SetIsDrawable(true);
+    picture_horizontal->SetContentsOpaque(false);
+    CopyProperties(background.get(), picture_horizontal.get());
+    auto& effect_node = CreateEffectNode(picture_horizontal.get());
+    effect_node.backdrop_filters.Append(
+        FilterOperation::CreateGrayscaleFilter(1.0));
+    effect_node.blend_mode = SkBlendMode::kMultiply;
+    root->AddChild(picture_horizontal);
 
+    scoped_refptr<PictureLayer> mask = PictureLayer::Create(&mask_client_);
+    mask->SetBounds(bounds_);
+    SetupMaskProperties(picture_horizontal.get(), mask.get());
+    root->AddChild(mask);
+  }
+
+  const gfx::Size bounds_;
+  CheckerContentLayerClient picture_client_vertical_;
+  CheckerContentLayerClient picture_client_horizontal_;
+  CircleContentLayerClient mask_client_;
+};
+
+INSTANTIATE_TEST_SUITE_P(PixelResourceTest,
+                         LayerTreeHostMasksForBackdropFiltersAndBlendPixelTest,
+                         ::testing::ValuesIn(kTestCases));
+
+TEST_P(LayerTreeHostMasksForBackdropFiltersAndBlendPixelTest, Test) {
   base::FilePath result_path(
       FILE_PATH_LITERAL("mask_of_backdrop_filter_and_blend_.png"));
   if (raster_type() != GPU) {
@@ -1041,7 +1080,7 @@
   } else {
     result_path = result_path.InsertBeforeExtensionASCII(GetRendererSuffix());
   }
-  RunPixelResourceTest(background, result_path);
+  RunPixelResourceTestWithLayerList(result_path);
 }
 
 }  // namespace
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 81dc18b..2d8e322 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -2151,10 +2151,12 @@
   void SetupTree() override {
     scoped_refptr<Layer> root = Layer::Create();
     root->SetBounds(gfx::Size(10, 10));
+    // child_layer_ is not drawable.
     child_layer_ = base::MakeRefCounted<UpdateCountingLayer>(&client_);
     child_layer_->SetBounds(gfx::Size(10, 10));
     mask_layer_ = base::MakeRefCounted<UpdateCountingLayer>(&client_);
     mask_layer_->SetBounds(gfx::Size(10, 10));
+    mask_layer_->SetIsDrawable(true);
     child_layer_->SetMaskLayer(mask_layer_);
     root->AddChild(child_layer_);
     layer_tree_host()->SetRootLayer(root);
@@ -2170,8 +2172,8 @@
     switch (layer_tree_host()->SourceFrameNumber()) {
       case 1:
         // Root and mask layer should have the same source frame number as they
-        // will be in the layer update list but the child is not as it has empty
-        // bounds.
+        // will be in the layer update list but the child is not as it doesn't
+        // draw content.
         EXPECT_EQ(mask_layer_->update_count(), 1);
         EXPECT_EQ(child_layer_->update_count(), 0);
 
@@ -2182,17 +2184,22 @@
   }
 
   void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
+    auto* mask_surface =
+        GetRenderSurface(impl->sync_tree()->LayerById(mask_layer_->id()));
+    auto* root_surface =
+        GetRenderSurface(impl->sync_tree()->root_layer_for_testing());
+    ASSERT_TRUE(mask_surface);
     switch (index_) {
-      case 0:
+      case 0: {
         index_++;
-        EXPECT_FALSE(
-            GetRenderSurface(impl->sync_tree()->root_layer_for_testing())
-                ->MaskLayer());
+        auto* child_surface =
+            GetRenderSurface(impl->sync_tree()->LayerById(child_layer_->id()));
+        EXPECT_EQ(child_surface, mask_surface->render_target());
+        EXPECT_NE(child_surface, root_surface);
         break;
+      }
       case 1:
-        EXPECT_TRUE(
-            GetRenderSurface(impl->sync_tree()->root_layer_for_testing())
-                ->MaskLayer());
+        EXPECT_EQ(mask_surface->render_target(), root_surface);
         EndTest();
         break;
     }
@@ -7554,14 +7561,12 @@
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
 
   void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
-    LayerTreeHostCommon::CallFunctionForEveryLayer(
-        host_impl->sync_tree(), [this](LayerImpl* layer) {
-          const std::vector<int>& list =
-              layer->IsAffectedByPageScale()
-                  ? this->affected_by_page_scale_
-                  : this->not_affected_by_page_scale_;
-          EXPECT_TRUE(base::Contains(list, layer->id()));
-        });
+    for (auto* layer : *host_impl->sync_tree()) {
+      const std::vector<int>& list = layer->IsAffectedByPageScale()
+                                         ? this->affected_by_page_scale_
+                                         : this->not_affected_by_page_scale_;
+      EXPECT_TRUE(base::Contains(list, layer->id()));
+    }
 
     EndTest();
   }
diff --git a/cc/trees/layer_tree_host_unittest_masks.cc b/cc/trees/layer_tree_host_unittest_masks.cc
index 41a9958..665af89d 100644
--- a/cc/trees/layer_tree_host_unittest_masks.cc
+++ b/cc/trees/layer_tree_host_unittest_masks.cc
@@ -75,7 +75,7 @@
   DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
                                    LayerTreeHostImpl::FrameData* frame_data,
                                    DrawResult draw_result) override {
-    EXPECT_EQ(2u, frame_data->render_passes.size());
+    EXPECT_EQ(3u, frame_data->render_passes.size());
     viz::RenderPass* root_pass = frame_data->render_passes.back().get();
     EXPECT_EQ(2u, root_pass->quad_list.size());
 
@@ -90,11 +90,13 @@
     gfx::Rect rect_in_target_space = MathUtil::MapEnclosingClippedRect(
         render_pass_quad->shared_quad_state->quad_to_target_transform,
         render_pass_quad->rect);
-    EXPECT_EQ(gfx::Rect(0, 0, 50, 50).ToString(),
-              rect_in_target_space.ToString());
-    EXPECT_EQ(gfx::ScaleRect(gfx::RectF(50.f, 50.f, 50.f, 50.f), 1.f / 100.f)
-                  .ToString(),
-              render_pass_quad->mask_uv_rect.ToString());
+    EXPECT_EQ(gfx::Rect(0, 0, 50, 50), rect_in_target_space);
+
+    // We use kDstIn blend mode instead of the mask feature of RenderPass.
+    EXPECT_EQ(gfx::RectF(), render_pass_quad->mask_uv_rect);
+    viz::RenderPass* mask_pass = frame_data->render_passes[1].get();
+    EXPECT_EQ(SkBlendMode::kDstIn,
+              mask_pass->quad_list.front()->shared_quad_state->blend_mode);
     EndTest();
     return draw_result;
   }
@@ -106,6 +108,92 @@
 SINGLE_AND_MULTI_THREAD_TEST_F(
     LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin);
 
+class LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOriginWithLayerList
+    : public LayerTreeTest {
+ protected:
+  LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOriginWithLayerList() {
+    SetUseLayerLists();
+  }
+
+  void SetupTree() override {
+    // The masked layer has bounds 50x50, but it has a child that causes
+    // the surface bounds to be larger. It also has a parent that clips the
+    // masked layer and its surface.
+
+    SetInitialRootBounds(gfx::Size(100, 100));
+    LayerTreeTest::SetupTree();
+
+    Layer* root = layer_tree_host()->root_layer();
+
+    gfx::Size layer_size(100, 100);
+    SetupViewport(root, gfx::Size(50, 50), layer_size);
+
+    auto* container = layer_tree_host()->outer_viewport_container_layer();
+    auto* scroll = layer_tree_host()->outer_viewport_scroll_layer();
+    container->SetMasksToBounds(true);
+    CreateClipNode(container);
+    scroll->SetClipTreeIndex(container->clip_tree_index());
+    SetScrollOffset(scroll, gfx::ScrollOffset(50, 50));
+
+    client_.set_bounds(root->bounds());
+    auto content_layer = FakePictureLayer::Create(&client_);
+    content_layer->SetBounds(layer_size);
+    CopyProperties(scroll, content_layer.get());
+    root->AddChild(content_layer);
+
+    std::unique_ptr<RecordingSource> recording_source =
+        FakeRecordingSource::CreateFilledRecordingSource(gfx::Size(100, 100));
+    PaintFlags paint1, paint2;
+    static_cast<FakeRecordingSource*>(recording_source.get())
+        ->add_draw_rect_with_flags(gfx::Rect(0, 0, 100, 90), paint1);
+    static_cast<FakeRecordingSource*>(recording_source.get())
+        ->add_draw_rect_with_flags(gfx::Rect(0, 90, 100, 10), paint2);
+    client_.set_fill_with_nonsolid_color(true);
+    static_cast<FakeRecordingSource*>(recording_source.get())->Rerecord();
+
+    auto mask_layer = FakePictureLayer::CreateWithRecordingSource(
+        &client_, std::move(recording_source));
+    SetupMaskProperties(content_layer.get(), mask_layer.get());
+    root->AddChild(mask_layer);
+
+    mask_layer_id_ = mask_layer->id();
+  }
+
+  void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+  DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+                                   LayerTreeHostImpl::FrameData* frame_data,
+                                   DrawResult draw_result) override {
+    EXPECT_EQ(1u, frame_data->render_passes.size());
+    viz::RenderPass* pass = frame_data->render_passes.back().get();
+    EXPECT_EQ(3u, pass->quad_list.size());
+
+    // There's a solid color quad under everything.
+    EXPECT_EQ(viz::DrawQuad::Material::kSolidColor,
+              pass->quad_list.back()->material);
+
+    EXPECT_EQ(viz::DrawQuad::Material::kTiledContent,
+              pass->quad_list.ElementAt(1)->material);
+
+    auto* mask_quad = pass->quad_list.front();
+    EXPECT_EQ(viz::DrawQuad::Material::kTiledContent, mask_quad->material);
+    gfx::Rect rect_in_target_space = MathUtil::MapEnclosingClippedRect(
+        mask_quad->shared_quad_state->quad_to_target_transform,
+        mask_quad->rect);
+    EXPECT_EQ(gfx::Rect(0, 0, 50, 50), rect_in_target_space);
+    // We use kDstIn blend mode for mask.
+    EXPECT_EQ(SkBlendMode::kDstIn, mask_quad->shared_quad_state->blend_mode);
+    EndTest();
+    return draw_result;
+  }
+
+  int mask_layer_id_;
+  FakeContentLayerClient client_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+    LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOriginWithLayerList);
+
 class LayerTreeTestMaskLayerForSurfaceWithClippedLayer : public LayerTreeTest {
  protected:
   void SetupTree() override {
@@ -173,7 +261,7 @@
   DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
                                    LayerTreeHostImpl::FrameData* frame_data,
                                    DrawResult draw_result) override {
-    EXPECT_EQ(2u, frame_data->render_passes.size());
+    EXPECT_EQ(3u, frame_data->render_passes.size());
     viz::RenderPass* root_pass = frame_data->render_passes.back().get();
     EXPECT_EQ(2u, root_pass->quad_list.size());
 
@@ -191,13 +279,12 @@
         render_pass_quad->rect);
     EXPECT_EQ(gfx::Rect(20, 10, 10, 20).ToString(),
               rect_in_target_space.ToString());
-    // The masked layer is 50x50, but the surface size is 10x20. So the texture
-    // coords in the mask are scaled by 10/50 and 20/50.
-    // The surface is clipped to (20,10) so the mask texture coords are offset
-    // by 20/50 and 10/50
-    EXPECT_EQ(gfx::ScaleRect(gfx::RectF(20.f, 10.f, 10.f, 20.f), 1.f / 50.f)
-                  .ToString(),
-              render_pass_quad->mask_uv_rect.ToString());
+
+    // We use kDstIn blend mode instead of the mask feature of RenderPass.
+    EXPECT_EQ(gfx::RectF(), render_pass_quad->mask_uv_rect);
+    viz::RenderPass* mask_pass = frame_data->render_passes[1].get();
+    EXPECT_EQ(SkBlendMode::kDstIn,
+              mask_pass->quad_list.front()->shared_quad_state->blend_mode);
     EndTest();
     return draw_result;
   }
@@ -285,7 +372,7 @@
   DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
                                    LayerTreeHostImpl::FrameData* frame_data,
                                    DrawResult draw_result) override {
-    EXPECT_EQ(2u, frame_data->render_passes.size());
+    EXPECT_EQ(3u, frame_data->render_passes.size());
     viz::RenderPass* root_pass = frame_data->render_passes.back().get();
     EXPECT_EQ(2u, root_pass->quad_list.size());
 
@@ -309,13 +396,12 @@
         render_pass_quad->visible_rect);
     EXPECT_EQ(gfx::Rect(20, 10, 20, 40).ToString(),
               visible_rect_in_target_space.ToString());
-    // The masked layer is 50x50, but the surface size is 10x20. So the texture
-    // coords in the mask are scaled by 10/50 and 20/50.
-    // The surface is clipped to (20,10) so the mask texture coords are offset
-    // by 20/50 and 10/50
-    EXPECT_EQ(gfx::ScaleRect(gfx::RectF(20.f, 10.f, 10.f, 20.f), 1.f / 50.f)
-                  .ToString(),
-              render_pass_quad->mask_uv_rect.ToString());
+
+    // We use kDstIn blend mode instead of the mask feature of RenderPass.
+    EXPECT_EQ(gfx::RectF(), render_pass_quad->mask_uv_rect);
+    viz::RenderPass* mask_pass = frame_data->render_passes[1].get();
+    EXPECT_EQ(SkBlendMode::kDstIn,
+              mask_pass->quad_list.front()->shared_quad_state->blend_mode);
     EndTest();
     return draw_result;
   }
@@ -383,7 +469,7 @@
   DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
                                    LayerTreeHostImpl::FrameData* frame_data,
                                    DrawResult draw_result) override {
-    EXPECT_EQ(2u, frame_data->render_passes.size());
+    EXPECT_EQ(3u, frame_data->render_passes.size());
     viz::RenderPass* root_pass = frame_data->render_passes.back().get();
     EXPECT_EQ(2u, root_pass->quad_list.size());
 
@@ -398,22 +484,25 @@
     gfx::Rect rect_in_target_space = MathUtil::MapEnclosingClippedRect(
         render_pass_quad->shared_quad_state->quad_to_target_transform,
         render_pass_quad->rect);
+
+    // We use kDstIn blend mode instead of the mask feature of RenderPass.
+    EXPECT_EQ(gfx::RectF(), render_pass_quad->mask_uv_rect);
+    viz::RenderPass* mask_pass = frame_data->render_passes[1].get();
+    EXPECT_EQ(SkBlendMode::kDstIn,
+              mask_pass->quad_list.front()->shared_quad_state->blend_mode);
+
     switch (host_impl->active_tree()->source_frame_number()) {
       case 0:
         // Check that the tree scaling is correctly taken into account for the
         // mask, that should fully map onto the quad.
         EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
                   rect_in_target_space.ToString());
-        EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(),
-                  render_pass_quad->mask_uv_rect.ToString());
         break;
       case 1:
         // Applying a DSF should change the render surface size, but won't
         // affect which part of the mask is used.
         EXPECT_EQ(gfx::Rect(0, 0, 200, 200).ToString(),
                   rect_in_target_space.ToString());
-        EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(),
-                  render_pass_quad->mask_uv_rect.ToString());
         EndTest();
         break;
     }
@@ -484,7 +573,7 @@
   DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
                                    LayerTreeHostImpl::FrameData* frame_data,
                                    DrawResult draw_result) override {
-    EXPECT_EQ(2u, frame_data->render_passes.size());
+    EXPECT_EQ(3u, frame_data->render_passes.size());
     viz::RenderPass* root_pass = frame_data->render_passes.back().get();
     EXPECT_EQ(2u, root_pass->quad_list.size());
 
@@ -499,9 +588,12 @@
         viz::RenderPassDrawQuad::MaterialCast(root_pass->quad_list.front());
     EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
               render_pass_quad->rect.ToString());
-    // The mask layer is 100x100, but is backed by a 120x150 image.
-    EXPECT_EQ(gfx::RectF(0.0f, 0.0f, 100.f / 120.0f, 100.f / 150.0f).ToString(),
-              render_pass_quad->mask_uv_rect.ToString());
+
+    // We use kDstIn blend mode instead of the mask feature of RenderPass.
+    EXPECT_EQ(gfx::RectF(), render_pass_quad->mask_uv_rect);
+    viz::RenderPass* mask_pass = frame_data->render_passes[1].get();
+    EXPECT_EQ(SkBlendMode::kDstIn,
+              mask_pass->quad_list.front()->shared_quad_state->blend_mode);
     EndTest();
     return draw_result;
   }
diff --git a/cc/trees/layer_tree_host_unittest_occlusion.cc b/cc/trees/layer_tree_host_unittest_occlusion.cc
index 244eb6f..47fb835 100644
--- a/cc/trees/layer_tree_host_unittest_occlusion.cc
+++ b/cc/trees/layer_tree_host_unittest_occlusion.cc
@@ -148,10 +148,10 @@
     make_surface_bigger->SetIsDrawable(true);
     child_->AddChild(make_surface_bigger);
 
-    scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client_);
-    mask->SetBounds(gfx::Size(30, 40));
-    mask->SetIsDrawable(true);
-    child_->SetMaskLayer(mask);
+    mask_ = PictureLayer::Create(&client_);
+    mask_->SetBounds(gfx::Size(30, 40));
+    mask_->SetIsDrawable(true);
+    child_->SetMaskLayer(mask_);
 
     scoped_refptr<Layer> child2 = Layer::Create();
     child2->SetBounds(gfx::Size(10, 12));
@@ -170,28 +170,42 @@
   void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
     LayerImpl* root = impl->active_tree()->root_layer_for_testing();
     LayerImpl* child = impl->active_tree()->LayerById(child_->id());
-    RenderSurfaceImpl* surface = GetRenderSurface(child);
-    LayerImpl* mask = surface->MaskLayer();
+    RenderSurfaceImpl* child_surface = GetRenderSurface(child);
+    LayerImpl* mask = impl->active_tree()->LayerById(mask_->id());
+    RenderSurfaceImpl* mask_surface = GetRenderSurface(mask);
 
     // Verify the draw properties are valid.
     EXPECT_TRUE(root->contributes_to_drawn_render_surface());
     EXPECT_TRUE(child->contributes_to_drawn_render_surface());
-    EXPECT_TRUE(GetRenderSurface(child));
-    EXPECT_EQ(GetRenderSurface(child), child->render_target());
+    EXPECT_TRUE(mask->contributes_to_drawn_render_surface());
+    EXPECT_TRUE(child_surface);
+    EXPECT_EQ(child_surface, child->render_target());
 
-    gfx::Transform transform = surface->draw_transform();
+    gfx::Transform transform = child_surface->draw_transform();
     transform.PreconcatTransform(child->DrawTransform());
-
     EXPECT_OCCLUSION_EQ(
         Occlusion(transform, SimpleEnclosedRegion(),
                   SimpleEnclosedRegion(gfx::Rect(13, 9, 10, 11))),
-        mask->draw_properties().occlusion_in_content_space);
+        child_surface->occlusion_in_content_space());
+
+    // Mask layer has its own transform and render surface in layer tree mode.
+    EXPECT_NE(child_surface, mask_surface);
+    EXPECT_OCCLUSION_EQ(
+        Occlusion(transform, SimpleEnclosedRegion(),
+                  SimpleEnclosedRegion(gfx::Rect(13, 9, 10, 11))),
+        mask_surface->occlusion_in_content_space());
+
+    EXPECT_OCCLUSION_EQ(Occlusion(gfx::Transform(),
+                                  SimpleEnclosedRegion(gfx::Rect(3, 4, 10, 10)),
+                                  SimpleEnclosedRegion()),
+                        mask->draw_properties().occlusion_in_content_space);
     EndTest();
   }
 
  private:
   FakeContentLayerClient client_;
   scoped_refptr<Layer> child_;
+  scoped_refptr<PictureLayer> mask_;
 };
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestDrawPropertiesOnMask);
@@ -208,21 +222,21 @@
     gfx::Transform scale;
     scale.Scale(2, 2);
 
-    child_ = Layer::Create();
-    child_->SetBounds(gfx::Size(30, 40));
-    child_->SetTransform(scale);
-    root->AddChild(child_);
+    scoped_refptr<Layer> child = Layer::Create();
+    child->SetBounds(gfx::Size(30, 40));
+    child->SetTransform(scale);
+    root->AddChild(child);
 
     scoped_refptr<Layer> grand_child = Layer::Create();
     grand_child->SetBounds(gfx::Size(100, 100));
     grand_child->SetPosition(gfx::PointF(-10.f, -15.f));
     grand_child->SetIsDrawable(true);
-    child_->AddChild(grand_child);
+    child->AddChild(grand_child);
 
-    scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client_);
-    mask->SetBounds(gfx::Size(30, 40));
-    mask->SetIsDrawable(true);
-    child_->SetMaskLayer(mask);
+    mask_ = PictureLayer::Create(&client_);
+    mask_->SetBounds(gfx::Size(30, 40));
+    mask_->SetIsDrawable(true);
+    child->SetMaskLayer(mask_);
 
     scoped_refptr<Layer> child2 = Layer::Create();
     child2->SetBounds(gfx::Size(10, 11));
@@ -239,22 +253,21 @@
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
 
   void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
-    LayerImpl* child = impl->active_tree()->LayerById(child_->id());
-    LayerImpl* mask = GetRenderSurface(child)->MaskLayer();
+    LayerImpl* mask = impl->active_tree()->LayerById(mask_->id());
 
     gfx::Transform scale;
     scale.Scale(2, 2);
 
     EXPECT_OCCLUSION_EQ(
-        Occlusion(scale, SimpleEnclosedRegion(),
-                  SimpleEnclosedRegion(gfx::Rect(13, 15, 10, 11))),
+        Occlusion(scale, SimpleEnclosedRegion(gfx::Rect(13, 15, 10, 11)),
+                  SimpleEnclosedRegion()),
         mask->draw_properties().occlusion_in_content_space);
     EndTest();
   }
 
  private:
   FakeContentLayerClient client_;
-  scoped_refptr<Layer> child_;
+  scoped_refptr<PictureLayer> mask_;
 };
 
 SINGLE_AND_MULTI_THREAD_TEST_F(
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index a64f3c6..49956bd 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -151,7 +151,6 @@
   // Need to explicitly clear the tree prior to destroying this so that
   // the LayerTreeImpl pointer is still valid in the LayerImpl dtor.
   DCHECK(LayerListIsEmpty());
-  DCHECK(layers_.empty());
 }
 
 void LayerTreeImpl::Shutdown() {
@@ -159,26 +158,25 @@
   BreakSwapPromises(IsActiveTree() ? SwapPromise::SWAP_FAILS
                                    : SwapPromise::ACTIVATION_FAILS);
   DCHECK(LayerListIsEmpty());
-  DCHECK(layers_.empty());
 }
 
 void LayerTreeImpl::ReleaseResources() {
-  for (auto& layer : layers_)
+  for (auto* layer : *this)
     layer->ReleaseResources();
 }
 
 void LayerTreeImpl::OnPurgeMemory() {
-  for (auto& layer : layers_)
+  for (auto* layer : *this)
     layer->OnPurgeMemory();
 }
 
 void LayerTreeImpl::ReleaseTileResources() {
-  for (auto& layer : layers_)
+  for (auto* layer : *this)
     layer->ReleaseTileResources();
 }
 
 void LayerTreeImpl::RecreateTileResources() {
-  for (auto& layer : layers_)
+  for (auto* layer : *this)
     layer->RecreateTileResources();
 }
 
@@ -436,7 +434,7 @@
 }
 
 bool LayerTreeImpl::IsRootLayer(const LayerImpl* layer) const {
-  return layer_list_.empty() ? false : layer_list_[0] == layer;
+  return !layer_list_.empty() && layer_list_[0].get() == layer;
 }
 
 gfx::ScrollOffset LayerTreeImpl::TotalScrollOffset() const {
@@ -468,10 +466,9 @@
 }
 
 OwnedLayerImplList LayerTreeImpl::DetachLayers() {
-  layer_list_.clear();
   render_surface_list_.clear();
   set_needs_update_draw_properties();
-  return std::move(layers_);
+  return std::move(layer_list_);
 }
 
 OwnedLayerImplList LayerTreeImpl::DetachLayersKeepingRootLayerForTesting() {
@@ -647,10 +644,9 @@
 }
 
 void LayerTreeImpl::HandleScrollbarShowRequestsFromMain() {
-  LayerTreeHostCommon::CallFunctionForEveryLayer(this, [this](
-                                                           LayerImpl* layer) {
+  for (auto* layer : *this) {
     if (!layer->needs_show_scrollbars())
-      return;
+      continue;
     ScrollbarAnimationController* controller =
         host_impl_->ScrollbarAnimationControllerForElementId(
             layer->element_id());
@@ -658,7 +654,7 @@
       controller->DidRequestShowFromMainThread();
       layer->set_needs_show_scrollbars(false);
     }
-  });
+  }
 }
 
 void LayerTreeImpl::MoveChangeTrackingToLayers() {
@@ -683,30 +679,6 @@
     layer->ResetRasterScale();
 }
 
-LayerImplList::const_iterator LayerTreeImpl::begin() const {
-  return layer_list_.cbegin();
-}
-
-LayerImplList::const_iterator LayerTreeImpl::end() const {
-  return layer_list_.cend();
-}
-
-LayerImplList::const_reverse_iterator LayerTreeImpl::rbegin() const {
-  return layer_list_.crbegin();
-}
-
-LayerImplList::const_reverse_iterator LayerTreeImpl::rend() const {
-  return layer_list_.crend();
-}
-
-LayerImplList::reverse_iterator LayerTreeImpl::rbegin() {
-  return layer_list_.rbegin();
-}
-
-LayerImplList::reverse_iterator LayerTreeImpl::rend() {
-  return layer_list_.rend();
-}
-
 bool LayerTreeImpl::IsElementInPropertyTree(ElementId element_id) const {
   return property_trees()->HasElement(element_id);
 }
@@ -1300,7 +1272,7 @@
 
 // For unit tests, we use the layer's id as its element id.
 void LayerTreeImpl::SetElementIdsForTesting() {
-  for (auto& layer : layers_) {
+  for (auto* layer : *this) {
     if (!layer->element_id())
       layer->SetElementId(LayerIdToElementIdForTesting(layer->id()));
   }
@@ -1344,7 +1316,7 @@
     // calculations except when this function is explicitly passed a flag asking
     // us to skip it.
     LayerTreeHostCommon::CalcDrawPropsImplInputs inputs(
-        layer_list_[0], GetDeviceViewport(), host_impl_->DrawTransform(),
+        layer_list_[0].get(), GetDeviceViewport(), host_impl_->DrawTransform(),
         device_scale_factor(), current_page_scale_factor(), PageScaleLayer(),
         InnerViewportScrollLayer(), OuterViewportScrollLayer(),
         elastic_overscroll()->Current(IsActiveTree()),
@@ -1405,14 +1377,6 @@
             occlusion_tracker.GetCurrentOcclusionForContributingSurface(
                 draw_transform);
         render_surface->set_occlusion_in_content_space(occlusion);
-        // Masks are used to draw the contributing surface, so should have
-        // the same occlusion as the surface (nothing inside the surface
-        // occludes them).
-        if (LayerImpl* mask = render_surface->MaskLayer()) {
-          mask->draw_properties().occlusion_in_content_space =
-              occlusion_tracker.GetCurrentOcclusionForContributingSurface(
-                  draw_transform * render_surface->SurfaceScale());
-        }
       }
 
       occlusion_tracker.LeaveLayer(it);
@@ -1501,11 +1465,11 @@
 // TODO(masonfreed): If this shows up on profiles, this could use
 // a layer_element_map_ approach similar to LayerById().
 LayerImpl* LayerTreeImpl::LayerByElementId(ElementId element_id) const {
-  auto it = std::find_if(layer_list_.rbegin(), layer_list_.rend(),
-                         [&element_id](LayerImpl* layer_impl) {
-                           return layer_impl->element_id() == element_id;
-                         });
-  if (it == layer_list_.rend())
+  auto it =
+      std::find_if(rbegin(), rend(), [&element_id](LayerImpl* layer_impl) {
+        return layer_impl->element_id() == element_id;
+      });
+  if (it == rend())
     return nullptr;
   return *it;
 }
@@ -1556,19 +1520,9 @@
 }
 
 void LayerTreeImpl::AddLayer(std::unique_ptr<LayerImpl> layer) {
-  DCHECK(!base::Contains(layer_list_, layer.get()));
-  layer_list_.push_back(layer.get());
-  AddOwnedLayer(std::move(layer));
-}
-
-void LayerTreeImpl::AddMaskLayer(std::unique_ptr<LayerImpl> layer) {
-  AddOwnedLayer(std::move(layer));
-}
-
-void LayerTreeImpl::AddOwnedLayer(std::unique_ptr<LayerImpl> layer) {
-  DCHECK(!base::Contains(layers_, layer));
   DCHECK(layer);
-  layers_.push_back(std::move(layer));
+  DCHECK(!base::Contains(layer_list_, layer));
+  layer_list_.push_back(std::move(layer));
   set_needs_update_draw_properties();
 }
 
@@ -1586,7 +1540,7 @@
   // if we were in a good state.
   host_impl_->ResetRequiresHighResToDraw();
 
-  for (auto& layer : layers_)
+  for (auto* layer : *this)
     layer->DidBecomeActive();
 
   for (const auto& swap_promise : swap_promise_list_)
@@ -1752,11 +1706,10 @@
 
 void LayerTreeImpl::GetAllPrioritizedTilesForTracing(
     std::vector<PrioritizedTile>* prioritized_tiles) const {
-  for (auto it = layer_list_.rbegin(); it != layer_list_.rend(); ++it) {
-    LayerImpl* layer_impl = *it;
-    if (!layer_impl->contributes_to_drawn_render_surface())
+  for (auto* layer : base::Reversed(*this)) {
+    if (!layer->contributes_to_drawn_render_surface())
       continue;
-    layer_impl->GetAllPrioritizedTilesForTracing(prioritized_tiles);
+    layer->GetAllPrioritizedTilesForTracing(prioritized_tiles);
   }
 }
 
@@ -1766,10 +1719,10 @@
   state->SetInteger("source_frame_number", source_frame_number_);
 
   state->BeginArray("render_surface_layer_list");
-  for (auto it = layer_list_.rbegin(); it != layer_list_.rend(); ++it) {
-    if (!(*it)->contributes_to_drawn_render_surface())
+  for (auto* layer : base::Reversed(*this)) {
+    if (layer->contributes_to_drawn_render_surface())
       continue;
-    viz::TracedValue::AppendIDRef(*it, state);
+    viz::TracedValue::AppendIDRef(layer, state);
   }
   state->EndArray();
 
@@ -2177,11 +2130,12 @@
 
     float distance_to_intersection = 0.f;
     bool hit = false;
-    if (layer->Is3dSorted())
+    if (layer->Is3dSorted()) {
       hit =
           PointHitsLayer(layer, screen_space_point, &distance_to_intersection);
-    else
+    } else {
       hit = PointHitsLayer(layer, screen_space_point, nullptr);
+    }
 
     if (!hit)
       continue;
@@ -2219,7 +2173,7 @@
     return nullptr;
 
   FindClosestMatchingLayerState state;
-  LayerImpl* root_layer = layer_list_[0];
+  LayerImpl* root_layer = layer_list_[0].get();
   FindClosestMatchingLayer(screen_space_point, root_layer,
                            HitTestScrollingLayerOrScrollbarFunctor(), &state);
   return state.closest_match;
@@ -2236,7 +2190,7 @@
   if (!UpdateDrawProperties())
     return nullptr;
   FindClosestMatchingLayerState state;
-  FindClosestMatchingLayer(screen_space_point, layer_list_[0],
+  FindClosestMatchingLayer(screen_space_point, layer_list_[0].get(),
                            HitTestVisibleScrollableOrTouchableFunctor(),
                            &state);
   return state.closest_match;
@@ -2269,7 +2223,8 @@
   if (!UpdateDrawProperties())
     return nullptr;
   FindClosestMatchingLayerState state;
-  FindClosestMatchingLayer(screen_space_point, layer_list_[0], func, &state);
+  FindClosestMatchingLayer(screen_space_point, layer_list_[0].get(), func,
+                           &state);
   return state.closest_match;
 }
 
@@ -2438,7 +2393,7 @@
 void LayerTreeImpl::ResetAllChangeTracking() {
   layers_that_should_push_properties_.clear();
   // Iterate over all layers, including masks.
-  for (auto& layer : layers_)
+  for (auto* layer : *this)
     layer->ResetChangeTracking();
   property_trees_.ResetAllChangeTracking();
 }
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index e96cdd1..4d28bc6 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -163,7 +163,7 @@
   // Other public methods
   // ---------------------------------------------------------------------------
   LayerImpl* root_layer_for_testing() {
-    return layer_list_.empty() ? nullptr : layer_list_[0];
+    return layer_list_.empty() ? nullptr : layer_list_[0].get();
   }
   const RenderSurfaceImpl* RootRenderSurface() const;
   bool LayerListIsEmpty() const;
@@ -191,12 +191,35 @@
 
   void ForceRecalculateRasterScales();
 
-  LayerImplList::const_iterator begin() const;
-  LayerImplList::const_iterator end() const;
-  LayerImplList::const_reverse_iterator rbegin() const;
-  LayerImplList::const_reverse_iterator rend() const;
-  LayerImplList::reverse_iterator rbegin();
-  LayerImplList::reverse_iterator rend();
+  // Adapts an iterator of std::unique_ptr<LayerImpl> to an iterator of
+  // LayerImpl*.
+  template <typename Iterator>
+  class IteratorAdapter {
+   public:
+    explicit IteratorAdapter(Iterator it) : it_(it) {}
+    bool operator==(IteratorAdapter o) const { return it_ == o.it_; }
+    bool operator!=(IteratorAdapter o) const { return !(*this == o); }
+    LayerImpl* operator*() const { return it_->get(); }
+    LayerImpl* operator->() const { return it_->get(); }
+    IteratorAdapter& operator++() {
+      ++it_;
+      return *this;
+    }
+
+   private:
+    Iterator it_;
+  };
+  using const_iterator = IteratorAdapter<OwnedLayerImplList::const_iterator>;
+  using const_reverse_iterator =
+      IteratorAdapter<OwnedLayerImplList::const_reverse_iterator>;
+  const_iterator begin() const { return const_iterator(layer_list_.cbegin()); }
+  const_iterator end() const { return const_iterator(layer_list_.cend()); }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(layer_list_.crbegin());
+  }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(layer_list_.crend());
+  }
 
   void SetTransformMutated(ElementId element_id,
                            const gfx::Transform& transform);
@@ -485,11 +508,6 @@
   // Append a layer to the list.
   void AddLayer(std::unique_ptr<LayerImpl> layer);
 
-  // Mask layer is special: it's managed by LayerTreeImpl (in layers_), but
-  // not in layer_list_. TODO(wangxianzhu): Remove this when we switch ui
-  // compositor to property based masks.
-  void AddMaskLayer(std::unique_ptr<LayerImpl> mask_layer);
-
   size_t NumLayers();
 
   void DidBecomeActive();
@@ -678,8 +696,6 @@
  private:
   friend class LayerTreeHost;
 
-  void AddOwnedLayer(std::unique_ptr<LayerImpl> layer);
-
   TransformNode* PageScaleTransformNode();
   void UpdatePageScaleNode();
 
@@ -725,13 +741,9 @@
   // TODO(wangxianzhu): Combine layers_ and layer_list_ when we remove
   // support of mask layers.
 
-  // Contains all managed layers, including layers in layer_list_ and mask
-  // layers.
-  OwnedLayerImplList layers_;
-  // Maps from layer id to layer (for each layer in layers_).
+  OwnedLayerImplList layer_list_;
+  // Maps from layer id to layer.
   LayerImplMap layer_id_map_;
-  // Contains non-mask layers, sorted in draw order.
-  LayerImplList layer_list_;
 
   // Set of layers that need to push properties.
   base::flat_set<LayerImpl*> layers_that_should_push_properties_;
diff --git a/cc/trees/occlusion_tracker.cc b/cc/trees/occlusion_tracker.cc
index e4da0f3f..363461f 100644
--- a/cc/trees/occlusion_tracker.cc
+++ b/cc/trees/occlusion_tracker.cc
@@ -196,8 +196,7 @@
 
   // If the occlusion within the surface can not be applied to things outside of
   // the surface's subtree, then clear the occlusion here so it won't be used.
-  if (finished_target_surface->HasMask() ||
-      finished_target_surface->HasMaskingContributingSurface() ||
+  if (finished_target_surface->HasMaskingContributingSurface() ||
       finished_target_surface->draw_opacity() < 1 ||
       !finished_target_surface->UsesDefaultBlendMode() ||
       target_is_only_for_copy_request_or_force_render_surface ||
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index f8264369..7e4aa2da 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -708,7 +708,6 @@
 
 void EffectTree::clear() {
   PropertyTree<EffectNode>::clear();
-  mask_layer_ids_.clear();
   render_surfaces_.clear();
   render_surfaces_.push_back(nullptr);
 
@@ -1024,10 +1023,6 @@
   return id_1;
 }
 
-void EffectTree::AddMaskLayerId(int id) {
-  mask_layer_ids_.push_back(id);
-}
-
 bool EffectTree::ContributesToDrawnSurface(int id) {
   // All drawn nodes contribute to drawn surface.
   // Exception : Nodes that are hidden and are drawn only for the sake of
@@ -1138,7 +1133,7 @@
   for (; effect_node->id != kContentsRootNodeId;
        effect_node = Node(effect_node->parent_id)) {
     if (!effect_node->rounded_corner_bounds.IsEmpty() ||
-        effect_node->has_masking_child || effect_node->is_masked)
+        effect_node->has_masking_child)
       return true;
   }
   return false;
@@ -1176,7 +1171,6 @@
 EffectTree& EffectTree::operator=(const EffectTree& from) {
   PropertyTree::operator=(from);
   render_surfaces_.resize(size());
-  mask_layer_ids_ = from.mask_layer_ids_;
   // copy_requests_ are omitted here, since these need to be moved rather
   // than copied or assigned.
 
@@ -1184,8 +1178,7 @@
 }
 
 bool EffectTree::operator==(const EffectTree& other) const {
-  return PropertyTree::operator==(other) &&
-         mask_layer_ids_ == other.mask_layer_ids_;
+  return PropertyTree::operator==(other);
 }
 
 ScrollTree::ScrollTree()
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h
index 19a905e..01b096e 100644
--- a/cc/trees/property_tree.h
+++ b/cc/trees/property_tree.h
@@ -325,9 +325,6 @@
   // surface.
   int LowestCommonAncestorWithRenderSurface(int id_1, int id_2) const;
 
-  void AddMaskLayerId(int id);
-  const std::vector<int>& mask_layer_ids() const { return mask_layer_ids_; }
-
   RenderSurfaceImpl* GetRenderSurface(int id) {
     return render_surfaces_[id].get();
   }
@@ -371,9 +368,6 @@
   std::unordered_multimap<int, std::unique_ptr<viz::CopyOutputRequest>>
       copy_requests_;
 
-  // Unsorted list of all mask layer ids that effect nodes refer to.
-  std::vector<int> mask_layer_ids_;
-
   // Indexed by node id.
   std::vector<std::unique_ptr<RenderSurfaceImpl>> render_surfaces_;
 };
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index e8c70c2..5cb5415 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -167,7 +167,7 @@
 // -------------------------------------------------------------------
 
 bool LayerClipsSubtreeToItsBounds(Layer* layer) {
-  return layer->masks_to_bounds() || layer->mask_layer();
+  return layer->masks_to_bounds() || layer->IsMaskedByChild();
 }
 
 bool LayerClipsSubtree(Layer* layer) {
@@ -350,22 +350,18 @@
   if (is_root)
     return RenderSurfaceReason::kRoot;
 
-  // If the layer uses a mask.
-  if (layer->mask_layer()) {
+  if (layer->IsMaskedByChild()) {
     return RenderSurfaceReason::kMask;
   }
 
-  // If the layer uses trilinear filtering.
   if (layer->trilinear_filtering()) {
     return RenderSurfaceReason::kTrilinearFiltering;
   }
 
-  // If the layer uses a CSS filter.
   if (!layer->filters().IsEmpty()) {
     return RenderSurfaceReason::kFilter;
   }
 
-  // If the layer uses a CSS backdrop-filter.
   if (!layer->backdrop_filters().IsEmpty()) {
     return RenderSurfaceReason::kBackdropFilter;
   }
@@ -521,7 +517,6 @@
   node->backdrop_filters = layer->backdrop_filters();
   node->backdrop_filter_bounds = layer->backdrop_filter_bounds();
   node->backdrop_filter_quality = layer->backdrop_filter_quality();
-  node->backdrop_mask_element_id = layer->backdrop_mask_element_id();
   node->filters_origin = layer->filters_origin();
   node->trilinear_filtering = layer->trilinear_filtering();
   node->has_potential_opacity_animation = has_potential_opacity_animation;
@@ -543,12 +538,6 @@
           ? node_id
           : data_from_ancestor.closest_ancestor_with_copy_request;
 
-  if (layer->mask_layer()) {
-    node->mask_layer_id = layer->mask_layer()->id();
-    effect_tree_.AddMaskLayerId(node->mask_layer_id);
-    node->is_masked = true;
-  }
-
   if (layer->HasRoundedCorner()) {
     // This is currently in the local space of the layer and hence in an invalid
     // space. Once we have the associated transform node for this effect node,
@@ -786,17 +775,6 @@
   created_render_surface = UpdateRenderSurfaceIfNeeded(
       data_from_parent.effect_tree_parent, &data_for_children,
       subtree_has_rounded_corner, created_transform_node);
-
-  if (layer->mask_layer()) {
-    layer->mask_layer()->set_property_tree_sequence_number(
-        property_trees_.sequence_number);
-    layer->mask_layer()->SetOffsetToTransformParent(
-        layer->offset_to_transform_parent());
-    layer->mask_layer()->SetTransformTreeIndex(layer->transform_tree_index());
-    layer->mask_layer()->SetClipTreeIndex(layer->clip_tree_index());
-    layer->mask_layer()->SetEffectTreeIndex(layer->effect_tree_index());
-    layer->mask_layer()->SetScrollTreeIndex(layer->scroll_tree_index());
-  }
 }
 
 void PropertyTreeBuilderContext::BuildPropertyTrees() {
diff --git a/cc/trees/tree_synchronizer.cc b/cc/trees/tree_synchronizer.cc
index 6d2bab4a..0419820 100644
--- a/cc/trees/tree_synchronizer.cc
+++ b/cc/trees/tree_synchronizer.cc
@@ -103,12 +103,6 @@
   }
 
   PushLayerList(&old_layer_map, source_tree, tree_impl);
-
-  for (int id : property_trees->effect_tree.mask_layer_ids()) {
-    std::unique_ptr<LayerImpl> layer_impl(ReuseOrCreateLayerImpl(
-        &old_layer_map, source_tree->LayerById(id), tree_impl));
-    tree_impl->AddMaskLayer(std::move(layer_impl));
-  }
 }
 
 }  // namespace
diff --git a/cc/trees/tree_synchronizer_unittest.cc b/cc/trees/tree_synchronizer_unittest.cc
index 0869072..b383409 100644
--- a/cc/trees/tree_synchronizer_unittest.cc
+++ b/cc/trees/tree_synchronizer_unittest.cc
@@ -109,16 +109,6 @@
 
     EXPECT_EQ(layer->non_fast_scrollable_region(),
               layer_impl->non_fast_scrollable_region());
-
-    const EffectTree& effect_tree = tree_impl->property_trees()->effect_tree;
-    if (layer->mask_layer()) {
-      SCOPED_TRACE("mask_layer");
-      int mask_layer_id = layer->mask_layer()->id();
-      EXPECT_TRUE(tree_impl->LayerById(mask_layer_id));
-      EXPECT_EQ(
-          mask_layer_id,
-          effect_tree.Node(layer_impl->effect_tree_index())->mask_layer_id);
-    }
   }
 }
 
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index a1cca37..cd3c295b 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1780,14 +1780,18 @@
   }
 }
 
-monochrome_public_apk_or_module_tmpl("monochrome_public_apk") {
-  version_code = monochrome_version_code
-  version_name = chrome_version_name
-  apk_name = "MonochromePublic"
-  target_type = "android_apk"
-}
-
 if (public_android_sdk) {
+  # Can't defined monochrome_public_apk without public_android_sdk or else the
+  # shared_resources_whitelist_target will not be applied, and R8 -checkdiscards
+  # will fail.
+  # https://crbug.com/1000763
+  monochrome_public_apk_or_module_tmpl("monochrome_public_apk") {
+    version_code = monochrome_version_code
+    version_name = chrome_version_name
+    apk_name = "MonochromePublic"
+    target_type = "android_apk"
+  }
+
   trichrome_library_apk_tmpl("trichrome_library_apk") {
     apk_name = "TrichromeLibrary"
     android_manifest = trichrome_library_android_manifest
@@ -2140,11 +2144,13 @@
   android_test_apk_name = "ChromeSmokeTest"
 }
 
-instrumentation_test_runner("monochrome_public_smoke_test") {
-  apk_under_test = ":monochrome_public_apk"
-  android_test_apk = ":chrome_smoke_test_apk"
-  android_test_apk_name = "ChromeSmokeTest"
-  never_incremental = true
+if (public_android_sdk) {
+  instrumentation_test_runner("monochrome_public_smoke_test") {
+    apk_under_test = ":monochrome_public_apk"
+    android_test_apk = ":chrome_smoke_test_apk"
+    android_test_apk_name = "ChromeSmokeTest"
+    never_incremental = true
+  }
 }
 
 android_test_apk("chrome_bundle_smoke_test_apk") {
@@ -2196,7 +2202,7 @@
   extra_args = _bundle_smoke_test_extra_args
 }
 
-if (defined(expected_static_initializer_count)) {
+if (public_android_sdk && defined(expected_static_initializer_count)) {
   action_with_pydeps("monochrome_static_initializers") {
     script = "//build/android/gyp/assert_static_initializers.py"
     inputs = [
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index f1b2f69..5f80686 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1827,24 +1827,14 @@
   "java/src/org/chromium/chrome/browser/widget/ThumbnailProvider.java",
   "java/src/org/chromium/chrome/browser/widget/ThumbnailProviderImpl.java",
   "java/src/org/chromium/chrome/browser/widget/ThumbnailStorageDelegate.java",
-  "java/src/org/chromium/chrome/browser/widget/TintedDrawable.java",
   "java/src/org/chromium/chrome/browser/widget/ViewHighlighter.java",
   "java/src/org/chromium/chrome/browser/widget/ViewResourceFrameLayout.java",
-  "java/src/org/chromium/chrome/browser/widget/animation/AnimatorProperties.java",
-  "java/src/org/chromium/chrome/browser/widget/animation/CancelAwareAnimatorListener.java",
-  "java/src/org/chromium/chrome/browser/widget/animation/FocusAnimator.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserver.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetSwipeDetector.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/EmptyBottomSheetObserver.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/TouchRestrictingFrameLayout.java",
-  "java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserver.java",
-  "java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserverAdapter.java",
-  "java/src/org/chromium/chrome/browser/widget/displaystyle/HorizontalDisplayStyle.java",
-  "java/src/org/chromium/chrome/browser/widget/displaystyle/UiConfig.java",
-  "java/src/org/chromium/chrome/browser/widget/displaystyle/VerticalDisplayStyle.java",
-  "java/src/org/chromium/chrome/browser/widget/displaystyle/ViewResizer.java",
   "java/src/org/chromium/chrome/browser/widget/dragreorder/DragReorderableListAdapter.java",
   "java/src/org/chromium/chrome/browser/widget/dragreorder/DragStateDelegate.java",
   "java/src/org/chromium/chrome/browser/widget/prefeditor/Completable.java",
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index de70446..9e474f15 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -34,6 +34,7 @@
     "//chrome/android:chrome_java",
     "//chrome/android:chrome_public_java",
     "//chrome/android/public/profiles:java",
+    "//chrome/browser/ui/android/widget:java",
     "//chrome/lib/image_fetcher/public/android:java",
     "//components/policy/android:policy_java",
     "//components/signin/core/browser/android:java",
diff --git a/chrome/android/features/autofill_assistant/java/DEPS b/chrome/android/features/autofill_assistant/java/DEPS
index c3de06b..3db3194 100644
--- a/chrome/android/features/autofill_assistant/java/DEPS
+++ b/chrome/android/features/autofill_assistant/java/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+chrome/lib/image_fetcher",
+  "+chrome/browser/ui/android/widget",
   "+content/public/android/java/src/org/chromium/content_public/browser",
 ]
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java
index c9ea331..6608f71 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java
@@ -17,7 +17,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 
 /**
  * This class provides a vertical expander widget, which allows to toggle between a collapsed and an
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
index 91ee1f9..81912ef 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
@@ -150,8 +150,7 @@
 
         return new BottomSheetController(activity, activity.getLifecycleDispatcher(),
                 activity.getActivityTabProvider(), activity.getScrim(), bottomSheet,
-                activity.getCompositorViewHolder().getLayoutManager().getOverlayPanelManager(),
-                /* suppressSheetForContextualSearch= */ false);
+                activity.getCompositorViewHolder().getLayoutManager().getOverlayPanelManager());
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index ed90a64a..a163a61 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -146,6 +146,7 @@
     "//chrome/android/features/start_surface/internal:java",
     "//chrome/android/public/profiles:java",
     "//chrome/app:java_strings_grd",
+    "//chrome/browser/ui/android/widget:java",
     "//chrome/lib/lifecycle/public/android:java",
     "//chrome/lib/util/public/android:java",
     "//components/embedder_support/android:web_contents_delegate_java",
diff --git a/chrome/android/features/tab_ui/java/DEPS b/chrome/android/features/tab_ui/java/DEPS
index af7fdd5..af94253 100644
--- a/chrome/android/features/tab_ui/java/DEPS
+++ b/chrome/android/features/tab_ui/java/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
  "+chrome/lib/lifecycle/public/android/java/src/org/chromium/chrome/browser/lifecycle",
+ "+chrome/browser/ui/android/widget",
 
  "+components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement",
  "+components/module_installer",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorToolbar.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorToolbar.java
index 6171fbf5..467e3f2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorToolbar.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorToolbar.java
@@ -11,8 +11,8 @@
 import android.util.AttributeSet;
 import android.widget.Button;
 
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.widget.NumberRollView;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 import org.chromium.chrome.browser.widget.selection.SelectableListToolbar;
 import org.chromium.chrome.tab_ui.R;
 
diff --git a/chrome/android/feed/core/DEPS b/chrome/android/feed/core/DEPS
index 49d9287..1aacfe85 100644
--- a/chrome/android/feed/core/DEPS
+++ b/chrome/android/feed/core/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chrome/lib/lifecycle/public",
+  "+chrome/browser/ui/android/widget",
   "+components/background_task_scheduler",
   "+components/feature_engagement",
   "+components/feed",
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentBridge.java
index 03eef13d..5eb7bbf8 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentBridge.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentBridge.java
@@ -15,6 +15,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.profiles.Profile;
 
 import java.util.HashMap;
@@ -35,7 +36,7 @@
      * @param profile {@link Profile} of the user we are rendering the Feed for.
      */
     public FeedContentBridge(Profile profile) {
-        mNativeFeedContentBridge = nativeInit(profile);
+        mNativeFeedContentBridge = FeedContentBridgeJni.get().init(FeedContentBridge.this, profile);
     }
 
     /**
@@ -48,7 +49,7 @@
     /** Cleans up native half of this bridge. */
     public void destroy() {
         assert mNativeFeedContentBridge != 0;
-        nativeDestroy(mNativeFeedContentBridge);
+        FeedContentBridgeJni.get().destroy(mNativeFeedContentBridge, FeedContentBridge.this);
         mNativeFeedContentBridge = 0;
     }
 
@@ -56,54 +57,62 @@
             Callback<Void> failureCallback) {
         assert mNativeFeedContentBridge != 0;
         String[] keysArray = keys.toArray(new String[keys.size()]);
-        nativeLoadContent(mNativeFeedContentBridge, keysArray, successCallback, failureCallback);
+        FeedContentBridgeJni.get().loadContent(mNativeFeedContentBridge, FeedContentBridge.this,
+                keysArray, successCallback, failureCallback);
     }
 
     public void loadContentByPrefix(String prefix, Callback<Map<String, byte[]>> successCallback,
             Callback<Void> failureCallback) {
         assert mNativeFeedContentBridge != 0;
-        nativeLoadContentByPrefix(
-                mNativeFeedContentBridge, prefix, successCallback, failureCallback);
+        FeedContentBridgeJni.get().loadContentByPrefix(mNativeFeedContentBridge,
+                FeedContentBridge.this, prefix, successCallback, failureCallback);
     }
 
     public void loadAllContentKeys(
             Callback<String[]> successCallback, Callback<Void> failureCallback) {
         assert mNativeFeedContentBridge != 0;
-        nativeLoadAllContentKeys(mNativeFeedContentBridge, successCallback, failureCallback);
+        FeedContentBridgeJni.get().loadAllContentKeys(
+                mNativeFeedContentBridge, FeedContentBridge.this, successCallback, failureCallback);
     }
 
     public void commitContentMutation(ContentMutation contentMutation, Callback<Boolean> callback) {
         assert mNativeFeedContentBridge != 0;
 
-        nativeCreateContentMutation(mNativeFeedContentBridge);
+        FeedContentBridgeJni.get().createContentMutation(
+                mNativeFeedContentBridge, FeedContentBridge.this);
         for (ContentOperation operation : contentMutation.getOperations()) {
             switch (operation.getType()) {
                 case Type.UPSERT:
                     Upsert upsert = (Upsert) operation;
-                    nativeAppendUpsertOperation(
-                            mNativeFeedContentBridge, upsert.getKey(), upsert.getValue());
+                    FeedContentBridgeJni.get().appendUpsertOperation(mNativeFeedContentBridge,
+                            FeedContentBridge.this, upsert.getKey(), upsert.getValue());
                     break;
                 case Type.DELETE:
                     Delete delete = (Delete) operation;
-                    nativeAppendDeleteOperation(mNativeFeedContentBridge, delete.getKey());
+                    FeedContentBridgeJni.get().appendDeleteOperation(
+                            mNativeFeedContentBridge, FeedContentBridge.this, delete.getKey());
                     break;
                 case Type.DELETE_BY_PREFIX:
                     DeleteByPrefix deleteByPrefix = (DeleteByPrefix) operation;
-                    nativeAppendDeleteByPrefixOperation(
-                            mNativeFeedContentBridge, deleteByPrefix.getPrefix());
+                    FeedContentBridgeJni.get().appendDeleteByPrefixOperation(
+                            mNativeFeedContentBridge, FeedContentBridge.this,
+                            deleteByPrefix.getPrefix());
                     break;
                 case Type.DELETE_ALL:
-                    nativeAppendDeleteAllOperation(mNativeFeedContentBridge);
+                    FeedContentBridgeJni.get().appendDeleteAllOperation(
+                            mNativeFeedContentBridge, FeedContentBridge.this);
                     break;
                 default:
                     // Operation type is not supported, therefore failing immediately.
                     assert false : "Unsupported type of operation.";
-                    nativeDeleteContentMutation(mNativeFeedContentBridge);
+                    FeedContentBridgeJni.get().deleteContentMutation(
+                            mNativeFeedContentBridge, FeedContentBridge.this);
                     callback.onResult(false);
                     return;
             }
         }
-        nativeCommitContentMutation(mNativeFeedContentBridge, callback);
+        FeedContentBridgeJni.get().commitContentMutation(
+                mNativeFeedContentBridge, FeedContentBridge.this, callback);
     }
 
     @CalledByNative
@@ -116,22 +125,27 @@
         return valueMap;
     }
 
-    private native long nativeInit(Profile profile);
-    private native void nativeDestroy(long nativeFeedContentBridge);
-    private native void nativeLoadContent(long nativeFeedContentBridge, String[] keys,
-            Callback<Map<String, byte[]>> successCallback, Callback<Void> failureCallback);
-    private native void nativeLoadContentByPrefix(long nativeFeedContentBridge, String prefix,
-            Callback<Map<String, byte[]>> successCallback, Callback<Void> failureCallback);
-    private native void nativeLoadAllContentKeys(long nativeFeedContentBridge,
-            Callback<String[]> successCallback, Callback<Void> failureCallback);
-    private native void nativeCommitContentMutation(
-            long nativeFeedContentBridge, Callback<Boolean> callback);
-    private native void nativeCreateContentMutation(long nativeFeedContentBridge);
-    private native void nativeDeleteContentMutation(long nativeFeedContentBridge);
-    private native void nativeAppendDeleteOperation(long nativeFeedContentBridge, String key);
-    private native void nativeAppendDeleteByPrefixOperation(
-            long nativeFeedContentBridge, String prefix);
-    private native void nativeAppendUpsertOperation(
-            long nativeFeedContentBridge, String key, byte[] data);
-    private native void nativeAppendDeleteAllOperation(long nativeFeedContentBridge);
+    @NativeMethods
+    interface Natives {
+        long init(FeedContentBridge caller, Profile profile);
+        void destroy(long nativeFeedContentBridge, FeedContentBridge caller);
+        void loadContent(long nativeFeedContentBridge, FeedContentBridge caller, String[] keys,
+                Callback<Map<String, byte[]>> successCallback, Callback<Void> failureCallback);
+        void loadContentByPrefix(long nativeFeedContentBridge, FeedContentBridge caller,
+                String prefix, Callback<Map<String, byte[]>> successCallback,
+                Callback<Void> failureCallback);
+        void loadAllContentKeys(long nativeFeedContentBridge, FeedContentBridge caller,
+                Callback<String[]> successCallback, Callback<Void> failureCallback);
+        void commitContentMutation(
+                long nativeFeedContentBridge, FeedContentBridge caller, Callback<Boolean> callback);
+        void createContentMutation(long nativeFeedContentBridge, FeedContentBridge caller);
+        void deleteContentMutation(long nativeFeedContentBridge, FeedContentBridge caller);
+        void appendDeleteOperation(
+                long nativeFeedContentBridge, FeedContentBridge caller, String key);
+        void appendDeleteByPrefixOperation(
+                long nativeFeedContentBridge, FeedContentBridge caller, String prefix);
+        void appendUpsertOperation(
+                long nativeFeedContentBridge, FeedContentBridge caller, String key, byte[] data);
+        void appendDeleteAllOperation(long nativeFeedContentBridge, FeedContentBridge caller);
+    }
 }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java
index c8de06b..f99d372 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java
@@ -13,6 +13,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.profiles.Profile;
 
 /**
@@ -30,7 +31,7 @@
      */
 
     public FeedJournalBridge(Profile profile) {
-        mNativeFeedJournalBridge = nativeInit(profile);
+        mNativeFeedJournalBridge = FeedJournalBridgeJni.get().init(FeedJournalBridge.this, profile);
     }
 
     /**
@@ -42,7 +43,7 @@
     /** Cleans up native half of this bridge. */
     public void destroy() {
         assert mNativeFeedJournalBridge != 0;
-        nativeDestroy(mNativeFeedJournalBridge);
+        FeedJournalBridgeJni.get().destroy(mNativeFeedJournalBridge, FeedJournalBridge.this);
         mNativeFeedJournalBridge = 0;
     }
 
@@ -50,7 +51,8 @@
     public void loadJournal(String journalName, Callback<byte[][]> successCallback,
             Callback<Void> failureCallback) {
         assert mNativeFeedJournalBridge != 0;
-        nativeLoadJournal(mNativeFeedJournalBridge, journalName, successCallback, failureCallback);
+        FeedJournalBridgeJni.get().loadJournal(mNativeFeedJournalBridge, FeedJournalBridge.this,
+                journalName, successCallback, failureCallback);
     }
 
     /**
@@ -62,68 +64,82 @@
     public void commitJournalMutation(JournalMutation mutation, Callback<Boolean> callback) {
         assert mNativeFeedJournalBridge != 0;
 
-        nativeStartJournalMutation(mNativeFeedJournalBridge, mutation.getJournalName());
+        FeedJournalBridgeJni.get().startJournalMutation(
+                mNativeFeedJournalBridge, FeedJournalBridge.this, mutation.getJournalName());
         for (JournalOperation operation : mutation.getOperations()) {
             switch (operation.getType()) {
                 case Type.APPEND:
                     Append append = (Append) operation;
-                    nativeAddAppendOperation(mNativeFeedJournalBridge, append.getValue());
+                    FeedJournalBridgeJni.get().addAppendOperation(
+                            mNativeFeedJournalBridge, FeedJournalBridge.this, append.getValue());
                     break;
                 case Type.COPY:
                     Copy copy = (Copy) operation;
-                    nativeAddCopyOperation(mNativeFeedJournalBridge, copy.getToJournalName());
+                    FeedJournalBridgeJni.get().addCopyOperation(mNativeFeedJournalBridge,
+                            FeedJournalBridge.this, copy.getToJournalName());
                     break;
                 case Type.DELETE:
-                    nativeAddDeleteOperation(mNativeFeedJournalBridge);
+                    FeedJournalBridgeJni.get().addDeleteOperation(
+                            mNativeFeedJournalBridge, FeedJournalBridge.this);
                     break;
                 default:
                     // Operation type is not supported, therefore failing immediately.
                     assert false : "Unsupported type of operation.";
-                    nativeDeleteJournalMutation(mNativeFeedJournalBridge);
+                    FeedJournalBridgeJni.get().deleteJournalMutation(
+                            mNativeFeedJournalBridge, FeedJournalBridge.this);
                     callback.onResult(false);
                     return;
             }
         }
-        nativeCommitJournalMutation(mNativeFeedJournalBridge, callback);
+        FeedJournalBridgeJni.get().commitJournalMutation(
+                mNativeFeedJournalBridge, FeedJournalBridge.this, callback);
     }
 
     /** Determines whether a journal exists and asynchronously responds. */
     public void doesJournalExist(
             String journalName, Callback<Boolean> successCallback, Callback<Void> failureCallback) {
         assert mNativeFeedJournalBridge != 0;
-        nativeDoesJournalExist(
-                mNativeFeedJournalBridge, journalName, successCallback, failureCallback);
+        FeedJournalBridgeJni.get().doesJournalExist(mNativeFeedJournalBridge,
+                FeedJournalBridge.this, journalName, successCallback, failureCallback);
     }
 
     /** Asynchronously retrieve a list of all current journals' name. */
     public void loadAllJournalKeys(
             Callback<String[]> successCallback, Callback<Void> failureCallback) {
         assert mNativeFeedJournalBridge != 0;
-        nativeLoadAllJournalKeys(mNativeFeedJournalBridge, successCallback, failureCallback);
+        FeedJournalBridgeJni.get().loadAllJournalKeys(
+                mNativeFeedJournalBridge, FeedJournalBridge.this, successCallback, failureCallback);
     }
 
     /** Delete all journals. Reports success or failure. */
     public void deleteAllJournals(Callback<Boolean> callback) {
         assert mNativeFeedJournalBridge != 0;
-        nativeDeleteAllJournals(mNativeFeedJournalBridge, callback);
+        FeedJournalBridgeJni.get().deleteAllJournals(
+                mNativeFeedJournalBridge, FeedJournalBridge.this, callback);
     }
 
-    private native long nativeInit(Profile profile);
-    private native void nativeDestroy(long nativeFeedJournalBridge);
-    private native void nativeLoadJournal(long nativeFeedJournalBridge, String journalName,
-            Callback<byte[][]> successCallback, Callback<Void> failureCallback);
-    private native void nativeCommitJournalMutation(
-            long nativeFeedJournalBridge, Callback<Boolean> callback);
-    private native void nativeStartJournalMutation(
-            long nativeFeedJournalBridge, String journalName);
-    private native void nativeDeleteJournalMutation(long nativeFeedJournalBridge);
-    private native void nativeAddAppendOperation(long nativeFeedJournalBridge, byte[] value);
-    private native void nativeAddCopyOperation(long nativeFeedJournalBridge, String toJournalName);
-    private native void nativeAddDeleteOperation(long nativeFeedJournalBridge);
-    private native void nativeDoesJournalExist(long nativeFeedJournalBridge, String journalName,
-            Callback<Boolean> successCallback, Callback<Void> failureCallback);
-    private native void nativeLoadAllJournalKeys(long nativeFeedJournalBridge,
-            Callback<String[]> successCallback, Callback<Void> failureCallback);
-    private native void nativeDeleteAllJournals(
-            long nativeFeedJournalBridge, Callback<Boolean> callback);
+    @NativeMethods
+    interface Natives {
+        long init(FeedJournalBridge caller, Profile profile);
+        void destroy(long nativeFeedJournalBridge, FeedJournalBridge caller);
+        void loadJournal(long nativeFeedJournalBridge, FeedJournalBridge caller, String journalName,
+                Callback<byte[][]> successCallback, Callback<Void> failureCallback);
+        void commitJournalMutation(
+                long nativeFeedJournalBridge, FeedJournalBridge caller, Callback<Boolean> callback);
+        void startJournalMutation(
+                long nativeFeedJournalBridge, FeedJournalBridge caller, String journalName);
+        void deleteJournalMutation(long nativeFeedJournalBridge, FeedJournalBridge caller);
+        void addAppendOperation(
+                long nativeFeedJournalBridge, FeedJournalBridge caller, byte[] value);
+        void addCopyOperation(
+                long nativeFeedJournalBridge, FeedJournalBridge caller, String toJournalName);
+        void addDeleteOperation(long nativeFeedJournalBridge, FeedJournalBridge caller);
+        void doesJournalExist(long nativeFeedJournalBridge, FeedJournalBridge caller,
+                String journalName, Callback<Boolean> successCallback,
+                Callback<Void> failureCallback);
+        void loadAllJournalKeys(long nativeFeedJournalBridge, FeedJournalBridge caller,
+                Callback<String[]> successCallback, Callback<Void> failureCallback);
+        void deleteAllJournals(
+                long nativeFeedJournalBridge, FeedJournalBridge caller, Callback<Boolean> callback);
+    }
 }
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
index bfc9d62..7ee6596 100644
--- 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
@@ -7,6 +7,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.profiles.Profile;
 
 /**
@@ -23,7 +24,7 @@
      * @param profile Profile of the user whose lifecycle events we care about.
      */
     public FeedLifecycleBridge(Profile profile) {
-        mNativeBridge = nativeInit(profile);
+        mNativeBridge = FeedLifecycleBridgeJni.get().init(FeedLifecycleBridge.this, profile);
     }
 
     /*
@@ -31,7 +32,7 @@
      */
     public void destroy() {
         assert mNativeBridge != 0;
-        nativeDestroy(mNativeBridge);
+        FeedLifecycleBridgeJni.get().destroy(mNativeBridge, FeedLifecycleBridge.this);
         mNativeBridge = 0;
     }
 
@@ -53,6 +54,9 @@
         }
     }
 
-    private native long nativeInit(Profile profile);
-    private native void nativeDestroy(long nativeFeedLifecycleBridge);
+    @NativeMethods
+    interface Natives {
+        long init(FeedLifecycleBridge caller, Profile profile);
+        void destroy(long nativeFeedLifecycleBridge, FeedLifecycleBridge caller);
+    }
 }
\ No newline at end of file
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
index 769a101..5ce2508 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
@@ -21,6 +21,7 @@
 import com.google.search.now.ui.action.FeedActionProto;
 
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.ui.mojom.WindowOpenDisposition;
@@ -43,7 +44,7 @@
      * @param profile {@link Profile} of the user we are rendering the Feed for.
      */
     public FeedLoggingBridge(Profile profile) {
-        mNativeFeedLoggingBridge = nativeInit(profile);
+        mNativeFeedLoggingBridge = FeedLoggingBridgeJni.get().init(FeedLoggingBridge.this, profile);
     }
 
     /** Cleans up native half of this bridge. */
@@ -52,7 +53,7 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeDestroy(mNativeFeedLoggingBridge);
+        FeedLoggingBridgeJni.get().destroy(mNativeFeedLoggingBridge, FeedLoggingBridge.this);
         mNativeFeedLoggingBridge = 0;
     }
 
@@ -62,7 +63,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnContentViewed(mNativeFeedLoggingBridge, data.getPositionInStream(),
+        FeedLoggingBridgeJni.get().onContentViewed(mNativeFeedLoggingBridge, FeedLoggingBridge.this,
+                data.getPositionInStream(),
                 TimeUnit.SECONDS.toMillis(data.getPublishedTimeSeconds()),
                 TimeUnit.SECONDS.toMillis(data.getTimeContentBecameAvailable()), data.getScore(),
                 data.isAvailableOffline());
@@ -74,8 +76,9 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnContentDismissed(mNativeFeedLoggingBridge, data.getPositionInStream(),
-                data.getRepresentationUri(), wasCommitted);
+        FeedLoggingBridgeJni.get().onContentDismissed(mNativeFeedLoggingBridge,
+                FeedLoggingBridge.this, data.getPositionInStream(), data.getRepresentationUri(),
+                wasCommitted);
     }
 
     @Override
@@ -84,7 +87,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnContentSwiped(mNativeFeedLoggingBridge);
+        FeedLoggingBridgeJni.get().onContentSwiped(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this);
     }
 
     @Override
@@ -102,7 +106,7 @@
         if (mNativeFeedLoggingBridge == 0) return;
 
         recordUserAction(actionType);
-        nativeOnClientAction(mNativeFeedLoggingBridge,
+        FeedLoggingBridgeJni.get().onClientAction(mNativeFeedLoggingBridge, FeedLoggingBridge.this,
                 feedActionToWindowOpenDisposition(actionType), data.getPositionInStream(),
                 TimeUnit.SECONDS.toMillis(data.getPublishedTimeSeconds()), data.getScore(),
                 data.isAvailableOffline());
@@ -114,7 +118,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnContentContextMenuOpened(mNativeFeedLoggingBridge, data.getPositionInStream(),
+        FeedLoggingBridgeJni.get().onContentContextMenuOpened(mNativeFeedLoggingBridge,
+                FeedLoggingBridge.this, data.getPositionInStream(),
                 TimeUnit.SECONDS.toMillis(data.getPublishedTimeSeconds()), data.getScore());
     }
 
@@ -124,7 +129,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnMoreButtonViewed(mNativeFeedLoggingBridge, position);
+        FeedLoggingBridgeJni.get().onMoreButtonViewed(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, position);
     }
 
     @Override
@@ -133,7 +139,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnMoreButtonClicked(mNativeFeedLoggingBridge, position);
+        FeedLoggingBridgeJni.get().onMoreButtonClicked(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, position);
     }
 
     @Override
@@ -144,12 +151,12 @@
 
         // TODO(crbug.com/935602): Fail to compile when new values are added to NotInterestedInData.
         if (interestType == FeedActionProto.NotInterestedInData.RecordedInterestType.TOPIC_VALUE) {
-            nativeOnNotInterestedInTopic(
-                    mNativeFeedLoggingBridge, data.getPositionInStream(), wasCommitted);
+            FeedLoggingBridgeJni.get().onNotInterestedInTopic(mNativeFeedLoggingBridge,
+                    FeedLoggingBridge.this, data.getPositionInStream(), wasCommitted);
         } else if (interestType
                 == FeedActionProto.NotInterestedInData.RecordedInterestType.SOURCE_VALUE) {
-            nativeOnNotInterestedInSource(
-                    mNativeFeedLoggingBridge, data.getPositionInStream(), wasCommitted);
+            FeedLoggingBridgeJni.get().onNotInterestedInSource(mNativeFeedLoggingBridge,
+                    FeedLoggingBridge.this, data.getPositionInStream(), wasCommitted);
         }
     }
 
@@ -159,7 +166,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnOpenedWithContent(mNativeFeedLoggingBridge, timeToPopulateMs, contentCount);
+        FeedLoggingBridgeJni.get().onOpenedWithContent(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, timeToPopulateMs, contentCount);
     }
 
     @Override
@@ -168,7 +176,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnOpenedWithNoImmediateContent(mNativeFeedLoggingBridge);
+        FeedLoggingBridgeJni.get().onOpenedWithNoImmediateContent(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this);
     }
 
     @Override
@@ -177,7 +186,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnOpenedWithNoContent(mNativeFeedLoggingBridge);
+        FeedLoggingBridgeJni.get().onOpenedWithNoContent(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this);
     }
 
     @Override
@@ -186,7 +196,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnSpinnerStarted(mNativeFeedLoggingBridge, spinnerType);
+        FeedLoggingBridgeJni.get().onSpinnerStarted(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, spinnerType);
     }
 
     @Override
@@ -195,7 +206,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnSpinnerFinished(mNativeFeedLoggingBridge, timeShownMs, spinnerType);
+        FeedLoggingBridgeJni.get().onSpinnerFinished(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, timeShownMs, spinnerType);
     }
 
     @Override
@@ -204,8 +216,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeOnSpinnerDestroyedWithoutCompleting(
-                mNativeFeedLoggingBridge, timeShownMs, spinnerType);
+        FeedLoggingBridgeJni.get().onSpinnerDestroyedWithoutCompleting(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, timeShownMs, spinnerType);
     }
 
     @Override
@@ -215,59 +227,66 @@
         for (int i = 0; i < pietErrorCodes.size(); ++i) {
             pietErrorCodesArray[i] = pietErrorCodes.get(i);
         }
-        nativeOnPietFrameRenderingEvent(mNativeFeedLoggingBridge, pietErrorCodesArray);
+        FeedLoggingBridgeJni.get().onPietFrameRenderingEvent(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, pietErrorCodesArray);
     }
 
     @Override
     public void onVisualElementClicked(ElementLoggingData data, int elementType) {
         if (mNativeFeedLoggingBridge == 0) return;
-        nativeOnVisualElementClicked(mNativeFeedLoggingBridge, elementType,
-                data.getPositionInStream(),
+        FeedLoggingBridgeJni.get().onVisualElementClicked(mNativeFeedLoggingBridge,
+                FeedLoggingBridge.this, elementType, data.getPositionInStream(),
                 TimeUnit.SECONDS.toMillis(data.getTimeContentBecameAvailable()));
     }
 
     @Override
     public void onVisualElementViewed(ElementLoggingData data, int elementType) {
         if (mNativeFeedLoggingBridge == 0) return;
-        nativeOnVisualElementViewed(mNativeFeedLoggingBridge, elementType,
-                data.getPositionInStream(),
+        FeedLoggingBridgeJni.get().onVisualElementViewed(mNativeFeedLoggingBridge,
+                FeedLoggingBridge.this, elementType, data.getPositionInStream(),
                 TimeUnit.SECONDS.toMillis(data.getTimeContentBecameAvailable()));
     }
 
     @Override
     public void onInternalError(@InternalFeedError int internalError) {
         if (mNativeFeedLoggingBridge == 0) return;
-        nativeOnInternalError(mNativeFeedLoggingBridge, internalError);
+        FeedLoggingBridgeJni.get().onInternalError(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, internalError);
     }
 
     @Override
     public void onTokenCompleted(boolean wasSynthetic, int contentCount, int tokenCount) {
         if (mNativeFeedLoggingBridge == 0) return;
-        nativeOnTokenCompleted(mNativeFeedLoggingBridge, wasSynthetic, contentCount, tokenCount);
+        FeedLoggingBridgeJni.get().onTokenCompleted(mNativeFeedLoggingBridge,
+                FeedLoggingBridge.this, wasSynthetic, contentCount, tokenCount);
     }
 
     @Override
     public void onTokenFailedToComplete(boolean wasSynthetic, int failureCount) {
         if (mNativeFeedLoggingBridge == 0) return;
-        nativeOnTokenFailedToComplete(mNativeFeedLoggingBridge, wasSynthetic, failureCount);
+        FeedLoggingBridgeJni.get().onTokenFailedToComplete(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, wasSynthetic, failureCount);
     }
 
     @Override
     public void onServerRequest(@RequestReason int requestReason) {
         if (mNativeFeedLoggingBridge == 0) return;
-        nativeOnServerRequest(mNativeFeedLoggingBridge, requestReason);
+        FeedLoggingBridgeJni.get().onServerRequest(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, requestReason);
     }
 
     @Override
     public void onZeroStateShown(@ZeroStateShowReason int zeroStateShowReason) {
         if (mNativeFeedLoggingBridge == 0) return;
-        nativeOnZeroStateShown(mNativeFeedLoggingBridge, zeroStateShowReason);
+        FeedLoggingBridgeJni.get().onZeroStateShown(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, zeroStateShowReason);
     }
 
     @Override
     public void onZeroStateRefreshCompleted(int newContentCount, int newTokenCount) {
         if (mNativeFeedLoggingBridge == 0) return;
-        nativeOnZeroStateRefreshCompleted(mNativeFeedLoggingBridge, newContentCount, newTokenCount);
+        FeedLoggingBridgeJni.get().onZeroStateRefreshCompleted(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, newContentCount, newTokenCount);
     }
 
     @Override
@@ -284,7 +303,8 @@
     @Override
     public void onTaskFinished(@Task int task, int delayTime, int taskTime) {
         if (mNativeFeedLoggingBridge == 0) return;
-        nativeOnTaskFinished(mNativeFeedLoggingBridge, task, delayTime, taskTime);
+        FeedLoggingBridgeJni.get().onTaskFinished(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this, task, delayTime, taskTime);
     }
 
     /**
@@ -299,8 +319,8 @@
         // methods. This method is called by objects not controlled by Feed lifetimes, and destroy()
         // may have already been called if Feed is disabled by policy.
         if (mNativeFeedLoggingBridge != 0) {
-            nativeOnContentTargetVisited(
-                    mNativeFeedLoggingBridge, visitTimeMs, isOffline, returnToNtp);
+            FeedLoggingBridgeJni.get().onContentTargetVisited(mNativeFeedLoggingBridge,
+                    FeedLoggingBridge.this, visitTimeMs, isOffline, returnToNtp);
         }
     }
 
@@ -346,7 +366,8 @@
         // See https://crbug.com/901414.
         if (mNativeFeedLoggingBridge == 0) return;
 
-        nativeReportScrolledAfterOpen(mNativeFeedLoggingBridge);
+        FeedLoggingBridgeJni.get().reportScrolledAfterOpen(
+                mNativeFeedLoggingBridge, FeedLoggingBridge.this);
     }
 
     /**
@@ -374,53 +395,61 @@
         public void onScrolled(int dx, int dy) {}
     }
 
-    private native long nativeInit(Profile profile);
-    private native void nativeDestroy(long nativeFeedLoggingBridge);
-    private native void nativeOnContentViewed(long nativeFeedLoggingBridge, int position,
-            long publishedTimeMs, long timeContentBecameAvailableMs, float score,
-            boolean isAvailableOffline);
-    private native void nativeOnContentDismissed(
-            long nativeFeedLoggingBridge, int position, String uri, boolean wasCommitted);
-    private native void nativeOnContentSwiped(long nativeFeedLoggingBridge);
-    private native void nativeOnClientAction(long nativeFeedLoggingBridge,
-            int windowOpenDisposition, int position, long publishedTimeMs, float score,
-            boolean isAvailableOffline);
-    private native void nativeOnContentContextMenuOpened(
-            long nativeFeedLoggingBridge, int position, long publishedTimeMs, float score);
-    private native void nativeOnMoreButtonViewed(long nativeFeedLoggingBridge, int position);
-    private native void nativeOnMoreButtonClicked(long nativeFeedLoggingBridge, int position);
-    private native void nativeOnNotInterestedInSource(
-            long nativeFeedLoggingBridge, int position, boolean wasCommitted);
-    private native void nativeOnNotInterestedInTopic(
-            long nativeFeedLoggingBridge, int position, boolean wasCommitted);
-    private native void nativeOnOpenedWithContent(
-            long nativeFeedLoggingBridge, int timeToPopulateMs, int contentCount);
-    private native void nativeOnOpenedWithNoImmediateContent(long nativeFeedLoggingBridge);
-    private native void nativeOnOpenedWithNoContent(long nativeFeedLoggingBridge);
-    private native void nativeOnSpinnerStarted(long nativeFeedLoggingBridge, int spinnerType);
-    private native void nativeOnSpinnerFinished(
-            long nativeFeedLoggingBridge, long spinnerShownTimeMs, int spinnerType);
-    private native void nativeOnSpinnerDestroyedWithoutCompleting(
-            long nativeFeedLoggingBridge, long spinnerShownTimeMs, int spinnerType);
-    private native void nativeOnPietFrameRenderingEvent(
-            long nativeFeedLoggingBridge, int[] pietErrorCodes);
-    private native void nativeOnVisualElementClicked(long nativeFeedLoggingBridge, int elementType,
-            int position, long timeContentBecameAvailableMs);
-    private native void nativeOnVisualElementViewed(long nativeFeedLoggingBridge, int elementType,
-            int position, long timeContentBecameAvailableMs);
-    private native void nativeOnInternalError(long nativeFeedLoggingBridge, int internalError);
-    private native void nativeOnTokenCompleted(
-            long nativeFeedLoggingBridge, boolean wasSynthetic, int contentCount, int tokenCount);
-    private native void nativeOnTokenFailedToComplete(
-            long nativeFeedLoggingBridge, boolean wasSynthetic, int failureCount);
-    private native void nativeOnServerRequest(long nativeFeedLoggingBridge, int requestReason);
-    private native void nativeOnZeroStateShown(
-            long nativeFeedLoggingBridge, int zeroStateShowReason);
-    private native void nativeOnZeroStateRefreshCompleted(
-            long nativeFeedLoggingBridge, int newContentCount, int newTokenCount);
-    private native void nativeOnTaskFinished(
-            long nativeFeedLoggingBridge, int task, int delayTimeMs, int taskTimeMs);
-    private native void nativeOnContentTargetVisited(
-            long nativeFeedLoggingBridge, long visitTimeMs, boolean isOffline, boolean returnToNtp);
-    private native void nativeReportScrolledAfterOpen(long nativeFeedLoggingBridge);
+    @NativeMethods
+    interface Natives {
+        long init(FeedLoggingBridge caller, Profile profile);
+        void destroy(long nativeFeedLoggingBridge, FeedLoggingBridge caller);
+        void onContentViewed(long nativeFeedLoggingBridge, FeedLoggingBridge caller, int position,
+                long publishedTimeMs, long timeContentBecameAvailableMs, float score,
+                boolean isAvailableOffline);
+        void onContentDismissed(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                int position, String uri, boolean wasCommitted);
+        void onContentSwiped(long nativeFeedLoggingBridge, FeedLoggingBridge caller);
+        void onClientAction(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                int windowOpenDisposition, int position, long publishedTimeMs, float score,
+                boolean isAvailableOffline);
+        void onContentContextMenuOpened(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                int position, long publishedTimeMs, float score);
+        void onMoreButtonViewed(
+                long nativeFeedLoggingBridge, FeedLoggingBridge caller, int position);
+        void onMoreButtonClicked(
+                long nativeFeedLoggingBridge, FeedLoggingBridge caller, int position);
+        void onNotInterestedInSource(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                int position, boolean wasCommitted);
+        void onNotInterestedInTopic(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                int position, boolean wasCommitted);
+        void onOpenedWithContent(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                int timeToPopulateMs, int contentCount);
+        void onOpenedWithNoImmediateContent(long nativeFeedLoggingBridge, FeedLoggingBridge caller);
+        void onOpenedWithNoContent(long nativeFeedLoggingBridge, FeedLoggingBridge caller);
+        void onSpinnerStarted(
+                long nativeFeedLoggingBridge, FeedLoggingBridge caller, int spinnerType);
+        void onSpinnerFinished(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                long spinnerShownTimeMs, int spinnerType);
+        void onSpinnerDestroyedWithoutCompleting(long nativeFeedLoggingBridge,
+                FeedLoggingBridge caller, long spinnerShownTimeMs, int spinnerType);
+        void onPietFrameRenderingEvent(
+                long nativeFeedLoggingBridge, FeedLoggingBridge caller, int[] pietErrorCodes);
+        void onVisualElementClicked(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                int elementType, int position, long timeContentBecameAvailableMs);
+        void onVisualElementViewed(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                int elementType, int position, long timeContentBecameAvailableMs);
+        void onInternalError(
+                long nativeFeedLoggingBridge, FeedLoggingBridge caller, int internalError);
+        void onTokenCompleted(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                boolean wasSynthetic, int contentCount, int tokenCount);
+        void onTokenFailedToComplete(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                boolean wasSynthetic, int failureCount);
+        void onServerRequest(
+                long nativeFeedLoggingBridge, FeedLoggingBridge caller, int requestReason);
+        void onZeroStateShown(
+                long nativeFeedLoggingBridge, FeedLoggingBridge caller, int zeroStateShowReason);
+        void onZeroStateRefreshCompleted(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                int newContentCount, int newTokenCount);
+        void onTaskFinished(long nativeFeedLoggingBridge, FeedLoggingBridge caller, int task,
+                int delayTimeMs, int taskTimeMs);
+        void onContentTargetVisited(long nativeFeedLoggingBridge, FeedLoggingBridge caller,
+                long visitTimeMs, boolean isOffline, boolean returnToNtp);
+        void reportScrolledAfterOpen(long nativeFeedLoggingBridge, FeedLoggingBridge caller);
+    }
 }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNetworkBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNetworkBridge.java
index dda572cb..a3d4aff 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNetworkBridge.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNetworkBridge.java
@@ -12,6 +12,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.profiles.Profile;
 
 /**
@@ -28,7 +29,7 @@
      * @param profile Profile of the user we are rendering the Feed for.
      */
     public FeedNetworkBridge(Profile profile) {
-        mNativeBridge = nativeInit(profile);
+        mNativeBridge = FeedNetworkBridgeJni.get().init(FeedNetworkBridge.this, profile);
     }
 
     /*
@@ -36,7 +37,7 @@
      */
     public void destroy() {
         assert mNativeBridge != 0;
-        nativeDestroy(mNativeBridge);
+        FeedNetworkBridgeJni.get().destroy(mNativeBridge, FeedNetworkBridge.this);
         mNativeBridge = 0;
     }
 
@@ -45,8 +46,8 @@
         if (mNativeBridge == 0) {
             responseConsumer.accept(createHttpResponse(500, new byte[0]));
         } else {
-            nativeSendNetworkRequest(mNativeBridge, request.getUri().toString(),
-                    request.getMethod(), request.getBody(),
+            FeedNetworkBridgeJni.get().sendNetworkRequest(mNativeBridge, FeedNetworkBridge.this,
+                    request.getUri().toString(), request.getMethod(), request.getBody(),
                     result -> responseConsumer.accept(result));
         }
     }
@@ -57,7 +58,7 @@
         // See https://crbug.com/901414.
         if (mNativeBridge == 0) return;
 
-        nativeCancelRequests(mNativeBridge);
+        FeedNetworkBridgeJni.get().cancelRequests(mNativeBridge, FeedNetworkBridge.this);
     }
 
     @CalledByNative
@@ -65,9 +66,12 @@
         return new HttpResponse(code, body);
     }
 
-    private native long nativeInit(Profile profile);
-    private native void nativeDestroy(long nativeFeedNetworkBridge);
-    private native void nativeSendNetworkRequest(long nativeFeedNetworkBridge, String url,
-            String requestType, byte[] body, Callback<HttpResponse> resultCallback);
-    private native void nativeCancelRequests(long nativeFeedNetworkBridge);
+    @NativeMethods
+    interface Natives {
+        long init(FeedNetworkBridge caller, Profile profile);
+        void destroy(long nativeFeedNetworkBridge, FeedNetworkBridge caller);
+        void sendNetworkRequest(long nativeFeedNetworkBridge, FeedNetworkBridge caller, String url,
+                String requestType, byte[] body, Callback<HttpResponse> resultCallback);
+        void cancelRequests(long nativeFeedNetworkBridge, FeedNetworkBridge caller);
+    }
 }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedOfflineBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedOfflineBridge.java
index f0bd865..ffb2094 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedOfflineBridge.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedOfflineBridge.java
@@ -13,6 +13,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.profiles.Profile;
 
 import java.util.ArrayList;
@@ -42,7 +43,7 @@
      * @param knownContent Interface to access information about the Feed's articles.
      */
     public FeedOfflineBridge(Profile profile, KnownContent knownContent) {
-        mNativeBridge = nativeInit(profile);
+        mNativeBridge = FeedOfflineBridgeJni.get().init(FeedOfflineBridge.this, profile);
         mKnownContentApi = knownContent;
         mKnownContentApi.addListener(this);
     }
@@ -50,7 +51,7 @@
     @Override
     public void destroy() {
         assert mNativeBridge != 0;
-        nativeDestroy(mNativeBridge);
+        FeedOfflineBridgeJni.get().destroy(mNativeBridge, FeedOfflineBridge.this);
         mNativeBridge = 0;
         mKnownContentApi.removeListener(this);
     }
@@ -60,7 +61,8 @@
         if (mNativeBridge == 0) {
             return 0L;
         } else {
-            return (Long) nativeGetOfflineId(mNativeBridge, url);
+            return (Long) FeedOfflineBridgeJni.get().getOfflineId(
+                    mNativeBridge, FeedOfflineBridge.this, url);
         }
     }
 
@@ -71,7 +73,8 @@
             urlListConsumer.accept(Collections.emptyList());
         } else {
             String[] urlsArray = urlsToRetrieve.toArray(new String[urlsToRetrieve.size()]);
-            nativeGetOfflineStatus(mNativeBridge, urlsArray,
+            FeedOfflineBridgeJni.get().getOfflineStatus(mNativeBridge, FeedOfflineBridge.this,
+                    urlsArray,
                     (String[] urlsAsArray) -> urlListConsumer.accept(Arrays.asList(urlsAsArray)));
         }
     }
@@ -88,7 +91,7 @@
         if (mNativeBridge != 0) {
             mListeners.remove(offlineStatusListener);
             if (mListeners.isEmpty()) {
-                nativeOnNoListeners(mNativeBridge);
+                FeedOfflineBridgeJni.get().onNoListeners(mNativeBridge, FeedOfflineBridge.this);
             }
         }
     }
@@ -98,7 +101,7 @@
         if (mNativeBridge != 0) {
             List<String> userDrivenRemovals = takeUserDriveRemovalsOnly(contentRemoved);
             if (!userDrivenRemovals.isEmpty()) {
-                nativeOnContentRemoved(mNativeBridge,
+                FeedOfflineBridgeJni.get().onContentRemoved(mNativeBridge, FeedOfflineBridge.this,
                         userDrivenRemovals.toArray(new String[userDrivenRemovals.size()]));
             }
         }
@@ -107,7 +110,7 @@
     @Override
     public void onNewContentReceived(boolean isNewRefresh, long contentCreationDateTimeMs) {
         if (mNativeBridge != 0) {
-            nativeOnNewContentReceived(mNativeBridge);
+            FeedOfflineBridgeJni.get().onNewContentReceived(mNativeBridge, FeedOfflineBridge.this);
         }
     }
 
@@ -141,11 +144,12 @@
 
             for (ContentMetadata metadata : metadataList) {
                 long time_published_ms = TimeUnit.SECONDS.toMillis(metadata.getTimePublished());
-                nativeAppendContentMetadata(mNativeBridge, metadata.getUrl(), metadata.getTitle(),
+                FeedOfflineBridgeJni.get().appendContentMetadata(mNativeBridge,
+                        FeedOfflineBridge.this, metadata.getUrl(), metadata.getTitle(),
                         time_published_ms, metadata.getImageUrl(), metadata.getPublisher(),
                         metadata.getFaviconUrl(), metadata.getSnippet());
             }
-            nativeOnGetKnownContentDone(mNativeBridge);
+            FeedOfflineBridgeJni.get().onGetKnownContentDone(mNativeBridge, FeedOfflineBridge.this);
         });
     }
 
@@ -156,16 +160,20 @@
         }
     }
 
-    private native long nativeInit(Profile profile);
-    private native void nativeDestroy(long nativeFeedOfflineBridge);
-    private native Object nativeGetOfflineId(long nativeFeedOfflineBridge, String url);
-    private native void nativeGetOfflineStatus(long nativeFeedOfflineBridge,
-            String[] urlsToRetrieve, Callback<String[]> urlListConsumer);
-    private native void nativeOnContentRemoved(long nativeFeedOfflineBridge, String[] urlsRemoved);
-    private native void nativeOnNewContentReceived(long nativeFeedOfflineBridge);
-    private native void nativeOnNoListeners(long nativeFeedOfflineBridge);
-    private native void nativeAppendContentMetadata(long nativeFeedOfflineBridge, String url,
-            String title, long timePublishedMs, String imageUrl, String publisher,
-            String faviconUrl, String snippet);
-    private native void nativeOnGetKnownContentDone(long nativeFeedOfflineBridge);
+    @NativeMethods
+    interface Natives {
+        long init(FeedOfflineBridge caller, Profile profile);
+        void destroy(long nativeFeedOfflineBridge, FeedOfflineBridge caller);
+        Object getOfflineId(long nativeFeedOfflineBridge, FeedOfflineBridge caller, String url);
+        void getOfflineStatus(long nativeFeedOfflineBridge, FeedOfflineBridge caller,
+                String[] urlsToRetrieve, Callback<String[]> urlListConsumer);
+        void onContentRemoved(
+                long nativeFeedOfflineBridge, FeedOfflineBridge caller, String[] urlsRemoved);
+        void onNewContentReceived(long nativeFeedOfflineBridge, FeedOfflineBridge caller);
+        void onNoListeners(long nativeFeedOfflineBridge, FeedOfflineBridge caller);
+        void appendContentMetadata(long nativeFeedOfflineBridge, FeedOfflineBridge caller,
+                String url, String title, long timePublishedMs, String imageUrl, String publisher,
+                String faviconUrl, String snippet);
+        void onGetKnownContentDone(long nativeFeedOfflineBridge, FeedOfflineBridge caller);
+    }
 }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSchedulerBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSchedulerBridge.java
index f473d796..f46d6ae 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSchedulerBridge.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSchedulerBridge.java
@@ -11,6 +11,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.feed.NativeRequestBehavior;
 
@@ -28,13 +29,13 @@
      * @param profile Profile of the user we are rendering the Feed for.
      */
     public FeedSchedulerBridge(Profile profile) {
-        mNativeBridge = nativeInit(profile);
+        mNativeBridge = FeedSchedulerBridgeJni.get().init(FeedSchedulerBridge.this, profile);
     }
 
     @Override
     public void destroy() {
         assert mNativeBridge != 0;
-        nativeDestroy(mNativeBridge);
+        FeedSchedulerBridgeJni.get().destroy(mNativeBridge, FeedSchedulerBridge.this);
         mNativeBridge = 0;
     }
 
@@ -56,7 +57,8 @@
         if (mNativeBridge == 0) return SchedulerApi.RequestBehavior.UNKNOWN;
 
         @NativeRequestBehavior
-        int nativeBehavior = nativeShouldSessionRequestData(mNativeBridge, sessionState.hasContent,
+        int nativeBehavior = FeedSchedulerBridgeJni.get().shouldSessionRequestData(mNativeBridge,
+                FeedSchedulerBridge.this, sessionState.hasContent,
                 sessionState.contentCreationDateTimeMs, sessionState.hasOutstandingRequest);
         // If this breaks, it is because SchedulerApi.RequestBehavior and the NativeRequestBehavior
         // defined in feed_scheduler_host.h have diverged. If this happens during a feed DEPS roll,
@@ -84,39 +86,43 @@
     @Override
     public void onReceiveNewContent(long contentCreationDateTimeMs) {
         if (mNativeBridge != 0) {
-            nativeOnReceiveNewContent(mNativeBridge, contentCreationDateTimeMs);
+            FeedSchedulerBridgeJni.get().onReceiveNewContent(
+                    mNativeBridge, FeedSchedulerBridge.this, contentCreationDateTimeMs);
         }
     }
 
     @Override
     public void onRequestError(int networkResponseCode) {
         if (mNativeBridge != 0) {
-            nativeOnRequestError(mNativeBridge, networkResponseCode);
+            FeedSchedulerBridgeJni.get().onRequestError(
+                    mNativeBridge, FeedSchedulerBridge.this, networkResponseCode);
         }
     }
 
     @Override
     public void onForegrounded() {
         assert mNativeBridge != 0;
-        nativeOnForegrounded(mNativeBridge);
+        FeedSchedulerBridgeJni.get().onForegrounded(mNativeBridge, FeedSchedulerBridge.this);
     }
 
     @Override
     public void onFixedTimer(Runnable onCompletion) {
         assert mNativeBridge != 0;
-        nativeOnFixedTimer(mNativeBridge, onCompletion);
+        FeedSchedulerBridgeJni.get().onFixedTimer(
+                mNativeBridge, FeedSchedulerBridge.this, onCompletion);
     }
 
     @Override
     public void onSuggestionConsumed() {
         assert mNativeBridge != 0;
-        nativeOnSuggestionConsumed(mNativeBridge);
+        FeedSchedulerBridgeJni.get().onSuggestionConsumed(mNativeBridge, FeedSchedulerBridge.this);
     }
 
     @Override
     public boolean onArticlesCleared(boolean suppressRefreshes) {
         assert mNativeBridge != 0;
-        return nativeOnArticlesCleared(mNativeBridge, suppressRefreshes);
+        return FeedSchedulerBridgeJni.get().onArticlesCleared(
+                mNativeBridge, FeedSchedulerBridge.this, suppressRefreshes);
     }
 
     @CalledByNative
@@ -138,17 +144,21 @@
         FeedRefreshTask.cancelWakeUp();
     }
 
-    private native long nativeInit(Profile profile);
-    private native void nativeDestroy(long nativeFeedSchedulerBridge);
-    private native int nativeShouldSessionRequestData(long nativeFeedSchedulerBridge,
-            boolean hasContent, long contentCreationDateTimeMs, boolean hasOutstandingRequest);
-    private native void nativeOnReceiveNewContent(
-            long nativeFeedSchedulerBridge, long contentCreationDateTimeMs);
-    private native void nativeOnRequestError(
-            long nativeFeedSchedulerBridge, int networkResponseCode);
-    private native void nativeOnForegrounded(long nativeFeedSchedulerBridge);
-    private native void nativeOnFixedTimer(long nativeFeedSchedulerBridge, Runnable onCompletion);
-    private native void nativeOnSuggestionConsumed(long nativeFeedSchedulerBridge);
-    private native boolean nativeOnArticlesCleared(
-            long nativeFeedSchedulerBridge, boolean suppressRefreshes);
+    @NativeMethods
+    interface Natives {
+        long init(FeedSchedulerBridge caller, Profile profile);
+        void destroy(long nativeFeedSchedulerBridge, FeedSchedulerBridge caller);
+        int shouldSessionRequestData(long nativeFeedSchedulerBridge, FeedSchedulerBridge caller,
+                boolean hasContent, long contentCreationDateTimeMs, boolean hasOutstandingRequest);
+        void onReceiveNewContent(long nativeFeedSchedulerBridge, FeedSchedulerBridge caller,
+                long contentCreationDateTimeMs);
+        void onRequestError(long nativeFeedSchedulerBridge, FeedSchedulerBridge caller,
+                int networkResponseCode);
+        void onForegrounded(long nativeFeedSchedulerBridge, FeedSchedulerBridge caller);
+        void onFixedTimer(
+                long nativeFeedSchedulerBridge, FeedSchedulerBridge caller, Runnable onCompletion);
+        void onSuggestionConsumed(long nativeFeedSchedulerBridge, FeedSchedulerBridge caller);
+        boolean onArticlesCleared(long nativeFeedSchedulerBridge, FeedSchedulerBridge caller,
+                boolean suppressRefreshes);
+    }
 }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
index 0246250c8..8431f5b 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -46,9 +46,9 @@
 import org.chromium.chrome.browser.signin.PersonalizedSigninPromoView;
 import org.chromium.chrome.browser.snackbar.Snackbar;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.ViewResizer;
 import org.chromium.chrome.browser.util.ViewUtils;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
-import org.chromium.chrome.browser.widget.displaystyle.ViewResizer;
 import org.chromium.ui.UiUtils;
 
 import java.util.Arrays;
@@ -290,8 +290,9 @@
 
         mRootView = new RootView(mActivity);
         mRootView.setPadding(0, resources.getDimensionPixelOffset(R.dimen.tab_strip_height), 0, 0);
-        if (historyNavigationDelegate != null)
+        if (historyNavigationDelegate != null) {
             mRootView.setNavigationDelegate(historyNavigationDelegate);
+        }
         mUiConfig = new UiConfig(mRootView);
 
         // Mediator should be created before any Stream changes.
diff --git a/chrome/android/java/res/values/attrs.xml b/chrome/android/java/res/values/attrs.xml
index b02fa410..a7e14bfd 100644
--- a/chrome/android/java/res/values/attrs.xml
+++ b/chrome/android/java/res/values/attrs.xml
@@ -7,8 +7,6 @@
     <declare-styleable name="HyperlinkPreference">
         <!-- The URL to load when the preference is clicked -->
         <attr name="url" format="string" />
-        <!-- True if the hyperlink should look like an <a> element. Default is false. -->
-        <attr name="imitateWebLink" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="LearnMorePreference">
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index ef4c939..6d21b21a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1425,13 +1425,9 @@
         getCompositorViewHolder().addCompositorViewResizer(
                 mManualFillingComponent.getKeyboardExtensionViewResizer());
 
-        if (mBottomSheet == null && shouldInitializeBottomSheet()) {
-            // TODO(yusufo): Unify initialization.
-            initializeBottomSheet(true);
-        }
-
-        if (mBottomSheet != null) {
-            mEphemeralTabCoordinator = new EphemeralTabCoordinator(this, mBottomSheetController);
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.EPHEMERAL_TAB_USING_BOTTOM_SHEET)) {
+            mEphemeralTabCoordinator =
+                    new EphemeralTabCoordinator(this, getBottomSheetController());
         }
 
         // TODO(crbug.com/959841): Once crrev.com/c/1669360 is submitted, make the code below
@@ -1452,7 +1448,10 @@
      */
     protected void registerDirectActions() {
         mDirectActionInitializer.registerCommonChromeActions(this, getActivityType(), this,
-                this::onBackPressed, mTabModelSelector, mBottomSheetController, mScrimView);
+                this::onBackPressed, mTabModelSelector,
+                AutofillAssistantFacade.areDirectActionsAvailable(getActivityType()) ?
+                        getBottomSheetController() : null,
+                mScrimView);
     }
 
     /**
@@ -1472,18 +1471,9 @@
     }
 
     /**
-     * @return Whether this Activity should initialize the BottomSheet and BottomSheetController.
-     */
-    protected boolean shouldInitializeBottomSheet() {
-        return AutofillAssistantFacade.areDirectActionsAvailable(getActivityType());
-    }
-
-    /**
      * Initializes the {@link BottomSheet} and {@link BottomSheetController} for use.
-     * @param suppressSheetForContextualSearch Whether the sheet should be suppressed when
-     *                                         Contextual search is showing.
      */
-    protected void initializeBottomSheet(boolean suppressSheetForContextualSearch) {
+    protected void initializeBottomSheet() {
         ViewGroup coordinator = findViewById(R.id.coordinator);
         getLayoutInflater().inflate(R.layout.bottom_sheet, coordinator);
         mBottomSheet = coordinator.findViewById(R.id.bottom_sheet);
@@ -1493,8 +1483,7 @@
 
         mBottomSheetController = new BottomSheetController(this, getLifecycleDispatcher(),
                 mActivityTabProvider, mScrimView, mBottomSheet,
-                getCompositorViewHolder().getLayoutManager().getOverlayPanelManager(),
-                suppressSheetForContextualSearch);
+                getCompositorViewHolder().getLayoutManager().getOverlayPanelManager());
     }
 
     /**
@@ -2550,9 +2539,12 @@
         return toPropagate == null || super.dispatchKeyEvent(toPropagate);
     }
 
-    /** Returns {@link BottomSheetController}, if present. */
-    @Nullable
+    /**
+     * @return A lazily created {@link BottomSheetController}. The first time this method is called,
+     *         a new controller is created.
+     */
     public BottomSheetController getBottomSheetController() {
+        if (mBottomSheetController == null) initializeBottomSheet();
         return mBottomSheetController;
     }
 
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 ad35eae..2015811 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -110,7 +110,6 @@
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.SearchEngineChoiceNotification;
-import org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfAndroidBridge;
 import org.chromium.chrome.browser.signin.SigninPromoUtil;
 import org.chromium.chrome.browser.snackbar.undo.UndoBarController;
 import org.chromium.chrome.browser.suggestions.SuggestionsEventReporterBridge;
@@ -748,10 +747,6 @@
             };
             OnClickListener bookmarkClickHandler = v -> addOrEditBookmark(getActivityTab());
 
-            if (shouldInitializeBottomSheet()) {
-                initializeBottomSheet(false);
-            }
-
             getToolbarManager().initializeWithNative(mTabModelSelectorImpl,
                     getFullscreenManager().getBrowserVisibilityDelegate(), mOverviewModeController,
                     mLayoutManager, tabSwitcherClickHandler, newTabClickHandler,
@@ -2359,13 +2354,6 @@
     }
 
     @Override
-    protected boolean shouldInitializeBottomSheet() {
-        return super.shouldInitializeBottomSheet() || FeatureUtilities.isTabGroupsAndroidEnabled()
-                || ChromeFeatureList.isEnabled(ChromeFeatureList.OVERSCROLL_HISTORY_NAVIGATION)
-                || SendTabToSelfAndroidBridge.isSendingEnabled();
-    }
-
-    @Override
     public void onScreenshotTaken() {
         Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile());
         tracker.notifyEvent(EventConstants.SCREENSHOT_TAKEN_CHROME_IN_FOREGROUND);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java
index 962d0e1..87c2566 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java
@@ -20,7 +20,7 @@
 import org.chromium.chrome.browser.SynchronousInitializationActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
-import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.components.bookmarks.BookmarkId;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java
index c065e5b3..84476fc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java
@@ -17,7 +17,7 @@
 import org.chromium.chrome.browser.SynchronousInitializationActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
-import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.url_formatter.UrlFormatter;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
index e12af9b..40bac04d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
@@ -28,9 +28,9 @@
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.chrome.browser.util.UrlConstants;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkType;
 import org.chromium.ui.base.DeviceFormFactor;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelAnimation.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelAnimation.java
index 40a72a3..9eac3e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelAnimation.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelAnimation.java
@@ -15,8 +15,8 @@
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.PanelState;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
+import org.chromium.chrome.browser.ui.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.chrome.browser.util.MathUtils;
-import org.chromium.chrome.browser.widget.animation.CancelAwareAnimatorListener;
 
 /**
  * Base abstract class for animating the Overlay Panel.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
index 9a855a94..695566f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
@@ -27,8 +27,8 @@
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.IntentUtils;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 import org.chromium.ui.widget.Toast;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 10eda8b1..e3b87ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -604,11 +604,6 @@
         return component;
     }
 
-    @Override
-    protected boolean shouldInitializeBottomSheet() {
-        return super.shouldInitializeBottomSheet() || isAutofillAssistantEnabled();
-    }
-
     private boolean isAutofillAssistantEnabled() {
         return ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_ASSISTANT)
                 && AutofillAssistantFacade.isConfigured(getInitialIntent().getExtras());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
index c631d83..01a7b6f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -47,10 +47,10 @@
 import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleMetrics;
 import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.chrome.browser.util.UrlConstants;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index 8b0c3c8c..13b21ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -71,7 +71,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -117,18 +116,6 @@
     private static final String PREF_IS_DOWNLOAD_HOME_ENABLED =
             "org.chromium.chrome.browser.download.IS_DOWNLOAD_HOME_ENABLED";
 
-    // Set will be more expensive to initialize, so use an ArrayList here.
-    private static final List<String> MIME_TYPES_TO_OPEN = new ArrayList<String>(Arrays.asList(
-            OMADownloadHandler.OMA_DOWNLOAD_DESCRIPTOR_MIME,
-            "application/pdf",
-            "application/x-x509-ca-cert",
-            "application/x-x509-user-cert",
-            "application/x-x509-server-cert",
-            "application/x-pkcs12",
-            "application/application/x-pem-file",
-            "application/pkix-cert",
-            "application/x-wifi-config"));
-
     private static final Set<String> sFirstSeenDownloadIds = new HashSet<String>();
     private static final Set<String> sBackgroundDownloadIds = new HashSet<String>();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
index 055d66a..d3dbd62 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
@@ -23,8 +23,8 @@
 import org.chromium.chrome.browser.download.home.DownloadManagerUiConfig;
 import org.chromium.chrome.browser.download.home.list.DateOrderedListCoordinator.DateOrderedListObserver;
 import org.chromium.chrome.browser.download.home.list.holder.ListItemViewHolder;
-import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.HorizontalDisplayStyle;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.ui.modelutil.ForwardingListObservable;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.modelutil.RecyclerViewAdapter;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java
index d528315..da05ee6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java
@@ -11,7 +11,7 @@
 
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.download.home.list.ListItem;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.chrome.browser.widget.selection.SelectableListToolbar;
 import org.chromium.chrome.download.R;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java
index 023b14a..10326e48 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java
@@ -30,8 +30,8 @@
 import org.chromium.chrome.browser.download.ui.BackendProvider.DownloadDelegate;
 import org.chromium.chrome.browser.download.ui.DownloadHistoryItemWrapper.DownloadItemWrapper;
 import org.chromium.chrome.browser.download.ui.DownloadHistoryItemWrapper.OfflineItemWrapper;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.chrome.browser.widget.DateDividedAdapter;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.chrome.download.R;
 import org.chromium.components.download.DownloadState;
@@ -293,8 +293,9 @@
                     && wrapper.isVisibleToUser(DownloadFilter.Type.ALL)) {
                 itemCounts[wrapper.getFilterType()]++;
 
-                if (DownloadUtils.isDownloadViewed(wrapper.getItem()))
+                if (DownloadUtils.isDownloadViewed(wrapper.getItem())) {
                     viewedItemCounts[wrapper.getFilterType()]++;
+                }
                 if (!isOffTheRecord && wrapper.getFilterType() == DownloadFilter.Type.OTHER) {
                     RecordHistogram.recordEnumeratedHistogram(
                             "Android.DownloadManager.OtherExtensions.InitialCount",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java
index 978475a..98286d6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java
@@ -16,7 +16,7 @@
 import android.widget.FrameLayout;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.widget.animation.CancelAwareAnimatorListener;
+import org.chromium.chrome.browser.ui.widget.animation.CancelAwareAnimatorListener;
 
 /**
  * A tablet specific version of the {@link FindToolbar}.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
index a0f0ab6..0c6ddd8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
@@ -16,10 +16,10 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.favicon.FaviconHelper;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.UrlConstants;
 import org.chromium.chrome.browser.util.ViewUtils;
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 import org.chromium.content_public.browser.NavigationEntry;
 import org.chromium.content_public.browser.NavigationHistory;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
@@ -33,7 +33,7 @@
 import java.util.Set;
 
 /**
- * Mediattor class for navigation sheet.
+ * Mediator class for navigation sheet.
  */
 class NavigationSheetMediator {
     private final ClickListener mClickListener;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
index 1e4f9806..efa2c0c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
@@ -48,11 +48,11 @@
 import org.chromium.chrome.browser.suggestions.tile.TileGridLayout;
 import org.chromium.chrome.browser.suggestions.tile.TileGroup;
 import org.chromium.chrome.browser.suggestions.tile.TileRenderer;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.chrome.browser.util.ViewUtils;
 import org.chromium.chrome.browser.vr.VrModeObserver;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
 import org.chromium.ui.base.DeviceFormFactor;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index 714630f..d8ea29eb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -31,9 +31,9 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
 import org.chromium.chrome.browser.suggestions.tile.TileGroup;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.ViewResizer;
 import org.chromium.chrome.browser.util.ViewUtils;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
-import org.chromium.chrome.browser.widget.displaystyle.ViewResizer;
 
 /**
  * The native new tab page, represented by some basic data such as title and url, and an Android
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsGroupView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsGroupView.java
index b3089798..3a214bbd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsGroupView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsGroupView.java
@@ -15,7 +15,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ntp.ForeignSessionHelper.ForeignSession;
-import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 
 /**
  * Header view shown above each group of items on the Recent Tabs page. Shows the name of the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java
index 461424b1..031442d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java
@@ -21,7 +21,7 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsRanker;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java
index 5382876..ccea493 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java
@@ -16,8 +16,8 @@
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.ContextMenuManager.ContextMenuItemId;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
-import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.HorizontalDisplayStyle;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 
 /**
  * Holder for a generic card.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
index eabb6000..ead15ebf4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -33,7 +33,7 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsConfig;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.ui.modelutil.ListObservable;
 
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/PersonalizedPromoViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/PersonalizedPromoViewHolder.java
index 8ec47c3..f51515d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/PersonalizedPromoViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/PersonalizedPromoViewHolder.java
@@ -15,7 +15,7 @@
 import org.chromium.chrome.browser.signin.SigninPromoController;
 import org.chromium.chrome.browser.signin.SigninPromoUtil;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 
 /**
  * View Holder for {@link SignInPromo} if the personalized promo is to be shown.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusCardViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusCardViewHolder.java
index 39d3b12..bcd23731 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusCardViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusCardViewHolder.java
@@ -17,7 +17,7 @@
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.suggestions.SuggestionsMetrics;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 
 /**
  * ViewHolder for Status and Promo cards.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderViewHolder.java
index 8dd2814..e7f6dc2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderViewHolder.java
@@ -10,7 +10,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 
 /**
  * View holder for the header of a section of cards.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
index 27dd1bbf..55c7feb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
@@ -22,8 +22,8 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsOfflineModelObserver;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
-import org.chromium.chrome.browser.widget.displaystyle.DisplayStyleObserverAdapter;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.DisplayStyleObserverAdapter;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
index c9f7732..c196326 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
@@ -20,7 +20,7 @@
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.toolbar.top.ToolbarTablet;
-import org.chromium.chrome.browser.widget.animation.CancelAwareAnimatorListener;
+import org.chromium.chrome.browser.ui.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java
index 48238ba..71830e51 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java
@@ -25,9 +25,9 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.KeyNavigationUtil;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionView.java
index c8ef243..a822fff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionView.java
@@ -20,8 +20,8 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.ColorUtils;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 
 /**
  * Container view for omnibox entity suggestions.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoView.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoView.java
index c6b0258f..0994c40 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoView.java
@@ -29,7 +29,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
index f9e2fdc..bbe68e9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
@@ -29,8 +29,8 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.widget.AlwaysDismissedDialog;
+import org.chromium.chrome.browser.ui.widget.animation.AnimatorProperties;
 import org.chromium.chrome.browser.util.ColorUtils;
-import org.chromium.chrome.browser.widget.animation.AnimatorProperties;
 
 /**
  * A fullscreen semitransparent dialog used for dimming Chrome when overlaying a bottom sheet
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestHeader.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestHeader.java
index 9e69872bd..5de1787 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestHeader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestHeader.java
@@ -19,9 +19,9 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.UrlConstants;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 
 /** This class represents a bar to display at the top of the payment request UI. */
 public class PaymentRequestHeader extends FrameLayout {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
index bfbbf49..aa5363f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
@@ -38,7 +38,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.widget.DualControlLayout;
-import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.widget.prefeditor.EditableOption;
 import org.chromium.ui.HorizontalListDividerDrawable;
 import org.chromium.ui.UiUtils;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
index 5b403b7..78873e1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
@@ -45,7 +45,7 @@
 import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.OptionSection;
 import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.SectionSeparator;
 import org.chromium.chrome.browser.ui.widget.FadingEdgeScrollView;
-import org.chromium.chrome.browser.widget.animation.FocusAnimator;
+import org.chromium.chrome.browser.ui.widget.animation.FocusAnimator;
 import org.chromium.chrome.browser.widget.prefeditor.EditableOption;
 import org.chromium.chrome.browser.widget.prefeditor.EditorDialog;
 import org.chromium.chrome.browser.widget.prefeditor.EditorObserverForTest;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/HyperlinkPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/HyperlinkPreference.java
index 78718fb..b2dc511 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/HyperlinkPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/HyperlinkPreference.java
@@ -7,13 +7,8 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceViewHolder;
 import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.TextView;
 
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.ui.base.LocalizationUtils;
@@ -25,18 +20,14 @@
 public class HyperlinkPreference extends Preference {
 
     private final int mUrlResId;
-    private final int mColor;
-    private final boolean mImitateWebLink;
 
     public HyperlinkPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.HyperlinkPreference, 0, 0);
         mUrlResId = a.getResourceId(R.styleable.HyperlinkPreference_url, 0);
-        mImitateWebLink = a.getBoolean(R.styleable.HyperlinkPreference_imitateWebLink, false);
         a.recycle();
-        mColor = ApiCompatibilityUtils.getColor(
-                context.getResources(), R.color.default_text_color_link);
+        setSingleLineTitle(false);
     }
 
     @Override
@@ -44,24 +35,4 @@
         CustomTabActivity.showInfoPage(WindowAndroid.activityFromContext(getContext()),
                 LocalizationUtils.substituteLocalePlaceholder(getContext().getString(mUrlResId)));
     }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder holder) {
-        super.onBindViewHolder(holder);
-        TextView titleView = (TextView) holder.findViewById(android.R.id.title);
-        titleView.setSingleLine(false);
-
-        if (mImitateWebLink) {
-            setSelectable(false);
-
-            titleView.setClickable(true);
-            titleView.setTextColor(mColor);
-            titleView.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    HyperlinkPreference.this.onClick();
-                }
-            });
-        }
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/languages/LanguageListPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/languages/LanguageListPreference.java
index fe5720f..2c757fb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/languages/LanguageListPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/languages/LanguageListPreference.java
@@ -18,7 +18,7 @@
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.ui.widget.ListMenuButton;
 import org.chromium.chrome.browser.ui.widget.ListMenuButton.Item;
-import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 
 import java.util.ArrayList;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfMetrics.java
index ac4d97cf..18f92a0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfMetrics.java
@@ -6,6 +6,7 @@
 
 import androidx.annotation.IntDef;
 
+import org.chromium.base.metrics.CachedMetrics;
 import org.chromium.base.metrics.RecordHistogram;
 
 import java.lang.annotation.Retention;
@@ -55,9 +56,12 @@
             int NUM_ENTRIES = 3;
         }
 
+        private static final CachedMetrics.EnumeratedHistogramSample CLICK_RESULT_METRIC =
+                new CachedMetrics.EnumeratedHistogramSample(
+                        "SendTabToSelf.Notification", InteractionType.NUM_ENTRIES);
+
         public static void recordClickResult(@InteractionType int result) {
-            RecordHistogram.recordEnumeratedHistogram(
-                    "SendTabToSelf.Notification", result, InteractionType.NUM_ENTRIES);
+            CLICK_RESULT_METRIC.record(result);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
index afdd650..a84007f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
@@ -27,7 +27,6 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
-import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler;
 import org.chromium.components.signin.AccountIdProvider;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountTrackerService;
@@ -459,8 +458,6 @@
         } else if (AccountIdProvider.getInstance().canBeUsed()) {
             mSignInState.mBlockedOnAccountSeeding = true;
         } else {
-            Activity activity = mSignInState.mActivity;
-            handleGooglePlayServicesUnavailability(activity, !isForceSigninEnabled());
             Log.w(TAG, "Cancelling the sign-in process as Google Play services is unavailable");
             abortSignIn();
         }
@@ -752,13 +749,6 @@
         return SigninManagerJni.get().extractDomainName(email);
     }
 
-    private void handleGooglePlayServicesUnavailability(Activity activity, boolean cancelable) {
-        UserRecoverableErrorHandler errorHandler = activity != null
-                ? new UserRecoverableErrorHandler.ModalDialog(activity, cancelable)
-                : new UserRecoverableErrorHandler.SystemNotification();
-        ExternalAuthUtils.getInstance().canUseGooglePlayServices(errorHandler);
-    }
-
     private boolean isGooglePlayServicesPresent(Context context) {
         return !ExternalAuthUtils.getInstance().isGooglePlayServicesMissing(context);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsConfig.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsConfig.java
index 6adab5b..3f3b1f27 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsConfig.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsConfig.java
@@ -12,9 +12,9 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.chrome.browser.util.FeatureUtilities;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRecyclerView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRecyclerView.java
index 4fc32d7..cba965f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRecyclerView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRecyclerView.java
@@ -36,7 +36,7 @@
 import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
 import org.chromium.chrome.browser.ntp.cards.ScrollToLoadListener;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java
index 2c27273..99cd1c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java
@@ -20,7 +20,7 @@
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 import org.chromium.chrome.browser.suggestions.SuggestionsConfig;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 
 /**
  * The model and controller for a group of site suggestions.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawable.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawable.java
index 36fec4c..daf169a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawable.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawable.java
@@ -16,8 +16,8 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.ColorUtils;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 
 import java.util.Locale;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
index d91491852..ea755221 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
@@ -59,9 +59,9 @@
 import org.chromium.chrome.browser.tab.TrustedCdn;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.chrome.browser.toolbar.ToolbarTabController;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.chrome.browser.util.ColorUtils;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.common.ContentUrlConstants;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbarAnimationDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbarAnimationDelegate.java
index a2fbf49..a68448d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbarAnimationDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbarAnimationDelegate.java
@@ -14,7 +14,7 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.widget.animation.CancelAwareAnimatorListener;
+import org.chromium.chrome.browser.ui.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
index 500f4f7c..8fe04f4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
@@ -31,9 +31,9 @@
 import org.chromium.chrome.browser.toolbar.MenuButton;
 import org.chromium.chrome.browser.toolbar.NewTabButton;
 import org.chromium.chrome.browser.toolbar.TabCountProvider;
+import org.chromium.chrome.browser.ui.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
-import org.chromium.chrome.browser.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.widget.OptimizedFrameLayout;
 
@@ -269,8 +269,9 @@
         mIncognitoStateProvider = provider;
         mIncognitoStateProvider.addIncognitoStateObserverAndTrigger(this);
 
-        if (mNewTabImageButton != null)
+        if (mNewTabImageButton != null) {
             mNewTabImageButton.setIncognitoStateProvider(mIncognitoStateProvider);
+        }
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 0f54c4b2f..806ead7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -71,11 +71,11 @@
 import org.chromium.chrome.browser.toolbar.TabCountProvider.TabCountObserver;
 import org.chromium.chrome.browser.toolbar.TabSwitcherDrawable;
 import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator.UrlExpansionObserver;
+import org.chromium.chrome.browser.ui.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 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.components.feature_engagement.EventConstants;
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/AccessibilityUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/util/AccessibilityUtil.java
index 1dcc13d..3c8d25f1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/AccessibilityUtil.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/AccessibilityUtil.java
@@ -8,11 +8,7 @@
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.net.Uri;
 import android.os.Build;
-import android.support.v7.app.AlertDialog;
 import android.view.Gravity;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
@@ -21,10 +17,7 @@
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ApplicationStatus.ActivityStateListener;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.PackageUtils;
 import org.chromium.base.TraceEvent;
-import org.chromium.chrome.R;
-import org.chromium.ui.UiUtils;
 import org.chromium.ui.widget.Toast;
 
 import java.util.List;
@@ -33,21 +26,6 @@
  * Exposes information about the current accessibility state
  */
 public class AccessibilityUtil {
-    // Whether we've already shown an alert that they have an old version of TalkBack running.
-    private static boolean sOldTalkBackVersionAlertShown;
-
-    // The link to download or update TalkBack from the Play Store.
-    private static final String TALKBACK_MARKET_LINK =
-            "market://search?q=pname:com.google.android.marvin.talkback";
-
-    // The package name for TalkBack, an Android accessibility service.
-    private static final String TALKBACK_PACKAGE_NAME =
-            "com.google.android.marvin.talkback";
-
-    // The minimum TalkBack version that we support. This is the version that shipped with
-    // KitKat, from fall 2013. Versions older than that should be updated.
-    private static final int MIN_TALKBACK_VERSION = 105;
-
     private static Boolean sIsAccessibilityEnabled;
     private static ActivityStateListener sActivityStateListener;
 
@@ -115,7 +93,7 @@
     }
 
     /**
-     * Checks whether the given {@link AccesibilityServiceInfo} can perform gestures.
+     * Checks whether the given {@link AccessibilityServiceInfo} can perform gestures.
      * @param service The service to check.
      * @return Whether the {@code service} can perform gestures. On N+, this relies on the
      *         capabilities the service can perform. On L & M, this looks specifically for
@@ -134,69 +112,6 @@
     }
 
     /**
-     * Checks to see if an old version of TalkBack is running that Chrome doesn't support,
-     * and if so, shows an alert dialog prompting the user to update the app.
-     * @param context A {@link Context} instance.
-     * @return        True if the dialog was shown.
-     */
-    public static boolean showWarningIfOldTalkbackRunning(Context context) {
-        AccessibilityManager manager = (AccessibilityManager)
-                context.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (manager == null) return false;
-
-        boolean isTalkbackRunning = false;
-        try {
-            List<AccessibilityServiceInfo> services =
-                    manager.getEnabledAccessibilityServiceList(
-                            AccessibilityServiceInfo.FEEDBACK_SPOKEN);
-            for (AccessibilityServiceInfo service : services) {
-                if (service.getId().contains(TALKBACK_PACKAGE_NAME)) isTalkbackRunning = true;
-            }
-        } catch (NullPointerException e) {
-            // getEnabledAccessibilityServiceList() can throw an NPE due to a bad
-            // AccessibilityService.
-        }
-        if (!isTalkbackRunning) return false;
-
-        if (PackageUtils.getPackageVersion(context, TALKBACK_PACKAGE_NAME) < MIN_TALKBACK_VERSION
-                && !sOldTalkBackVersionAlertShown) {
-            showOldTalkbackVersionAlertOnce(context);
-            return true;
-        }
-
-        return false;
-    }
-
-    private static void showOldTalkbackVersionAlertOnce(final Context context) {
-        if (sOldTalkBackVersionAlertShown) return;
-        sOldTalkBackVersionAlertShown = true;
-
-        AlertDialog.Builder builder =
-                new UiUtils
-                        .CompatibleAlertDialogBuilder(context, R.style.Theme_Chromium_AlertDialog)
-                        .setTitle(R.string.old_talkback_title)
-                        .setPositiveButton(R.string.update_from_market,
-                                new DialogInterface.OnClickListener() {
-                                    @Override
-                                    public void onClick(DialogInterface dialog, int id) {
-                                        Uri marketUri = Uri.parse(TALKBACK_MARKET_LINK);
-                                        Intent marketIntent =
-                                                new Intent(Intent.ACTION_VIEW, marketUri);
-                                        context.startActivity(marketIntent);
-                                    }
-                                })
-                        .setNegativeButton(R.string.cancel_talkback_alert,
-                                new DialogInterface.OnClickListener() {
-                                    @Override
-                                    public void onClick(DialogInterface dialog, int id) {
-                                        // Do nothing, this alert is only shown once either way.
-                                    }
-                                });
-        AlertDialog dialog = builder.create();
-        dialog.show();
-    }
-
-    /**
      * Shows the content description toast for items on the toolbar.
      * @param context The context to use for the toast.
      * @param view The view to anchor the toast.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index 437c2da..974672d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -51,9 +51,9 @@
 import org.chromium.chrome.browser.tab.TabObserverRegistrar;
 import org.chromium.chrome.browser.tab.TabState;
 import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.usage_stats.UsageStatsService;
 import org.chromium.chrome.browser.util.ColorUtils;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationHandle;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/ScrimView.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ScrimView.java
index bb49bfc..2cf172f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/ScrimView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ScrimView.java
@@ -21,8 +21,8 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter;
+import org.chromium.chrome.browser.ui.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.chrome.browser.util.MathUtils;
-import org.chromium.chrome.browser.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/animation/AnimatorProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/animation/AnimatorProperties.java
deleted file mode 100644
index 14a27c1c..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/animation/AnimatorProperties.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.widget.animation;
-
-import android.graphics.drawable.Drawable;
-import android.util.Property;
-
-/**
- * Holds different {@link Property} types that can be used with ObjectAnimators.
- */
-public class AnimatorProperties {
-    public static final Property<Drawable, Integer> DRAWABLE_ALPHA_PROPERTY =
-            new Property<Drawable, Integer>(Integer.class, "alpha") {
-        @Override
-        public Integer get(Drawable d) {
-            // getAlpha() is only exposed on drawable in API 19+, so we rely on animations
-            // always setting the starting and ending values instead of relying on this
-            // property.
-            return 0;
-        }
-
-        @Override
-        public void set(Drawable d, Integer alpha) {
-            d.setAlpha(alpha);
-        }
-    };
-}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java
index 0a08e96..03331e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java
@@ -11,7 +11,6 @@
 import org.chromium.chrome.browser.ActivityTabProvider.HintlessActivityTabObserver;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager;
-import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager.OverlayPanelManagerObserver;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
@@ -64,9 +63,6 @@
     /** The manager for overlay panels to attach listeners to. */
     private OverlayPanelManager mOverlayPanelManager;
 
-    /** Whether the bottom sheet should be suppressed when Contextual Search is showing. */
-    private boolean mSuppressSheetForContextualSearch;
-
     /** A means for getting the activity's current tab and observing change events. */
     private ActivityTabProvider mTabProvider;
 
@@ -81,18 +77,14 @@
      * @param scrim The scrim that shows when the bottom sheet is opened.
      * @param bottomSheet The bottom sheet that this class will be controlling.
      * @param overlayManager The manager for overlay panels to attach listeners to.
-     * @param suppressSheetForContextualSearch Whether the bottom sheet should be suppressed when
-     *                                         Contextual Search is showing.
      */
     public BottomSheetController(final Activity activity,
             final ActivityLifecycleDispatcher lifecycleDispatcher,
             final ActivityTabProvider activityTabProvider, final ScrimView scrim,
-            BottomSheet bottomSheet, OverlayPanelManager overlayManager,
-            boolean suppressSheetForContextualSearch) {
+            BottomSheet bottomSheet, OverlayPanelManager overlayManager) {
         mBottomSheet = bottomSheet;
         mTabProvider = activityTabProvider;
         mOverlayPanelManager = overlayManager;
-        mSuppressSheetForContextualSearch = suppressSheetForContextualSearch;
         mSnackbarManager = new SnackbarManager(
                 activity, mBottomSheet.findViewById(R.id.bottom_sheet_snackbar_container));
 
@@ -216,20 +208,6 @@
                 mSnackbarManager.dismissAllSnackbars();
             }
         });
-
-        if (mSuppressSheetForContextualSearch) {
-            mOverlayPanelManager.addObserver(new OverlayPanelManagerObserver() {
-                @Override
-                public void onOverlayPanelShown() {
-                    suppressSheet(StateChangeReason.COMPOSITED_UI);
-                }
-
-                @Override
-                public void onOverlayPanelHidden() {
-                    unsuppressSheet();
-                }
-            });
-        }
     }
 
     // Destroyable implementation.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorDialog.java
index bea6cac..3ee4344 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorDialog.java
@@ -50,7 +50,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.ui.widget.AlwaysDismissedDialog;
 import org.chromium.chrome.browser.ui.widget.FadingEdgeScrollView;
-import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorTextField.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorTextField.java
index dd8573d..19ad109 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorTextField.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorTextField.java
@@ -28,8 +28,8 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.widget.ChromeTextInputLayout;
-import org.chromium.chrome.browser.widget.TintedDrawable;
 
 /** Handles validation and display of one field from the {@link EditorFieldModel}. */
 @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/HintedDropDownAdapterWithPlusIcon.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/HintedDropDownAdapterWithPlusIcon.java
index 22c5c6f..fa83ebc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/HintedDropDownAdapterWithPlusIcon.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/HintedDropDownAdapterWithPlusIcon.java
@@ -13,7 +13,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.ui.UiUtils;
 
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java
index 630de4c..e0bb801 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java
@@ -18,7 +18,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 
 /**
  * Default implementation of SelectableItemViewBase.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
index 77cd144..a92eca8f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
@@ -31,10 +31,10 @@
 import org.chromium.chrome.browser.ui.widget.FadingShadow;
 import org.chromium.chrome.browser.ui.widget.FadingShadowView;
 import org.chromium.chrome.browser.ui.widget.LoadingView;
-import org.chromium.chrome.browser.widget.displaystyle.DisplayStyleObserver;
-import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig.DisplayStyle;
+import org.chromium.chrome.browser.ui.widget.displaystyle.DisplayStyleObserver;
+import org.chromium.chrome.browser.ui.widget.displaystyle.HorizontalDisplayStyle;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig.DisplayStyle;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver;
 
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
index 0d06610..503e49f7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
@@ -39,14 +39,14 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.toolbar.top.ActionModeController;
 import org.chromium.chrome.browser.toolbar.top.ToolbarActionModeCallback;
+import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.displaystyle.DisplayStyleObserver;
+import org.chromium.chrome.browser.ui.widget.displaystyle.HorizontalDisplayStyle;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.vr.VrModeObserver;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
 import org.chromium.chrome.browser.widget.NumberRollView;
-import org.chromium.chrome.browser.widget.TintedDrawable;
-import org.chromium.chrome.browser.widget.displaystyle.DisplayStyleObserver;
-import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.UiUtils;
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 65cdddc2..674da814 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2204,15 +2204,9 @@
       </message>
 
       <!-- TalkBack upgrade -->
-      <message name="IDS_OLD_TALKBACK_TITLE" desc="Title of dialog notifying user that the version of TalkBack they're running is too old.">
-        You need to update TalkBack to a newer version.
-      </message>
       <message name="IDS_UPDATE_FROM_MARKET" desc="Title of button that will direct the user to update TalkBack from the market (Play Store)">
         Update
       </message>
-      <message name="IDS_CANCEL_TALKBACK_ALERT" desc="Title of button that will cancel the TalkBack alert dialog dialog.">
-        Cancel
-      </message>
 
       <!-- Android NFC Beam strings -->
       <message name="IDS_NFC_BEAM_ERROR_OVERLAY_ACTIVE" desc="Android Beam error - a tab is not in the foreground [CHAR-LIMIT=40]">
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
index ef5068c..ea1eb93f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
@@ -59,10 +59,10 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
 import org.chromium.chrome.browser.suggestions.ThumbnailGradient;
+import org.chromium.chrome.browser.ui.widget.displaystyle.HorizontalDisplayStyle;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.VerticalDisplayStyle;
 import org.chromium.chrome.browser.widget.ThumbnailProvider;
-import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
-import org.chromium.chrome.browser.widget.displaystyle.VerticalDisplayStyle;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.util.RenderTestRule;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java
index 3577c13..7a6d290 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java
@@ -52,9 +52,9 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.chrome.browser.util.UrlConstants;
 import org.chromium.chrome.browser.util.ViewUtils;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.NewTabPageTestUtils;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ScrimTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ScrimTest.java
index 87c4e6a..4c8073b6c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ScrimTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ScrimTest.java
@@ -74,8 +74,7 @@
             mSheetController = new BottomSheetController(activity,
                     activity.getLifecycleDispatcher(), activity.getActivityTabProvider(), mScrim,
                     mBottomSheet,
-                    activity.getCompositorViewHolder().getLayoutManager().getOverlayPanelManager(),
-                    true);
+                    activity.getCompositorViewHolder().getLayoutManager().getOverlayPanelManager());
         });
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
index a9783a4..afa737e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
@@ -75,8 +75,7 @@
             mSheetController = new BottomSheetController(activity,
                     activity.getLifecycleDispatcher(), activity.getActivityTabProvider(), scrim,
                     mBottomSheet,
-                    activity.getCompositorViewHolder().getLayoutManager().getOverlayPanelManager(),
-                    true);
+                    activity.getCompositorViewHolder().getLayoutManager().getOverlayPanelManager());
 
             mLowPriorityContent = new TestBottomSheetContent(
                     mActivityTestRule.getActivity(), ContentPriority.LOW, false);
diff --git a/chrome/android/junit/DEPS b/chrome/android/junit/DEPS
index 69fb45d..22f9b33 100644
--- a/chrome/android/junit/DEPS
+++ b/chrome/android/junit/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "!clank/java/src/org/chromium/chrome/browser/AppHooksImpl.java",
   "+chrome/lib/lifecycle/public",
+  "+chrome/browser/ui/android/widget",
   "+components/autofill/android/java/src/org/chromium/components/autofill",
   "+components/background_task_scheduler/android",
   "+components/bookmarks/common/android",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsUnitTestUtils.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsUnitTestUtils.java
index 6f076bd..edda909 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsUnitTestUtils.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsUnitTestUtils.java
@@ -11,9 +11,9 @@
 import org.chromium.chrome.browser.ntp.snippets.CategoryInt;
 import org.chromium.chrome.browser.ntp.snippets.SectionHeaderViewHolder;
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder;
-import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
-import org.chromium.chrome.browser.widget.displaystyle.VerticalDisplayStyle;
+import org.chromium.chrome.browser.ui.widget.displaystyle.HorizontalDisplayStyle;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.VerticalDisplayStyle;
 
 /**
  * JUnit specific utility classes for testing suggestions code. Other utility code is in
diff --git a/chrome/android/monochrome/BUILD.gn b/chrome/android/monochrome/BUILD.gn
index 5932ab54..ebde7fe 100644
--- a/chrome/android/monochrome/BUILD.gn
+++ b/chrome/android/monochrome/BUILD.gn
@@ -19,22 +19,24 @@
   jacoco_never_instrument = true
 }
 
-group("monochrome_apk_checker") {
-  testonly = true
-  data_deps = [
-    "//chrome/android:chrome_modern_public_apk",
-    "//chrome/android:monochrome_public_apk",
-  ]
-  if (public_android_sdk) {
-    # system_webview_apk only defined for public sdk builds, so this dependency
-    # must be guarded.
-    data_deps += [ "//android_webview:system_webview_apk" ]
-  }
+if (public_android_sdk) {
+  group("monochrome_apk_checker") {
+    testonly = true
+    data_deps = [
+      "//chrome/android:chrome_modern_public_apk",
+      "//chrome/android:monochrome_public_apk",
+    ]
+    if (public_android_sdk) {
+      # system_webview_apk only defined for public sdk builds, so this dependency
+      # must be guarded.
+      data_deps += [ "//android_webview:system_webview_apk" ]
+    }
 
-  data = [
-    "./scripts/monochrome_apk_checker.py",
-    "//testing/scripts/monochrome_apk_checker_wrapper.py",
-    "//testing/scripts/common.py",
-    "//testing/xvfb.py",
-  ]
+    data = [
+      "./scripts/monochrome_apk_checker.py",
+      "//testing/scripts/monochrome_apk_checker_wrapper.py",
+      "//testing/scripts/common.py",
+      "//testing/xvfb.py",
+    ]
+  }
 }
diff --git a/chrome/android/touchless/java/DEPS b/chrome/android/touchless/java/DEPS
index 0966a704..c17ad3c 100644
--- a/chrome/android/touchless/java/DEPS
+++ b/chrome/android/touchless/java/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chrome/lib/lifecycle/public",
+  "+chrome/browser/ui/android/widget",
   "+components/feature_engagement/public",
   "+components/offline_items_collection/core/android/java",
   "+content/public/android/java/src/org/chromium/content_public",
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessActionItemViewHolder.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessActionItemViewHolder.java
index ca4b875..cb215cc 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessActionItemViewHolder.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessActionItemViewHolder.java
@@ -15,7 +15,7 @@
 import org.chromium.chrome.browser.ntp.cards.ActionItem;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.chrome.touchless.R;
 import org.chromium.ui.widget.ChromeImageView;
 import org.chromium.ui.widget.Toast;
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessArticleViewHolder.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessArticleViewHolder.java
index 2c625c8..6db2539f 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessArticleViewHolder.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessArticleViewHolder.java
@@ -16,7 +16,7 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsBinder;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.chrome.touchless.R;
 
 /**
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessNewTabPage.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessNewTabPage.java
index 9774482..a7be9503 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessNewTabPage.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessNewTabPage.java
@@ -28,8 +28,8 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.chrome.browser.util.UrlConstants;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
 import org.chromium.chrome.touchless.R;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessNewTabPageAdapter.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessNewTabPageAdapter.java
index 96d4053..f9df8b6b 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessNewTabPageAdapter.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessNewTabPageAdapter.java
@@ -17,7 +17,7 @@
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.List;
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 1d59c1a..6dff8b89 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2717,6 +2717,8 @@
       "android/webapk/webapk_post_share_target_navigator.cc",
       "android/webapk/webapk_post_share_target_navigator.h",
       "android/webapk/webapk_types.h",
+      "android/webapk/webapk_ukm_recorder.cc",
+      "android/webapk/webapk_ukm_recorder.h",
       "android/webapk/webapk_update_data_fetcher.cc",
       "android/webapk/webapk_update_data_fetcher.h",
       "android/webapk/webapk_update_manager.cc",
@@ -2726,8 +2728,6 @@
       "android/webapps/add_to_homescreen_data_fetcher.h",
       "android/webapps/add_to_homescreen_manager.cc",
       "android/webapps/add_to_homescreen_manager.h",
-      "android/webapps/webapk_ukm_recorder.cc",
-      "android/webapps/webapk_ukm_recorder.h",
       "android/webapps/webapp_registry.cc",
       "android/webapps/webapp_registry.h",
       "android/widget/thumbnail_generator.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index da6838f8..cd4006de 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1332,6 +1332,17 @@
 };
 #endif  // !OS_ANDROID
 
+// TODO(crbug.com/991082): Remove after proper service worker support for
+// back-forward cache is implemented.
+const FeatureEntry::FeatureParam kBackForwardCache_ServiceWorkerSupport[] = {
+    {"service_worker_supported", "true"},
+};
+
+const FeatureEntry::FeatureVariation kBackForwardCacheVariations[] = {
+    {" even for ServiceWorker-controlled pages",
+     kBackForwardCache_ServiceWorkerSupport, 1, nullptr},
+};
+
 // RECORDING USER METRICS FOR FLAGS:
 // -----------------------------------------------------------------------------
 // The first line of the entry is the internal name.
@@ -4537,7 +4548,9 @@
 
     {"back-forward-cache", flag_descriptions::kBackForwardCacheName,
      flag_descriptions::kBackForwardCacheDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kBackForwardCache)},
+     FEATURE_WITH_PARAMS_VALUE_TYPE(features::kBackForwardCache,
+                                    kBackForwardCacheVariations,
+                                    "BackForwardCache")},
 
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
diff --git a/chrome/browser/android/omnibox/omnibox_prerender.cc b/chrome/browser/android/omnibox/omnibox_prerender.cc
index efe2d590..34164fe 100644
--- a/chrome/browser/android/omnibox/omnibox_prerender.cc
+++ b/chrome/browser/android/omnibox/omnibox_prerender.cc
@@ -10,6 +10,8 @@
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/predictors/autocomplete_action_predictor.h"
 #include "chrome/browser/predictors/autocomplete_action_predictor_factory.h"
+#include "chrome/browser/predictors/loading_predictor.h"
+#include "chrome/browser/predictors/loading_predictor_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "components/omnibox/browser/autocomplete_match.h"
@@ -98,19 +100,17 @@
       action_predictor->RecommendAction(url_string, *default_match);
 
   GURL current_url = GURL(current_url_string);
+  // Ask for prerendering if the destination URL is different than the
+  // current URL.
+  if (default_match->destination_url == current_url)
+    return;
+
   switch (recommended_action) {
     case AutocompleteActionPredictor::ACTION_PRERENDER:
-      // Ask for prerendering if the destination URL is different than the
-      // current URL.
-      if (default_match->destination_url != current_url) {
-        DoPrerender(
-            *default_match,
-            profile,
-            web_contents);
-      }
+      DoPrerender(*default_match, profile, web_contents);
       break;
     case AutocompleteActionPredictor::ACTION_PRECONNECT:
-      // TODO (apiccion) add preconnect logic
+      DoPreconnect(*default_match, profile);
       break;
     case AutocompleteActionPredictor::ACTION_NONE:
       break;
@@ -136,3 +136,14 @@
           web_contents->GetController().GetDefaultSessionStorageNamespace(),
           container_bounds.size());
 }
+
+void OmniboxPrerender::DoPreconnect(const AutocompleteMatch& match,
+                                    Profile* profile) {
+  auto* loading_predictor =
+      predictors::LoadingPredictorFactory::GetForProfile(profile);
+  if (loading_predictor) {
+    loading_predictor->PrepareForPageLoad(
+        match.destination_url, predictors::HintOrigin::OMNIBOX,
+        predictors::AutocompleteActionPredictor::IsPreconnectable(match));
+  }
+}
diff --git a/chrome/browser/android/omnibox/omnibox_prerender.h b/chrome/browser/android/omnibox/omnibox_prerender.h
index 6248b61..fa4f7e0 100644
--- a/chrome/browser/android/omnibox/omnibox_prerender.h
+++ b/chrome/browser/android/omnibox/omnibox_prerender.h
@@ -63,6 +63,7 @@
   void DoPrerender(const AutocompleteMatch& match,
                    Profile* profile,
                    content::WebContents* web_contents);
+  void DoPreconnect(const AutocompleteMatch& match, Profile* profile);
   JavaObjectWeakGlobalRef weak_java_omnibox_;
 
   DISALLOW_COPY_AND_ASSIGN(OmniboxPrerender);
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc
index e764183..143937c4 100644
--- a/chrome/browser/android/webapk/webapk_installer.cc
+++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -34,7 +34,7 @@
 #include "chrome/browser/android/webapk/webapk_icon_hasher.h"
 #include "chrome/browser/android/webapk/webapk_install_service.h"
 #include "chrome/browser/android/webapk/webapk_metrics.h"
-#include "chrome/browser/android/webapps/webapk_ukm_recorder.h"
+#include "chrome/browser/android/webapk/webapk_ukm_recorder.h"
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
diff --git a/chrome/browser/android/webapps/webapk_ukm_recorder.cc b/chrome/browser/android/webapk/webapk_ukm_recorder.cc
similarity index 98%
rename from chrome/browser/android/webapps/webapk_ukm_recorder.cc
rename to chrome/browser/android/webapk/webapk_ukm_recorder.cc
index 3daab3c..b80df83 100644
--- a/chrome/browser/android/webapps/webapk_ukm_recorder.cc
+++ b/chrome/browser/android/webapk/webapk_ukm_recorder.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/android/webapps/webapk_ukm_recorder.h"
+#include "chrome/browser/android/webapk/webapk_ukm_recorder.h"
 
 #include <jni.h>
 
diff --git a/chrome/browser/android/webapps/webapk_ukm_recorder.h b/chrome/browser/android/webapk/webapk_ukm_recorder.h
similarity index 86%
rename from chrome/browser/android/webapps/webapk_ukm_recorder.h
rename to chrome/browser/android/webapk/webapk_ukm_recorder.h
index d8c02537..6e87fbb 100644
--- a/chrome/browser/android/webapps/webapk_ukm_recorder.h
+++ b/chrome/browser/android/webapk/webapk_ukm_recorder.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ANDROID_WEBAPPS_WEBAPK_UKM_RECORDER_H_
-#define CHROME_BROWSER_ANDROID_WEBAPPS_WEBAPK_UKM_RECORDER_H_
+#ifndef CHROME_BROWSER_ANDROID_WEBAPK_WEBAPK_UKM_RECORDER_H_
+#define CHROME_BROWSER_ANDROID_WEBAPK_WEBAPK_UKM_RECORDER_H_
 
 #include <stdint.h>
 
@@ -35,4 +35,4 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(WebApkUkmRecorder);
 };
 
-#endif  // CHROME_BROWSER_ANDROID_WEBAPPS_WEBAPK_UKM_RECORDER_H_
+#endif  // CHROME_BROWSER_ANDROID_WEBAPK_WEBAPK_UKM_RECORDER_H_
diff --git a/chrome/browser/availability/availability_prober.cc b/chrome/browser/availability/availability_prober.cc
index 98a6d99..b758c326 100644
--- a/chrome/browser/availability/availability_prober.cc
+++ b/chrome/browser/availability/availability_prober.cc
@@ -43,6 +43,7 @@
 const char kCachePrefKeyPrefix[] = "Availability.Prober.cache";
 
 const char kSuccessHistogram[] = "Availability.Prober.DidSucceed";
+const char kFinalResultHistogram[] = "Availability.Prober.FinalState";
 const char kTimeUntilSuccess[] = "Availability.Prober.TimeUntilSuccess";
 const char kTimeUntilFailure[] = "Availability.Prober.TimeUntilFailure";
 const char kAttemptsBeforeSuccessHistogram[] =
@@ -331,6 +332,23 @@
   network_connection_tracker_->AddNetworkConnectionObserver(this);
 }
 
+void AvailabilityProber::OnProbingEnd() {
+  base::Value* cache_entry =
+      cached_probe_results_->FindKey(GetCacheKeyForCurrentNetwork());
+  if (cache_entry) {
+    base::Optional<AvailabilityProberCacheEntry> entry =
+        DecodeCacheEntryValue(*cache_entry);
+    if (entry.has_value()) {
+      base::BooleanHistogram::FactoryGet(
+          AppendNameToHistogram(kFinalResultHistogram),
+          base::HistogramBase::kUmaTargetedHistogramFlag)
+          ->Add(entry.value().is_success());
+    }
+  }
+
+  ResetState();
+}
+
 void AvailabilityProber::ResetState() {
   time_when_set_active_ = base::nullopt;
   successive_retry_count_ = 0;
@@ -408,7 +426,7 @@
   DCHECK(!url_loader_);
 
   if (!delegate_->ShouldSendNextProbe()) {
-    ResetState();
+    OnProbingEnd();
     return;
   }
 
@@ -531,7 +549,7 @@
     return;
   }
 
-  ResetState();
+  OnProbingEnd();
 }
 
 void AvailabilityProber::ProcessProbeSuccess() {
@@ -561,7 +579,7 @@
   }
 
   RecordProbeResult(true);
-  ResetState();
+  OnProbingEnd();
 }
 
 base::Optional<bool> AvailabilityProber::LastProbeWasSuccessful() {
diff --git a/chrome/browser/availability/availability_prober.h b/chrome/browser/availability/availability_prober.h
index ac85b7a..ae15d10 100644
--- a/chrome/browser/availability/availability_prober.h
+++ b/chrome/browser/availability/availability_prober.h
@@ -217,6 +217,11 @@
   void OnApplicationStateChange(base::android::ApplicationState new_state);
 #endif
 
+  // This is called whenever the prober goes inactive. This is caused whenever
+  // the probe succeeds, fails and there are no more retries, or the delegate
+  // stops the probing.
+  void OnProbingEnd();
+
   // Must outlive |this|.
   Delegate* delegate_;
 
diff --git a/chrome/browser/availability/availability_prober_unittest.cc b/chrome/browser/availability/availability_prober_unittest.cc
index d4e69e5..c97d0f7b 100644
--- a/chrome/browser/availability/availability_prober_unittest.cc
+++ b/chrome/browser/availability/availability_prober_unittest.cc
@@ -221,6 +221,8 @@
   histogram_tester.ExpectUniqueSample(
       "Availability.Prober.DidSucceed.Litepages", true, 1);
   histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.FinalState.Litepages", true, 1);
+  histogram_tester.ExpectUniqueSample(
       "Availability.Prober.NumAttemptsBeforeSuccess.Litepages", 1, 1);
   histogram_tester.ExpectUniqueSample(
       "Availability.Prober.ResponseCode.Litepages", net::HTTP_OK, 1);
@@ -246,6 +248,8 @@
   histogram_tester.ExpectUniqueSample(
       "Availability.Prober.DidSucceed.Litepages", true, 1);
   histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.FinalState.Litepages", true, 1);
+  histogram_tester.ExpectUniqueSample(
       "Availability.Prober.ResponseCode.Litepages", net::HTTP_OK, 1);
   histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::OK), 1);
@@ -395,6 +399,8 @@
   histogram_tester.ExpectUniqueSample(
       "Availability.Prober.DidSucceed.Litepages", true, 1);
   histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.FinalState.Litepages", true, 1);
+  histogram_tester.ExpectUniqueSample(
       "Availability.Prober.ResponseCode.Litepages", net::HTTP_OK, 1);
   histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::OK), 1);
@@ -435,6 +441,8 @@
 
   histogram_tester.ExpectUniqueSample(
       "Availability.Prober.DidSucceed.Litepages", false, 4);
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.FinalState.Litepages", false, 1);
   histogram_tester.ExpectTotalCount(
       "Availability.Prober.ResponseCode.Litepages", 0);
   histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
@@ -458,6 +466,8 @@
 
   histogram_tester.ExpectUniqueSample(
       "Availability.Prober.DidSucceed.Litepages", false, 4);
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.FinalState.Litepages", false, 1);
   histogram_tester.ExpectTotalCount(
       "Availability.Prober.ResponseCode.Litepages", 0);
   histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
@@ -479,6 +489,8 @@
   histogram_tester.ExpectUniqueSample(
       "Availability.Prober.DidSucceed.Litepages", false, 4);
   histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.FinalState.Litepages", false, 1);
+  histogram_tester.ExpectUniqueSample(
       "Availability.Prober.ResponseCode.Litepages", net::HTTP_NOT_FOUND, 4);
   histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::OK), 4);
@@ -563,6 +575,11 @@
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
   EXPECT_TRUE(prober->is_active());
 
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", false, 1);
+  histogram_tester.ExpectTotalCount("Availability.Prober.FinalState.Litepages",
+                                    0);
+
   // First retry.
   FastForward(base::TimeDelta::FromMilliseconds(999));
   VerifyNoRequests();
@@ -572,6 +589,11 @@
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
   EXPECT_TRUE(prober->is_active());
 
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", false, 2);
+  histogram_tester.ExpectTotalCount("Availability.Prober.FinalState.Litepages",
+                                    0);
+
   // Second retry should be another 1000ms later and be the final one.
   FastForward(base::TimeDelta::FromMilliseconds(999));
   VerifyNoRequests();
@@ -583,6 +605,8 @@
 
   histogram_tester.ExpectUniqueSample(
       "Availability.Prober.DidSucceed.Litepages", false, 3);
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.FinalState.Litepages", false, 1);
   histogram_tester.ExpectTotalCount(
       "Availability.Prober.ResponseCode.Litepages", 0);
   histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
@@ -608,6 +632,11 @@
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
   EXPECT_TRUE(prober->is_active());
 
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", false, 1);
+  histogram_tester.ExpectTotalCount("Availability.Prober.FinalState.Litepages",
+                                    0);
+
   // First retry.
   FastForward(base::TimeDelta::FromMilliseconds(999));
   VerifyNoRequests();
@@ -617,6 +646,11 @@
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
   EXPECT_TRUE(prober->is_active());
 
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", false, 2);
+  histogram_tester.ExpectTotalCount("Availability.Prober.FinalState.Litepages",
+                                    0);
+
   // Second retry should be another 1000ms later and be the final one.
   FastForward(base::TimeDelta::FromMilliseconds(999));
   VerifyNoRequests();
@@ -631,6 +665,8 @@
   histogram_tester.ExpectBucketCount("Availability.Prober.DidSucceed.Litepages",
                                      true, 1);
   histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.FinalState.Litepages", true, 1);
+  histogram_tester.ExpectUniqueSample(
       "Availability.Prober.NumAttemptsBeforeSuccess.Litepages", 3, 1);
   histogram_tester.ExpectUniqueSample(
       "Availability.Prober.ResponseCode.Litepages", net::HTTP_OK, 1);
@@ -794,6 +830,8 @@
 
   histogram_tester.ExpectTotalCount("Availability.Prober.DidSucceed.Litepages",
                                     0);
+  histogram_tester.ExpectTotalCount("Availability.Prober.FinalState.Litepages",
+                                    0);
 }
 
 TEST_F(AvailabilityProberTest, DelegateStopsRetries) {
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index ef52a97e..48c6c03 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1123,9 +1123,6 @@
 #endif
 
   extra_parts_.push_back(new ChromeContentBrowserClientPerformanceManagerPart);
-
-  gpu_binder_registry_.AddInterface(
-      base::Bind(&metrics::CallStackProfileCollector::Create));
 }
 
 ChromeContentBrowserClient::~ChromeContentBrowserClient() {
@@ -3883,12 +3880,10 @@
       interface_name, std::move(interface_pipe), render_process_host, origin);
 }
 
-void ChromeContentBrowserClient::BindInterfaceRequest(
-    const service_manager::BindSourceInfo& source_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle* interface_pipe) {
-  if (source_info.identity.name() == content::mojom::kGpuServiceName)
-    gpu_binder_registry_.TryBindInterface(interface_name, interface_pipe);
+void ChromeContentBrowserClient::BindGpuHostReceiver(
+    mojo::GenericPendingReceiver receiver) {
+  if (auto r = receiver.As<metrics::mojom::CallStackProfileCollector>())
+    metrics::CallStackProfileCollector::Create(std::move(r));
 }
 
 void ChromeContentBrowserClient::BindHostReceiverForRenderer(
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index a219479..22e3d7b 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -398,10 +398,7 @@
       const url::Origin& origin,
       const std::string& interface_name,
       mojo::ScopedMessagePipeHandle interface_pipe) override;
-  void BindInterfaceRequest(
-      const service_manager::BindSourceInfo& source_info,
-      const std::string& interface_name,
-      mojo::ScopedMessagePipeHandle* interface_pipe) override;
+  void BindGpuHostReceiver(mojo::GenericPendingReceiver receiver) override;
   void BindHostReceiverForRenderer(
       content::RenderProcessHost* render_process_host,
       mojo::GenericPendingReceiver receiver) override;
@@ -686,8 +683,6 @@
   // Parts are deleted in the reverse order they are added.
   std::vector<ChromeContentBrowserClientParts*> extra_parts_;
 
-  service_manager::BinderRegistry gpu_binder_registry_;
-
   scoped_refptr<safe_browsing::SafeBrowsingService> safe_browsing_service_;
   scoped_refptr<safe_browsing::UrlCheckerDelegate>
       safe_browsing_url_checker_delegate_;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 563aeb3..8dadbe0 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -553,10 +553,6 @@
     "arc/instance_throttle/arc_boot_phase_throttle_observer.h",
     "arc/instance_throttle/arc_instance_throttle.cc",
     "arc/instance_throttle/arc_instance_throttle.h",
-    "arc/instance_throttle/arc_throttle_observer.cc",
-    "arc/instance_throttle/arc_throttle_observer.h",
-    "arc/instance_throttle/window_throttle_observer_base.cc",
-    "arc/instance_throttle/window_throttle_observer_base.h",
     "arc/intent_helper/arc_external_protocol_dialog.cc",
     "arc/intent_helper/arc_external_protocol_dialog.h",
     "arc/intent_helper/arc_intent_picker_app_fetcher.cc",
@@ -2113,6 +2109,8 @@
     "tether/tether_service.h",
     "tether/tether_service_factory.cc",
     "tether/tether_service_factory.h",
+    "throttle_observer.cc",
+    "throttle_observer.h",
     "tpm_firmware_update.cc",
     "tpm_firmware_update.h",
     "tpm_firmware_update_notification.cc",
@@ -2154,6 +2152,8 @@
     "wilco_dtc_supportd/wilco_dtc_supportd_notification_controller.h",
     "wilco_dtc_supportd/wilco_dtc_supportd_web_request_service.cc",
     "wilco_dtc_supportd/wilco_dtc_supportd_web_request_service.h",
+    "window_throttle_observer_base.cc",
+    "window_throttle_observer_base.h",
 
     # Extension API implementations.
     "extensions/autotest_private/autotest_private_api.cc",
@@ -2443,7 +2443,6 @@
     "arc/instance_throttle/arc_active_window_throttle_observer_unittest.cc",
     "arc/instance_throttle/arc_boot_phase_throttle_observer_unittest.cc",
     "arc/instance_throttle/arc_instance_throttle_unittest.cc",
-    "arc/instance_throttle/arc_throttle_observer_unittest.cc",
     "arc/intent_helper/arc_external_protocol_dialog_unittest.cc",
     "arc/intent_helper/arc_intent_picker_app_fetcher_unittest.cc",
     "arc/intent_helper/arc_settings_service_unittest.cc",
@@ -2803,6 +2802,7 @@
     "system_logs/single_debug_daemon_log_source_unittest.cc",
     "system_logs/single_log_file_log_source_unittest.cc",
     "tether/tether_service_unittest.cc",
+    "throttle_observer_unittest.cc",
     "tpm_firmware_update_unittest.cc",
     "ui/gnubby_notification_unittest.cc",
     "ui/idle_app_name_notification_view_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/instance_throttle/arc_active_window_throttle_observer.cc b/chrome/browser/chromeos/arc/instance_throttle/arc_active_window_throttle_observer.cc
index 7b3377c..75891b8 100644
--- a/chrome/browser/chromeos/arc/instance_throttle/arc_active_window_throttle_observer.cc
+++ b/chrome/browser/chromeos/arc/instance_throttle/arc_active_window_throttle_observer.cc
@@ -9,7 +9,7 @@
 namespace arc {
 
 ArcActiveWindowThrottleObserver::ArcActiveWindowThrottleObserver()
-    : WindowThrottleObserverBase(ArcThrottleObserver::PriorityLevel::CRITICAL,
+    : WindowThrottleObserverBase(ThrottleObserver::PriorityLevel::CRITICAL,
                                  "ArcWindowIsActiveWindow") {}
 
 bool ArcActiveWindowThrottleObserver::ProcessWindowActivation(
diff --git a/chrome/browser/chromeos/arc/instance_throttle/arc_active_window_throttle_observer.h b/chrome/browser/chromeos/arc/instance_throttle/arc_active_window_throttle_observer.h
index 95b507d..b30b690 100644
--- a/chrome/browser/chromeos/arc/instance_throttle/arc_active_window_throttle_observer.h
+++ b/chrome/browser/chromeos/arc/instance_throttle/arc_active_window_throttle_observer.h
@@ -6,14 +6,14 @@
 #define CHROME_BROWSER_CHROMEOS_ARC_INSTANCE_THROTTLE_ARC_ACTIVE_WINDOW_THROTTLE_OBSERVER_H_
 
 #include "base/macros.h"
-#include "chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h"
-#include "chrome/browser/chromeos/arc/instance_throttle/window_throttle_observer_base.h"
+#include "chrome/browser/chromeos/window_throttle_observer_base.h"
 
 namespace arc {
 
 // This class observes window activations and sets the state to active if the
 // currently active window is an ARC window.
-class ArcActiveWindowThrottleObserver : public WindowThrottleObserverBase {
+class ArcActiveWindowThrottleObserver
+    : public chromeos::WindowThrottleObserverBase {
  public:
   ArcActiveWindowThrottleObserver();
   ~ArcActiveWindowThrottleObserver() override = default;
diff --git a/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer.cc b/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer.cc
index 1ea8a7e..92079e7e 100644
--- a/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer.cc
+++ b/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer.cc
@@ -11,23 +11,21 @@
 namespace arc {
 
 ArcBootPhaseThrottleObserver::ArcBootPhaseThrottleObserver()
-    : ArcThrottleObserver(ArcThrottleObserver::PriorityLevel::CRITICAL,
-                          "ArcIsBooting") {}
+    : ThrottleObserver(ThrottleObserver::PriorityLevel::CRITICAL,
+                       "ArcIsBooting") {}
 
 void ArcBootPhaseThrottleObserver::StartObserving(
-    ArcBridgeService* arc_bridge_service,
     content::BrowserContext* context,
     const ObserverStateChangedCallback& callback) {
-  DCHECK(!context_ && !boot_phase_monitor_);
-  ArcThrottleObserver::StartObserving(arc_bridge_service, context, callback);
-  context_ = context;
+  DCHECK(!boot_phase_monitor_);
+  ThrottleObserver::StartObserving(context, callback);
 
   auto* session_manager = ArcSessionManager::Get();
   DCHECK(session_manager);
   session_manager->AddObserver(this);
 
   boot_phase_monitor_ =
-      ArcBootPhaseMonitorBridge::GetForBrowserContext(context_);
+      ArcBootPhaseMonitorBridge::GetForBrowserContext(context);
   DCHECK(boot_phase_monitor_);
   boot_phase_monitor_->AddObserver(this);
 
@@ -44,8 +42,7 @@
   DCHECK(session_manager);
   session_manager->RemoveObserver(this);
 
-  context_ = nullptr;
-  ArcThrottleObserver::StopObserving();
+  ThrottleObserver::StopObserving();
 }
 
 void ArcBootPhaseThrottleObserver::OnArcStarted() {
@@ -85,7 +82,7 @@
     SetActive(false);
     return;
   }
-  auto* profile = Profile::FromBrowserContext(context_);
+  auto* profile = Profile::FromBrowserContext(context());
   const bool enabled_by_policy =
       IsArcPlayStoreEnabledForProfile(profile) &&
       IsArcPlayStoreEnabledPreferenceManagedForProfile(profile);
diff --git a/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer.h b/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer.h
index 9b0c0f7..0947e6f 100644
--- a/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer.h
+++ b/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer.h
@@ -8,7 +8,7 @@
 #include "base/macros.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h"
-#include "chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h"
+#include "chrome/browser/chromeos/throttle_observer.h"
 #include "chrome/browser/sessions/session_restore_observer.h"
 
 namespace content {
@@ -17,11 +17,9 @@
 
 namespace arc {
 
-class ArcBridgeService;
-
 // This class observes phases of ARC boot and unthrottles the container
 // when ARC is booting or restarting.
-class ArcBootPhaseThrottleObserver : public ArcThrottleObserver,
+class ArcBootPhaseThrottleObserver : public chromeos::ThrottleObserver,
                                      public ArcSessionManager::Observer,
                                      public ArcBootPhaseMonitorBridge::Observer,
                                      public SessionRestoreObserver {
@@ -29,9 +27,8 @@
   ArcBootPhaseThrottleObserver();
   ~ArcBootPhaseThrottleObserver() override = default;
 
-  // ArcThrottleObserver:
-  void StartObserving(ArcBridgeService* arc_bridge_service,
-                      content::BrowserContext* context,
+  // chromeos::ThrottleObserver:
+  void StartObserving(content::BrowserContext* context,
                       const ObserverStateChangedCallback& callback) override;
   void StopObserving() override;
 
@@ -53,7 +50,6 @@
   // enable since in these cases ARC should always be unthrottled during boot.
   void MaybeSetActive();
 
-  content::BrowserContext* context_ = nullptr;
   ArcBootPhaseMonitorBridge* boot_phase_monitor_ = nullptr;
   bool session_restore_loading_ = false;
   bool arc_is_booting_ = false;
diff --git a/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer_unittest.cc b/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer_unittest.cc
index 1e04e35..b932992 100644
--- a/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer_unittest.cc
+++ b/chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer_unittest.cc
@@ -45,7 +45,7 @@
 
     ArcBootPhaseMonitorBridge::GetForBrowserContextForTesting(profile());
     observer()->StartObserving(
-        nullptr, profile(),
+        profile(),
         ArcBootPhaseThrottleObserver::ObserverStateChangedCallback());
   }
 
diff --git a/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle.cc b/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle.cc
index eabfad92..00821f7 100644
--- a/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle.cc
+++ b/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle.cc
@@ -10,7 +10,7 @@
 #include "base/memory/singleton.h"
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h"
-#include "chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h"
+#include "chrome/browser/chromeos/throttle_observer.h"
 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "components/arc/arc_util.h"
 
@@ -73,15 +73,14 @@
 
 ArcInstanceThrottle::ArcInstanceThrottle(content::BrowserContext* context,
                                          ArcBridgeService* bridge_service)
-    : arc_bridge_service_(bridge_service),
-      context_(context),
+    : context_(context),
       delegate_(std::make_unique<DefaultDelegateImpl>()),
       weak_ptr_factory_(this) {
   auto callback =
       base::BindRepeating(&ArcInstanceThrottle::OnObserverStateChanged,
                           weak_ptr_factory_.GetWeakPtr());
   for (auto* observer : GetAllObservers())
-    observer->StartObserving(bridge_service, context, callback);
+    observer->StartObserving(context, callback);
 }
 
 ArcInstanceThrottle::~ArcInstanceThrottle() = default;
@@ -92,9 +91,9 @@
 }
 
 void ArcInstanceThrottle::OnObserverStateChanged() {
-  ArcThrottleObserver::PriorityLevel max_level =
-      ArcThrottleObserver::PriorityLevel::LOW;
-  ArcThrottleObserver* effective_observer = nullptr;
+  chromeos::ThrottleObserver::PriorityLevel max_level =
+      chromeos::ThrottleObserver::PriorityLevel::LOW;
+  chromeos::ThrottleObserver* effective_observer = nullptr;
   std::string active_observers;
 
   for (auto* observer : GetAllObservers()) {
@@ -125,24 +124,25 @@
 }
 
 void ArcInstanceThrottle::ThrottleInstance(
-    ArcThrottleObserver::PriorityLevel level) {
+    chromeos::ThrottleObserver::PriorityLevel level) {
   if (level_ == level)
     return;
   level_ = level;
   switch (level_) {
-    case ArcThrottleObserver::PriorityLevel::CRITICAL:
-    case ArcThrottleObserver::PriorityLevel::IMPORTANT:
-    case ArcThrottleObserver::PriorityLevel::NORMAL:
+    case chromeos::ThrottleObserver::PriorityLevel::CRITICAL:
+    case chromeos::ThrottleObserver::PriorityLevel::IMPORTANT:
+    case chromeos::ThrottleObserver::PriorityLevel::NORMAL:
       delegate_->SetCpuRestriction(false);
       break;
-    case ArcThrottleObserver::PriorityLevel::LOW:
-    case ArcThrottleObserver::PriorityLevel::UNKNOWN:
+    case chromeos::ThrottleObserver::PriorityLevel::LOW:
+    case chromeos::ThrottleObserver::PriorityLevel::UNKNOWN:
       delegate_->SetCpuRestriction(true);
       break;
   }
 }
 
-std::vector<ArcThrottleObserver*> ArcInstanceThrottle::GetAllObservers() {
+std::vector<chromeos::ThrottleObserver*>
+ArcInstanceThrottle::GetAllObservers() {
   if (!observers_for_testing_.empty())
     return observers_for_testing_;
   return {&active_window_throttle_observer_, &boot_phase_throttle_observer_};
@@ -153,7 +153,7 @@
 }
 
 void ArcInstanceThrottle::SetObserversForTesting(
-    const std::vector<ArcThrottleObserver*>& observers) {
+    const std::vector<chromeos::ThrottleObserver*>& observers) {
   for (auto* observer : GetAllObservers())
     observer->StopObserving();
   observers_for_testing_ = observers;
@@ -161,6 +161,6 @@
       base::BindRepeating(&ArcInstanceThrottle::OnObserverStateChanged,
                           weak_ptr_factory_.GetWeakPtr());
   for (auto* observer : GetAllObservers())
-    observer->StartObserving(arc_bridge_service_, context_, callback);
+    observer->StartObserving(context_, callback);
 }
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle.h b/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle.h
index c1cc998..57f6dc1 100644
--- a/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle.h
+++ b/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle.h
@@ -14,7 +14,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/arc/instance_throttle/arc_active_window_throttle_observer.h"
 #include "chrome/browser/chromeos/arc/instance_throttle/arc_boot_phase_throttle_observer.h"
-#include "chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h"
+#include "chrome/browser/chromeos/throttle_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
 
 namespace content {
@@ -60,24 +60,23 @@
   // Functions for testing
   void NotifyObserverStateChangedForTesting();
   void SetObserversForTesting(
-      const std::vector<ArcThrottleObserver*>& observers);
+      const std::vector<chromeos::ThrottleObserver*>& observers);
 
   void set_delegate_for_testing(std::unique_ptr<Delegate> delegate) {
     delegate_ = std::move(delegate);
   }
 
  private:
-  std::vector<ArcThrottleObserver*> GetAllObservers();
+  std::vector<chromeos::ThrottleObserver*> GetAllObservers();
   void OnObserverStateChanged();
-  void ThrottleInstance(ArcThrottleObserver::PriorityLevel level);
+  void ThrottleInstance(chromeos::ThrottleObserver::PriorityLevel level);
 
-  ArcBridgeService* arc_bridge_service_;
   content::BrowserContext* context_;
-  std::vector<ArcThrottleObserver*> observers_for_testing_;
+  std::vector<chromeos::ThrottleObserver*> observers_for_testing_;
   std::unique_ptr<Delegate> delegate_;
-  ArcThrottleObserver::PriorityLevel level_{
-      ArcThrottleObserver::PriorityLevel::UNKNOWN};
-  ArcThrottleObserver* last_effective_observer_ = nullptr;
+  chromeos::ThrottleObserver::PriorityLevel level_{
+      chromeos::ThrottleObserver::PriorityLevel::UNKNOWN};
+  chromeos::ThrottleObserver* last_effective_observer_ = nullptr;
   base::TimeTicks last_throttle_transition_;
 
   // Throttle Observers
diff --git a/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle_unittest.cc b/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle_unittest.cc
index 4f91a2038..27f59a4 100644
--- a/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle_unittest.cc
+++ b/chrome/browser/chromeos/arc/instance_throttle/arc_instance_throttle_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/test/task_environment.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h"
-#include "chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h"
+#include "chrome/browser/chromeos/throttle_observer.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_service_manager.h"
@@ -67,8 +67,10 @@
     return enable_cpu_restriction_counter_;
   }
 
-  ArcThrottleObserver* critical_observer() { return &critical_observer_; }
-  ArcThrottleObserver* low_observer() { return &low_observer_; }
+  chromeos::ThrottleObserver* critical_observer() {
+    return &critical_observer_;
+  }
+  chromeos::ThrottleObserver* low_observer() { return &low_observer_; }
   const std::string& last_recorded_observer_name() const {
     return last_recorded_observer_name_;
   }
@@ -102,10 +104,10 @@
   ArcInstanceThrottle* arc_instance_throttle_;
   size_t disable_cpu_restriction_counter_;
   size_t enable_cpu_restriction_counter_;
-  ArcThrottleObserver critical_observer_{
-      ArcThrottleObserver::PriorityLevel::CRITICAL, "CriticalObserver"};
-  ArcThrottleObserver low_observer_{ArcThrottleObserver::PriorityLevel::LOW,
-                                    "LowObserver"};
+  chromeos::ThrottleObserver critical_observer_{
+      chromeos::ThrottleObserver::PriorityLevel::CRITICAL, "CriticalObserver"};
+  chromeos::ThrottleObserver low_observer_{
+      chromeos::ThrottleObserver::PriorityLevel::LOW, "LowObserver"};
   std::string last_recorded_observer_name_;
   size_t record_uma_counter_;
 
@@ -120,8 +122,8 @@
 // a change in observers, but skips adjusting throttle if the new level is same
 // as before.
 TEST_F(ArcInstanceThrottleTest, TestOnObserverStateChanged) {
-  std::vector<ArcThrottleObserver*> observers = {critical_observer(),
-                                                 low_observer()};
+  std::vector<chromeos::ThrottleObserver*> observers = {critical_observer(),
+                                                        low_observer()};
   arc_instance_throttle()->SetObserversForTesting(observers);
   EXPECT_EQ(0U, disable_cpu_restriction_counter());
   EXPECT_EQ(0U, enable_cpu_restriction_counter());
@@ -149,8 +151,8 @@
 // Tests that ArcInstanceThrottle records the duration that the effective
 // observer is active.
 TEST_F(ArcInstanceThrottleTest, RecordCpuRestrictionDisabledUMA) {
-  std::vector<ArcThrottleObserver*> observers = {critical_observer(),
-                                                 low_observer()};
+  std::vector<chromeos::ThrottleObserver*> observers = {critical_observer(),
+                                                        low_observer()};
   arc_instance_throttle()->SetObserversForTesting(observers);
   EXPECT_EQ(0U, uma_count());
 
diff --git a/chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.cc b/chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.cc
deleted file mode 100644
index 6642464..0000000
--- a/chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h"
-
-namespace arc {
-
-namespace {
-
-std::string LevelToString(ArcThrottleObserver::PriorityLevel level) {
-  switch (level) {
-    case ArcThrottleObserver::PriorityLevel::LOW:
-      return "PriorityLevel::LOW";
-    case ArcThrottleObserver::PriorityLevel::NORMAL:
-      return "PriorityLevel::NORMAL";
-    case ArcThrottleObserver::PriorityLevel::IMPORTANT:
-      return "PriorityLevel::IMPORTANT";
-    case ArcThrottleObserver::PriorityLevel::CRITICAL:
-      return "PriorityLevel::CRITICAL";
-    case ArcThrottleObserver::PriorityLevel::UNKNOWN:
-      return "PriorityLevel::UNKNOWN";
-  }
-}
-
-}  // namespace
-
-ArcThrottleObserver::ArcThrottleObserver(
-    ArcThrottleObserver::PriorityLevel level,
-    const std::string& name)
-    : level_(level), name_(name) {}
-
-ArcThrottleObserver::~ArcThrottleObserver() = default;
-
-void ArcThrottleObserver::StartObserving(
-    ArcBridgeService* arc_bridge_service,
-    content::BrowserContext* context,
-    const ObserverStateChangedCallback& callback) {
-  DCHECK(!callback_);
-  callback_ = callback;
-}
-
-void ArcThrottleObserver::StopObserving() {
-  callback_.Reset();
-}
-
-void ArcThrottleObserver::SetActive(bool active) {
-  if (active_ == active)
-    return;
-  active_ = active;
-  if (callback_)
-    callback_.Run();
-}
-
-std::string ArcThrottleObserver::GetDebugDescription() const {
-  return ("ArcThrottleObserver(" + name() + ", " + LevelToString(level()) +
-          ", " + (active() ? "active" : "inactive") + ")");
-}
-
-}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer_unittest.cc b/chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer_unittest.cc
deleted file mode 100644
index a800012..0000000
--- a/chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer_unittest.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h"
-
-#include "base/bind.h"
-#include "base/memory/weak_ptr.h"
-#include "base/template_util.h"
-#include "base/test/task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace arc {
-
-class ArcThrottleObserverTest
-    : public testing::Test,
-      public base::SupportsWeakPtr<ArcThrottleObserverTest> {
- public:
-  ArcThrottleObserverTest() {
-    observer_.StartObserving(
-        nullptr /* ArcBridgeService* */, nullptr /* content::BrowserContext* */,
-        base::BindRepeating(&ArcThrottleObserverTest::OnObserverStateChanged,
-                            AsWeakPtr()));
-  }
-
-  void OnObserverStateChanged() { notify_count_++; }
-
- protected:
-  ArcThrottleObserver* observer() { return &observer_; }
-  size_t notify_count() const { return notify_count_; }
-
- private:
-  ArcThrottleObserver observer_{ArcThrottleObserver::PriorityLevel::LOW,
-                                "TestObserver"};
-  size_t notify_count_{0};
-
-  DISALLOW_COPY_AND_ASSIGN(ArcThrottleObserverTest);
-};
-
-// Tests that ArcThrottleObserver can be constructed and destructed.
-TEST_F(ArcThrottleObserverTest, TestConstructDestruct) {}
-
-// Tests that ArcThrottleObserver notifies observers only when its 'active'
-// state changes
-TEST_F(ArcThrottleObserverTest, TestSetActive) {
-  EXPECT_EQ(0U, notify_count());
-  EXPECT_FALSE(observer()->active());
-
-  observer()->SetActive(true);
-  EXPECT_TRUE(observer()->active());
-  EXPECT_EQ(1U, notify_count());
-
-  observer()->SetActive(true);
-  EXPECT_TRUE(observer()->active());
-  EXPECT_EQ(1U, notify_count());
-
-  observer()->SetActive(false);
-  EXPECT_FALSE(observer()->active());
-  EXPECT_EQ(2U, notify_count());
-}
-
-}  // namespace arc
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index 12d5ef2..fa66ea9 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -1113,7 +1113,6 @@
   request.set_vm_name(std::move(vm_name));
   request.set_container_name(std::move(container_name));
   request.set_owner_id(owner_id_);
-  request.set_async(true);
   if (auto* integration_service =
           drive::DriveIntegrationServiceFactory::GetForProfile(profile_)) {
     request.set_drivefs_mount_path(
diff --git a/chrome/browser/chromeos/throttle_observer.cc b/chrome/browser/chromeos/throttle_observer.cc
new file mode 100644
index 0000000..f1210d2
--- /dev/null
+++ b/chrome/browser/chromeos/throttle_observer.cc
@@ -0,0 +1,59 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/throttle_observer.h"
+
+namespace chromeos {
+namespace {
+
+std::string LevelToString(ThrottleObserver::PriorityLevel level) {
+  switch (level) {
+    case ThrottleObserver::PriorityLevel::LOW:
+      return "PriorityLevel::LOW";
+    case ThrottleObserver::PriorityLevel::NORMAL:
+      return "PriorityLevel::NORMAL";
+    case ThrottleObserver::PriorityLevel::IMPORTANT:
+      return "PriorityLevel::IMPORTANT";
+    case ThrottleObserver::PriorityLevel::CRITICAL:
+      return "PriorityLevel::CRITICAL";
+    case ThrottleObserver::PriorityLevel::UNKNOWN:
+      return "PriorityLevel::UNKNOWN";
+  }
+}
+
+}  // namespace
+
+ThrottleObserver::ThrottleObserver(ThrottleObserver::PriorityLevel level,
+                                   const std::string& name)
+    : level_(level), name_(name) {}
+
+ThrottleObserver::~ThrottleObserver() = default;
+
+void ThrottleObserver::StartObserving(
+    content::BrowserContext* context,
+    const ObserverStateChangedCallback& callback) {
+  DCHECK(!callback_);
+  callback_ = callback;
+  context_ = context;
+}
+
+void ThrottleObserver::StopObserving() {
+  callback_.Reset();
+  context_ = nullptr;
+}
+
+void ThrottleObserver::SetActive(bool active) {
+  if (active_ == active)
+    return;
+  active_ = active;
+  if (callback_)
+    callback_.Run();
+}
+
+std::string ThrottleObserver::GetDebugDescription() const {
+  return ("ThrottleObserver(" + name() + ", " + LevelToString(level()) + ", " +
+          (active() ? "active" : "inactive") + ")");
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h b/chrome/browser/chromeos/throttle_observer.h
similarity index 70%
rename from chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h
rename to chrome/browser/chromeos/throttle_observer.h
index 2abf55a..3d0c22c7 100644
--- a/chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h
+++ b/chrome/browser/chromeos/throttle_observer.h
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_ARC_INSTANCE_THROTTLE_ARC_THROTTLE_OBSERVER_H_
-#define CHROME_BROWSER_CHROMEOS_ARC_INSTANCE_THROTTLE_ARC_THROTTLE_OBSERVER_H_
+#ifndef CHROME_BROWSER_CHROMEOS_THROTTLE_OBSERVER_H_
+#define CHROME_BROWSER_CHROMEOS_THROTTLE_OBSERVER_H_
 
 #include <string>
 
 #include "base/callback.h"
+#include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 
@@ -15,26 +16,25 @@
 class BrowserContext;
 }
 
-namespace arc {
-class ArcBridgeService;
+namespace chromeos {
 
 // Base throttle observer class. Each throttle observer watches a particular
 // condition (window activates, mojom instance disconnects, and so on) and
-// notifies any observers when there is a change.
-class ArcThrottleObserver {
+// calls the ObserverStateChangedCallback when there is a change.
+class ThrottleObserver {
  public:
   using ObserverStateChangedCallback = base::RepeatingCallback<void()>;
   enum class PriorityLevel { UNKNOWN, LOW, NORMAL, IMPORTANT, CRITICAL };
 
-  ArcThrottleObserver(PriorityLevel level, const std::string& name);
-  virtual ~ArcThrottleObserver();
+  ThrottleObserver(PriorityLevel level, const std::string& name);
+  virtual ~ThrottleObserver();
 
   // Starts observing. This is overridden in derived classes to register self as
   // observer for a particular condition. However, the base method should be
   // called in overridden methods, so that the callback_ member is initialized.
-  virtual void StartObserving(ArcBridgeService* arc_bridge_service,
-                              content::BrowserContext* content,
+  virtual void StartObserving(content::BrowserContext* content,
                               const ObserverStateChangedCallback& callback);
+
   // Stops observing. This method is the last place in which context can be
   // used.
   virtual void StopObserving();
@@ -50,15 +50,19 @@
   bool active() const { return active_; }
 
  protected:
+  content::BrowserContext* context() { return context_; }
+
   const PriorityLevel level_{PriorityLevel::UNKNOWN};
   bool active_ = false;
   const std::string name_;  // For logging purposes
   ObserverStateChangedCallback callback_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(ArcThrottleObserver);
+  content::BrowserContext* context_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThrottleObserver);
 };
 
-}  // namespace arc
+}  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_ARC_INSTANCE_THROTTLE_ARC_THROTTLE_OBSERVER_H_
+#endif  // CHROME_BROWSER_CHROMEOS_THROTTLE_OBSERVER_H_
diff --git a/chrome/browser/chromeos/throttle_observer_unittest.cc b/chrome/browser/chromeos/throttle_observer_unittest.cc
new file mode 100644
index 0000000..20b2d9e
--- /dev/null
+++ b/chrome/browser/chromeos/throttle_observer_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/throttle_observer.h"
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "base/template_util.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+class ThrottleObserverTest
+    : public testing::Test,
+      public base::SupportsWeakPtr<ThrottleObserverTest> {
+ public:
+  ThrottleObserverTest() {
+    observer_.StartObserving(
+        nullptr /* content::BrowserContext* */,
+        base::BindRepeating(&ThrottleObserverTest::OnObserverStateChanged,
+                            AsWeakPtr()));
+  }
+
+  void OnObserverStateChanged() { notify_count_++; }
+
+ protected:
+  ThrottleObserver* observer() { return &observer_; }
+  size_t notify_count() const { return notify_count_; }
+
+ private:
+  ThrottleObserver observer_{ThrottleObserver::PriorityLevel::LOW,
+                             "TestObserver"};
+  size_t notify_count_{0};
+
+  DISALLOW_COPY_AND_ASSIGN(ThrottleObserverTest);
+};
+
+// Tests that ThrottleObserver can be constructed and destructed.
+TEST_F(ThrottleObserverTest, TestConstructDestruct) {}
+
+// Tests that ThrottleObserver notifies observers only when its 'active'
+// state changes
+TEST_F(ThrottleObserverTest, TestSetActive) {
+  EXPECT_EQ(0U, notify_count());
+  EXPECT_FALSE(observer()->active());
+
+  observer()->SetActive(true);
+  EXPECT_TRUE(observer()->active());
+  EXPECT_EQ(1U, notify_count());
+
+  observer()->SetActive(true);
+  EXPECT_TRUE(observer()->active());
+  EXPECT_EQ(1U, notify_count());
+
+  observer()->SetActive(false);
+  EXPECT_FALSE(observer()->active());
+  EXPECT_EQ(2U, notify_count());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/arc/instance_throttle/window_throttle_observer_base.cc b/chrome/browser/chromeos/window_throttle_observer_base.cc
similarity index 88%
rename from chrome/browser/chromeos/arc/instance_throttle/window_throttle_observer_base.cc
rename to chrome/browser/chromeos/window_throttle_observer_base.cc
index 5a2fe86a..4aecc9ee 100644
--- a/chrome/browser/chromeos/arc/instance_throttle/window_throttle_observer_base.cc
+++ b/chrome/browser/chromeos/window_throttle_observer_base.cc
@@ -2,15 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/arc/instance_throttle/window_throttle_observer_base.h"
+#include "chrome/browser/chromeos/window_throttle_observer_base.h"
 
 #include "ash/public/cpp/shell_window_ids.h"
-#include "chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h"
 #include "components/exo/wm_helper.h"
 #include "ui/aura/window.h"
 #include "ui/wm/public/activation_change_observer.h"
 
-namespace arc {
+namespace chromeos {
 namespace {
 
 // Returns true if the window is in the app list window container.
@@ -63,22 +62,21 @@
 }  // namespace
 
 WindowThrottleObserverBase::WindowThrottleObserverBase(
-    ArcThrottleObserver::PriorityLevel level,
+    ThrottleObserver::PriorityLevel level,
     std::string name)
-    : ArcThrottleObserver(level, name) {}
+    : ThrottleObserver(level, name) {}
 
 void WindowThrottleObserverBase::StartObserving(
-    ArcBridgeService* arc_bridge_service,
     content::BrowserContext* context,
     const ObserverStateChangedCallback& callback) {
-  ArcThrottleObserver::StartObserving(arc_bridge_service, context, callback);
+  ThrottleObserver::StartObserving(context, callback);
   if (!exo::WMHelper::HasInstance())  // for unit testing
     return;
   exo::WMHelper::GetInstance()->AddActivationObserver(this);
 }
 
 void WindowThrottleObserverBase::StopObserving() {
-  ArcThrottleObserver::StopObserving();
+  ThrottleObserver::StopObserving();
   if (!exo::WMHelper::HasInstance())
     return;
   exo::WMHelper::GetInstance()->RemoveActivationObserver(this);
@@ -92,4 +90,4 @@
   SetActive(ProcessWindowActivation(reason, gained_active, lost_active));
 }
 
-}  // namespace arc
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/arc/instance_throttle/window_throttle_observer_base.h b/chrome/browser/chromeos/window_throttle_observer_base.h
similarity index 65%
rename from chrome/browser/chromeos/arc/instance_throttle/window_throttle_observer_base.h
rename to chrome/browser/chromeos/window_throttle_observer_base.h
index a4c1aa80..abad75a 100644
--- a/chrome/browser/chromeos/arc/instance_throttle/window_throttle_observer_base.h
+++ b/chrome/browser/chromeos/window_throttle_observer_base.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_ARC_INSTANCE_THROTTLE_WINDOW_THROTTLE_OBSERVER_BASE_H_
-#define CHROME_BROWSER_CHROMEOS_ARC_INSTANCE_THROTTLE_WINDOW_THROTTLE_OBSERVER_BASE_H_
+#ifndef CHROME_BROWSER_CHROMEOS_WINDOW_THROTTLE_OBSERVER_BASE_H_
+#define CHROME_BROWSER_CHROMEOS_WINDOW_THROTTLE_OBSERVER_BASE_H_
 
 #include "base/macros.h"
-#include "chrome/browser/chromeos/arc/instance_throttle/arc_throttle_observer.h"
+#include "chrome/browser/chromeos/throttle_observer.h"
 #include "ui/wm/public/activation_change_observer.h"
 
 namespace content {
@@ -17,21 +17,18 @@
 class Window;
 }
 
-namespace arc {
-
-class ArcBridgeService;
+namespace chromeos {
 
 // Base class for locks that observe changes in window activation.
-class WindowThrottleObserverBase : public ArcThrottleObserver,
+class WindowThrottleObserverBase : public ThrottleObserver,
                                    public wm::ActivationChangeObserver {
  public:
-  WindowThrottleObserverBase(ArcThrottleObserver::PriorityLevel level,
+  WindowThrottleObserverBase(ThrottleObserver::PriorityLevel level,
                              std::string name);
   ~WindowThrottleObserverBase() override = default;
 
-  // ArcThrottleObserver:
-  void StartObserving(ArcBridgeService* arc_bridge_service,
-                      content::BrowserContext* context,
+  // ThrottleObserver:
+  void StartObserving(content::BrowserContext* context,
                       const ObserverStateChangedCallback& callback) override;
   void StopObserving() override;
 
@@ -51,6 +48,6 @@
   DISALLOW_COPY_AND_ASSIGN(WindowThrottleObserverBase);
 };
 
-}  // namespace arc
+}  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_ARC_INSTANCE_THROTTLE_WINDOW_THROTTLE_OBSERVER_BASE_H_
+#endif  // CHROME_BROWSER_CHROMEOS_WINDOW_THROTTLE_OBSERVER_BASE_H_
diff --git a/chrome/browser/enterprise_reporting/report_uploader.cc b/chrome/browser/enterprise_reporting/report_uploader.cc
index 614087a3..38795ba 100644
--- a/chrome/browser/enterprise_reporting/report_uploader.cc
+++ b/chrome/browser/enterprise_reporting/report_uploader.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 
@@ -25,6 +26,10 @@
     false   // Do not always use initial delay.
 };
 
+void RecordReportResponseMetrics(ReportResponseMetricsStatus status) {
+  base::UmaHistogramEnumeration("Enterprise.CloudReportingResponse", status);
+}
+
 }  // namespace
 
 ReportUploader::ReportUploader(policy::CloudPolicyClient* client,
@@ -51,27 +56,40 @@
 void ReportUploader::OnRequestFinished(bool status) {
   if (status) {
     NextRequest();
+    RecordReportResponseMetrics(ReportResponseMetricsStatus::kSuccess);
     return;
   }
 
   switch (client_->status()) {
     case policy::DM_STATUS_REQUEST_FAILED:         // network error
+      RecordReportResponseMetrics(ReportResponseMetricsStatus::kNetworkError);
+      Retry();
+      break;
     case policy::DM_STATUS_TEMPORARY_UNAVAILABLE:  // 5xx server error
+      RecordReportResponseMetrics(
+          ReportResponseMetricsStatus::kTemporaryServerError);
+      Retry();
+      break;
     // DM_STATUS_SERVICE_DEVICE_ID_CONFLICT is caused by 409 conflict. It can
     // be caused by either device id conflict or DDS concur error which is
     // a database error. We only want to retry for the second case. However,
     // there is no way for us to tell difference right now so we will retry
     // regardless.
     case policy::DM_STATUS_SERVICE_DEVICE_ID_CONFLICT:
+      RecordReportResponseMetrics(
+          ReportResponseMetricsStatus::kDDSConcurrencyError);
       Retry();
       break;
     case policy::DM_STATUS_REQUEST_TOO_LARGE:
       // Treats the REQUEST_TOO_LARGE error as a success upload. It's likely
       // a calculation error during request generating and there is nothing
       // can be done here.
+      RecordReportResponseMetrics(
+          ReportResponseMetricsStatus::kRequestTooLargeError);
       NextRequest();
       break;
     default:
+      RecordReportResponseMetrics(ReportResponseMetricsStatus::kOtherError);
       SendResponse(ReportStatus::kPersistentError);
       break;
   }
diff --git a/chrome/browser/enterprise_reporting/report_uploader.h b/chrome/browser/enterprise_reporting/report_uploader.h
index e4337b56..44b6900 100644
--- a/chrome/browser/enterprise_reporting/report_uploader.h
+++ b/chrome/browser/enterprise_reporting/report_uploader.h
@@ -90,6 +90,16 @@
   DISALLOW_COPY_AND_ASSIGN(ReportUploader);
 };
 
+enum ReportResponseMetricsStatus {
+  kSuccess = 0,
+  kNetworkError = 1,
+  kTemporaryServerError = 2,
+  kDDSConcurrencyError = 3,
+  kRequestTooLargeError = 4,
+  kOtherError = 5,
+  kMaxValue = kOtherError,
+};
+
 }  // namespace enterprise_reporting
 
 #endif  // CHROME_BROWSER_ENTERPRISE_REPORTING_REPORT_UPLOADER_H_
diff --git a/chrome/browser/enterprise_reporting/report_uploader_unittest.cc b/chrome/browser/enterprise_reporting/report_uploader_unittest.cc
index f00e0f1..b1b25b1 100644
--- a/chrome/browser/enterprise_reporting/report_uploader_unittest.cc
+++ b/chrome/browser/enterprise_reporting/report_uploader_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -20,6 +21,8 @@
 namespace enterprise_reporting {
 namespace {
 constexpr const char* kOsUserNames[] = {"name1", "name2"};
+constexpr char kResponseMetricsName[] = "Enterprise.CloudReportingResponse";
+
 }  // namespace
 
 class ReportUploaderTest : public ::testing::Test {
@@ -82,6 +85,7 @@
   std::unique_ptr<ReportUploader> uploader_;
   policy::MockCloudPolicyClient client_;
   bool has_responded_ = false;
+  base::HistogramTester histogram_tester_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ReportUploaderTest);
@@ -98,6 +102,8 @@
                                 ReportUploader::kSuccess);
   RunNextTask();
   EXPECT_TRUE(has_responded_);
+  histogram_tester_.ExpectUniqueSample(
+      kResponseMetricsName, ReportResponseMetricsStatus::kSuccess, 1);
   ::testing::Mock::VerifyAndClearExpectations(&client_);
 }
 
@@ -110,6 +116,8 @@
                                 ReportUploader::kPersistentError);
   RunNextTask();
   EXPECT_TRUE(has_responded_);
+  histogram_tester_.ExpectUniqueSample(
+      kResponseMetricsName, ReportResponseMetricsStatus::kOtherError, 1);
   ::testing::Mock::VerifyAndClearExpectations(&client_);
 }
 
@@ -124,6 +132,9 @@
                                 ReportUploader::kSuccess);
   RunNextTask();
   EXPECT_TRUE(has_responded_);
+  histogram_tester_.ExpectUniqueSample(
+      kResponseMetricsName, ReportResponseMetricsStatus::kRequestTooLargeError,
+      2);
   ::testing::Mock::VerifyAndClearExpectations(&client_);
 }
 
@@ -143,6 +154,12 @@
   RunNextTask();
   EXPECT_TRUE(has_responded_);
   ::testing::Mock::VerifyAndClearExpectations(&client_);
+  histogram_tester_.ExpectTotalCount(kResponseMetricsName, 2);
+  histogram_tester_.ExpectBucketCount(kResponseMetricsName,
+                                      ReportResponseMetricsStatus::kSuccess, 1);
+  histogram_tester_.ExpectBucketCount(
+      kResponseMetricsName, ReportResponseMetricsStatus::kTemporaryServerError,
+      1);
 }
 
 TEST_F(ReportUploaderTest, RetryAndFailedWithPersistentError) {
@@ -155,6 +172,10 @@
                                 ReportUploader::kPersistentError);
   RunNextTask();
 
+  histogram_tester_.ExpectUniqueSample(
+      kResponseMetricsName, ReportResponseMetricsStatus::kTemporaryServerError,
+      1);
+
   // No response, request is retried.
   EXPECT_FALSE(has_responded_);
   // Error is changed.
@@ -162,6 +183,9 @@
   RunNextTask();
   EXPECT_TRUE(has_responded_);
   ::testing::Mock::VerifyAndClearExpectations(&client_);
+  histogram_tester_.ExpectTotalCount(kResponseMetricsName, 2);
+  histogram_tester_.ExpectBucketCount(
+      kResponseMetricsName, ReportResponseMetricsStatus::kOtherError, 1);
 }
 
 TEST_F(ReportUploaderTest, RetryAndFailedWithTransientError) {
@@ -174,10 +198,17 @@
                                 ReportUploader::kTransientError);
   RunNextTask();
 
+  histogram_tester_.ExpectUniqueSample(
+      kResponseMetricsName, ReportResponseMetricsStatus::kTemporaryServerError,
+      1);
+
   // No response, request is retried.
   EXPECT_FALSE(has_responded_);
   RunNextTask();
   EXPECT_TRUE(has_responded_);
+  histogram_tester_.ExpectUniqueSample(
+      kResponseMetricsName, ReportResponseMetricsStatus::kTemporaryServerError,
+      2);
   ::testing::Mock::VerifyAndClearExpectations(&client_);
 }
 
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index 542abde..23337da2 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -94,17 +94,6 @@
 }
 
 #if defined(OS_CHROMEOS)
-Profile* GetPrimaryProfile() {
-  // Obtains the primary profile.
-  if (!user_manager::UserManager::IsInitialized())
-    return nullptr;
-  const user_manager::User* primary_user =
-      user_manager::UserManager::Get()->GetPrimaryUser();
-  if (!primary_user)
-    return nullptr;
-  return chromeos::ProfileHelper::Get()->GetProfileByUser(primary_user);
-}
-
 bool ShouldUseBuiltinCertVerifier(Profile* profile) {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(chromeos::switches::kForceCertVerifierBuiltin))
@@ -115,41 +104,12 @@
     return true;
   }
 
-  // TODO(https://crbug.com/982936): Instead of evaluating the primary profile
-  // prefs explicitly, register the pref with LocalState and evaluate the pref
-  // there. Note that currently that is not possible because the LocalState
-  // prefs may not reflect the primary profile policy state at the time this
-  // function is executed.
-
-  // Explicitly check if |profile| is the primary profile. GetPrimaryProfile
-  // only returns non-null pretty late in the primary profile initialization
-  // stage, and the NetworkContext is created before that.
-  Profile* primary_profile = nullptr;
-  if (chromeos::ProfileHelper::Get()->IsPrimaryProfile(profile))
-    primary_profile = profile;
-
-  if (!primary_profile)
-    primary_profile = GetPrimaryProfile();
-  if (!primary_profile) {
-    NOTREACHED();
-    return base::FeatureList::IsEnabled(
-        net::features::kCertVerifierBuiltinFeature);
-  }
-
-  // The policy evaluated through the pref is not updatable dynamically, so
-  // assert that the pref system is already initialized at the time this is
-  // called.
-  DCHECK_NE(PrefService::INITIALIZATION_STATUS_WAITING,
-            primary_profile->GetPrefs()->GetInitializationStatus());
   const PrefService::Preference* builtin_cert_verifier_enabled_pref =
-      primary_profile->GetPrefs()->FindPreference(
+      g_browser_process->local_state()->FindPreference(
           prefs::kBuiltinCertificateVerifierEnabled);
   if (builtin_cert_verifier_enabled_pref->IsManaged())
     return builtin_cert_verifier_enabled_pref->GetValue()->GetBool();
 
-  // TODO(https://crbug.com/939344): Also evaluate whether there are
-  // extension-specific certificates to be used, and if yes, enable the built-in
-  // cert verifier.
   return base::FeatureList::IsEnabled(
       net::features::kCertVerifierBuiltinFeature);
 }
@@ -274,18 +234,18 @@
 void ProfileNetworkContextService::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterBooleanPref(prefs::kQuicAllowed, true);
-#if defined(OS_CHROMEOS)
-  // Note that the default value is not relevant because the pref is only
-  // evaluated when it is managed.
-  registry->RegisterBooleanPref(prefs::kBuiltinCertificateVerifierEnabled,
-                                false);
-#endif
 }
 
 // static
 void ProfileNetworkContextService::RegisterLocalStatePrefs(
     PrefRegistrySimple* registry) {
   registry->RegisterListPref(prefs::kHSTSPolicyBypassList);
+#if defined(OS_CHROMEOS)
+  // Note that the default value is not relevant because the pref is only
+  // evaluated when it is managed.
+  registry->RegisterBooleanPref(prefs::kBuiltinCertificateVerifierEnabled,
+                                false);
+#endif
 }
 
 void ProfileNetworkContextService::DisableQuicIfNotAllowed() {
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 6ce972a..2804343 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -300,9 +300,12 @@
         "var oldSendScriptingMessage = "
         "    PDFViewer.prototype.sendScriptingMessage_;"
         "PDFViewer.prototype.sendScriptingMessage_ = function(message) {"
-        "  oldSendScriptingMessage.bind(this)(message);"
-        "  if (message.type == 'getSelectedTextReply')"
-        "    this.parentWindow_.postMessage('flush', '*');"
+        "  try {"
+        "    oldSendScriptingMessage.bind(this)(message);"
+        "  } finally {"
+        "    if (message.type == 'getSelectedTextReply')"
+        "      this.parentWindow_.postMessage('flush', '*');"
+        "  }"
         "}"));
 
     // Add an event listener for flush messages and request the selected text.
@@ -756,6 +759,12 @@
                            true);
 }
 
+// TODO(crbug.com/1004425): Should be allowed?
+IN_PROC_BROWSER_TEST_F(PDFExtensionTest, EnsureOpaqueOriginRepliesBlocked) {
+  TestGetSelectedTextReply(
+      embedded_test_server()->GetURL("/pdf/data_url_rectangles.html"), false);
+}
+
 // Ensure that the PDF component extension cannot be loaded directly.
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, BlockDirectAccess) {
   WebContents* web_contents = GetActiveWebContents();
diff --git a/chrome/browser/resources/local_ntp/customize.js b/chrome/browser/resources/local_ntp/customize.js
index 6d530d04..c5ccf952 100644
--- a/chrome/browser/resources/local_ntp/customize.js
+++ b/chrome/browser/resources/local_ntp/customize.js
@@ -1520,7 +1520,6 @@
 customize.init = function(showErrorNotification, hideCustomLinkNotification) {
   ntpApiHandle = window.chrome.embeddedSearch.newTabPage;
   const editDialog = $(customize.IDS.EDIT_BG_DIALOG);
-  const menu = $(customize.IDS.MENU);
 
   $(customize.IDS.OPTIONS_TITLE).textContent =
       configData.translatedStrings.customizeThisPage;
@@ -1555,6 +1554,8 @@
     }
   };
   $(customize.IDS.EDIT_BG).onclick = function(event) {
+    $(customize.IDS.CUSTOMIZATION_MENU)
+        .classList.add(customize.CLASSES.MOUSE_NAV);
     editDialog.classList.add(customize.CLASSES.MOUSE_NAV);
     editBackgroundInteraction();
   };
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index cf1641a..ef4c5ba 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -754,7 +754,13 @@
   font-size: 12px;
   line-height: 32px;
   margin-bottom: 0;
-  max-width: 505px;
+  /* TODO(crbug.com/969062): this magic constant would be better implemented as
+   * real multi-line promo text support or a better QA process to check that
+   * promo messages aren't ellided. It's ultimately quite hard to make any pixel
+   * value here useful as font face, sizes, and zoom can all vary. Pushing for
+   * a more dynamic UI or better qualification process has been met with
+   * significant resistance, so we keep arbitrarily changing this value. */
+  max-width: 537px;
   overflow: hidden;
   padding: 0 16px;
   pointer-events: all;
@@ -1085,6 +1091,7 @@
   }
 }
 
+#customization-menu.using-mouse-nav,
 .using-mouse-nav .bg-sel-tile:focus {
   outline: none;
 }
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html
index 74d602e6..078fc8f 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -19,43 +19,24 @@
 <dom-module id="settings-a11y-page">
   <template>
     <style include="settings-shared"></style>
-    <template is="dom-if" if="[[showCaptionSettings_]]">
-      <cr-link-row class="hr" id="captions" label="$i18n{captionsTitle}"
-          on-click="onCaptionsClick_">
-      </cr-link-row>
-    </template>
     <settings-animated-pages id="pages" current-route="{{currentRoute}}"
         section="a11y" focus-config="[[focusConfig_]]">
-<if expr="not chromeos">
       <div route-path="default">
-      <settings-toggle-button
-          id="a11yImageLabels"
-          hidden$="[[!showAccessibilityLabelsSetting_]]"
-          pref="{{prefs.settings.a11y.enable_accessibility_image_labels}}"
-          on-change="onToggleAccessibilityImageLabels_"
-          label="$i18n{accessibleImageLabelsTitle}"
-          sub-label="$i18n{accessibleImageLabelsSubtitle}">
-      </settings-toggle-button>
-      <cr-link-row class="hr" label="$i18n{moreFeaturesLink}"
-          on-click="onMoreFeaturesLinkClick_" sub-label="$i18n{a11yWebStore}"
-          external>
-      </cr-link-row>
-      </div>
-</if>
-<if expr="chromeos or is_linux or is_win">
-      <template is="dom-if" if="[[showCaptionSettings_]]">
-        <template is="dom-if" route-path="/captions">
-          <settings-subpage
-              associated-control="[[$$('#captions')]]"
-              page-title="$i18n{captionsTitle}">
-            <settings-captions prefs="{{prefs}}"></settings-captions>
-          </settings-subpage>
-        </template>
-      </template>
+        <cr-link-row class="hr" id="captions" label="$i18n{captionsTitle}"
+            on-click="onCaptionsClick_" external$="[[captionSettingsOpensExternally_]]">
+        </cr-link-row>
+<if expr="not chromeos">
+        <settings-toggle-button
+            id="a11yImageLabels"
+            hidden$="[[!showAccessibilityLabelsSetting_]]"
+            pref="{{prefs.settings.a11y.enable_accessibility_image_labels}}"
+            on-change="onToggleAccessibilityImageLabels_"
+            label="$i18n{accessibleImageLabelsTitle}"
+            sub-label="$i18n{accessibleImageLabelsSubtitle}">
+        </settings-toggle-button>
 </if>
 <if expr="chromeos">
-      <template is="dom-if" if="[[pageVisibility.webstoreLink]]">
-        <div route-path="default">
+        <template is="dom-if" if="[[pageVisibility.webstoreLink]]">
           <settings-toggle-button
               id="a11yImageLabels"
               hidden$="[[!showAccessibilityLabelsSetting_]]"
@@ -73,7 +54,26 @@
               on-click="onManageAccessibilityFeaturesTap_"
               sub-label="$i18n{moreFeaturesLinkDescription}">
           </cr-link-row>
-        </div>
+        </template>
+</if>
+        <cr-link-row class="hr" label="$i18n{moreFeaturesLink}"
+            on-click="onMoreFeaturesLinkClick_" sub-label="$i18n{a11yWebStore}"
+            hidden="[[pageVisibility.webstoreLink]]" external>
+        </cr-link-row>
+      </div>
+<if expr="not is_macosx">
+      <template is="dom-if" if="[[showCaptionSettings_]]">
+        <template is="dom-if" route-path="/captions">
+          <settings-subpage
+              associated-control="[[$$('#captions')]]"
+              page-title="$i18n{captionsTitle}">
+            <settings-captions prefs="{{prefs}}"></settings-captions>
+          </settings-subpage>
+        </template>
+      </template>
+</if>
+<if expr="chromeos">
+      <template is="dom-if" if="[[pageVisibility.webstoreLink]]">
         <template is="dom-if" route-path="/manageAccessibility">
           <settings-subpage
               associated-control="[[$$('#subpage-trigger')]]"
@@ -86,8 +86,7 @@
           <settings-subpage
               associated-control="[[$$('#subpage-trigger')]]"
               page-title="$i18n{manageTtsSettings}">
-            <settings-tts-subpage prefs="{{prefs}}">
-            </settings-tts-subpage>
+            <settings-tts-subpage prefs="{{prefs}}"></settings-tts-subpage>
           </settings-subpage>
         </template>
         <template is="dom-if" route-path="/manageAccessibility/switchAccess">
@@ -100,12 +99,6 @@
       </template>
 </if>
     </settings-animated-pages>
-<if expr="chromeos">
-    <cr-link-row class="hr" label="$i18n{moreFeaturesLink}"
-        on-click="onMoreFeaturesLinkClick_" sub-label="$i18n{a11yWebStore}"
-        hidden="[[pageVisibility.webstoreLink]]" external>
-    </cr-link-row>
-</if>
   </template>
   <script src="a11y_page.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.js b/chrome/browser/resources/settings/a11y_page/a11y_page.js
index 6629ff7..3104568 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.js
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.js
@@ -67,6 +67,26 @@
       },
     },
 
+    /**
+     * Whether the caption settings link opens externally.
+     * @private {boolean}
+     */
+    captionSettingsOpensExternally_: {
+      type: Boolean,
+      value: function() {
+        let opensExternally = false;
+        // <if expr="is_macosx">
+        opensExternally = true;
+        // </if>
+
+        // <if expr="is_win">
+        opensExternally = loadTimeData.getBoolean('isWindows10OrNewer');
+        // </if>
+
+        return opensExternally;
+      },
+    },
+
     // <if expr="chromeos">
     /**
      * Whether to show experimental accessibility features.
diff --git a/chrome/browser/resources/settings/search_settings.js b/chrome/browser/resources/settings/search_settings.js
index 98d7979..a98f501 100644
--- a/chrome/browser/resources/settings/search_settings.js
+++ b/chrome/browser/resources/settings/search_settings.js
@@ -153,7 +153,7 @@
       parent = parent.nodeType == Node.DOCUMENT_FRAGMENT_NODE ?
           parent.host :
           parent.parentNode;
-      if (parent.nodeName == 'SETTINGS-SUBPAGE') {
+      if (parent && parent.nodeName == 'SETTINGS-SUBPAGE') {
         // TODO(dpapad): Cast to SettingsSubpageElement here.
         associatedControl = assert(
             parent.associatedControl,
diff --git a/chrome/browser/ui/android/widget/BUILD.gn b/chrome/browser/ui/android/widget/BUILD.gn
index d9997707..da3befb 100644
--- a/chrome/browser/ui/android/widget/BUILD.gn
+++ b/chrome/browser/ui/android/widget/BUILD.gn
@@ -15,6 +15,16 @@
     "java/src/org/chromium/chrome/browser/ui/widget/FadingEdgeScrollView.java",
     "java/src/org/chromium/chrome/browser/ui/widget/ListMenuButton.java",
     "java/src/org/chromium/chrome/browser/ui/widget/LoadingView.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/TintedDrawable.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/animation/AnimatorProperties.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/animation/CancelAwareAnimatorListener.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/animation/FocusAnimator.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/displaystyle/DisplayStyleObserver.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/displaystyle/DisplayStyleObserverAdapter.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/displaystyle/HorizontalDisplayStyle.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/displaystyle/UiConfig.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/displaystyle/VerticalDisplayStyle.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/displaystyle/ViewResizer.java",
     "java/src/org/chromium/chrome/browser/ui/widget/text/AccessibleTextView.java",
     "java/src/org/chromium/chrome/browser/ui/widget/text/AlertDialogEditText.java",
     "java/src/org/chromium/chrome/browser/ui/widget/text/TextViewWithCompoundDrawables.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/TintedDrawable.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/TintedDrawable.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/widget/TintedDrawable.java
rename to chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/TintedDrawable.java
index fb60e32f..16d811e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/TintedDrawable.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/TintedDrawable.java
@@ -2,7 +2,7 @@
 // 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.widget;
+package org.chromium.chrome.browser.ui.widget;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -12,8 +12,6 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.support.v7.content.res.AppCompatResources;
 
-import org.chromium.chrome.R;
-
 /**
  * Implementation of BitmapDrawable that allows to tint the color of the drawable for all
  * bitmap drawable states.
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/AnimatorProperties.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/AnimatorProperties.java
new file mode 100644
index 0000000..33ef12b
--- /dev/null
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/AnimatorProperties.java
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ui.widget.animation;
+
+import android.graphics.drawable.Drawable;
+import android.util.Property;
+
+/**
+ * Holds different {@link Property} types that can be used with ObjectAnimators.
+ */
+public class AnimatorProperties {
+    public static final Property<Drawable, Integer> DRAWABLE_ALPHA_PROPERTY =
+            new Property<Drawable, Integer>(Integer.class, "alpha") {
+                @Override
+                public Integer get(Drawable d) {
+                    // getAlpha() is only exposed on drawable in API 19+, so we rely on animations
+                    // always setting the starting and ending values instead of relying on this
+                    // property.
+                    return 0;
+                }
+
+                @Override
+                public void set(Drawable d, Integer alpha) {
+                    d.setAlpha(alpha);
+                }
+            };
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/animation/CancelAwareAnimatorListener.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/CancelAwareAnimatorListener.java
similarity index 89%
rename from chrome/android/java/src/org/chromium/chrome/browser/widget/animation/CancelAwareAnimatorListener.java
rename to chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/CancelAwareAnimatorListener.java
index 76ef1171..d8039dda 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/animation/CancelAwareAnimatorListener.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/CancelAwareAnimatorListener.java
@@ -2,7 +2,7 @@
 // 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.widget.animation;
+package org.chromium.chrome.browser.ui.widget.animation;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -41,16 +41,16 @@
     /**
      * Notifies the start of the animator.
      */
-    public void onStart(Animator animator) { }
+    public void onStart(Animator animator) {}
 
     /**
      * Notifies that the animator was cancelled.
      */
-    public void onCancel(Animator animator) { }
+    public void onCancel(Animator animator) {}
 
     /**
      * Notifies that the animator has finished running. This method will not be called if the
      * animator is canclled.
      */
-    public void onEnd(Animator animator) { }
+    public void onEnd(Animator animator) {}
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/animation/FocusAnimator.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/FocusAnimator.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/widget/animation/FocusAnimator.java
rename to chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/FocusAnimator.java
index deb5b17..27b4a1de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/animation/FocusAnimator.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/FocusAnimator.java
@@ -2,7 +2,7 @@
 // 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.widget.animation;
+package org.chromium.chrome.browser.ui.widget.animation;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserver.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/DisplayStyleObserver.java
similarity index 74%
rename from chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserver.java
rename to chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/DisplayStyleObserver.java
index a58b93633..6fdaa91 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserver.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/DisplayStyleObserver.java
@@ -2,14 +2,14 @@
 // 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.widget.displaystyle;
+package org.chromium.chrome.browser.ui.widget.displaystyle;
 
 /**
  * Gets notified of changes in the display style.
  *
  * @see UiConfig.DisplayStyle
  * @see UiConfig#getCurrentDisplayStyle()
- * @see org.chromium.chrome.browser.widget.displaystyle.DisplayStyleObserverAdapter
+ * @see DisplayStyleObserverAdapter
  */
 public interface DisplayStyleObserver {
     void onDisplayStyleChanged(UiConfig.DisplayStyle newDisplayStyle);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserverAdapter.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/DisplayStyleObserverAdapter.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserverAdapter.java
rename to chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/DisplayStyleObserverAdapter.java
index 343bf3db..1ea95501 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserverAdapter.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/DisplayStyleObserverAdapter.java
@@ -2,7 +2,7 @@
 // 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.widget.displaystyle;
+package org.chromium.chrome.browser.ui.widget.displaystyle;
 
 import android.view.View;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/HorizontalDisplayStyle.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/HorizontalDisplayStyle.java
similarity index 90%
rename from chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/HorizontalDisplayStyle.java
rename to chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/HorizontalDisplayStyle.java
index 57e1acc..a0c45cd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/HorizontalDisplayStyle.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/HorizontalDisplayStyle.java
@@ -2,7 +2,7 @@
 // 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.widget.displaystyle;
+package org.chromium.chrome.browser.ui.widget.displaystyle;
 
 import androidx.annotation.IntDef;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/OWNERS b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/OWNERS
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/OWNERS
rename to chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/OWNERS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/UiConfig.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/UiConfig.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/UiConfig.java
rename to chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/UiConfig.java
index 834e485..b524c8c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/UiConfig.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/UiConfig.java
@@ -2,7 +2,7 @@
 // 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.widget.displaystyle;
+package org.chromium.chrome.browser.ui.widget.displaystyle;
 
 import android.content.Context;
 import android.view.View;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/VerticalDisplayStyle.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/VerticalDisplayStyle.java
similarity index 89%
rename from chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/VerticalDisplayStyle.java
rename to chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/VerticalDisplayStyle.java
index 32db1266..645998b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/VerticalDisplayStyle.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/VerticalDisplayStyle.java
@@ -2,7 +2,7 @@
 // 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.widget.displaystyle;
+package org.chromium.chrome.browser.ui.widget.displaystyle;
 
 import androidx.annotation.IntDef;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/ViewResizer.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/ViewResizer.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/ViewResizer.java
rename to chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/ViewResizer.java
index 749d3e44..77f25ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/ViewResizer.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/displaystyle/ViewResizer.java
@@ -2,7 +2,7 @@
 // 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.widget.displaystyle;
+package org.chromium.chrome.browser.ui.widget.displaystyle;
 
 import android.content.res.Resources;
 import android.support.v4.view.ViewCompat;
diff --git a/chrome/browser/ui/app_list/search/mixer.cc b/chrome/browser/ui/app_list/search/mixer.cc
index e15bf9f..011e089b 100644
--- a/chrome/browser/ui/app_list/search/mixer.cc
+++ b/chrome/browser/ui/app_list/search/mixer.cc
@@ -29,7 +29,14 @@
     : result(result), score(score) {}
 
 bool Mixer::SortData::operator<(const SortData& other) const {
-  // This data precedes (less than) |other| if it has higher score.
+  // This data precedes (less than) |other| if it has specified display index or
+  // higher score.
+  ash::SearchResultDisplayIndex index1 = result->display_index();
+  ash::SearchResultDisplayIndex index2 = other.result->display_index();
+  // The |kUndefined| index is larger than other specified indexes.
+  if (index1 != index2)
+    return index1 < index2;
+
   return score > other.score;
 }
 
diff --git a/chrome/browser/ui/app_list/search/tests/mixer_unittest.cc b/chrome/browser/ui/app_list/search/tests/mixer_unittest.cc
index 8a3e04bd..01df87f 100644
--- a/chrome/browser/ui/app_list/search/tests/mixer_unittest.cc
+++ b/chrome/browser/ui/app_list/search/tests/mixer_unittest.cc
@@ -77,6 +77,7 @@
         count_(0),
         bad_relevance_range_(false),
         small_relevance_range_(false),
+        last_result_has_display_index_(false),
         display_type_(ash::SearchResultDisplayType::kList),
         result_type_(result_type) {}
   ~TestSearchProvider() override {}
@@ -99,6 +100,10 @@
       TestSearchResult* result = new TestSearchResult(id, relevance);
       result->SetDisplayType(display_type_);
       result->SetResultType(result_type_);
+
+      if (last_result_has_display_index_ && i == count_ - 1)
+        result->SetDisplayIndex(ash::SearchResultDisplayIndex::kFirstIndex);
+
       Add(std::unique_ptr<ChromeSearchResult>(result));
     }
   }
@@ -110,12 +115,16 @@
   void set_count(size_t count) { count_ = count; }
   void set_bad_relevance_range() { bad_relevance_range_ = true; }
   void set_small_relevance_range() { small_relevance_range_ = true; }
+  void set_last_result_has_display_index() {
+    last_result_has_display_index_ = true;
+  }
 
  private:
   std::string prefix_;
   size_t count_;
   bool bad_relevance_range_;
   bool small_relevance_range_;
+  bool last_result_has_display_index_;
   ChromeSearchResult::DisplayType display_type_;
   ResultType result_type_;
 
@@ -237,8 +246,7 @@
       {2, 10, 2,
        "app0,omnibox0,app1,omnibox1,omnibox2,omnibox3,playstore0,playstore1"},
       {2, 0, 2, "app0,app1,playstore0,playstore1"},
-      {0, 0, 0, ""},
-  };
+      {0, 0, 0, ""}};
 
   for (size_t i = 0; i < base::size(kTestCases); ++i) {
     app_provider()->set_count(kTestCases[i].app_results);
@@ -250,6 +258,34 @@
   }
 }
 
+// Tests that results with display index defined, will be shown in the final
+// results.
+TEST_F(MixerTest, ResultsWithDisplayIndex) {
+  CreateMixer();
+
+  // If the last result has no display index defined, it will not shown in the
+  // final results.
+  app_provider()->set_count(6);
+  omnibox_provider()->set_count(6);
+  playstore_provider()->set_count(6);
+  RunQuery();
+
+  EXPECT_EQ(
+      "app0,omnibox0,app1,omnibox1,app2,omnibox2,app3,omnibox3,playstore0,"
+      "playstore1",
+      GetResults());
+
+  // If the last result has display index defined, it will be in the final
+  // results.
+  app_provider()->set_last_result_has_display_index();
+  RunQuery();
+
+  EXPECT_EQ(
+      "app5,app0,omnibox0,app1,omnibox1,app2,omnibox2,omnibox3,playstore0,"
+      "playstore1",
+      GetResults());
+}
+
 TEST_F(MixerTest, RemoveDuplicates) {
   CreateMixer();
 
diff --git a/chrome/browser/ui/ash/screen_rotation_interactive_uitest.cc b/chrome/browser/ui/ash/screen_rotation_interactive_uitest.cc
index 0f276415..399b8e4 100644
--- a/chrome/browser/ui/ash/screen_rotation_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/screen_rotation_interactive_uitest.cc
@@ -146,7 +146,7 @@
   waiter.Wait();
 }
 
-IN_PROC_BROWSER_TEST_P(ScreenRotationTest, RotateInTableOverview) {
+IN_PROC_BROWSER_TEST_P(ScreenRotationTest, RotateInTabletOverview) {
   // Browser window is used just to identify display.
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
   gfx::NativeWindow browser_window =
@@ -176,8 +176,8 @@
   waiter.Wait();
 }
 
-// TODO(oshma): Support split screen in tablet mode.
-// TODO(oshma): Support overview mode.
+// TODO(oshima): Support split screen in tablet mode.
+// TODO(oshima): Support overview mode.
 
 INSTANTIATE_TEST_SUITE_P(,
                          ScreenRotationTest,
diff --git a/chrome/browser/ui/tabs/tab_utils.cc b/chrome/browser/ui/tabs/tab_utils.cc
index e34276d9..a4aee330 100644
--- a/chrome/browser/ui/tabs/tab_utils.cc
+++ b/chrome/browser/ui/tabs/tab_utils.cc
@@ -15,9 +15,11 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/usb/usb_tab_helper.h"
 #include "chrome/browser/vr/vr_tab_helper.h"
+#include "chrome/grit/generated_resources.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/url_constants.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace chrome {
 
@@ -77,6 +79,44 @@
   return TabAlertState::NONE;
 }
 
+base::string16 GetTabAlertStateText(const TabAlertState alert_state) {
+  switch (alert_state) {
+    case TabAlertState::AUDIO_PLAYING:
+      return l10n_util::GetStringUTF16(
+          IDS_TOOLTIP_TAB_ALERT_STATE_AUDIO_PLAYING);
+    case TabAlertState::AUDIO_MUTING:
+      return l10n_util::GetStringUTF16(
+          IDS_TOOLTIP_TAB_ALERT_STATE_AUDIO_MUTING);
+    case TabAlertState::MEDIA_RECORDING:
+      return l10n_util::GetStringUTF16(
+          IDS_TOOLTIP_TAB_ALERT_STATE_MEDIA_RECORDING);
+    case TabAlertState::TAB_CAPTURING:
+      return l10n_util::GetStringUTF16(
+          IDS_TOOLTIP_TAB_ALERT_STATE_TAB_CAPTURING);
+    case TabAlertState::BLUETOOTH_CONNECTED:
+      return l10n_util::GetStringUTF16(
+          IDS_TOOLTIP_TAB_ALERT_STATE_BLUETOOTH_CONNECTED);
+    case TabAlertState::USB_CONNECTED:
+      return l10n_util::GetStringUTF16(
+          IDS_TOOLTIP_TAB_ALERT_STATE_USB_CONNECTED);
+    case TabAlertState::SERIAL_CONNECTED:
+      return l10n_util::GetStringUTF16(
+          IDS_TOOLTIP_TAB_ALERT_STATE_SERIAL_CONNECTED);
+    case TabAlertState::PIP_PLAYING:
+      return l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ALERT_STATE_PIP_PLAYING);
+    case TabAlertState::DESKTOP_CAPTURING:
+      return l10n_util::GetStringUTF16(
+          IDS_TOOLTIP_TAB_ALERT_STATE_DESKTOP_CAPTURING);
+    case TabAlertState::VR_PRESENTING_IN_HEADSET:
+      return l10n_util::GetStringUTF16(
+          IDS_TOOLTIP_TAB_ALERT_STATE_VR_PRESENTING);
+    case TabAlertState::NONE:
+      return base::string16();
+  }
+  NOTREACHED();
+  return base::string16();
+}
+
 bool CanToggleAudioMute(content::WebContents* contents) {
   switch (chrome::GetTabAlertStateForContents(contents)) {
     case TabAlertState::NONE:
diff --git a/chrome/browser/ui/tabs/tab_utils.h b/chrome/browser/ui/tabs/tab_utils.h
index e9ff577..c4316dd 100644
--- a/chrome/browser/ui/tabs/tab_utils.h
+++ b/chrome/browser/ui/tabs/tab_utils.h
@@ -61,6 +61,9 @@
 // relevant to user privacy concerns is selected.
 TabAlertState GetTabAlertStateForContents(content::WebContents* contents);
 
+// Returns a localized string describing the |alert_state|.
+base::string16 GetTabAlertStateText(const TabAlertState alert_state);
+
 // Returns true if audio mute can be activated/deactivated for the given
 // |contents|.
 bool CanToggleAudioMute(content::WebContents* contents);
diff --git a/chrome/browser/ui/views/passwords/credentials_item_view.cc b/chrome/browser/ui/views/passwords/credentials_item_view.cc
index 15201ce..2f7ca7c 100644
--- a/chrome/browser/ui/views/passwords/credentials_item_view.cc
+++ b/chrome/browser/ui/views/passwords/credentials_item_view.cc
@@ -62,17 +62,15 @@
     const base::string16& lower_text,
     SkColor hover_color,
     const autofill::PasswordForm* form,
-    network::mojom::URLLoaderFactory* loader_factory)
-    : Button(button_listener),
-      form_(form),
-      upper_label_(nullptr),
-      lower_label_(nullptr),
-      info_icon_(nullptr),
-      hover_color_(hover_color) {
+    network::mojom::URLLoaderFactory* loader_factory,
+    int upper_text_style,
+    int lower_text_style)
+    : Button(button_listener), form_(form), hover_color_(hover_color) {
   set_notify_enter_exit_on_child(true);
   // Create an image-view for the avatar. Make sure it ignores events so that
   // the parent can receive the events instead.
-  image_view_ = new CircularImageView;
+  auto image_view = std::make_unique<CircularImageView>();
+  image_view_ = image_view.get();
   image_view_->set_can_process_events_within_subtree(false);
   gfx::Image image = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
       IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE);
@@ -85,31 +83,30 @@
         form_->icon_url, weak_ptr_factory_.GetWeakPtr());
     fetcher->Start(loader_factory);
   }
-  AddChildView(image_view_);
+  AddChildView(std::move(image_view));
 
   // TODO(tapted): Check these (and the STYLE_ values below) against the spec on
   // http://crbug.com/651681.
   const int kLabelContext = CONTEXT_BODY_TEXT_SMALL;
 
   if (!upper_text.empty()) {
-    upper_label_ = new views::Label(upper_text, kLabelContext,
-                                    views::style::STYLE_PRIMARY);
-    upper_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    AddChildView(upper_label_);
+    auto upper_label = std::make_unique<views::Label>(upper_text, kLabelContext,
+                                                      upper_text_style);
+    upper_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    upper_label_ = AddChildView(std::move(upper_label));
   }
 
   if (!lower_text.empty()) {
-    lower_label_ = new views::Label(lower_text, kLabelContext,
-                                    views::style::STYLE_SECONDARY);
-    lower_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    lower_label_->SetMultiLine(true);
-    AddChildView(lower_label_);
+    auto lower_label = std::make_unique<views::Label>(lower_text, kLabelContext,
+                                                      lower_text_style);
+    lower_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    lower_label->SetMultiLine(true);
+    lower_label_ = AddChildView(std::move(lower_label));
   }
 
   if (form_->is_public_suffix_match) {
-    info_icon_ = new views::TooltipIcon(
-        base::UTF8ToUTF16(form_->origin.GetOrigin().spec()));
-    AddChildView(info_icon_);
+    info_icon_ = AddChildView(std::make_unique<views::TooltipIcon>(
+        base::UTF8ToUTF16(form_->origin.GetOrigin().spec())));
   }
 
   if (!upper_text.empty() && !lower_text.empty())
diff --git a/chrome/browser/ui/views/passwords/credentials_item_view.h b/chrome/browser/ui/views/passwords/credentials_item_view.h
index b95bc6a..c7dbde1d 100644
--- a/chrome/browser/ui/views/passwords/credentials_item_view.h
+++ b/chrome/browser/ui/views/passwords/credentials_item_view.h
@@ -9,6 +9,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/passwords/account_avatar_fetcher.h"
 #include "ui/views/controls/button/button.h"
+#include "ui/views/style/typography.h"
 
 namespace autofill {
 struct PasswordForm;
@@ -39,7 +40,9 @@
                       const base::string16& lower_text,
                       SkColor hover_color,
                       const autofill::PasswordForm* form,
-                      network::mojom::URLLoaderFactory* loader_factory);
+                      network::mojom::URLLoaderFactory* loader_factory,
+                      int upper_text_style = views::style::STYLE_PRIMARY,
+                      int lower_text_style = views::style::STYLE_SECONDARY);
   ~CredentialsItemView() override;
 
   const autofill::PasswordForm* form() const { return form_; }
@@ -62,9 +65,9 @@
   const autofill::PasswordForm* form_;
 
   views::ImageView* image_view_;
-  views::Label* upper_label_;
-  views::Label* lower_label_;
-  views::ImageView* info_icon_;
+  views::Label* upper_label_ = nullptr;
+  views::Label* lower_label_ = nullptr;
+  views::ImageView* info_icon_ = nullptr;
 
   SkColor hover_color_;
 
diff --git a/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc b/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
index 3d915c8..64cb729 100644
--- a/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
+++ b/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/passwords/password_dialog_prompts.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/passwords/credentials_item_view.h"
 #include "chrome/grit/generated_resources.h"
@@ -44,7 +45,8 @@
       form.username_value, kButtonHoverColor, &form,
       content::BrowserContext::GetDefaultStoragePartition(model()->GetProfile())
           ->GetURLLoaderFactoryForBrowserProcess()
-          .get());
+          .get(),
+      STYLE_HINT, views::style::STYLE_PRIMARY);
   credential->SetEnabled(false);
   AddChildView(credential);
 
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index 0d1eaf52..78edbd1d 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -742,6 +742,8 @@
 }
 
 void Tab::AlertStateChanged() {
+  if (controller_->HoverCardIsShowingForTab(this))
+    controller_->UpdateHoverCard(this);
   Layout();
 }
 
@@ -821,51 +823,7 @@
   base::string16 result = title;
   if (!result.empty())
     result.append(1, '\n');
-  switch (alert_state) {
-    case TabAlertState::AUDIO_PLAYING:
-      result.append(
-          l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ALERT_STATE_AUDIO_PLAYING));
-      break;
-    case TabAlertState::AUDIO_MUTING:
-      result.append(
-          l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ALERT_STATE_AUDIO_MUTING));
-      break;
-    case TabAlertState::MEDIA_RECORDING:
-      result.append(l10n_util::GetStringUTF16(
-          IDS_TOOLTIP_TAB_ALERT_STATE_MEDIA_RECORDING));
-      break;
-    case TabAlertState::TAB_CAPTURING:
-      result.append(
-          l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ALERT_STATE_TAB_CAPTURING));
-      break;
-    case TabAlertState::BLUETOOTH_CONNECTED:
-      result.append(l10n_util::GetStringUTF16(
-          IDS_TOOLTIP_TAB_ALERT_STATE_BLUETOOTH_CONNECTED));
-      break;
-    case TabAlertState::USB_CONNECTED:
-      result.append(
-          l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ALERT_STATE_USB_CONNECTED));
-      break;
-    case TabAlertState::SERIAL_CONNECTED:
-      result.append(l10n_util::GetStringUTF16(
-          IDS_TOOLTIP_TAB_ALERT_STATE_SERIAL_CONNECTED));
-      break;
-    case TabAlertState::PIP_PLAYING:
-      result.append(
-          l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ALERT_STATE_PIP_PLAYING));
-      break;
-    case TabAlertState::DESKTOP_CAPTURING:
-      result.append(l10n_util::GetStringUTF16(
-          IDS_TOOLTIP_TAB_ALERT_STATE_DESKTOP_CAPTURING));
-      break;
-    case TabAlertState::VR_PRESENTING_IN_HEADSET:
-      result.append(
-          l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ALERT_STATE_VR_PRESENTING));
-      break;
-    case TabAlertState::NONE:
-      NOTREACHED();
-      break;
-  }
+  result.append(chrome::GetTabAlertStateText(alert_state));
   return result;
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index cf1f435..de6448d 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -317,6 +317,13 @@
                                 /* adjust_height_for_width */ true));
   AddChildView(title_label_);
 
+  alert_state_label_ = new views::Label(
+      base::string16(), CONTEXT_BODY_TEXT_LARGE, views::style::STYLE_SECONDARY);
+  alert_state_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  alert_state_label_->SetMultiLine(false);
+  alert_state_label_->SetVisible(false);
+  AddChildView(alert_state_label_);
+
   domain_label_ = new views::Label(
       base::string16(), CONTEXT_BODY_TEXT_LARGE, views::style::STYLE_SECONDARY,
       gfx::DirectionalityMode::DIRECTIONALITY_AS_URL);
@@ -353,6 +360,9 @@
                             views::FlexSpecification::ForSizeRule(
                                 views::MinimumFlexSizeRule::kScaleToMinimum,
                                 views::MaximumFlexSizeRule::kPreferred));
+  alert_state_label_->SetProperty(views::kMarginsKey,
+                                  gfx::Insets(kLineSpacing, kVerticalMargin,
+                                              kLineSpacing, kVerticalMargin));
   domain_label_->SetProperty(views::kMarginsKey,
                              gfx::Insets(kLineSpacing, kVerticalMargin,
                                          kHorizontalMargin, kVerticalMargin));
@@ -543,6 +553,7 @@
 
 void TabHoverCardBubbleView::UpdateCardContent(const Tab* tab) {
   base::string16 title;
+  TabAlertState alert_state;
   GURL domain_url;
   // Use committed URL to determine if no page has yet loaded, since the title
   // can be blank for some web pages.
@@ -551,9 +562,11 @@
     title = tab->data().IsCrashed()
                 ? l10n_util::GetStringUTF16(IDS_HOVER_CARD_CRASHED_TITLE)
                 : l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE);
+    alert_state = TabAlertState::NONE;
   } else {
     domain_url = tab->data().last_committed_url;
     title = tab->data().title;
+    alert_state = tab->data().alert_state;
   }
   base::string16 domain;
   if (domain_url.SchemeIsFile()) {
@@ -572,6 +585,14 @@
         net::UnescapeRule::NORMAL, nullptr, nullptr, nullptr);
   }
   title_label_->SetText(title);
+  // If there is no alert state do not show the label.
+  if (alert_state == TabAlertState::NONE) {
+    alert_state_label_->SetText(base::string16());
+    alert_state_label_->SetVisible(false);
+  } else {
+    alert_state_label_->SetText(chrome::GetTabAlertStateText(alert_state));
+    alert_state_label_->SetVisible(true);
+  }
   domain_label_->SetText(domain);
 
   // If the preview image feature is not enabled, |preview_image_| will be null.
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h
index eaa40d8..e43d4cf 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h
@@ -11,6 +11,7 @@
 #include "base/scoped_observer.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "chrome/browser/ui/tabs/tab_utils.h"
 #include "chrome/browser/ui/thumbnails/thumbnail_image.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 
@@ -72,7 +73,7 @@
 
   void FadeInToShow();
 
-  // Updates and formats title, domain, and preview image.
+  // Updates and formats title, alert state, domain, and preview image.
   void UpdateCardContent(const Tab* tab);
 
   void RegisterToThumbnailImageUpdates(
@@ -107,6 +108,7 @@
 
   views::Widget* widget_ = nullptr;
   views::Label* title_label_ = nullptr;
+  views::Label* alert_state_label_ = nullptr;
   views::Label* domain_label_ = nullptr;
   views::ImageView* preview_image_ = nullptr;
 
diff --git a/chrome/browser/ui/webui/devtools_ui_data_source.cc b/chrome/browser/ui/webui/devtools_ui_data_source.cc
index c29ccd0..95e73c5 100644
--- a/chrome/browser/ui/webui/devtools_ui_data_source.cc
+++ b/chrome/browser/ui/webui/devtools_ui_data_source.cc
@@ -57,9 +57,6 @@
   } else if (base::EndsWith(filename, ".js",
                             base::CompareCase::INSENSITIVE_ASCII)) {
     return "application/javascript";
-  } else if (base::EndsWith(filename, ".mjs",
-                            base::CompareCase::INSENSITIVE_ASCII)) {
-    return "application/javascript";
   } else if (base::EndsWith(filename, ".png",
                             base::CompareCase::INSENSITIVE_ASCII)) {
     return "image/png";
diff --git a/chrome/browser/web_applications/components/BUILD.gn b/chrome/browser/web_applications/components/BUILD.gn
index 3c3060d2..2ba3ece 100644
--- a/chrome/browser/web_applications/components/BUILD.gn
+++ b/chrome/browser/web_applications/components/BUILD.gn
@@ -37,6 +37,8 @@
     "web_app_constants.h",
     "web_app_data_retriever.cc",
     "web_app_data_retriever.h",
+    "web_app_file_extension_registration.h",
+    "web_app_file_extension_registration_win.cc",
     "web_app_helpers.cc",
     "web_app_helpers.h",
     "web_app_icon_generator.cc",
@@ -102,6 +104,7 @@
   testonly = true
 
   sources = [
+    "file_handler_manager_unittest.cc",
     "pending_app_manager_unittest.cc",
     "web_app_data_retriever_unittest.cc",
     "web_app_helpers_unittest.cc",
diff --git a/chrome/browser/web_applications/components/file_handler_manager.cc b/chrome/browser/web_applications/components/file_handler_manager.cc
index 5ab054b..c05ac3e 100644
--- a/chrome/browser/web_applications/components/file_handler_manager.cc
+++ b/chrome/browser/web_applications/components/file_handler_manager.cc
@@ -4,6 +4,11 @@
 
 #include "chrome/browser/web_applications/components/file_handler_manager.h"
 
+#include "base/feature_list.h"
+#include "build/build_config.h"
+#include "chrome/browser/web_applications/components/web_app_file_extension_registration.h"
+#include "third_party/blink/public/common/features.h"
+
 namespace web_app {
 
 FileHandlerManager::FileHandlerManager(Profile* profile)
@@ -17,17 +22,39 @@
 }
 
 void FileHandlerManager::OnWebAppInstalled(const AppId& installed_app_id) {
-  // TODO(davidbienvenu): Hook up with file association registration code on
-  // windows.
+#if defined(OS_WIN)
+  if (!base::FeatureList::IsEnabled(blink::features::kFileHandlingAPI))
+    return;
+  std::string app_name = registrar_->GetAppShortName(installed_app_id);
+  const std::vector<apps::FileHandlerInfo>* file_handlers =
+      GetFileHandlers(installed_app_id);
+  if (!file_handlers)
+    return;
+  std::set<std::string> file_extensions =
+      GetFileExtensionsFromFileHandlers(*file_handlers);
+  RegisterFileHandlersForWebApp(installed_app_id, app_name, *profile_,
+                                file_extensions);
+#endif  // OS_WIN
 }
 
 void FileHandlerManager::OnWebAppUninstalled(const AppId& installed_app_id) {
-  // TODO(davidbienvenu): Hook up to file association de-registration code on
-  // windows.
+#if defined(OS_WIN)
+  UnregisterFileHandlersForWebApp(installed_app_id, *profile_);
+#endif  // OS_WIN
 }
 
 void FileHandlerManager::OnAppRegistrarDestroyed() {
   registrar_observer_.RemoveAll();
 }
 
+std::set<std::string> GetFileExtensionsFromFileHandlers(
+    const std::vector<apps::FileHandlerInfo>& file_handlers) {
+  std::set<std::string> file_extensions;
+  for (const auto& file_handler : file_handlers) {
+    for (const auto& file_ext : file_handler.extensions)
+      file_extensions.insert(file_ext);
+  }
+  return file_extensions;
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/file_handler_manager.h b/chrome/browser/web_applications/components/file_handler_manager.h
index 91a8b22c..a88aee9 100644
--- a/chrome/browser/web_applications/components/file_handler_manager.h
+++ b/chrome/browser/web_applications/components/file_handler_manager.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_FILE_HANDLER_MANAGER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_FILE_HANDLER_MANAGER_H_
 
+#include <set>
+#include <string>
 #include <vector>
 
 #include "base/macros.h"
@@ -49,6 +51,10 @@
   DISALLOW_COPY_AND_ASSIGN(FileHandlerManager);
 };
 
+// Compute the set of file extensions specified in |file_handlers|.
+std::set<std::string> GetFileExtensionsFromFileHandlers(
+    const std::vector<apps::FileHandlerInfo>& file_handlers);
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_FILE_HANDLER_MANAGER_H_
diff --git a/chrome/browser/web_applications/components/file_handler_manager_unittest.cc b/chrome/browser/web_applications/components/file_handler_manager_unittest.cc
new file mode 100644
index 0000000..ee368406
--- /dev/null
+++ b/chrome/browser/web_applications/components/file_handler_manager_unittest.cc
@@ -0,0 +1,35 @@
+// Copyright 2019 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/web_applications/components/file_handler_manager.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "chrome/browser/web_applications/test/test_file_handler_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace web_app {
+
+TEST(FileHandlerUtilsTest, GetFileExtensionsFromFileHandlers) {
+  // Construct FileHandlerInfo vector with multiple file extensions.
+  const std::vector<std::string> test_file_extensions = {"txt", "xls", "doc"};
+  apps::FileHandlerInfo fhi1;
+  apps::FileHandlerInfo fhi2;
+  fhi1.extensions.insert(test_file_extensions[0]);
+  fhi1.extensions.insert(test_file_extensions[1]);
+  fhi2.extensions.insert(test_file_extensions[2]);
+  std::vector<apps::FileHandlerInfo> file_handlers = {fhi1, fhi2};
+  std::set<std::string> file_extensions =
+      GetFileExtensionsFromFileHandlers(file_handlers);
+
+  EXPECT_EQ(file_extensions.size(), test_file_extensions.size());
+  for (const auto& test_file_extension : test_file_extensions) {
+    EXPECT_TRUE(file_extensions.find(test_file_extension) !=
+                file_extensions.end());
+  }
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/components/web_app_file_extension_registration.h b/chrome/browser/web_applications/components/web_app_file_extension_registration.h
new file mode 100644
index 0000000..f214279
--- /dev/null
+++ b/chrome/browser/web_applications/components/web_app_file_extension_registration.h
@@ -0,0 +1,33 @@
+// Copyright 2019 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_WEB_APPLICATIONS_COMPONENTS_WEB_APP_FILE_EXTENSION_REGISTRATION_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_FILE_EXTENSION_REGISTRATION_H_
+
+#include <set>
+#include <string>
+
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+
+class Profile;
+
+namespace web_app {
+
+// Do OS-specific registration to handle opening files with the specified
+// |file_extensions| with the PWA with the specified |app_id|.
+// This may also involve creating a shim app to launch Chrome from.
+void RegisterFileHandlersForWebApp(
+    const AppId& app_id,
+    const std::string& app_name,
+    const Profile& profile,
+    const std::set<std::string>& file_extensions);
+
+// Undo the file extensions registration for the PWA with specified |app_id|.
+// If a shim app was required, also removes the shim app.
+void UnregisterFileHandlersForWebApp(const AppId& app_id,
+                                     const Profile& profile);
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_FILE_EXTENSION_REGISTRATION_H_
diff --git a/chrome/browser/web_applications/components/web_app_file_extension_registration_win.cc b/chrome/browser/web_applications/components/web_app_file_extension_registration_win.cc
new file mode 100644
index 0000000..82120fd
--- /dev/null
+++ b/chrome/browser/web_applications/components/web_app_file_extension_registration_win.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 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/web_applications/components/web_app_file_extension_registration.h"
+
+#include "chrome/browser/profiles/profile.h"
+
+namespace web_app {
+
+void RegisterFileHandlersForWebApp(
+    const AppId& app_id,
+    const std::string& app_name,
+    const Profile& profile,
+    const std::set<std::string>& file_extensions) {
+  // TODO(davidbienvenu): Setup shim app and windows registry for this |app_id|.
+}
+
+void UnregisterFileHandlersForWebApp(const AppId& app_id,
+                                     const Profile& profile) {
+  // TODO(davidbienvenu): Cleanup windows registry entries for this
+  // |app_id|.
+}
+
+}  // namespace web_app
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/NewTabPageControllerTest.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/NewTabPageControllerTest.java
index 15f065f..b13bfdf 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/NewTabPageControllerTest.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/NewTabPageControllerTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SmallTest;
 
 import org.junit.Assert;
@@ -56,6 +57,7 @@
         assertNotEquals(isHidden, mController.areArticlesHidden());
     }
 
+    @LargeTest
     @Test
     public void testScrollPage() {
         mController.scrollToTop();
diff --git a/chrome/test/base/browser_tests_main_chromeos.cc b/chrome/test/base/browser_tests_main_chromeos.cc
index 5426703..55106b29 100644
--- a/chrome/test/base/browser_tests_main_chromeos.cc
+++ b/chrome/test/base/browser_tests_main_chromeos.cc
@@ -30,10 +30,8 @@
  public:
   int RunTestSuite(int argc, char** argv) override {
     BrowserTestSuiteChromeOS test_suite(argc, argv);
-    // Browser tests are expected not to tear-down various globals and may
-    // complete with the thread priority being above NORMAL.
+    // Browser tests are expected not to tear-down various globals.
     test_suite.DisableCheckForLeakedGlobals();
-    test_suite.DisableCheckForThreadPriorityAtTestEnd();
     return test_suite.Run();
   }
 };
diff --git a/chrome/test/base/chrome_test_launcher.cc b/chrome/test/base/chrome_test_launcher.cc
index 76fdb406..ad45e43b 100644
--- a/chrome/test/base/chrome_test_launcher.cc
+++ b/chrome/test/base/chrome_test_launcher.cc
@@ -76,10 +76,8 @@
 
 int ChromeTestSuiteRunner::RunTestSuite(int argc, char** argv) {
   ChromeTestSuite test_suite(argc, argv);
-  // Browser tests are expected not to tear-down various globals and may
-  // complete with the thread priority being above NORMAL.
+  // Browser tests are expected not to tear-down various globals.
   test_suite.DisableCheckForLeakedGlobals();
-  test_suite.DisableCheckForThreadPriorityAtTestEnd();
 #if defined(OS_ANDROID)
   // Android browser tests run child processes as threads instead.
   content::ContentTestSuiteBase::RegisterInProcessThreads();
diff --git a/chrome/test/base/interactive_ui_tests_main.cc b/chrome/test/base/interactive_ui_tests_main.cc
index abc8ab2..051d4b3 100644
--- a/chrome/test/base/interactive_ui_tests_main.cc
+++ b/chrome/test/base/interactive_ui_tests_main.cc
@@ -41,10 +41,8 @@
  protected:
   // ChromeTestSuite overrides:
   void Initialize() override {
-    // Browser tests are expected not to tear-down various globals and may
-    // complete with the thread priority being above NORMAL.
+    // Browser tests are expected not to tear-down various globals.
     base::TestSuite::DisableCheckForLeakedGlobals();
-    base::TestSuite::DisableCheckForThreadPriorityAtTestEnd();
 
     ChromeTestSuite::Initialize();
 
diff --git a/chrome/test/data/pdf/data_url_rectangles.html b/chrome/test/data/pdf/data_url_rectangles.html
new file mode 100644
index 0000000..9510906
--- /dev/null
+++ b/chrome/test/data/pdf/data_url_rectangles.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>data: of //pdf/test/data/rectangles.pdf</title>
+<embed type="application/pdf" width="640" height="480"
+src="data:application/pdf;base64,
+JVBERi0xLjcKJaDypPQKMSAwIG9iaiA8PAogIC9UeXBlIC9DYXRhbG9nCiAgL1BhZ2VzIDIgMCBS
+Cj4+CmVuZG9iagoyIDAgb2JqIDw8CiAgL1R5cGUgL1BhZ2VzCiAgL01lZGlhQm94IFsgMCAwIDIw
+MCAzMDAgXQogIC9Db3VudCAxCiAgL0tpZHMgWyAzIDAgUiBdCj4+CmVuZG9iagozIDAgb2JqIDw8
+CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAgL0NvbnRlbnRzIDQgMCBSCj4+CmVuZG9i
+ago0IDAgb2JqIDw8Cj4+CnN0cmVhbQpxCjAgMCAwIHJnCjAgMjkwIDEwIDEwIHJlIEIqCjEwIDE1
+MCA1MCAzMCByZSBCKgowIDAgMSByZwoxOTAgMjkwIDEwIDEwIHJlIEIqCjcwIDIzMiA1MCAzMCBy
+ZSBCKgowIDEgMCByZwoxOTAgMCAxMCAxMCByZSBCKgoxMzAgMTUwIDUwIDMwIHJlIEIqCjEgMCAw
+IHJnCjAgMCAxMCAxMCByZSBCKgo3MCA2NyA1MCAzMCByZSBCKgpRCmVuZHN0cmVhbQplbmRvYmoK
+eHJlZgowIDUKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE1IDAwMDAwIG4gCjAwMDAwMDAw
+NjggMDAwMDAgbiAKMDAwMDAwMDE2MSAwMDAwMCBuIAowMDAwMDAwMjMwIDAwMDAwIG4gCnRyYWls
+ZXI8PCAvUm9vdCAxIDAgUiAvU2l6ZSA1ID4+CnN0YXJ0eHJlZgo0NTYKJSVFT0YK">
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 470d7e29..12f6da1 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -911,7 +911,9 @@
     "os": ["chromeos"],
     "test_policy": { "BuiltinCertificateVerifierEnabled": true },
     "pref_mappings": [
-      { "pref": "builtin_certificate_verifier_enabled" }
+      { "pref": "builtin_certificate_verifier_enabled",
+        "local_state": true
+      }
     ]
   },
 
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 0489240..880e0e4d 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -10,17 +10,16 @@
 // Polymer BrowserTest fixture.
 GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
+// Only run in release builds because we frequently see test timeouts in debug.
+// We suspect this is because the settings page loads slowly in debug.
+// https://crbug.com/1003483
+GEN('#if defined(NDEBUG)');
+
 GEN('#include "ash/public/cpp/ash_features.h"');
 GEN('#include "build/branding_buildflags.h"');
 GEN('#include "chrome/common/chrome_features.h"');
 GEN('#include "chromeos/constants/chromeos_features.h"');
 
-GEN('#if !defined(NDEBUG)');
-GEN('#define MAYBE_AllJsTests DISABLED_AllJsTests');
-GEN('#else');
-GEN('#define MAYBE_AllJsTests AllJsTests');
-GEN('#endif');
-
 // Generic test fixture for CrOS Polymer Settings elements to be overridden by
 // individual element tests.
 const OSSettingsBrowserTest = class extends PolymerTest {
@@ -95,7 +94,7 @@
   }
 };
 
-TEST_F('OSSettingsAddUsersTest', 'MAYBE_AllJsTests', () => {
+TEST_F('OSSettingsAddUsersTest', 'AllJsTests', () => {
   mocha.run();
 });
 
@@ -111,9 +110,6 @@
   }
 };
 
-// Times out on debug builders because the Settings page can take several
-// seconds to load in a Release build and several times that in a Debug build.
-// See https://crbug.com/558434.
 // Disabled due to flakiness on linux-chromeos-rel. https://crbug.com/992116
 TEST_F('OSSettingsAdvancedPageBrowserTest', 'DISABLED_AllJsTests', () => {
   // Run all registered tests.
@@ -192,8 +188,7 @@
   }
 };
 
-// Flaky in debug. https://crbug.com/1003483
-TEST_F('OSSettingsAppManagementPageTest', 'MAYBE_AllJsTests', () => {
+TEST_F('OSSettingsAppManagementPageTest', 'AllJsTests', () => {
   mocha.run();
 });
 
@@ -260,8 +255,7 @@
   }
 };
 
-// Flaky in debug. https://crbug.com/1003483
-TEST_F('OSSettingsAppManagementManagedAppTest', 'MAYBE_AllJsTests', () => {
+TEST_F('OSSettingsAppManagementManagedAppTest', 'AllJsTests', () => {
   mocha.run();
 });
 
@@ -548,7 +542,7 @@
   }
 };
 
-TEST_F('OSSettingsMainTest', 'MAYBE_AllJsTests', () => {
+TEST_F('OSSettingsMainTest', 'AllJsTests', () => {
   mocha.run();
 });
 
@@ -564,7 +558,7 @@
   }
 };
 
-TEST_F('OSSettingsMenuTest', 'MAYBE_AllJsTests', () => {
+TEST_F('OSSettingsMenuTest', 'AllJsTests', () => {
   mocha.run();
 });
 
@@ -855,8 +849,7 @@
   }
 };
 
-// Flaky in debug. https://crbug.com/1003483
-TEST_F('OSSettingsPersonalizationPageTest', 'MAYBE_AllJsTests', () => {
+TEST_F('OSSettingsPersonalizationPageTest', 'AllJsTests', () => {
   mocha.run();
 });
 
@@ -1012,8 +1005,7 @@
   }
 };
 
-// Flaky in debug. https://crbug.com/1003483
-TEST_F('OSSettingsResetPageTest', 'MAYBE_AllJsTests', () => {
+TEST_F('OSSettingsResetPageTest', 'AllJsTests', () => {
   mocha.run();
 });
 
@@ -1035,8 +1027,7 @@
   }
 };
 
-// Settings tests are flaky on debug. See https://crbug.com/968608.
-TEST_F('OSSettingsSearchPageTest', 'MAYBE_AllJsTests', () => {
+TEST_F('OSSettingsSearchPageTest', 'AllJsTests', () => {
   mocha.run();
 });
 
@@ -1058,7 +1049,8 @@
   }
 };
 
-// Settings tests are flaky on debug. See https://crbug.com/968608.
-TEST_F('OSSettingsSmbPageTest', 'MAYBE_AllJsTests', () => {
+TEST_F('OSSettingsSmbPageTest', 'AllJsTests', () => {
   mocha.run();
 });
+
+GEN('#endif  // defined(NDEBUG)');
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
index 1326a35d3..10fd8d8 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
@@ -7,6 +7,11 @@
 
 GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
+// Only run in release builds because we frequently see test timeouts in debug.
+// We suspect this is because the settings page loads slowly in debug.
+// https://crbug.com/1003483
+GEN('#if defined(NDEBUG)');
+
 GEN('#include "chromeos/constants/chromeos_features.h"');
 
 // Test fixture for the top-level OS settings UI.
@@ -29,15 +34,7 @@
   }
 };
 
-// Timeouts in debug because the page can be slow to load.
-// https://crbug.com/987512
-GEN('#if !defined(NDEBUG)');
-GEN('#define MAYBE_AllJsTests DISABLED_AllJsTests');
-GEN('#else');
-GEN('#define MAYBE_AllJsTests AllJsTests');
-GEN('#endif');
-
-TEST_F('OSSettingsUIBrowserTest', 'MAYBE_AllJsTests', () => {
+TEST_F('OSSettingsUIBrowserTest', 'AllJsTests', () => {
   suite('os-settings-ui', () => {
     let ui;
 
@@ -223,3 +220,5 @@
 
   mocha.run();
 });
+
+GEN('#endif  // defined(NDEBUG)');
diff --git a/chrome/test/data/webui/settings/search_settings_test.js b/chrome/test/data/webui/settings/search_settings_test.js
index 63067ea..971b59b 100644
--- a/chrome/test/data/webui/settings/search_settings_test.js
+++ b/chrome/test/data/webui/settings/search_settings_test.js
@@ -249,5 +249,24 @@
         });
       });
     });
+
+    test('match text outside of a settings section', async function() {
+      document.body.innerHTML = `
+          <div id="mydiv">Match</div>
+          <settings-section></settings-section>`;
+
+      const section = document.querySelector('settings-section');
+      const mydiv = document.querySelector('#mydiv');
+
+      await searchManager.search('Match', document.body);
+      assertTrue(section.hiddenBySearch);
+
+      const highlight = mydiv.querySelector('.search-highlight-wrapper');
+      assertTrue(!!highlight);
+
+      const searchHits = highlight.querySelectorAll('.search-highlight-hit');
+      assertEquals(1, searchHits.length);
+      assertEquals('Match', searchHits[0].textContent);
+    });
   });
 });
diff --git a/chromecast/app/cast_test_launcher.cc b/chromecast/app/cast_test_launcher.cc
index e9719c0..34693e3 100644
--- a/chromecast/app/cast_test_launcher.cc
+++ b/chromecast/app/cast_test_launcher.cc
@@ -23,10 +23,8 @@
 
   int RunTestSuite(int argc, char** argv) override {
     base::TestSuite test_suite(argc, argv);
-    // Browser tests are expected not to tear-down various globals and may
-    // complete with the thread priority being above NORMAL.
+    // Browser tests are expected not to tear-down various globals.
     test_suite.DisableCheckForLeakedGlobals();
-    test_suite.DisableCheckForThreadPriorityAtTestEnd();
     return test_suite.Run();
   }
 
diff --git a/chromeos/dbus/fake_cicerone_client.cc b/chromeos/dbus/fake_cicerone_client.cc
index 0608e6f..456a415b 100644
--- a/chromeos/dbus/fake_cicerone_client.cc
+++ b/chromeos/dbus/fake_cicerone_client.cc
@@ -201,30 +201,28 @@
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(std::move(callback), start_lxd_container_response_));
-  if (request.async()) {
-    // Trigger CiceroneClient::Observer::NotifyLxdContainerStartingSignal.
-    vm_tools::cicerone::LxdContainerStartingSignal signal;
+
+  // Trigger CiceroneClient::Observer::NotifyLxdContainerStartingSignal.
+  vm_tools::cicerone::LxdContainerStartingSignal signal;
+  signal.set_owner_id(request.owner_id());
+  signal.set_vm_name(request.vm_name());
+  signal.set_container_name(request.container_name());
+  signal.set_status(lxd_container_starting_signal_status_);
+  signal.mutable_os_release()->CopyFrom(lxd_container_os_release_);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&FakeCiceroneClient::NotifyLxdContainerStarting,
+                                base::Unretained(this), std::move(signal)));
+
+  if (send_container_started_signal_) {
+    // Trigger CiceroneClient::Observer::NotifyContainerStartedSignal.
+    vm_tools::cicerone::ContainerStartedSignal signal;
     signal.set_owner_id(request.owner_id());
     signal.set_vm_name(request.vm_name());
     signal.set_container_name(request.container_name());
-    signal.set_status(lxd_container_starting_signal_status_);
-    signal.mutable_os_release()->CopyFrom(lxd_container_os_release_);
+    signal.set_container_username(last_container_username_);
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&FakeCiceroneClient::NotifyLxdContainerStarting,
-                       base::Unretained(this), std::move(signal)));
-
-    if (send_container_started_signal_) {
-      // Trigger CiceroneClient::Observer::NotifyContainerStartedSignal.
-      vm_tools::cicerone::ContainerStartedSignal signal;
-      signal.set_owner_id(request.owner_id());
-      signal.set_vm_name(request.vm_name());
-      signal.set_container_name(request.container_name());
-      signal.set_container_username(last_container_username_);
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(&FakeCiceroneClient::NotifyContainerStarted,
-                                    base::Unretained(this), std::move(signal)));
-    }
+        FROM_HERE, base::BindOnce(&FakeCiceroneClient::NotifyContainerStarted,
+                                  base::Unretained(this), std::move(signal)));
   }
 }
 
diff --git a/chromeos/profiles/OWNERS b/chromeos/profiles/OWNERS
index 0d73e4e..d9a84e7 100644
--- a/chromeos/profiles/OWNERS
+++ b/chromeos/profiles/OWNERS
@@ -1 +1,6 @@
-per-file orderfile.newest.txt=*
+# Chrome OS Toolchain Team
+gbiv@chromium.org
+tcwang@chromium.org
+
+# For skia autoroller to submmit CLs
+chromium-autoroll@skia-public.iam.gserviceaccount.com
diff --git a/chromeos/profiles/airmont.afdo.newest.txt b/chromeos/profiles/airmont.afdo.newest.txt
new file mode 100644
index 0000000..705bd87
--- /dev/null
+++ b/chromeos/profiles/airmont.afdo.newest.txt
@@ -0,0 +1 @@
+chromeos-chrome-amd64-airmont-79-3900.0-1568629889-benchmark-78.0.3902.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/broadwell.afdo.newest.txt b/chromeos/profiles/broadwell.afdo.newest.txt
new file mode 100644
index 0000000..97d8aa23
--- /dev/null
+++ b/chromeos/profiles/broadwell.afdo.newest.txt
@@ -0,0 +1 @@
+chromeos-chrome-amd64-broadwell-79-3865.63-1568626808-benchmark-78.0.3902.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/silvermont.afdo.newest.txt b/chromeos/profiles/silvermont.afdo.newest.txt
new file mode 100644
index 0000000..92d2fcdc
--- /dev/null
+++ b/chromeos/profiles/silvermont.afdo.newest.txt
@@ -0,0 +1 @@
+chromeos-chrome-amd64-silvermont-79-3903.0-1568628088-benchmark-78.0.3902.0-r1-redacted.afdo.xz
diff --git a/chromeos/services/cros_healthd/public/cpp/service_connection.cc b/chromeos/services/cros_healthd/public/cpp/service_connection.cc
index e3fd1d6..bc0b2f23 100644
--- a/chromeos/services/cros_healthd/public/cpp/service_connection.cc
+++ b/chromeos/services/cros_healthd/public/cpp/service_connection.cc
@@ -27,9 +27,9 @@
 
  private:
   // ServiceConnection overrides:
-  void ProbeNonRemovableBlockDeviceInfo(
-      mojom::CrosHealthdService::ProbeNonRemovableBlockDeviceInfoCallback
-          callback) override;
+  void ProbeTelemetryInfo(
+      const std::vector<mojom::ProbeCategoryEnum>& categories_to_test,
+      mojom::CrosHealthdService::ProbeTelemetryInfoCallback callback) override;
 
   // Binds the top level interface |cros_healthd_service_| to an
   // implementation in the cros_healthd daemon, if it is not already bound. The
@@ -50,12 +50,13 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceConnectionImpl);
 };
 
-void ServiceConnectionImpl::ProbeNonRemovableBlockDeviceInfo(
-    mojom::CrosHealthdService::ProbeNonRemovableBlockDeviceInfoCallback
-        callback) {
+void ServiceConnectionImpl::ProbeTelemetryInfo(
+    const std::vector<mojom::ProbeCategoryEnum>& categories_to_test,
+    mojom::CrosHealthdService::ProbeTelemetryInfoCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   BindCrosHealthdServiceIfNeeded();
-  cros_healthd_service_->ProbeNonRemovableBlockDeviceInfo(std::move(callback));
+  cros_healthd_service_->ProbeTelemetryInfo(categories_to_test,
+                                            std::move(callback));
 }
 
 void ServiceConnectionImpl::BindCrosHealthdServiceIfNeeded() {
diff --git a/chromeos/services/cros_healthd/public/cpp/service_connection.h b/chromeos/services/cros_healthd/public/cpp/service_connection.h
index d91f05f..c545d91f 100644
--- a/chromeos/services/cros_healthd/public/cpp/service_connection.h
+++ b/chromeos/services/cros_healthd/public/cpp/service_connection.h
@@ -17,10 +17,12 @@
  public:
   static ServiceConnection* GetInstance();
 
-  // Gather various info on non-removeable block devices.
-  virtual void ProbeNonRemovableBlockDeviceInfo(
-      mojom::CrosHealthdService::ProbeNonRemovableBlockDeviceInfoCallback
-          callback) = 0;
+  // Gather pieces of information about the platform. See
+  // src/chromeos/service/cros_healthd/public/mojom/cros_healthd.mojom for
+  // details.
+  virtual void ProbeTelemetryInfo(
+      const std::vector<mojom::ProbeCategoryEnum>& categories_to_test,
+      mojom::CrosHealthdService::ProbeTelemetryInfoCallback callback) = 0;
 
  protected:
   ServiceConnection() = default;
diff --git a/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc b/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
index ea43f33ea..f644899c 100644
--- a/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
+++ b/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "chromeos/dbus/cros_healthd/cros_healthd_client.h"
@@ -25,7 +26,7 @@
 namespace cros_healthd {
 namespace {
 
-std::vector<mojom::NonRemovableBlockDeviceInfoPtr>
+base::Optional<std::vector<mojom::NonRemovableBlockDeviceInfoPtr>>
 MakeNonRemovableBlockDeviceInfo() {
   std::vector<mojom::NonRemovableBlockDeviceInfoPtr> info;
   info.push_back(mojom::NonRemovableBlockDeviceInfo::New(
@@ -34,7 +35,28 @@
   info.push_back(mojom::NonRemovableBlockDeviceInfo::New(
       "test_path2", 124 /* size */, "test_type2", 11 /* manfid */, "test_name2",
       767 /* serial */));
-  return info;
+  return base::Optional<std::vector<mojom::NonRemovableBlockDeviceInfoPtr>>(
+      std::move(info));
+}
+
+mojom::BatteryInfoPtr MakeBatteryInfo() {
+  return mojom::BatteryInfo::New(
+      2 /* cycle_count */, 12.9 /* voltage_now */,
+      "battery_vendor" /* vendor */, "serial_number" /* serial_number */,
+      5.275 /* charge_full_design */, 5.292 /* charge_full */,
+      11.55 /* voltage_min_design */, 51785890 /* manufacture_date_smart */);
+}
+
+mojom::CachedVpdInfoPtr MakeCachedVpdInfo() {
+  return mojom::CachedVpdInfo::New("fake_sku_number" /* sku_number */);
+}
+
+mojom::TelemetryInfoPtr MakeTelemetryInfo() {
+  return mojom::TelemetryInfo::New(
+      MakeBatteryInfo() /* battery_info */,
+      MakeNonRemovableBlockDeviceInfo() /* block_device_info */,
+      MakeCachedVpdInfo() /* vpd_info */
+  );
 }
 
 class MockCrosHealthdService : public mojom::CrosHealthdService {
@@ -43,8 +65,10 @@
     return receiver_.BindNewPipeAndPassRemote();
   }
 
-  MOCK_METHOD1(ProbeNonRemovableBlockDeviceInfo,
-               void(ProbeNonRemovableBlockDeviceInfoCallback callback));
+  MOCK_METHOD2(
+      ProbeTelemetryInfo,
+      void(const std::vector<mojom::ProbeCategoryEnum>& categories_to_test,
+           ProbeTelemetryInfoCallback callback));
 
  private:
   mojo::Receiver<mojom::CrosHealthdService> receiver_{this};
@@ -70,19 +94,22 @@
   DISALLOW_COPY_AND_ASSIGN(CrosHealthdServiceConnectionTest);
 };
 
-TEST_F(CrosHealthdServiceConnectionTest, ProbeNonRemovableBlockDeviceInfo) {
-  EXPECT_CALL(*mock_service(), ProbeNonRemovableBlockDeviceInfo(_))
-      .WillOnce(WithArgs<0>(Invoke(
-          [](mojom::CrosHealthdService::ProbeNonRemovableBlockDeviceInfoCallback
-                 callback) {
-            std::move(callback).Run(MakeNonRemovableBlockDeviceInfo());
+TEST_F(CrosHealthdServiceConnectionTest, ProbeTelemetryInfo) {
+  EXPECT_CALL(*mock_service(), ProbeTelemetryInfo(_, _))
+      .WillOnce(WithArgs<1>(Invoke(
+          [](mojom::CrosHealthdService::ProbeTelemetryInfoCallback callback) {
+            std::move(callback).Run(MakeTelemetryInfo());
           })));
+  const std::vector<mojom::ProbeCategoryEnum> categories_to_test = {
+      mojom::ProbeCategoryEnum::kBattery,
+      mojom::ProbeCategoryEnum::kNonRemovableBlockDevices,
+      mojom::ProbeCategoryEnum::kCachedVpdData};
   bool callback_done = false;
-  ServiceConnection::GetInstance()->ProbeNonRemovableBlockDeviceInfo(
+  ServiceConnection::GetInstance()->ProbeTelemetryInfo(
+      categories_to_test,
       base::BindOnce(
-          [](bool* callback_done,
-             std::vector<mojom::NonRemovableBlockDeviceInfoPtr> info) {
-            EXPECT_EQ(info, MakeNonRemovableBlockDeviceInfo());
+          [](bool* callback_done, mojom::TelemetryInfoPtr info) {
+            EXPECT_EQ(info, MakeTelemetryInfo());
             *callback_done = true;
           },
           &callback_done));
diff --git a/chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom b/chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom
index d98cbe9..3d1e10b5 100644
--- a/chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom
+++ b/chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom
@@ -16,10 +16,26 @@
 interface CrosHealthdService {
   // ------------------- Start of probe definitions ---------------------
 
-  // Returns an array of all non-removable block devices and associated
-  // up-to-date info.
-  ProbeNonRemovableBlockDeviceInfo()
-      => (array<NonRemovableBlockDeviceInfo> devices);
+  // Returns telemetry information for the desired categories. This IPC is
+  // exposed so that the browser can pull data from cros_healthd. The browser
+  // will periodically call ProbeTelemetryInfo and upload the results to the EMM
+  // API in the cloud, where the data collected from cros_healthd will be made
+  // available to OEMs. Since the browser is making requests and cros_healthd is
+  // replying, we can consider cros_healthd the privileged side of this
+  // communication. ProbeTelemetryInfo only returns parsed data, so the browser
+  // will not do any parsing. cros_healthd does not process input that could be
+  // controlled by a malicious attacker.
+  //
+  // The request:
+  // * |categories| - list of each of the categories that ProbeTelemetryInfo
+  //                  should return information for.
+  //
+  // The response:
+  // * |telemetry_info| - information for each of the requested categories. Only
+  //                      the fields corresponding to the requested categories
+  //                      will be non-null.
+  ProbeTelemetryInfo(array<ProbeCategoryEnum> categories)
+      => (TelemetryInfo telemetry_info);
 
   // ------------------- End of probe definitions -----------------------
 };
diff --git a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
index 41dee83..f28c1a5f 100644
--- a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
+++ b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
@@ -10,6 +10,37 @@
 
 module chromeos.cros_healthd.mojom;
 
+// An enumeration of each category of information that cros_healthd can report.
+[Extensible]
+enum ProbeCategoryEnum {
+  kBattery,
+  kNonRemovableBlockDevices,
+  kCachedVpdData,
+};
+
+// Information related to the main battery.
+struct BatteryInfo {
+  // TODO(https://crbug.com/979245): Update "smart" cycle count.
+  int64 cycle_count;
+  // Current battery voltage (V)
+  double voltage_now;
+  // Manufacturer of the battery
+  string vendor;
+  // Serial number of the battery
+  string serial_number;
+  // Design capacity (Ah)
+  double charge_full_design;
+  // Full capacity (Ah)
+  double charge_full;
+  // Desired minimum output voltage (V)
+  double voltage_min_design;
+  // Smart Manufacture Date is defined in
+  // http://sbs-forum.org/specs/sbdat110.pdf. The value is calculated by
+  // ((year-1980) * 512 + month * 32 + day).
+  int64 manufacture_date_smart;
+};
+
+// Information related to a specific non-removable block device.
 struct NonRemovableBlockDeviceInfo {
   // The path of this storage on the system. It is useful if caller needs to
   // correlate with other information.
@@ -25,3 +56,27 @@
   // PSN: Product serial number, 32 bits
   uint32 serial;
 };
+
+// Cached VPD read from sysfs.
+struct CachedVpdInfo {
+  // Contents of /sys/firmware/vpd/ro/sku_number.
+  string sku_number;
+};
+
+// A collection of all the device's telemetry information that cros_healthd is
+// capable of reporting. Note that every field in TelemetryInfo is nullable, and
+// the response for a particular ProbeTelemetryInfo request will only contain
+// fields corresponding to the categories passed to the ProbeTelemetryInfo
+// request.
+struct TelemetryInfo {
+  // Information about the device's main battery. Only present when kBattery was
+  // included in the categories input to ProbeTelemetryInfo.
+  BatteryInfo? battery_info;
+  // Information about all of the device's non-removable block devices. Only
+  // present when kNonRemovableBlockDevices was included in the categories input
+  // to ProbeTelemetryInfo.
+  array<NonRemovableBlockDeviceInfo>? block_device_info;
+  // Only present when kCachedVpdData was included in the categories input to
+  // ProbeTelemetryInfo.
+  CachedVpdInfo? vpd_info;
+};
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index ab11f3c6..4cd28db83 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -277,7 +277,9 @@
   return;
 #else
   if (opt_in) {
-    GetOrCreateFIDOAuthenticator()->Register(std::move(creation_options));
+    GetOrCreateFIDOAuthenticator()->Register(
+        /*card_authorization_token=*/std::string(),
+        std::move(creation_options));
   } else {
     GetOrCreateFIDOAuthenticator()->OptOut();
   }
@@ -338,23 +340,43 @@
                                  response.cvc);
   can_fetch_unmask_details_.Signal();
 
-  if (!response.did_succeed)
+  if (!response.did_succeed || response.card_authorization_token.empty())
     return;
 
 #if defined(OS_ANDROID)
-  // Now that unmask flow is complete, on Android, if GetRealPan includes
-  // |creation_options|, completely hand over registration flow to
-  // CreditCardFIDOAuthenticator.
+  // Opt-in was already offered at this point for Android.
+  bool should_offer_fido_auth = false;
+#elif !defined(OS_IOS)
+  bool should_offer_fido_auth = unmask_details_.offer_fido_opt_in;
+#endif
+
+#if !defined(OS_IOS)
+  // Now that unmask flow is complete and form is filled, the remaining flows
+  // will be completely handed over to CreditCardFIDOAuthenticator.
+  // If the GetRealPan response includes |creation_options| or
+  // |request_options|, that means the user showed intention to opt-in while
+  // unmasking (this can only happen on Android) and must complete the challenge
+  // before successfully opting-in. If UnmaskDetails contained
+  // |request_options|, that means the user is already opted-into FIDO auth, and
+  // this is the first time use of a new card, and must complete the challenge
+  // to successfully authorize the card. Otherwise, if on desktop and eligible,
+  // show the dialog that offers opting-in to FIDO authentication in the future.
   if (response.creation_options.has_value()) {
     DCHECK(response.creation_options->is_dict());
     GetOrCreateFIDOAuthenticator()->Register(
-        response.creation_options->Clone());
+        response.card_authorization_token, response.creation_options->Clone());
+  } else if (response.request_options.has_value()) {
+    DCHECK(response.request_options->is_dict());
+    GetOrCreateFIDOAuthenticator()->Authorize(
+        response.card_authorization_token, response.request_options->Clone());
+  } else if (should_offer_fido_auth) {
+    GetOrCreateFIDOAuthenticator()->ShowWebauthnOfferDialog(
+        response.card_authorization_token);
+  } else if (unmask_details_.fido_request_options.is_dict()) {
+    GetOrCreateFIDOAuthenticator()->Authorize(
+        response.card_authorization_token,
+        std::move(unmask_details_.fido_request_options));
   }
-#elif !defined(OS_IOS)
-  // CreditCardFIDOAuthenticator does not exist on iOS.
-  // On desktop, prompts dialog to show the authentication offer.
-  if (unmask_details_.offer_fido_opt_in)
-    GetOrCreateFIDOAuthenticator()->ShowWebauthnOfferDialog();
 #endif
 }
 
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index 51aa87d..dcd91cf 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -218,7 +218,8 @@
   // Returns true if full card request was sent from CVC auth.
   bool GetRealPanForCVCAuth(AutofillClient::PaymentsRpcResult result,
                             const std::string& real_pan,
-                            bool fido_opt_in = false) {
+                            bool fido_opt_in = false,
+                            bool follow_with_fido_auth = false) {
     payments::FullCardRequest* full_card_request =
         GetCVCAuthenticator()->full_card_request_.get();
 
@@ -227,9 +228,13 @@
 
     payments::PaymentsClient::UnmaskResponseDetails response;
 #if !defined(OS_IOS)
+    response.card_authorization_token = "dummy_card_authorization_token";
     if (fido_opt_in) {
       response.fido_creation_options = GetTestCreationOptions();
     }
+    if (follow_with_fido_auth) {
+      response.fido_request_options = GetTestRequestOptions();
+    }
 #endif
     full_card_request->OnDidGetRealPan(result,
                                        response.with_real_pan(real_pan));
@@ -298,6 +303,11 @@
     GetFIDOAuthenticator()->OnDidGetOptChangeResult(result, response);
   }
 
+  // Mocks user response for the offer dialog.
+  void AcceptWebauthnOfferDialog(bool did_accept) {
+    GetFIDOAuthenticator()->OnWebauthnOfferDialogUserResponse(did_accept);
+  }
+
   TestCreditCardFIDOAuthenticator* GetFIDOAuthenticator() {
     return static_cast<TestCreditCardFIDOAuthenticator*>(
         credit_card_access_manager_->GetOrCreateFIDOAuthenticator());
@@ -587,14 +597,10 @@
   EXPECT_TRUE(accessor_->did_succeed());
   EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number());
 }
-#endif
 
-// TODO(crbug.com/991037): Add tests for desktop separately after the
-// WebauthnOfferDelegate functions are implemented since the flows are different
-// on desktop and Android.
 #if defined(OS_ANDROID)
 // Ensures that the WebAuthn enrollment prompt is invoked after user opts in.
-TEST_F(CreditCardAccessManagerTest, FIDOEnrollmentSuccess) {
+TEST_F(CreditCardAccessManagerTest, FIDOEnrollmentSuccess_Android) {
   CreateServerCard(kTestGUID, kTestNumber);
   CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
   GetFIDOAuthenticator()->SetUserVerifiable(true);
@@ -663,7 +669,117 @@
 
   EXPECT_FALSE(GetFIDOAuthenticator()->IsUserOptedIn());
 }
-#endif
+
+// Ensures that use of new card invokes authorization flow when user is
+// opted-in.
+TEST_F(CreditCardAccessManagerTest, FIDONewCardAuthorization) {
+  CreateServerCard(kTestGUID, kTestNumber);
+  CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+  // Opt the user in, but don't include the card above.
+  std::string other_server_id = "00000000-0000-0000-0000-000000000034";
+  payments_client_->AddFidoEligibleCard(other_server_id, kCredentialId,
+                                        kGooglePaymentsRpid);
+  GetFIDOAuthenticator()->SetUserVerifiable(true);
+  SetUserOptedIn(true);
+
+  credit_card_access_manager_->PrepareToFetchCreditCard();
+  WaitForCallbacks();
+
+  credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
+  InvokeUnmaskDetailsTimeout();
+  WaitForCallbacks();
+
+  EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber,
+                                   /*fido_opt_in=*/false,
+                                   /*follow_with_fido_auth=*/true));
+
+  // Mock user response and OptChange payments call.
+  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::FOLLOWUP_AFTER_CVC_AUTH_FLOW,
+            GetFIDOAuthenticator()->current_flow());
+  TestCreditCardFIDOAuthenticator::GetAssertion(GetFIDOAuthenticator(),
+                                                /*did_succeed=*/true);
+  OptChange(AutofillClient::SUCCESS, true);
+
+  EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());
+}
+#else  // defined(OS_ANDROID)
+// Ensures that the WebAuthn enrollment prompt is invoked after user opts in. In
+// this case, the user is not yet enrolled server-side, and thus receives
+// |creation_options|.
+TEST_F(CreditCardAccessManagerTest,
+       FIDOEnrollmentSuccess_CreationOptions_Desktop) {
+  CreateServerCard(kTestGUID, kTestNumber);
+  CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+  GetFIDOAuthenticator()->SetUserVerifiable(true);
+  SetUserOptedIn(false);
+  payments_client_->AllowFidoRegistration(true);
+
+  credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
+  InvokeUnmaskDetailsTimeout();
+  WaitForCallbacks();
+
+  // Mock user and payments response.
+  AcceptWebauthnOfferDialog(/*did_accept=*/true);
+  EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber,
+                                   /*fido_opt_in=*/false));
+  WaitForCallbacks();
+
+  OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/false,
+            /*include_creation_options=*/true);
+
+  // Mock user response and OptChange payments call.
+  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
+            GetFIDOAuthenticator()->current_flow());
+  TestCreditCardFIDOAuthenticator::MakeCredential(GetFIDOAuthenticator(),
+                                                  /*did_succeed=*/true);
+  OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/true);
+
+  EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId());
+  EXPECT_EQ(kTestChallenge,
+            BytesToBase64(GetFIDOAuthenticator()->GetChallenge()));
+  EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());
+}
+
+// Ensures that the WebAuthn enrollment prompt is invoked after user opts in. In
+// this case, the user is already enrolled server-side, and thus receives
+// |request_options|.
+TEST_F(CreditCardAccessManagerTest,
+       FIDOEnrollmentSuccess_RequestOptions_Desktop) {
+  CreateServerCard(kTestGUID, kTestNumber);
+  CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+  GetFIDOAuthenticator()->SetUserVerifiable(true);
+  SetUserOptedIn(false);
+  payments_client_->AllowFidoRegistration(true);
+
+  credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
+  InvokeUnmaskDetailsTimeout();
+  WaitForCallbacks();
+
+  // Mock user and payments response.
+  AcceptWebauthnOfferDialog(/*did_accept=*/true);
+  EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber,
+                                   /*fido_opt_in=*/false));
+  WaitForCallbacks();
+
+  OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/false,
+            /*include_creation_options=*/false,
+            /*include_request_options=*/true);
+
+  // Mock user response and OptChange payments call.
+  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
+            GetFIDOAuthenticator()->current_flow());
+  TestCreditCardFIDOAuthenticator::GetAssertion(GetFIDOAuthenticator(),
+                                                /*did_succeed=*/true);
+  OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/true);
+
+  EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId());
+  EXPECT_EQ(kTestChallenge,
+            BytesToBase64(GetFIDOAuthenticator()->GetChallenge()));
+  EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());
+}
+
+#endif  // defined(OS_ANDROID)
+#endif  // !defined(OS_IOS)
 
 // Ensures that |is_authentication_in_progress_| is set correctly.
 TEST_F(CreditCardAccessManagerTest, AuthenticationInProgress) {
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
index 7a021d8..d8f1cb1 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
@@ -41,14 +41,16 @@
     const payments::FullCardRequest& full_card_request,
     const CreditCard& card,
     const base::string16& cvc) {
+  payments::PaymentsClient::UnmaskResponseDetails response =
+      full_card_request.unmask_response_details();
   requester_->OnCVCAuthenticationComplete(
       CVCAuthenticationResponse()
           .with_did_succeed(true)
           .with_card(&card)
           .with_cvc(cvc)
-          .with_creation_options(
-              std::move(full_card_request.unmask_response_details()
-                            .fido_creation_options)));
+          .with_creation_options(std::move(response.fido_creation_options))
+          .with_request_options(std::move(response.fido_request_options))
+          .with_card_authorization_token(response.card_authorization_token));
 }
 
 void CreditCardCVCAuthenticator::OnFullCardRequestFailed() {
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
index f065b6d0..a470eb8 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
@@ -47,11 +47,16 @@
       request_options = std::move(v);
       return *this;
     }
+    CVCAuthenticationResponse& with_card_authorization_token(std::string s) {
+      card_authorization_token = s;
+      return *this;
+    }
     bool did_succeed = false;
     const CreditCard* card = nullptr;
     base::string16 cvc = base::string16();
     base::Optional<base::Value> creation_options = base::nullopt;
     base::Optional<base::Value> request_options = base::nullopt;
+    std::string card_authorization_token = std::string();
   };
   class Requester {
    public:
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
index 3f9e7a7..44105f7 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
@@ -61,7 +61,9 @@
 
 CreditCardFIDOAuthenticator::~CreditCardFIDOAuthenticator() {}
 
-void CreditCardFIDOAuthenticator::ShowWebauthnOfferDialog() {
+void CreditCardFIDOAuthenticator::ShowWebauthnOfferDialog(
+    std::string card_authorization_token) {
+  card_authorization_token_ = card_authorization_token;
   autofill_client_->ShowWebauthnOfferDialog(base::BindRepeating(
       &CreditCardFIDOAuthenticator::OnWebauthnOfferDialogUserResponse,
       weak_ptr_factory_.GetWeakPtr()));
@@ -84,22 +86,39 @@
   }
 }
 
-void CreditCardFIDOAuthenticator::Register(base::Value creation_options) {
+void CreditCardFIDOAuthenticator::Register(std::string card_authorization_token,
+                                           base::Value creation_options) {
   // If |creation_options| is set, then must enroll a new credential. Otherwise
   // directly send request to payments for opting in.
+  card_authorization_token_ = card_authorization_token;
   if (creation_options.is_dict()) {
     if (IsValidCreationOptions(creation_options)) {
       current_flow_ = OPT_IN_WITH_CHALLENGE_FLOW;
       MakeCredential(ParseCreationOptions(creation_options));
     }
   } else {
-    current_flow_ = OPT_IN_WITHOUT_CHALLENGE_FLOW;
+    current_flow_ = OPT_IN_FETCH_CHALLENGE_FLOW;
     OptChange(/*opt_in=*/true);
   }
 }
 
+void CreditCardFIDOAuthenticator::Authorize(
+    std::string card_authorization_token,
+    base::Value request_options) {
+  card_authorization_token_ = card_authorization_token;
+  if (IsValidRequestOptions(request_options)) {
+    // If user is already opted-in, then a new card is trying to be
+    // authorized. Otherwise, a user with a credential on file is trying to
+    // opt-in.
+    current_flow_ = IsUserOptedIn() ? FOLLOWUP_AFTER_CVC_AUTH_FLOW
+                                    : OPT_IN_WITH_CHALLENGE_FLOW;
+    GetAssertion(ParseRequestOptions(std::move(request_options)));
+  }
+}
+
 void CreditCardFIDOAuthenticator::OptOut() {
   current_flow_ = OPT_OUT_FLOW;
+  card_authorization_token_ = std::string();
   OptChange(/*opt_in=*/false);
 }
 
@@ -160,8 +179,21 @@
 
 void CreditCardFIDOAuthenticator::GetAssertion(
     PublicKeyCredentialRequestOptionsPtr request_options) {
-  autofill_driver_->ConnectToAuthenticator(
-      authenticator_.BindNewPipeAndPassReceiver());
+  if (!authenticator_.is_bound()) {
+    autofill_driver_->ConnectToAuthenticator(
+        authenticator_.BindNewPipeAndPassReceiver());
+  }
+#if !defined(OS_ANDROID)
+  // On desktop, during an opt-in flow, close the WebAuthn offer dialog and get
+  // ready to show the OS level authentication dialog. If dialog is already
+  // closed, then the offer was declined during the fetching challenge process,
+  // and thus returned early.
+  if (current_flow_ == OPT_IN_WITH_CHALLENGE_FLOW &&
+      !autofill_client_->CloseWebauthnOfferDialog()) {
+    current_flow_ = NONE_FLOW;
+    return;
+  }
+#endif
   authenticator_->GetAssertion(
       std::move(request_options),
       base::BindOnce(&CreditCardFIDOAuthenticator::OnDidGetAssertion,
@@ -170,8 +202,10 @@
 
 void CreditCardFIDOAuthenticator::MakeCredential(
     PublicKeyCredentialCreationOptionsPtr creation_options) {
-  autofill_driver_->ConnectToAuthenticator(
-      authenticator_.BindNewPipeAndPassReceiver());
+  if (!authenticator_.is_bound()) {
+    autofill_driver_->ConnectToAuthenticator(
+        authenticator_.BindNewPipeAndPassReceiver());
+  }
 #if !defined(OS_ANDROID)
   // On desktop, close the WebAuthn offer dialog and get ready to show the OS
   // level authentication dialog. If dialog is already closed, then the offer
@@ -188,15 +222,29 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-void CreditCardFIDOAuthenticator::OptChange(bool opt_in,
-                                            base::Value attestation_response) {
+void CreditCardFIDOAuthenticator::OptChange(
+    bool opt_in,
+    base::Value authenticator_response) {
   payments::PaymentsClient::OptChangeRequestDetails request_details;
   request_details.app_locale =
       autofill_client_->GetPersonalDataManager()->app_locale();
   request_details.opt_in = opt_in;
-  if (attestation_response.is_dict()) {
+
+  // If |authenticator_response| is set, that means the user just signed a
+  // challenge. In which case, if |card_authorization_token_| is not empty, then
+  // that will be required to bind a previous CVC check with this signature.
+  // This will opt the user in and authorize the card corresponding to
+  // |card_authorization_token_|.
+  // If |authenticator_response| is not set, that means the user was fetching a
+  // challenge, in which case |card_authorization_token_| will be required for
+  // the subsequent OptChange call.
+  if (authenticator_response.is_dict()) {
     request_details.fido_authenticator_response =
-        std::move(attestation_response);
+        std::move(authenticator_response);
+    if (!card_authorization_token_.empty()) {
+      request_details.card_authorization_token = card_authorization_token_;
+      card_authorization_token_ = std::string();
+    }
   }
   payments_client_->OptChange(
       request_details,
@@ -209,18 +257,30 @@
     GetAssertionAuthenticatorResponsePtr assertion_response) {
   // End the flow if there was an authentication error.
   if (status != AuthenticatorStatus::SUCCESS) {
+    // Report failure to |requester_| if card unmasking was requested.
+    if (current_flow_ == AUTHENTICATION_FLOW)
+      requester_->OnFIDOAuthenticationComplete(/*did_succeed=*/false);
     current_flow_ = NONE_FLOW;
-    requester_->OnFIDOAuthenticationComplete(/*did_succeed=*/false);
     return;
   }
 
-  base::Value response = ParseAssertionResponse(std::move(assertion_response));
-  full_card_request_.reset(new payments::FullCardRequest(
-      autofill_client_, autofill_client_->GetPaymentsClient(),
-      autofill_client_->GetPersonalDataManager(), form_parsed_timestamp_));
-  full_card_request_->GetFullCardViaFIDO(
-      *card_, AutofillClient::UNMASK_FOR_AUTOFILL,
-      weak_ptr_factory_.GetWeakPtr(), std::move(response));
+  if (current_flow_ == AUTHENTICATION_FLOW) {
+    base::Value response =
+        ParseAssertionResponse(std::move(assertion_response));
+    full_card_request_.reset(new payments::FullCardRequest(
+        autofill_client_, autofill_client_->GetPaymentsClient(),
+        autofill_client_->GetPersonalDataManager(), form_parsed_timestamp_));
+    full_card_request_->GetFullCardViaFIDO(
+        *card_, AutofillClient::UNMASK_FOR_AUTOFILL,
+        weak_ptr_factory_.GetWeakPtr(), std::move(response));
+  } else {
+    DCHECK(current_flow_ == FOLLOWUP_AFTER_CVC_AUTH_FLOW ||
+           current_flow_ == OPT_IN_WITH_CHALLENGE_FLOW);
+    base::Value response = base::Value(base::Value::Type::DICTIONARY);
+    response.SetKey("fido_assertion_info",
+                    ParseAssertionResponse(std::move(assertion_response)));
+    OptChange(/*opt_in=*/true, std::move(response));
+  }
 }
 
 void CreditCardFIDOAuthenticator::OnDidMakeCredential(
@@ -239,9 +299,10 @@
 void CreditCardFIDOAuthenticator::OnDidGetOptChangeResult(
     AutofillClient::PaymentsRpcResult result,
     payments::PaymentsClient::OptChangeResponseDetails& response) {
-  DCHECK(current_flow_ == OPT_IN_WITHOUT_CHALLENGE_FLOW ||
+  DCHECK(current_flow_ == OPT_IN_FETCH_CHALLENGE_FLOW ||
          current_flow_ == OPT_OUT_FLOW ||
-         current_flow_ == OPT_IN_WITH_CHALLENGE_FLOW);
+         current_flow_ == OPT_IN_WITH_CHALLENGE_FLOW ||
+         current_flow_ == FOLLOWUP_AFTER_CVC_AUTH_FLOW);
   // End the flow if the server responded with an error.
   if (result != AutofillClient::PaymentsRpcResult::SUCCESS) {
     current_flow_ = NONE_FLOW;
@@ -255,12 +316,17 @@
         autofill_client_->GetPrefs(), response.user_is_opted_in.value());
   }
 
-  // If response contains |creation_options| and the last opt-in attempt did not
-  // include a challenge, then invoke WebAuthn registration prompt. Otherwise
-  // end the flow.
-  if (response.fido_creation_options.has_value() &&
-      current_flow_ == OPT_IN_WITHOUT_CHALLENGE_FLOW) {
-    Register(std::move(response.fido_creation_options.value()));
+  // If response contains |creation_options| or |request_options| and the last
+  // opt-in attempt did not include a challenge, then invoke WebAuthn
+  // registration/verification prompt. Otherwise end the flow.
+  if (current_flow_ == OPT_IN_FETCH_CHALLENGE_FLOW) {
+    if (response.fido_creation_options.has_value()) {
+      Register(card_authorization_token_,
+               std::move(response.fido_creation_options.value()));
+    } else if (response.fido_request_options.has_value()) {
+      Authorize(card_authorization_token_,
+                std::move(response.fido_request_options.value()));
+    }
   } else {
     current_flow_ = NONE_FLOW;
   }
@@ -269,9 +335,10 @@
 void CreditCardFIDOAuthenticator::OnWebauthnOfferDialogUserResponse(
     bool did_accept) {
   if (did_accept) {
-    Register();
+    Register(card_authorization_token_);
   } else {
     payments_client_->CancelRequest();
+    card_authorization_token_ = std::string();
     current_flow_ = NONE_FLOW;
   }
 }
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
index eeb1952d..60dd2c6 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
@@ -53,9 +53,11 @@
     // Registration flow, including a challenge to sign.
     OPT_IN_WITH_CHALLENGE_FLOW,
     // Opt-in attempt flow, no challenge to sign.
-    OPT_IN_WITHOUT_CHALLENGE_FLOW,
+    OPT_IN_FETCH_CHALLENGE_FLOW,
     // Opt-out flow.
     OPT_OUT_FLOW,
+    // Authorization of a new card.
+    FOLLOWUP_AFTER_CVC_AUTH_FLOW,
   };
   class Requester {
    public:
@@ -68,16 +70,25 @@
   ~CreditCardFIDOAuthenticator() override;
 
   // Offer the option to use WebAuthn for authenticating future card unmasking.
-  void ShowWebauthnOfferDialog();
+  void ShowWebauthnOfferDialog(std::string card_authorization_token);
 
-  // Authentication
+  // Invokes Authentication flow. Responds to |accessor_| with full pan.
   void Authenticate(const CreditCard* card,
                     base::WeakPtr<Requester> requester,
                     base::TimeTicks form_parsed_timestamp,
                     base::Value request_options);
 
-  // Registration
-  void Register(base::Value creation_options = base::Value());
+  // Invokes Registration flow. Sends credentials created from
+  // |creation_options| along with the |card_authorization_token| to Payments in
+  // order to enroll the user and authorize the corresponding card.
+  void Register(std::string card_authorization_token = std::string(),
+                base::Value creation_options = base::Value());
+
+  // Invokes an Authorization flow. Sends signature created from
+  // |request_options| along with the |card_authorization_token| to Payments in
+  // order to authorize the corresponding card.
+  void Authorize(std::string card_authorization_token,
+                 base::Value request_options);
 
   // Opts the user out.
   void OptOut();
@@ -124,7 +135,8 @@
       PublicKeyCredentialCreationOptionsPtr creation_options);
 
   // Makes a request to payments to either opt-in or opt-out the user.
-  void OptChange(bool opt_in, base::Value attestation_response = base::Value());
+  void OptChange(bool opt_in,
+                 base::Value authenticator_response = base::Value());
 
   // The callback invoked from the WebAuthn prompt including the
   // |assertion_response|, which will be sent to Google Payments to retrieve
@@ -193,6 +205,10 @@
   // The current flow in progress.
   Flow current_flow_ = NONE_FLOW;
 
+  // Token used for authorizing new cards. Helps tie CVC auth and FIDO calls
+  // together in order to support FIDO-only unmasking on future attempts.
+  std::string card_authorization_token_;
+
   // Meant for histograms recorded in FullCardRequest.
   base::TimeTicks form_parsed_timestamp_;
 
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
index ae007591..c088e89e 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
@@ -85,6 +85,7 @@
 const char kTestCredentialId[] = "VGhpcyBpcyBhIHRlc3QgQ3JlZGVudGlhbCBJRC4=";
 // Base64 encoding of "This is a test signature".
 const char kTestSignature[] = "VGhpcyBpcyBhIHRlc3Qgc2lnbmF0dXJl";
+const char kTestAuthToken[] = "dummy_card_authorization_token";
 
 std::string NextMonth() {
   base::Time::Exploded now;
@@ -422,8 +423,8 @@
       features::kAutofillCreditCardAuthentication);
   EXPECT_FALSE(fido_authenticator_->IsUserOptedIn());
 
-  fido_authenticator_->Register();
-  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITHOUT_CHALLENGE_FLOW,
+  fido_authenticator_->Register(kTestAuthToken);
+  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_FETCH_CHALLENGE_FLOW,
             fido_authenticator_->current_flow());
 
   // Mock payments response.
@@ -437,8 +438,8 @@
       features::kAutofillCreditCardAuthentication);
   EXPECT_FALSE(fido_authenticator_->IsUserOptedIn());
 
-  fido_authenticator_->Register();
-  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITHOUT_CHALLENGE_FLOW,
+  fido_authenticator_->Register(kTestAuthToken);
+  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_FETCH_CHALLENGE_FLOW,
             fido_authenticator_->current_flow());
 
   // Mock payments response.
@@ -453,6 +454,7 @@
   EXPECT_FALSE(fido_authenticator_->IsUserOptedIn());
 
   fido_authenticator_->Register(
+      kTestAuthToken,
       GetTestCreationOptions(/*challenge=*/"", kTestRelyingPartyId));
 
   EXPECT_FALSE(fido_authenticator_->IsUserOptedIn());
@@ -464,6 +466,7 @@
   EXPECT_FALSE(fido_authenticator_->IsUserOptedIn());
 
   fido_authenticator_->Register(
+      kTestAuthToken,
       GetTestCreationOptions(kTestChallenge, kTestRelyingPartyId));
   EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
             fido_authenticator_->current_flow());
@@ -480,6 +483,7 @@
   EXPECT_FALSE(fido_authenticator_->IsUserOptedIn());
 
   fido_authenticator_->Register(
+      kTestAuthToken,
       GetTestCreationOptions(kTestChallenge, kTestRelyingPartyId));
   EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
             fido_authenticator_->current_flow());
@@ -498,8 +502,8 @@
       features::kAutofillCreditCardAuthentication);
   EXPECT_FALSE(fido_authenticator_->IsUserOptedIn());
 
-  fido_authenticator_->Register();
-  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITHOUT_CHALLENGE_FLOW,
+  fido_authenticator_->Register(kTestAuthToken);
+  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_FETCH_CHALLENGE_FLOW,
             fido_authenticator_->current_flow());
 
   // Mock payments response with challenge to invoke enrollment flow.
@@ -517,6 +521,53 @@
   EXPECT_TRUE(fido_authenticator_->IsUserOptedIn());
 }
 
+TEST_F(CreditCardFIDOAuthenticatorTest,
+       Register_OptInAttemptReturnsRequestOptions) {
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kAutofillCreditCardAuthentication);
+  EXPECT_FALSE(fido_authenticator_->IsUserOptedIn());
+
+  fido_authenticator_->Register(kTestAuthToken);
+  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_FETCH_CHALLENGE_FLOW,
+            fido_authenticator_->current_flow());
+
+  // Mock payments response with challenge to invoke opt-in flow.
+  OptChange(AutofillClient::PaymentsRpcResult::SUCCESS,
+            /*user_is_opted_in=*/false, /*include_creation_options=*/false,
+            /*include_request_options=*/true);
+  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
+            fido_authenticator_->current_flow());
+  EXPECT_FALSE(fido_authenticator_->IsUserOptedIn());
+
+  // Mock user response and second payments response.
+  TestCreditCardFIDOAuthenticator::GetAssertion(fido_authenticator_.get(),
+                                                /*did_succeed=*/true);
+  OptChange(AutofillClient::PaymentsRpcResult::SUCCESS,
+            /*user_is_opted_in=*/true);
+  EXPECT_TRUE(fido_authenticator_->IsUserOptedIn());
+}
+
+TEST_F(CreditCardFIDOAuthenticatorTest, Register_NewCardAuthorization) {
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kAutofillCreditCardAuthentication);
+  ::autofill::prefs::SetCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs(),
+                                                  true);
+  EXPECT_TRUE(fido_authenticator_->IsUserOptedIn());
+
+  fido_authenticator_->Authorize(
+      kTestAuthToken, GetTestRequestOptions(kTestChallenge, kTestRelyingPartyId,
+                                            kTestCredentialId));
+  EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::FOLLOWUP_AFTER_CVC_AUTH_FLOW,
+            fido_authenticator_->current_flow());
+
+  // Mock user response and second payments response.
+  TestCreditCardFIDOAuthenticator::GetAssertion(fido_authenticator_.get(),
+                                                /*did_succeed=*/true);
+  OptChange(AutofillClient::PaymentsRpcResult::SUCCESS,
+            /*user_is_opted_in=*/true);
+  EXPECT_TRUE(fido_authenticator_->IsUserOptedIn());
+}
+
 TEST_F(CreditCardFIDOAuthenticatorTest, OptOut_Success) {
   scoped_feature_list_.InitAndEnableFeature(
       features::kAutofillCreditCardAuthentication);
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index af7a8db..3be99d4 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -498,6 +498,9 @@
   script = "//components/cronet/tools/generate_proguard_file.py"
   sources = [
     "//base/android/proguard/chromium_code.flags",
+
+    # Massage the proguard rules to work with AppReduce.
+    "//components/cronet/android/cronet_appreduce_workaround.patch",
     "//components/cronet/android/cronet_impl_native_proguard.cfg",
   ]
   outputs = [
diff --git a/components/cronet/android/cronet_appreduce_workaround.patch b/components/cronet/android/cronet_appreduce_workaround.patch
new file mode 100644
index 0000000..55de3bc
--- /dev/null
+++ b/components/cronet/android/cronet_appreduce_workaround.patch
@@ -0,0 +1,34 @@
+This patchfile is a stop-gap solution to accomodate differences in syntax for
+AppReduce, which is used internally in place of ProGuard. See crbug.com/1004516.
+
+This patches base/android/proguard/chromium_code.flags.
+
+It can be re-generated using these steps:
+
+cd src
+export ORIG_FLAGS="base/android/proguard/chromium_code.flags"
+cp $ORIG_FLAGS modified.flags
+$EDITOR modified.flags
+diff -u "$ORIG_FLAGS" modified.flags > "components/cronet/android/cronet_appreduce_workaround.patch"
+
+To test whether the patch applies cleanly use:
+
+autoninja -C out/Release cronet_combine_proguard_flags
+
+--- base/android/proguard/chromium_code.flags	2019-09-16 16:39:21.108477065 -0700
++++ modified.flags	2019-09-17 12:12:17.487606737 -0700
+@@ -50,9 +50,12 @@
+ -assumenosideeffects class ** {
+   # Remove @RemovableInRelease methods so long as return values are unused.
+   @org.chromium.base.annotations.RemovableInRelease <methods>;
++}
++
++-assumevalues class ** {
+   # Remove object @RemovableInRelease methods even when return value is used.
+-  # Note: * in return type does not match primitives.
+-  @org.chromium.base.annotations.RemovableInRelease * *(...) return null;
++  # Note: ** in return type does not match primitives.
++  @org.chromium.base.annotations.RemovableInRelease ** *(...) return null;
+   # Remove boolean @RemovableInRelease methods even when return value is used.
+   @org.chromium.base.annotations.RemovableInRelease boolean *(...) return false;
+ }
diff --git a/components/cronet/ios/test/cronet_prefs_test.mm b/components/cronet/ios/test/cronet_prefs_test.mm
index 4c47613..2a99e53 100644
--- a/components/cronet/ios/test/cronet_prefs_test.mm
+++ b/components/cronet/ios/test/cronet_prefs_test.mm
@@ -79,7 +79,7 @@
   NSURLSession* session_;
 };
 
-TEST_F(PrefsTest, DISABLED_HttpSeverProperties) {
+TEST_F(PrefsTest, HttpServerProperties) {
   base::FilePath storage_path;
   bool result = base::PathService::Get(base::DIR_CACHE, &storage_path);
   ASSERT_TRUE(result);
@@ -115,13 +115,11 @@
   // Check the file content
   ASSERT_TRUE(prefs_file_content);
   ASSERT_TRUE(
-      [prefs_file_content containsString:@"{\"http_server_properties\":{"])
+      [prefs_file_content containsString:@"{\"http_server_properties\":"])
       << "Unable to find 'http_server_properties' in the JSON prefs.";
-  ASSERT_TRUE(
-      [prefs_file_content containsString:@"\"supports_quic\":{\"address\""
-                                          ":\"127.0.0.1\",\"used_quic\":true}"])
+  ASSERT_TRUE([prefs_file_content containsString:@"\"supports_quic\":"])
       << "Unable to find 'supports_quic' in the JSON prefs.";
-  ASSERT_TRUE([prefs_file_content containsString:@"{\"server_info\":\""])
+  ASSERT_TRUE([prefs_file_content containsString:@"\"server_info\":"])
       << "Unable to find 'server_info' in the JSON prefs.";
 
   // Delete the prefs file to avoid side effects with other tests.
diff --git a/components/cronet/tools/generate_proguard_file.py b/components/cronet/tools/generate_proguard_file.py
index 6d7e530a..edaebb0 100755
--- a/components/cronet/tools/generate_proguard_file.py
+++ b/components/cronet/tools/generate_proguard_file.py
@@ -4,20 +4,42 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+# Tool that combines a sequence of input proguard files and outputs a single
+# proguard file.
+#
+# The final output file is formed by concatenating all of the
+# input proguard files, and then sequentally applying any .patch files that
+# were given in the input.
+#
+# This tool requires the ability to shell execute the 'patch' tool, and is
+# expected to only be run on Linux.
+
 import optparse
 import sys
+import subprocess
 
-# Combines files in |input_files| as one proguard file and write that to
-# |output_file|
-def GenerateProguardFile(output_file, input_files):
+
+def ReadFile(path):
+  with open(path, 'rb') as f:
+    return f.read()
+
+
+def IsPatchFile(path):
+  return path.endswith('.patch')
+
+
+def ApplyPatch(output_file, patch_file):
   try:
-    with open(output_file, "wb") as target:
-      for input_file in input_files:
-        f = open(input_file, "rb")
-        for line in f:
-          target.write(line)
-  except IOError:
-    raise Exception("Proguard file generation failed")
+    subprocess.check_call(['patch', '--quiet', output_file, patch_file])
+  except:
+    message = '''
+Failed applying patch %s to %s
+
+For help on fixing read the documentation in the patch file.
+
+'''
+    sys.stderr.write(message % (patch_file, output_file))
+    raise
 
 
 def main():
@@ -26,7 +48,18 @@
           help='Output file for the generated proguard file')
 
   options, input_files = parser.parse_args()
-  GenerateProguardFile(options.output_file, input_files)
+
+  proguard_files = [path for path in input_files if not IsPatchFile(path)]
+  patch_files = [path for path in input_files if IsPatchFile(path)]
+
+  # Concatenate all the proguard files.
+  with open(options.output_file, 'wb') as target:
+    for input_file in proguard_files:
+      target.write(ReadFile(input_file))
+
+  # Apply any patch files.
+  for patch_file in patch_files:
+    ApplyPatch(options.output_file, patch_file)
 
 
 if __name__ == '__main__':
diff --git a/components/dom_distiller/core/html/dom_distiller_viewer.html b/components/dom_distiller/core/html/dom_distiller_viewer.html
index f67f4ed..950be52 100644
--- a/components/dom_distiller/core/html/dom_distiller_viewer.html
+++ b/components/dom_distiller/core/html/dom_distiller_viewer.html
@@ -35,15 +35,5 @@
     </div>
     <a data-original-url="$7" id="closeReaderView">$8</a>
   </div>
-  <div id="feedbackContainer" class="footerFeedback hidden">
-    <div class="feedbackContent">
-      <div id="feedbackQuestion"></div>
-      <div class="feedbackButtonWrap">
-        <a class="feedbackButton" id="feedbackNo"></a>
-        <a class="feedbackButton" id="feedbackYes"></a>
-      </div>
-    </div>
-    <div class="clear"></div>
-  </div>
 </body>
 </html>
diff --git a/components/offline_pages/core/background/request_coordinator.cc b/components/offline_pages/core/background/request_coordinator.cc
index df0a163b..13607b0 100644
--- a/components/offline_pages/core/background/request_coordinator.cc
+++ b/components/offline_pages/core/background/request_coordinator.cc
@@ -545,22 +545,26 @@
     RequestAvailability availability,
     AddRequestResult result,
     const SavePageRequest& request) {
-  NotifyAdded(request);
-  // Inform the scheduler that we have an outstanding task.
-  scheduler_->Schedule(GetTriggerConditions(kUserRequest));
+  if (result == AddRequestResult::SUCCESS) {
+    NotifyAdded(request);
+    // Inform the scheduler that we have an outstanding task.
+    scheduler_->Schedule(GetTriggerConditions(kUserRequest));
 
-  if (availability == RequestAvailability::DISABLED_FOR_OFFLINER) {
-    // Mark attempt started (presuming it is disabled for background offliner
-    // because foreground offlining is happening).
-    queue_->MarkAttemptStarted(
-        request.request_id(),
-        base::BindOnce(&RequestCoordinator::MarkAttemptDone,
-                       weak_ptr_factory_.GetWeakPtr(), request.request_id(),
-                       request.client_id().name_space));
-  } else if (request.user_requested()) {
-    StartImmediatelyIfConnected();
+    if (availability == RequestAvailability::DISABLED_FOR_OFFLINER) {
+      // Mark attempt started (presuming it is disabled for background offliner
+      // because foreground offlining is happening).
+      queue_->MarkAttemptStarted(
+          request.request_id(),
+          base::BindOnce(&RequestCoordinator::MarkAttemptDone,
+                         weak_ptr_factory_.GetWeakPtr(), request.request_id(),
+                         request.client_id().name_space));
+    } else if (request.user_requested()) {
+      StartImmediatelyIfConnected();
+    }
+  } else {
+    event_logger_.RecordAddRequestFailed(request.client_id().name_space,
+                                         result);
   }
-
   std::move(save_page_later_callback).Run(result);
 }
 
diff --git a/components/offline_pages/core/background/request_coordinator_event_logger.cc b/components/offline_pages/core/background/request_coordinator_event_logger.cc
index ef2a136..26a18022 100644
--- a/components/offline_pages/core/background/request_coordinator_event_logger.cc
+++ b/components/offline_pages/core/background/request_coordinator_event_logger.cc
@@ -4,6 +4,10 @@
 
 #include "components/offline_pages/core/background/request_coordinator_event_logger.h"
 
+#include <string>
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+
 namespace offline_pages {
 
 namespace {
@@ -33,7 +37,7 @@
       return "DOWNLOAD_THROTTLED";
     default:
       NOTREACHED();
-      return std::to_string(static_cast<int>(result));
+      return base::NumberToString(static_cast<int>(result));
   }
 }
 
@@ -47,7 +51,7 @@
       return "REQUEST_DOES_NOT_EXIST";
     default:
       NOTREACHED();
-      return std::to_string(static_cast<int>(result));
+      return base::NumberToString(static_cast<int>(result));
   }
 }
 
@@ -57,7 +61,7 @@
     const std::string& name_space,
     Offliner::RequestStatus new_status,
     int64_t request_id) {
-  std::string request_id_str = std::to_string(request_id);
+  std::string request_id_str = base::NumberToString(request_id);
   RecordActivity("Background save attempt for " + name_space + ":" +
                  request_id_str + " - " +
                  Offliner::RequestStatusToString(new_status));
@@ -67,7 +71,7 @@
     const std::string& name_space,
     RequestNotifier::BackgroundSavePageResult result,
     int64_t request_id) {
-  std::string request_id_str = std::to_string(request_id);
+  std::string request_id_str = base::NumberToString(request_id);
   RecordActivity("Background save request removed " + name_space + ":" +
                  request_id_str + " - " +
                  BackgroundSavePageResultToString(result));
@@ -80,4 +84,12 @@
                  UpdateRequestResultToString(result));
 }
 
+void RequestCoordinatorEventLogger::RecordAddRequestFailed(
+    const std::string& name_space,
+    AddRequestResult result) {
+  RecordActivity(
+      base::StrCat({"Add request failed for ", name_space, " - code ",
+                    base::NumberToString(static_cast<int>(result))}));
+}
+
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/background/request_coordinator_event_logger.h b/components/offline_pages/core/background/request_coordinator_event_logger.h
index 92bd009..a86e91c 100644
--- a/components/offline_pages/core/background/request_coordinator_event_logger.h
+++ b/components/offline_pages/core/background/request_coordinator_event_logger.h
@@ -31,6 +31,9 @@
 
   void RecordUpdateRequestFailed(const std::string& name_space,
                                  UpdateRequestResult result);
+
+  void RecordAddRequestFailed(const std::string& name_space,
+                              AddRequestResult result);
 };
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/background/request_coordinator_unittest.cc b/components/offline_pages/core/background/request_coordinator_unittest.cc
index 79615d7b..e84b2de 100644
--- a/components/offline_pages/core/background/request_coordinator_unittest.cc
+++ b/components/offline_pages/core/background/request_coordinator_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/logging.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/system/sys_info.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_mock_time_task_runner.h"
@@ -80,17 +81,11 @@
 
 class ObserverStub : public RequestCoordinator::Observer {
  public:
-  ObserverStub()
-      : added_called_(false),
-        completed_called_(false),
-        changed_called_(false),
-        network_progress_called_(false),
-        network_progress_bytes_(0),
-        last_status_(RequestCoordinator::BackgroundSavePageResult::SUCCESS),
-        state_(SavePageRequest::RequestState::OFFLINING) {}
+  ObserverStub() { Clear(); }
 
   void Clear() {
     added_called_ = false;
+    added_call_count_ = 0;
     completed_called_ = false;
     changed_called_ = false;
     network_progress_called_ = false;
@@ -101,6 +96,7 @@
 
   void OnAdded(const SavePageRequest& request) override {
     added_called_ = true;
+    ++added_call_count_;
     state_ = request.request_state();
     pending_state_ = request.pending_state();
   }
@@ -125,6 +121,7 @@
   }
 
   bool added_called() { return added_called_; }
+  int added_call_count() const { return added_call_count_; }
   bool completed_called() { return completed_called_; }
   bool changed_called() { return changed_called_; }
   bool network_progress_called() { return network_progress_called_; }
@@ -137,6 +134,7 @@
 
  private:
   bool added_called_;
+  int added_call_count_;
   bool completed_called_;
   bool changed_called_;
   bool network_progress_called_;
@@ -1879,4 +1877,28 @@
   EXPECT_EQ(PendingState::PENDING_ANOTHER_DOWNLOAD, observer().pending_state());
 }
 
+TEST_F(RequestCoordinatorTest, SavePageLaterRejectedDuplicateUrl) {
+  // Request the same URL twice using the 'disallow_duplicate_requests' option,
+  // and verify the second request is rejected.
+  EnableOfflinerCallback(false);
+
+  RequestCoordinator::SavePageLaterParams params;
+  params.url = kUrl1;
+  params.client_id = kClientId1;
+  params.add_options.disallow_duplicate_requests = true;
+  std::vector<AddRequestResult> results;
+  auto callback = base::BindLambdaForTesting(
+      [&](AddRequestResult result) { results.push_back(result); });
+
+  EXPECT_NE(0, coordinator()->SavePageLater(params, callback));
+  EXPECT_NE(0, coordinator()->SavePageLater(params, callback));
+  PumpLoop();
+
+  // Only one is added.
+  EXPECT_EQ(1, observer().added_call_count());
+  EXPECT_EQ(std::vector<AddRequestResult>(
+                {AddRequestResult::SUCCESS, AddRequestResult::DUPLICATE_URL}),
+            results);
+}
+
 }  // namespace offline_pages
diff --git a/components/omnibox/browser/document_provider.h b/components/omnibox/browser/document_provider.h
index 36ccd799..7eb401d 100644
--- a/components/omnibox/browser/document_provider.h
+++ b/components/omnibox/browser/document_provider.h
@@ -104,6 +104,7 @@
   FRIEND_TEST_ALL_PREFIXES(DocumentProviderTest, GenerateLastModifiedString);
   FRIEND_TEST_ALL_PREFIXES(DocumentProviderTest, Scoring);
   FRIEND_TEST_ALL_PREFIXES(DocumentProviderTest, Caching);
+  FRIEND_TEST_ALL_PREFIXES(DocumentProviderTest, MinQueryLength);
 
   using MatchesCache = base::MRUCache<GURL, AutocompleteMatch>;
 
diff --git a/components/omnibox/browser/document_provider_unittest.cc b/components/omnibox/browser/document_provider_unittest.cc
index 7d6a218..6c7fdeea 100644
--- a/components/omnibox/browser/document_provider_unittest.cc
+++ b/components/omnibox/browser/document_provider_unittest.cc
@@ -916,3 +916,29 @@
               testing::ElementsAre(ACMatchClassification{0, 2},
                                    ACMatchClassification{5, 0}));
 }
+
+TEST_F(DocumentProviderTest, MinQueryLength) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(omnibox::kDocumentProvider);
+  EXPECT_CALL(*client_.get(), SearchSuggestEnabled())
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*client_.get(), IsAuthenticated()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*client_.get(), IsSyncActive()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*client_.get(), IsOffTheRecord()).WillRepeatedly(Return(false));
+
+  // Expect document provider to ignore inputs shorter than min_query_length_.
+  AutocompleteInput short_input(base::ASCIIToUTF16("12"),
+                                metrics::OmniboxEventProto::OTHER,
+                                TestSchemeClassifier());
+  short_input.set_want_asynchronous_matches(false);
+  provider_->Start(short_input, false);
+  EXPECT_NE(short_input.text(), provider_->input_.text());
+
+  // Expect document provider to process inputs longer than min_query_length_.
+  AutocompleteInput long_input(base::ASCIIToUTF16("123456"),
+                               metrics::OmniboxEventProto::OTHER,
+                               TestSchemeClassifier());
+  long_input.set_want_asynchronous_matches(false);
+  provider_->Start(long_input, false);
+  EXPECT_EQ(long_input.text(), provider_->input_.text());
+}
diff --git a/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc b/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc
index 96add61..97d9a8d 100644
--- a/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc
+++ b/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc
@@ -181,29 +181,29 @@
   quad4_root_1->SetNew(shared_quad_state4_root, /*rect=*/rect4_root,
                        /*visible_rect=*/rect4_root, /*render_pass_id=*/1,
                        /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                       /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1),
-                       gfx::PointF(), gfx::RectF(), false, 1.0f);
+                       gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                       1.0f);
   auto* quad4_root_2 =
       pass4_root->quad_list.AllocateAndConstruct<RenderPassDrawQuad>();
   quad4_root_2->SetNew(shared_quad_state4_root, /*rect=*/rect4_root,
                        /*visible_rect=*/rect4_root, /*render_pass_id=*/2,
                        /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                       /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1),
-                       gfx::PointF(), gfx::RectF(), false, 1.0f);
+                       gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                       1.0f);
   auto* quad4_root_3 =
       pass4_root->quad_list.AllocateAndConstruct<RenderPassDrawQuad>();
   quad4_root_3->SetNew(shared_quad_state4_root, /*rect=*/rect4_root,
                        /*visible_rect=*/rect4_root, /*render_pass_id=*/3,
                        /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                       /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1),
-                       gfx::PointF(), gfx::RectF(), false, 1.0f);
+                       gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                       1.0f);
   auto* quad4_root_4 =
       pass4_root->quad_list.AllocateAndConstruct<RenderPassDrawQuad>();
   quad4_root_4->SetNew(shared_quad_state4_root, /*rect=*/rect4_root,
                        /*visible_rect=*/rect4_root, /*render_pass_id=*/4,
                        /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                       /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1),
-                       gfx::PointF(), gfx::RectF(), false, 1.0f);
+                       gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                       1.0f);
   pass_list.push_back(std::move(pass4_root));
 
   auto compositor_frame =
@@ -460,8 +460,7 @@
   render_pass_quad_1->SetNew(
       pass2->shared_quad_state_list.back(), child_rect, child_rect,
       /*render_pass_id=*/1, /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-      /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1), gfx::PointF(),
-      gfx::RectF(), false, 1.0f);
+      gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
   SurfaceId child_surface_id3 = CreateChildSurfaceId(4);
   gfx::Rect child_rect3(500, 500, 100, 100);
   auto* surface_quad_3 = pass2->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
@@ -474,8 +473,7 @@
   render_pass_quad_2->SetNew(
       pass2->shared_quad_state_list.back(), child_rect2, child_rect2,
       /*render_pass_id=*/1, /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-      /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1), gfx::PointF(),
-      gfx::RectF(), false, 1.0f);
+      gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
   pass_list.push_back(std::move(pass2));
 
   // The root RenderPass that has three RenderPassDrawQuad point to pass2.
@@ -490,22 +488,19 @@
   render_pass_quad_3->SetNew(
       pass_root->shared_quad_state_list.back(), child_rect, child_rect,
       /*render_pass_id=*/4, /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-      /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1), gfx::PointF(),
-      gfx::RectF(), false, 1.0f);
+      gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
   auto* render_pass_quad_4 =
       pass_root->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   render_pass_quad_4->SetNew(
       pass_root->shared_quad_state_list.back(), child_rect2, child_rect2,
       /*render_pass_id=*/4, /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-      /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1), gfx::PointF(),
-      gfx::RectF(), false, 1.0f);
+      gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
   auto* render_pass_quad_5 =
       pass_root->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   render_pass_quad_5->SetNew(
       pass_root->shared_quad_state_list.back(), child_rect, child_rect,
       /*render_pass_id=*/4, /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-      /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 10), gfx::PointF(),
-      gfx::RectF(), false, 1.0f);
+      gfx::Vector2dF(1, 10), gfx::PointF(), gfx::RectF(), false, 1.0f);
   pass_list.push_back(std::move(pass_root));
 
   CompositorFrame compositor_frame =
diff --git a/components/viz/common/quads/draw_quad_unittest.cc b/components/viz/common/quads/draw_quad_unittest.cc
index 197e9dd..2ee5914e 100644
--- a/components/viz/common/quads/draw_quad_unittest.cc
+++ b/components/viz/common/quads/draw_quad_unittest.cc
@@ -161,11 +161,11 @@
   }                                                                           \
   SETUP_AND_COPY_QUAD_ALL_RP(Type, quad_all, copy_a);
 
-#define CREATE_QUAD_NEW_RP(Type, a, b, c, d, e, f, g, h, i, j, k, copy_a)    \
+#define CREATE_QUAD_NEW_RP(Type, a, b, c, d, e, f, g, h, i, j, copy_a)       \
   Type* quad_new = render_pass->CreateAndAppendDrawQuad<Type>();             \
   {                                                                          \
     QUAD_DATA quad_new->SetNew(shared_state, quad_rect, a, b, c, d, e, f, g, \
-                               h, i, j, k);                                  \
+                               h, i, j);                                     \
   }                                                                          \
   SETUP_AND_COPY_QUAD_NEW_RP(Type, quad_new, copy_a);
 
@@ -193,7 +193,6 @@
   ResourceId mask_resource_id = 78;
   gfx::RectF mask_uv_rect(0, 0, 33.f, 19.f);
   gfx::Size mask_texture_size(128, 134);
-  bool mask_applies_to_backdrop = false;
   gfx::Vector2dF filters_scale;
   gfx::PointF filters_origin;
   gfx::RectF tex_coord_rect(1, 1, 255, 254);
@@ -205,9 +204,9 @@
 
   CREATE_QUAD_NEW_RP(RenderPassDrawQuad, visible_rect, render_pass_id,
                      mask_resource_id, mask_uv_rect, mask_texture_size,
-                     mask_applies_to_backdrop, filters_scale, filters_origin,
-                     tex_coord_rect, force_anti_aliasing_off,
-                     backdrop_filter_quality, copied_render_pass_id);
+                     filters_scale, filters_origin, tex_coord_rect,
+                     force_anti_aliasing_off, backdrop_filter_quality,
+                     copied_render_pass_id);
   EXPECT_EQ(DrawQuad::Material::kRenderPass, copy_quad->material);
   EXPECT_EQ(visible_rect, copy_quad->visible_rect);
   EXPECT_EQ(copied_render_pass_id, copy_quad->render_pass_id);
@@ -215,24 +214,6 @@
   EXPECT_EQ(mask_uv_rect.ToString(), copy_quad->mask_uv_rect.ToString());
   EXPECT_EQ(mask_texture_size.ToString(),
             copy_quad->mask_texture_size.ToString());
-  EXPECT_EQ(mask_applies_to_backdrop, copy_quad->mask_applies_to_backdrop);
-  EXPECT_EQ(filters_scale, copy_quad->filters_scale);
-  EXPECT_EQ(filters_origin, copy_quad->filters_origin);
-  EXPECT_EQ(tex_coord_rect.ToString(), copy_quad->tex_coord_rect.ToString());
-
-  mask_applies_to_backdrop = true;
-  CREATE_QUAD_ALL_RP(RenderPassDrawQuad, render_pass_id, mask_resource_id,
-                     mask_uv_rect, mask_texture_size, mask_applies_to_backdrop,
-                     filters_scale, filters_origin, tex_coord_rect,
-                     force_anti_aliasing_off, backdrop_filter_quality,
-                     copied_render_pass_id);
-  EXPECT_EQ(DrawQuad::Material::kRenderPass, copy_quad->material);
-  EXPECT_EQ(copied_render_pass_id, copy_quad->render_pass_id);
-  EXPECT_EQ(mask_resource_id, copy_quad->mask_resource_id());
-  EXPECT_EQ(mask_uv_rect.ToString(), copy_quad->mask_uv_rect.ToString());
-  EXPECT_EQ(mask_texture_size.ToString(),
-            copy_quad->mask_texture_size.ToString());
-  EXPECT_EQ(mask_applies_to_backdrop, copy_quad->mask_applies_to_backdrop);
   EXPECT_EQ(filters_scale, copy_quad->filters_scale);
   EXPECT_EQ(filters_origin, copy_quad->filters_origin);
   EXPECT_EQ(tex_coord_rect.ToString(), copy_quad->tex_coord_rect.ToString());
@@ -548,7 +529,6 @@
   ResourceId mask_resource_id = 78;
   gfx::RectF mask_uv_rect(0.f, 0.f, 33.f, 19.f);
   gfx::Size mask_texture_size(128, 134);
-  bool mask_applies_to_backdrop = false;
   gfx::Vector2dF filters_scale(2.f, 3.f);
   gfx::PointF filters_origin(0.f, 0.f);
   gfx::RectF tex_coord_rect(1.f, 1.f, 33.f, 19.f);
@@ -560,33 +540,19 @@
   CREATE_SHARED_STATE();
   CREATE_QUAD_NEW_RP(RenderPassDrawQuad, visible_rect, render_pass_id,
                      mask_resource_id, mask_uv_rect, mask_texture_size,
-                     mask_applies_to_backdrop, filters_scale, filters_origin,
-                     tex_coord_rect, force_anti_aliasing_off,
-                     backdrop_filter_quality, copied_render_pass_id);
+                     filters_scale, filters_origin, tex_coord_rect,
+                     force_anti_aliasing_off, backdrop_filter_quality,
+                     copied_render_pass_id);
   EXPECT_EQ(mask_resource_id, quad_new->mask_resource_id());
   EXPECT_EQ(1, IterateAndCount(quad_new));
   EXPECT_EQ(mask_resource_id + 1, quad_new->mask_resource_id());
-  EXPECT_EQ(mask_applies_to_backdrop, quad_new->mask_applies_to_backdrop);
 
-  mask_applies_to_backdrop = true;
-  quad_new->SetNew(shared_state, visible_rect, visible_rect, render_pass_id,
-                   mask_resource_id, mask_uv_rect, mask_texture_size,
-                   mask_applies_to_backdrop, filters_scale, filters_origin,
-                   tex_coord_rect, force_anti_aliasing_off,
-                   backdrop_filter_quality);
-  EXPECT_EQ(mask_resource_id, quad_new->mask_resource_id());
-  EXPECT_EQ(1, IterateAndCount(quad_new));
-  EXPECT_EQ(mask_resource_id + 1, quad_new->mask_resource_id());
-  EXPECT_EQ(mask_applies_to_backdrop, quad_new->mask_applies_to_backdrop);
-
-  mask_applies_to_backdrop = false;
   ResourceId new_mask_resource_id = 0;
   gfx::Rect quad_rect(30, 40, 50, 60);
   quad_new->SetNew(shared_state, quad_rect, visible_rect, render_pass_id,
                    new_mask_resource_id, mask_uv_rect, mask_texture_size,
-                   mask_applies_to_backdrop, filters_scale, filters_origin,
-                   tex_coord_rect, force_anti_aliasing_off,
-                   backdrop_filter_quality);
+                   filters_scale, filters_origin, tex_coord_rect,
+                   force_anti_aliasing_off, backdrop_filter_quality);
   EXPECT_EQ(0, IterateAndCount(quad_new));
   EXPECT_EQ(0u, quad_new->mask_resource_id());
 }
diff --git a/components/viz/common/quads/render_pass_draw_quad.cc b/components/viz/common/quads/render_pass_draw_quad.cc
index d2e5017f..096a0013 100644
--- a/components/viz/common/quads/render_pass_draw_quad.cc
+++ b/components/viz/common/quads/render_pass_draw_quad.cc
@@ -26,7 +26,6 @@
                                 ResourceId mask_resource_id,
                                 const gfx::RectF& mask_uv_rect,
                                 const gfx::Size& mask_texture_size,
-                                bool mask_applies_to_backdrop,
                                 const gfx::Vector2dF& filters_scale,
                                 const gfx::PointF& filters_origin,
                                 const gfx::RectF& tex_coord_rect,
@@ -36,9 +35,9 @@
 
   bool needs_blending = true;
   SetAll(shared_quad_state, rect, visible_rect, needs_blending, render_pass_id,
-         mask_resource_id, mask_uv_rect, mask_texture_size,
-         mask_applies_to_backdrop, filters_scale, filters_origin,
-         tex_coord_rect, force_anti_aliasing_off, backdrop_filter_quality);
+         mask_resource_id, mask_uv_rect, mask_texture_size, filters_scale,
+         filters_origin, tex_coord_rect, force_anti_aliasing_off,
+         backdrop_filter_quality);
 }
 
 void RenderPassDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
@@ -49,7 +48,6 @@
                                 ResourceId mask_resource_id,
                                 const gfx::RectF& mask_uv_rect,
                                 const gfx::Size& mask_texture_size,
-                                bool mask_applies_to_backdrop,
                                 const gfx::Vector2dF& filters_scale,
                                 const gfx::PointF& filters_origin,
                                 const gfx::RectF& tex_coord_rect,
@@ -64,7 +62,6 @@
   resources.count = mask_resource_id ? 1 : 0;
   this->mask_uv_rect = mask_uv_rect;
   this->mask_texture_size = mask_texture_size;
-  this->mask_applies_to_backdrop = mask_applies_to_backdrop;
   this->filters_scale = filters_scale;
   this->filters_origin = filters_origin;
   this->tex_coord_rect = tex_coord_rect;
@@ -85,7 +82,6 @@
   value->SetInteger("mask_resource_id", resources.ids[kMaskResourceIdIndex]);
   cc::MathUtil::AddToTracedValue("mask_texture_size", mask_texture_size, value);
   cc::MathUtil::AddToTracedValue("mask_uv_rect", mask_uv_rect, value);
-  value->SetBoolean("mask_applies_to_backdrop", mask_applies_to_backdrop);
   cc::MathUtil::AddToTracedValue("tex_coord_rect", tex_coord_rect, value);
   value->SetBoolean("force_anti_aliasing_off", force_anti_aliasing_off);
   value->SetDouble("backdrop_filter_quality", backdrop_filter_quality);
diff --git a/components/viz/common/quads/render_pass_draw_quad.h b/components/viz/common/quads/render_pass_draw_quad.h
index ccb08c3..6d68a8d 100644
--- a/components/viz/common/quads/render_pass_draw_quad.h
+++ b/components/viz/common/quads/render_pass_draw_quad.h
@@ -34,7 +34,6 @@
               ResourceId mask_resource_id,
               const gfx::RectF& mask_uv_rect,
               const gfx::Size& mask_texture_size,
-              bool mask_applies_to_backdrop,
               const gfx::Vector2dF& filters_scale,
               const gfx::PointF& filters_origin,
               const gfx::RectF& tex_coord_rect,
@@ -49,7 +48,6 @@
               ResourceId mask_resource_id,
               const gfx::RectF& mask_uv_rect,
               const gfx::Size& mask_texture_size,
-              bool mask_applies_to_backdrop,
               const gfx::Vector2dF& filters_scale,
               const gfx::PointF& filters_origin,
               const gfx::RectF& tex_coord_rect,
@@ -75,7 +73,6 @@
   float backdrop_filter_quality;
 
   bool force_anti_aliasing_off;
-  bool mask_applies_to_backdrop;
 
   ResourceId mask_resource_id() const {
     return resources.ids[kMaskResourceIdIndex];
diff --git a/components/viz/common/quads/render_pass_unittest.cc b/components/viz/common/quads/render_pass_unittest.cc
index 3f083eb2..48a39eac 100644
--- a/components/viz/common/quads/render_pass_unittest.cc
+++ b/components/viz/common/quads/render_pass_unittest.cc
@@ -227,8 +227,8 @@
   auto pass_quad = std::make_unique<RenderPassDrawQuad>();
   pass_quad->SetNew(pass->shared_quad_state_list.back(), contrib_output_rect,
                     contrib_output_rect, contrib_id, 0, gfx::RectF(),
-                    gfx::Size(), false, gfx::Vector2dF(), gfx::PointF(),
-                    gfx::RectF(), false, 1.0f);
+                    gfx::Size(), gfx::Vector2dF(), gfx::PointF(), gfx::RectF(),
+                    false, 1.0f);
 
   pass_list.push_back(std::move(pass));
   pass_list.push_back(std::move(contrib));
diff --git a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
index 9116f9c..e072576 100644
--- a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
+++ b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
@@ -332,7 +332,6 @@
                /*mask_resource_id=*/ResourceId(),
                /*mask_uv_rect=*/gfx::RectF(),
                /*mask_texture_size=*/gfx::Size(),
-               /*mask_applies_to_backdrop=*/false,
                /*filters_scale=*/gfx::Vector2dF(),
                /*filters_origin=*/gfx::PointF(),
                /*tex_coord_rect=*/tex_coord_rect,
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index e2f3b6f..94d630c3 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -2569,7 +2569,7 @@
                                SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad1->SetNew(shared_quad_state2, rect1, rect1, render_pass_id,
-                  mask_resource_id, gfx::RectF(), gfx::Size(), false,
+                  mask_resource_id, gfx::RectF(), gfx::Size(),
                   gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
                   1.0f);
     EXPECT_EQ(1u, frame.render_pass_list.front()->quad_list.size());
@@ -2815,10 +2815,10 @@
                                rect4, is_clipped, opaque_content, opacity,
                                SkBlendMode::kSrcOver, 0);
     R1->SetNew(shared_quad_state, rect1, rect1, render_pass_id,
-               mask_resource_id, gfx::RectF(), gfx::Size(), false,
+               mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
     R2->SetNew(shared_quad_state, rect2, rect2, render_pass_id,
-               mask_resource_id, gfx::RectF(), gfx::Size(), false,
+               mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
     D1->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
     D2->SetNew(shared_quad_state4, rect4, rect4, SK_ColorBLACK, false);
@@ -2863,10 +2863,10 @@
                                rect6, is_clipped, opaque_content, opacity,
                                SkBlendMode::kSrcOver, 0);
     R1->SetNew(shared_quad_state, rect5, rect5, render_pass_id,
-               mask_resource_id, gfx::RectF(), gfx::Size(), false,
+               mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
     R2->SetNew(shared_quad_state, rect1, rect1, render_pass_id,
-               mask_resource_id, gfx::RectF(), gfx::Size(), false,
+               mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
     D1->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
     D2->SetNew(shared_quad_state4, rect6, rect6, SK_ColorBLACK, false);
@@ -2910,10 +2910,10 @@
                                rect7, is_clipped, opaque_content, opacity,
                                SkBlendMode::kSrcOver, 0);
     R1->SetNew(shared_quad_state, rect5, rect5, render_pass_id,
-               mask_resource_id, gfx::RectF(), gfx::Size(), false,
+               mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
     R2->SetNew(shared_quad_state, rect1, rect1, render_pass_id,
-               mask_resource_id, gfx::RectF(), gfx::Size(), false,
+               mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
     D1->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
     D2->SetNew(shared_quad_state4, rect7, rect7, SK_ColorBLACK, false);
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index 8c67161c..e138b733 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -982,72 +982,6 @@
     paint.setImageFilter(
         SkiaHelper::BuildOpacityFilter(quad->shared_quad_state->opacity));
   }
-  // Apply the mask image, if present, to filtered backdrop content. Note that
-  // this needs to be performed here, in addition to elsewhere, because of the
-  // order of operations:
-  //   1. Render the child render pass (containing backdrop-filtered element),
-  //      including any masks, typically built as child DstIn layers.
-  //   2. Render the parent render pass (containing the "backdrop image" to be
-  //      filtered).
-  //   3. Run this code, to filter, and possibly mask, the backdrop image.
-  sk_sp<const SkImage> mask_image = nullptr;
-  base::Optional<DisplayResourceProvider::ScopedReadLockSkImage>
-      backdrop_image_lock_sk;
-  base::Optional<DisplayResourceProvider::ScopedSamplerGL>
-      backdrop_image_lock_gl;
-  if (quad->mask_applies_to_backdrop && quad->mask_resource_id()) {
-    if (resource_provider_->GetResourceTextureTarget(
-            quad->mask_resource_id()) == GL_TEXTURE_RECTANGLE_ARB) {
-      // On some platforms, Skia doesn't know that the hardware supports
-      // GL_TEXTURE_RECTANGLE. So for texture rectangles, fall back to using
-      // CopyTextureCHROMIUM to copy from the mask resource to a newly-created
-      // texture, and then wrap that texture with an SkImage.
-      backdrop_image_lock_gl.emplace(resource_provider_,
-                                     quad->mask_resource_id(), GL_LINEAR);
-      GLenum source_id = backdrop_image_lock_gl->texture_id();
-      GLint internalformat = GLInternalFormat(
-          resource_provider_->GetResourceFormat(quad->mask_resource_id()));
-      GLuint dest_id;
-      gl_->GenTextures(1, &dest_id);
-      gl_->BindTexture(GL_TEXTURE_2D, dest_id);
-      // Format is the same as internalformat for the formats being considered.
-      GLint format = internalformat;
-      gl_->TexImage2D(GL_TEXTURE_2D, 0, internalformat,
-                      quad->mask_texture_size.width(),
-                      quad->mask_texture_size.height(), 0, format,
-                      GL_UNSIGNED_BYTE, nullptr);
-      gl_->CopyTextureCHROMIUM(source_id, 0, GL_TEXTURE_2D, dest_id, 0,
-                               internalformat, GL_UNSIGNED_BYTE,
-                               /*unpack_flip_y=*/false,
-                               /*unpack_premultiply_alpha=*/false,
-                               /*unpack_unmultiply_alpha=*/false);
-      mask_image = WrapTexture(dest_id, GL_TEXTURE_2D, quad->mask_texture_size,
-                               use_gr_context->context(),
-                               /*flip_texture=*/!FlippedFramebuffer(),
-                               GlFormatToSkFormat(internalformat),
-                               /*adopt_texture=*/true);
-    } else {
-      backdrop_image_lock_sk.emplace(
-          resource_provider_, quad->mask_resource_id(), kPremul_SkAlphaType,
-          kTopLeft_GrSurfaceOrigin);
-      mask_image = backdrop_image_lock_sk->TakeSkImage();
-    }
-    DCHECK(mask_image);
-  }
-  if (mask_image) {
-    // Scale normalized uv rect into absolute texel coordinates.
-    SkRect mask_rect = gfx::RectFToSkRect(
-        gfx::ScaleRect(quad->mask_uv_rect, quad->mask_texture_size.width(),
-                       quad->mask_texture_size.height()));
-    // Map to full quad rect so that mask coordinates don't change with
-    // clipping.
-    SkMatrix mask_to_quad_matrix = SkMatrix::MakeRectToRect(
-        mask_rect, gfx::RectToSkRect(quad->rect), SkMatrix::kFill_ScaleToFit);
-    paint.setMaskFilter(
-        SkShaderMaskFilter::Make(mask_image->makeShader(&mask_to_quad_matrix)));
-    DCHECK(paint.getMaskFilter());
-  }
-
   // Now paint the pre-filtered image onto the canvas (possibly with mask
   // applied).
   surface->getCanvas()->drawImageRect(filtered_image, subset, dest_rect,
@@ -1246,9 +1180,8 @@
         }
       }
       if (params->background_image_id) {
-        // Reset original background texture if there is not any mask, or if the
-        // mask was used for backdrop filter only.
-        if (!quad->mask_resource_id() || quad->mask_applies_to_backdrop) {
+        // Reset original background texture if there is not any mask.
+        if (!quad->mask_resource_id()) {
           gl_->DeleteTextures(1, &params->background_texture);
           params->background_texture = 0;
         }
@@ -1276,7 +1209,6 @@
   if (params->background_texture && params->background_image_id) {
     DCHECK(params->mask_for_background);
     DCHECK(quad->mask_resource_id());
-    DCHECK(!quad->mask_applies_to_backdrop);
   }
 
   DCHECK_EQ(params->background_texture || params->background_image_id,
@@ -1377,8 +1309,7 @@
 
 void GLRenderer::UpdateRPDQTexturesForSampling(
     DrawRenderPassDrawQuadParams* params) {
-  if (!params->quad->mask_applies_to_backdrop &&
-      params->quad->mask_resource_id()) {
+  if (params->quad->mask_resource_id()) {
     params->mask_resource_lock.reset(
         new DisplayResourceProvider::ScopedSamplerGL(
             resource_provider_, params->quad->mask_resource_id(), GL_TEXTURE1,
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index 74c2eca..aa340b5b 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -1386,7 +1386,7 @@
   RenderPassDrawQuad* quad =
       pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   quad->SetNew(pass->shared_quad_state_list.back(), kOverlayRect, kOverlayRect,
-               render_pass_id, 0, gfx::RectF(), gfx::Size(), false,
+               render_pass_id, 0, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
 
   CreateFullscreenCandidateQuad(
@@ -2840,7 +2840,7 @@
   auto* child_pass1_rpdq =
       root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   child_pass1_rpdq->SetNew(child_pass1_sqs, unit_rect, unit_rect,
-                           child_pass1_id, 0, gfx::RectF(), gfx::Size(), false,
+                           child_pass1_id, 0, gfx::RectF(), gfx::Size(),
                            gfx::Vector2dF(), gfx::PointF(),
                            gfx::RectF(0, 0, 1, 1), false, 1.0f);
 
@@ -2853,7 +2853,7 @@
   auto* child_pass2_rpdq =
       root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   child_pass2_rpdq->SetNew(child_pass2_sqs, unit_rect, unit_rect,
-                           child_pass2_id, 0, gfx::RectF(), gfx::Size(), false,
+                           child_pass2_id, 0, gfx::RectF(), gfx::Size(),
                            gfx::Vector2dF(), gfx::PointF(),
                            gfx::RectF(0, 0, 1, 1), false, 1.0f);
 
@@ -3711,8 +3711,7 @@
 TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadNoFilters) {
   quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
                 kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
-                false, gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
-                1.0f);
+                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
   ProcessForOverlays();
 
   EXPECT_EQ(1U, ca_layer_list_.size());
@@ -3733,8 +3732,7 @@
   render_pass_filters_[render_pass_id_] = &filters_;
   quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
                 kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
-                false, gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
-                1.0f);
+                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
   ProcessForOverlays();
 
   EXPECT_EQ(1U, ca_layer_list_.size());
@@ -3745,8 +3743,7 @@
   render_pass_filters_[render_pass_id_] = &filters_;
   quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
                 kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
-                false, gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false,
-                1.0f);
+                gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false, 1.0f);
   ProcessForOverlays();
   EXPECT_EQ(1U, ca_layer_list_.size());
 }
@@ -3756,8 +3753,7 @@
   render_pass_filters_[render_pass_id_] = &filters_;
   quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
                 kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
-                false, gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false,
-                1.0f);
+                gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false, 1.0f);
   ProcessForOverlays();
   EXPECT_EQ(1U, ca_layer_list_.size());
 }
@@ -3768,8 +3764,7 @@
   render_pass_filters_[render_pass_id_] = &filters_;
   quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
                 kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
-                false, gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false,
-                1.0f);
+                gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false, 1.0f);
   ProcessForOverlays();
   EXPECT_EQ(1U, ca_layer_list_.size());
 }
@@ -3779,19 +3774,7 @@
   render_pass_backdrop_filters_[render_pass_id_] = &backdrop_filters_;
   quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
                 kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
-                false, gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
-                1.0f);
-  ProcessForOverlays();
-  EXPECT_EQ(0U, ca_layer_list_.size());
-}
-
-TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBackgroundFilterWithMask) {
-  backdrop_filters_.Append(cc::FilterOperation::CreateGrayscaleFilter(0.1f));
-  render_pass_backdrop_filters_[render_pass_id_] = &backdrop_filters_;
-  quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
-                kOverlayRect, render_pass_id_, 2, gfx::RectF(), gfx::Size(),
-                true, gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
-                1.0f);
+                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
   ProcessForOverlays();
   EXPECT_EQ(0U, ca_layer_list_.size());
 }
@@ -3799,8 +3782,7 @@
 TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadMask) {
   quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
                 kOverlayRect, render_pass_id_, 2, gfx::RectF(), gfx::Size(),
-                false, gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
-                1.0f);
+                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
   ProcessForOverlays();
   EXPECT_EQ(1U, ca_layer_list_.size());
 }
@@ -3810,8 +3792,7 @@
   render_pass_filters_[render_pass_id_] = &filters_;
   quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
                 kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
-                false, gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
-                1.0f);
+                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
   ProcessForOverlays();
   EXPECT_EQ(0U, ca_layer_list_.size());
 }
@@ -3824,8 +3805,8 @@
     auto* quad = pass_->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
     quad->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
                  kOverlayRect, render_pass_id_, 2, gfx::RectF(), gfx::Size(),
-                 false, gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
-                 false, 1.0f);
+                 gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                 1.0f);
   }
 
   ProcessForOverlays();
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index e86e5ea..62b35741 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -182,7 +182,6 @@
                0,                 // mask_resource_id
                gfx::RectF(),      // mask_uv_rect
                gfx::Size(),       // mask_texture_size
-               false,             // mask_applies_to_backdrop
                gfx::Vector2dF(),  // filters scale
                gfx::PointF(),     // filter origin
                gfx::RectF(rect),  // tex_coord_rect
@@ -2086,7 +2085,7 @@
   auto* render_pass_quad =
       root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect,
-                           child_pass_id, 0, gfx::RectF(), gfx::Size(), false,
+                           child_pass_id, 0, gfx::RectF(), gfx::Size(),
                            gfx::Vector2dF(), gfx::PointF(),
                            gfx::RectF(pass_rect), false, 1.0f);
 
@@ -2146,7 +2145,7 @@
   auto* render_pass_quad =
       root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect,
-                           child_pass_id, 0, gfx::RectF(), gfx::Size(), false,
+                           child_pass_id, 0, gfx::RectF(), gfx::Size(),
                            gfx::Vector2dF(), gfx::PointF(),
                            gfx::RectF(pass_rect), false, 1.0f);
 
@@ -2207,7 +2206,7 @@
   auto* render_pass_quad =
       root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect,
-                           child_pass_id, 0, gfx::RectF(), gfx::Size(), false,
+                           child_pass_id, 0, gfx::RectF(), gfx::Size(),
                            gfx::Vector2dF(), gfx::PointF(),
                            gfx::RectF(pass_rect), false, 1.0f);
 
@@ -2289,7 +2288,7 @@
   auto* render_pass_quad =
       root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect,
-                           child_pass_id, 0, gfx::RectF(), gfx::Size(), false,
+                           child_pass_id, 0, gfx::RectF(), gfx::Size(),
                            gfx::Vector2dF(), gfx::PointF(),
                            gfx::RectF(pass_rect), false, 1.0f);
 
@@ -2481,7 +2480,6 @@
       gfx::ScaleRect(gfx::RectF(sub_rect), 2.f / mask_rect.width(),
                      2.f / mask_rect.height()),  // mask_uv_rect
       gfx::Size(mask_rect.size()),               // mask_texture_size
-      false,                                     // mask_applies_to_backdrop
       gfx::Vector2dF(),                          // filters scale
       gfx::PointF(),                             // filter origin
       gfx::RectF(sub_rect),                      // tex_coord_rect
@@ -2579,7 +2577,6 @@
       gfx::ScaleRect(gfx::RectF(sub_rect), 2.f / mask_rect.width(),
                      2.f / mask_rect.height()),  // mask_uv_rect
       gfx::Size(mask_rect.size()),               // mask_texture_size
-      false,                                     // mask_applies_to_backdrop
       gfx::Vector2dF(),                          // filters scale
       gfx::PointF(),                             // filter origin
       gfx::RectF(sub_rect),                      // tex_coord_rect
@@ -2665,7 +2662,6 @@
       gfx::ScaleRect(gfx::RectF(viewport_rect), 1.f / mask_rect.width(),
                      1.f / mask_rect.height()),  // mask_uv_rect
       gfx::Size(mask_rect.size()),               // mask_texture_size
-      false,                                     // mask_applies_to_backdrop
       gfx::Vector2dF(),                          // filters scale
       gfx::PointF(),                             // filter origin
       gfx::RectF(viewport_rect),                 // tex_coord_rect
@@ -2770,7 +2766,6 @@
       gfx::ScaleRect(gfx::RectF(viewport_rect), 1.f / mask_rect.width(),
                      1.f / mask_rect.height()),  // mask_uv_rect
       gfx::Size(mask_rect.size()),               // mask_texture_size
-      false,                                     // mask_applies_to_backdrop
       gfx::Vector2dF(),                          // filters scale
       gfx::PointF(),                             // filter origin
       gfx::RectF(viewport_rect),                 // tex_coord_rect
@@ -2888,7 +2883,6 @@
                                filter_pass_layer_rect_, filter_pass_id,
                                mapped_mask_resource_id, mask_uv_rect,
                                mask_texture_size,
-                               true,  // mask_applies_to_backdrop
                                gfx::Vector2dF(1.0f, 1.0f),  // filters_scale
                                gfx::PointF(),               // filters_origin
                                gfx::RectF(),                // tex_coord_rect
@@ -3304,7 +3298,7 @@
   RenderPassDrawQuad* pass_quad =
       root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   pass_quad->SetAll(pass_shared_state, rect, rect, needs_blending,
-                    child_pass_id, 0, gfx::RectF(), gfx::Size(), false,
+                    child_pass_id, 0, gfx::RectF(), gfx::Size(),
                     gfx::Vector2dF(), gfx::PointF(), gfx::RectF(rect),
                     force_anti_aliasing_off, backdrop_filter_quality);
 
@@ -3468,7 +3462,7 @@
       root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   child_pass_quad->SetNew(child_pass_shared_state, child_pass_rect,
                           child_pass_rect, child_pass_id, 0, gfx::RectF(),
-                          gfx::Size(), false, gfx::Vector2dF(), gfx::PointF(),
+                          gfx::Size(), gfx::Vector2dF(), gfx::PointF(),
                           gfx::RectF(child_pass_rect), false, 1.0f);
 
   RenderPassList pass_list;
diff --git a/components/viz/service/display/software_renderer.cc b/components/viz/service/display/software_renderer.cc
index e9ed3923..1f01fbe 100644
--- a/components/viz/service/display/software_renderer.cc
+++ b/components/viz/service/display/software_renderer.cc
@@ -522,7 +522,7 @@
                                       &content_mat);
   }
 
-  if (!quad->mask_applies_to_backdrop && quad->mask_resource_id()) {
+  if (quad->mask_resource_id()) {
     DisplayResourceProvider::ScopedReadLockSkImage mask_lock(
         resource_provider_, quad->mask_resource_id());
     if (!mask_lock.valid())
@@ -849,35 +849,6 @@
         SkiaHelper::BuildOpacityFilter(quad->shared_quad_state->opacity));
   }
 
-  // Apply the mask image, if present, to filtered backdrop content. Note that
-  // this needs to be performed here, in addition to elsewhere, because of the
-  // order of operations:
-  //   1. Render the child render pass (containing backdrop-filtered element),
-  //      including any masks, typically built as child DstIn layers.
-  //   2. Render the parent render pass (containing the "backdrop image" to be
-  //      filtered).
-  //   3. Run this code, to filter, and possibly mask, the backdrop image.
-  const SkImage* mask_image = nullptr;
-  base::Optional<DisplayResourceProvider::ScopedReadLockSkImage>
-      backdrop_image_lock;
-  if (quad->mask_applies_to_backdrop && quad->mask_resource_id()) {
-    backdrop_image_lock.emplace(resource_provider_, quad->mask_resource_id());
-    mask_image = backdrop_image_lock->sk_image();
-  }
-  if (mask_image) {
-    // Scale normalized uv rect into absolute texel coordinates.
-    SkRect mask_rect = gfx::RectFToSkRect(
-        gfx::ScaleRect(quad->mask_uv_rect, quad->mask_texture_size.width(),
-                       quad->mask_texture_size.height()));
-    // Map to full quad rect so that mask coordinates don't change with
-    // clipping.
-    SkMatrix mask_to_quad_matrix = SkMatrix::MakeRectToRect(
-        mask_rect, gfx::RectToSkRect(quad->rect), SkMatrix::kFill_ScaleToFit);
-    paint.setMaskFilter(
-        SkShaderMaskFilter::Make(mask_image->makeShader(&mask_to_quad_matrix)));
-    DCHECK(paint.getMaskFilter());
-  }
-
   // Now paint the pre-filtered image onto the canvas.
   SkRect src_rect =
       SkRect::MakeXYWH(0, 0, backdrop_bitmap.width(), backdrop_bitmap.height());
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index bac5070..d2a50d2 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -668,8 +668,7 @@
     RenderPassId remapped_pass_id = RemapPassId(last_pass.id, surface_id);
     quad->SetNew(shared_quad_state, scaled_rect, scaled_visible_rect,
                  remapped_pass_id, 0, gfx::RectF(), gfx::Size(),
-                 /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(),
-                 gfx::PointF(), gfx::RectF(scaled_rect),
+                 gfx::Vector2dF(), gfx::PointF(), gfx::RectF(scaled_rect),
                  /*force_anti_aliasing_off=*/false,
                  /* backdrop_filter_quality*/ 1.0f);
   }
@@ -800,8 +799,7 @@
       color_conversion_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   quad->SetNew(shared_quad_state, output_rect, output_rect,
                root_render_pass->id, 0, gfx::RectF(), gfx::Size(),
-               /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(),
-               gfx::PointF(), gfx::RectF(output_rect),
+               gfx::Vector2dF(), gfx::PointF(), gfx::RectF(output_rect),
                /*force_anti_aliasing_off=*/false,
                /*backdrop_filter_quality*/ 1.0f);
   dest_pass_list_->push_back(std::move(color_conversion_pass));
@@ -851,8 +849,7 @@
       display_transform_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   quad->SetNew(shared_quad_state, output_rect, output_rect,
                root_render_pass->id, 0, gfx::RectF(), gfx::Size(),
-               /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(),
-               gfx::PointF(), gfx::RectF(output_rect),
+               gfx::Vector2dF(), gfx::PointF(), gfx::RectF(output_rect),
                /*force_anti_aliasing_off=*/false,
                /*backdrop_filter_quality*/ 1.0f);
   dest_pass_list_->push_back(std::move(display_transform_pass));
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index 9a766f0..f0363a8c 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -383,8 +383,8 @@
                          0);
     auto* quad = pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
     quad->SetNew(shared_state, output_rect, output_rect, render_pass_id, 0,
-                 gfx::RectF(), gfx::Size(), false, gfx::Vector2dF(),
-                 gfx::PointF(), gfx::RectF(), false, 1.0f);
+                 gfx::RectF(), gfx::Size(), gfx::Vector2dF(), gfx::PointF(),
+                 gfx::RectF(), false, 1.0f);
   }
 
   static void AddYUVVideoQuad(RenderPass* pass, const gfx::Rect& output_rect) {
diff --git a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc
index 0f910e3..7c845f5 100644
--- a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc
+++ b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc
@@ -280,22 +280,22 @@
   quad5_root_1->SetNew(shared_quad_state5_root, /*rect=*/rect5_root,
                        /*visible_rect=*/rect5_root, /*render_pass_id=*/2,
                        /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                       /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1),
-                       gfx::PointF(), gfx::RectF(), false, 1.0f);
+                       gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                       1.0f);
   auto* quad5_root_2 =
       pass5_root->quad_list.AllocateAndConstruct<RenderPassDrawQuad>();
   quad5_root_2->SetNew(shared_quad_state5_root, /*rect=*/rect5_root,
                        /*visible_rect=*/rect5_root, /*render_pass_id=*/3,
                        /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                       /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1),
-                       gfx::PointF(), gfx::RectF(), false, 1.0f);
+                       gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                       1.0f);
   auto* quad5_root_3 =
       pass5_root->quad_list.AllocateAndConstruct<RenderPassDrawQuad>();
   quad5_root_3->SetNew(shared_quad_state5_root, /*rect=*/rect5_root,
                        /*visible_rect=*/rect5_root, /*render_pass_id=*/4,
                        /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                       /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1),
-                       gfx::PointF(), gfx::RectF(), false, 1.0f);
+                       gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                       1.0f);
   pass_list.push_back(std::move(pass5_root));
 
   SendRenderPassList(&pass_list);
@@ -439,7 +439,6 @@
     quad3_root_1->SetNew(shared_quad_state3_root, /*rect=*/rect3_root,
                          /*visible_rect=*/rect3_root, /*render_pass_id=*/3,
                          /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                         /*mask_applies_to_backdrop=*/false,
                          gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
                          false, 1.0f);
     auto* quad3_root_2 =
@@ -447,7 +446,6 @@
     quad3_root_2->SetNew(shared_quad_state3_root, /*rect=*/rect3_root,
                          /*visible_rect=*/rect3_root, /*render_pass_id=*/4,
                          /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                         /*mask_applies_to_backdrop=*/false,
                          gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
                          false, 1.0f);
     pass_list.push_back(std::move(pass3_root));
@@ -516,7 +514,6 @@
     quad4_root_1->SetNew(shared_quad_state4_root, /*rect=*/rect4_root,
                          /*visible_rect=*/rect4_root, /*render_pass_id=*/5,
                          /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                         /*mask_applies_to_backdrop=*/false,
                          gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
                          false, 1.0f);
     auto* quad4_root_2 =
@@ -524,7 +521,6 @@
     quad4_root_2->SetNew(shared_quad_state4_root, /*rect=*/rect4_root,
                          /*visible_rect=*/rect4_root, /*render_pass_id=*/6,
                          /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                         /*mask_applies_to_backdrop=*/false,
                          gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
                          false, 1.0f);
     pass_list.push_back(std::move(pass4_root));
@@ -595,7 +591,6 @@
     quad5_root_1->SetNew(shared_quad_state5_root, /*rect=*/rect5_root,
                          /*visible_rect=*/rect5_root, /*render_pass_id=*/7,
                          /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                         /*mask_applies_to_backdrop=*/false,
                          gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
                          false, 1.0f);
     auto* quad5_root_2 =
@@ -603,7 +598,6 @@
     quad5_root_2->SetNew(shared_quad_state5_root, /*rect=*/rect5_root,
                          /*visible_rect=*/rect5_root, /*render_pass_id=*/8,
                          /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                         /*mask_applies_to_backdrop=*/false,
                          gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
                          false, 1.0f);
     pass_list.push_back(std::move(pass5_root));
@@ -675,7 +669,6 @@
     quad6_root_1->SetNew(shared_quad_state6_root, /*rect=*/rect6_root,
                          /*visible_rect=*/rect6_root, /*render_pass_id=*/9,
                          /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                         /*mask_applies_to_backdrop=*/false,
                          gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
                          false, 1.0f);
     auto* quad6_root_2 =
@@ -683,7 +676,6 @@
     quad6_root_2->SetNew(shared_quad_state6_root, /*rect=*/rect6_root,
                          /*visible_rect=*/rect6_root, /*render_pass_id=*/10,
                          /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(),
-                         /*mask_applies_to_backdrop=*/false,
                          gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
                          false, 1.0f);
     pass_list.push_back(std::move(pass6_root));
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index bc086ef..b1cf940 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -417,7 +417,7 @@
   // Gets the text offsets where new lines start.
   std::vector<int> GetLineStartOffsets() const;
 
-  virtual gfx::NativeViewAccessible GetNativeViewAccessible();
+  gfx::NativeViewAccessible GetNativeViewAccessible() override;
 
   // AXPosition Support
 
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index 62f17dc..e76ed011 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -3116,6 +3116,7 @@
                        NSAccessibilityStartTextMarkerAttribute,
                        NSAccessibilitySubroleAttribute,
                        NSAccessibilityTitleAttribute,
+                       NSAccessibilityTitleUIElementAttribute,
                        NSAccessibilityTopLevelUIElementAttribute,
                        NSAccessibilityValueAttribute,
                        NSAccessibilityVisitedAttribute,
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 0a11baa..a6ddde7 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -51,14 +51,13 @@
         switches::kUseFakeUIForMediaStream);
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kIgnoreCertificateErrors);
-    feature_list_.InitWithFeatures(FeaturesToEnable(), {});
+    feature_list_.InitAndEnableFeatureWithParameters(
+        features::kBackForwardCache, GetFeatureParams());
 
     ContentBrowserTest::SetUpCommandLine(command_line);
   }
 
-  virtual std::vector<base::Feature> FeaturesToEnable() {
-    return std::vector<base::Feature>({features::kBackForwardCache});
-  }
+  virtual base::FieldTrialParams GetFeatureParams() { return {}; }
 
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
@@ -1828,11 +1827,8 @@
   ~BackForwardCacheBrowserTestWithServiceWorkerEnabled() override {}
 
  protected:
-  std::vector<base::Feature> FeaturesToEnable() override {
-    std::vector<base::Feature> result =
-        BackForwardCacheBrowserTest::FeaturesToEnable();
-    result.push_back(kBackForwardCacheWithServiceWorker);
-    return result;
+  base::FieldTrialParams GetFeatureParams() override {
+    return {{"service_worker_supported", "true"}};
   }
 };
 
diff --git a/content/browser/frame_host/back_forward_cache.cc b/content/browser/frame_host/back_forward_cache.cc
index 3d67b7cc..90b7294 100644
--- a/content/browser/frame_host/back_forward_cache.cc
+++ b/content/browser/frame_host/back_forward_cache.cc
@@ -11,6 +11,7 @@
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/common/page_messages.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/navigation_policy.h"
 #include "net/http/http_status_code.h"
 #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
@@ -91,6 +92,12 @@
   return true;
 }
 
+bool IsServiceWorkerSupported() {
+  static constexpr base::FeatureParam<bool> service_worker_supported(
+      &features::kBackForwardCache, "service_worker_supported", false);
+  return service_worker_supported.Get();
+}
+
 uint64_t GetDisallowedFeatures() {
   // TODO(lowell): Finalize disallowed feature list, and test for each
   // disallowed feature.
@@ -120,7 +127,7 @@
 
   uint64_t result = kAlwaysDisallowedFeatures;
 
-  if (!base::FeatureList::IsEnabled(kBackForwardCacheWithServiceWorker)) {
+  if (!IsServiceWorkerSupported()) {
     result |=
         ToFeatureBit(WebSchedulerTrackedFeature::kServiceWorkerControlledPage);
   }
@@ -129,9 +136,6 @@
 
 }  // namespace
 
-const base::Feature kBackForwardCacheWithServiceWorker = {
-    "BackForwardCacheWithServiceWorker", base::FEATURE_DISABLED_BY_DEFAULT};
-
 BackForwardCache::BackForwardCache() : weak_factory_(this) {}
 BackForwardCache::~BackForwardCache() = default;
 
diff --git a/content/browser/frame_host/back_forward_cache.h b/content/browser/frame_host/back_forward_cache.h
index a23765df..af4e448 100644
--- a/content/browser/frame_host/back_forward_cache.h
+++ b/content/browser/frame_host/back_forward_cache.h
@@ -147,12 +147,6 @@
   DISALLOW_COPY_AND_ASSIGN(BackForwardCache);
 };
 
-// TODO(crbug.com/991082): We need to implement frozen frame enumeration
-// before we can properly support pages with ServiceWorker in back-forward
-// cache. This flag allows to bypass this restriction for local testing.
-// Remove after ServiceWorker support is implemented.
-CONTENT_EXPORT extern const base::Feature kBackForwardCacheWithServiceWorker;
-
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_FRAME_HOST_BACK_FORWARD_CACHE_H_
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 7ae9b631e..591e39e 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -487,10 +487,7 @@
                        const std::string& interface_name,
                        mojo::ScopedMessagePipeHandle* interface_pipe,
                        service_manager::Connector* connector) override {
-    if (!registry_.TryBindInterface(interface_name, interface_pipe)) {
-      GetContentClient()->browser()->BindInterfaceRequest(
-          source_info, interface_name, interface_pipe);
-    }
+    registry_.TryBindInterface(interface_name, interface_pipe);
   }
 
   service_manager::BinderRegistry registry_;
@@ -619,7 +616,11 @@
     base::PostTask(
         FROM_HERE, {BrowserThread::UI},
         base::BindOnce(&FieldTrialRecorder::Create, std::move(receiver)));
+    return;
   }
+
+  GetContentClient()->browser()->BindGpuHostReceiver(
+      std::move(generic_receiver));
 }
 
 void GpuProcessHost::RunService(
diff --git a/content/browser/snapshot_browsertest.cc b/content/browser/snapshot_browsertest.cc
index 3a190a62..4e2c122 100644
--- a/content/browser/snapshot_browsertest.cc
+++ b/content/browser/snapshot_browsertest.cc
@@ -320,13 +320,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SnapshotBrowserTest, MAYBE_AsyncMultiWindowTest) {
-  // TODO(jonross): Re-enable this once the root cause of the failure has been
-  // fixed. https://crbug.com/1003375
-  const base::CommandLine* command_line =
-      base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kUseVulkan))
-    return;
-
   SetupTestServer();
 
   for (int i = 0; i < 3; ++i) {
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index a9131e7..c021a78c 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -115,7 +115,6 @@
 namespace service_manager {
 class Identity;
 class Service;
-struct BindSourceInfo;
 }
 
 namespace net {
@@ -967,14 +966,9 @@
       const std::string& interface_name,
       mojo::ScopedMessagePipeHandle interface_pipe) {}
 
-  // (Currently called only from GPUProcessHost, move somewhere more central).
-  // Called when a request to bind |interface_name| on |interface_pipe| is
-  // received from |source_info.identity|. If the request is bound,
-  // |interface_pipe| will become invalid (taken by the client).
-  virtual void BindInterfaceRequest(
-      const service_manager::BindSourceInfo& source_info,
-      const std::string& interface_name,
-      mojo::ScopedMessagePipeHandle* interface_pipe) {}
+  // Handles an unhandled incoming interface binding request from the GPU
+  // process. Called on the IO thread.
+  virtual void BindGpuHostReceiver(mojo::GenericPendingReceiver receiver) {}
 
   // Called on the main thread to handle an unhandled interface receiver binding
   // request from a render process. See |RenderThread::BindHostReceiver()|.
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 7a098ee..26a7850 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1479,12 +1479,14 @@
       render_view->GetWidget(), web_frame);
 
   render_widget->Init(std::move(show_callback), web_frame_widget);
-
   render_view->AttachWebFrameWidget(web_frame_widget);
-  // TODO(crbug.com/419087): This was added in 6ccadf770766e89c3 to prevent an
-  // empty ScreenInfo, but the WebView has already been created and initialized
-  // by RenderViewImpl, so this is surely redundant?
+
+  // This call makes sure the page zoom is propagated to the provisional frame
+  // since it has to go through the WebViewImpl, and it may not be be changed
+  // in OnSynchronizeVisualProperties(), which would pass it along if it did
+  // change.
   render_widget->UpdateWebViewWithDeviceScaleFactor();
+  render_widget->OnSynchronizeVisualProperties(params->visual_properties);
 
   // The WebFrame created here was already attached to the Page as its
   // main frame, and the WebFrameWidget has been initialized, so we can call
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 6e9a34c..1ed4369a 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -534,9 +534,7 @@
   if (params->window_was_created_with_opener)
     webview()->SetOpenedByDOM();
 
-  GetWidget()->UpdateWebViewWithDeviceScaleFactor();
   OnSetRendererPrefs(*params->renderer_preferences);
-  GetWidget()->OnSynchronizeVisualProperties(params->visual_properties);
 
   GetContentClient()->renderer()->RenderViewCreated(this);
   page_zoom_level_ = 0;
diff --git a/content/test/content_test_launcher.cc b/content/test/content_test_launcher.cc
index 7fd5bcb2..37aec6f6 100644
--- a/content/test/content_test_launcher.cc
+++ b/content/test/content_test_launcher.cc
@@ -39,10 +39,9 @@
 
  protected:
   void Initialize() override {
-    // Browser tests are expected not to tear-down various globals and may
-    // complete with the thread priority being above NORMAL.
+    // Browser tests are expected not to tear-down various globals. (Must run
+    // before the base class is initialized.)
     base::TestSuite::DisableCheckForLeakedGlobals();
-    base::TestSuite::DisableCheckForThreadPriorityAtTestEnd();
 
     ContentTestSuiteBase::Initialize();
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index bc395e6..052ca8c 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -29,7 +29,7 @@
 [ android ] Pixel_Canvas2DTabSwitch_SoftwareCompositing [ Skip ]
 
 # Skip tab switching tests on Android webview since it doesn't have tabs
-[ android-webview-instrumentation ] Pixel_Canvas2DTabSwitch [ Skip ]
+[ android android-webview-instrumentation ] Pixel_Canvas2DTabSwitch [ Skip ]
 
 # Tests running with SwiftShader are skipped on platforms where SwiftShader
 # isn't supported.
@@ -187,6 +187,13 @@
 # Fails on Intel
 crbug.com/991289 [ linux skia-renderer intel ] Pixel_CSS3DBlueBox [ Skip ]
 
+# Fails when the browser features SkiaRenderer & Vulkan are enabled on Intel
+crbug.com/1004837 [ linux skia-renderer use-vulkan intel ] Pixel_Canvas2DRedBox [ Skip ]
+crbug.com/1004837 [ linux skia-renderer use-vulkan intel ] Pixel_Canvas2DTabSwitch [ Skip ]
+crbug.com/1004840 [ linux skia-renderer use-vulkan intel ] Pixel_WebGLGreenTriangle_AA_Alpha [ Skip ]
+crbug.com/1004840 [ linux skia-renderer use-vulkan intel ] Pixel_WebGLGreenTriangle_AA_NoAlpha [ Skip ]
+crbug.com/1004840 [ linux skia-renderer use-vulkan intel ] Pixel_WebGLGreenTriangle_NoAA_NoAlpha [ Skip ]
+
 # Fails when the browser features SkiaRenderer & Vulkan are enabled on Android
 crbug.com/969864 [ android skia-renderer use-vulkan ] Pixel_2DCanvasWebGL [ Failure ]
 crbug.com/969864 [ android skia-renderer use-vulkan ] Pixel_BackgroundImage [ Skip ]
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index 5dfd6535..7dcfcd0 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -522,7 +522,9 @@
     java_files = java_sources_needing_jni
     deps = [
       "//base:base_java",
+      "//base:jni_java",
       "//components/location/android:location_java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   }
 }
diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java
index b6067bfb..d106844 100644
--- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java
+++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java
@@ -19,6 +19,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNIAdditionalImport;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.components.location.LocationUtils;
 
 import java.util.List;
@@ -288,7 +289,8 @@
 
             // Object can be destroyed, but Android keeps calling onScanResult.
             if (mNativeBluetoothAdapterAndroid != 0) {
-                nativeCreateOrUpdateDeviceOnScan(mNativeBluetoothAdapterAndroid,
+                ChromeBluetoothAdapterJni.get().createOrUpdateDeviceOnScan(
+                        mNativeBluetoothAdapterAndroid, ChromeBluetoothAdapter.this,
                         result.getDevice().getAddress(), result.getDevice(),
                         result.getScanRecord_getDeviceName(), result.getRssi(), uuid_strings,
                         result.getScanRecord_getTxPowerLevel(), serviceDataKeys, serviceDataValues,
@@ -299,7 +301,8 @@
         @Override
         public void onScanFailed(int errorCode) {
             Log.w(TAG, "onScanFailed: %d", errorCode);
-            nativeOnScanFailed(mNativeBluetoothAdapterAndroid);
+            ChromeBluetoothAdapterJni.get().onScanFailed(
+                    mNativeBluetoothAdapterAndroid, ChromeBluetoothAdapter.this);
         }
     }
 
@@ -315,10 +318,12 @@
 
             switch (state) {
                 case BluetoothAdapter.STATE_ON:
-                    nativeOnAdapterStateChanged(mNativeBluetoothAdapterAndroid, true);
+                    ChromeBluetoothAdapterJni.get().onAdapterStateChanged(
+                            mNativeBluetoothAdapterAndroid, ChromeBluetoothAdapter.this, true);
                     break;
                 case BluetoothAdapter.STATE_OFF:
-                    nativeOnAdapterStateChanged(mNativeBluetoothAdapterAndroid, false);
+                    ChromeBluetoothAdapterJni.get().onAdapterStateChanged(
+                            mNativeBluetoothAdapterAndroid, ChromeBluetoothAdapter.this, false);
                     break;
                 default:
                     // do nothing
@@ -342,20 +347,21 @@
         }
     }
 
-    // ---------------------------------------------------------------------------------------------
-    // BluetoothAdapterAndroid C++ methods declared for access from java:
+    @NativeMethods
+    interface Natives {
+        // Binds to BluetoothAdapterAndroid::OnScanFailed.
+        void onScanFailed(long nativeBluetoothAdapterAndroid, ChromeBluetoothAdapter caller);
 
-    // Binds to BluetoothAdapterAndroid::OnScanFailed.
-    private native void nativeOnScanFailed(long nativeBluetoothAdapterAndroid);
+        // Binds to BluetoothAdapterAndroid::CreateOrUpdateDeviceOnScan.
+        void createOrUpdateDeviceOnScan(long nativeBluetoothAdapterAndroid,
+                ChromeBluetoothAdapter caller, String address,
+                Wrappers.BluetoothDeviceWrapper deviceWrapper, String localName, int rssi,
+                String[] advertisedUuids, int txPower, String[] serviceDataKeys,
+                Object[] serviceDataValues, int[] manufacturerDataKeys,
+                Object[] manufacturerDataValues);
 
-    // Binds to BluetoothAdapterAndroid::CreateOrUpdateDeviceOnScan.
-    private native void nativeCreateOrUpdateDeviceOnScan(long nativeBluetoothAdapterAndroid,
-            String address, Wrappers.BluetoothDeviceWrapper deviceWrapper, String localName,
-            int rssi, String[] advertisedUuids, int txPower, String[] serviceDataKeys,
-            Object[] serviceDataValues, int[] manufacturerDataKeys,
-            Object[] manufacturerDataValues);
-
-    // Binds to BluetoothAdapterAndroid::nativeOnAdapterStateChanged
-    private native void nativeOnAdapterStateChanged(
-            long nativeBluetoothAdapterAndroid, boolean powered);
+        // Binds to BluetoothAdapterAndroid::nativeOnAdapterStateChanged
+        void onAdapterStateChanged(
+                long nativeBluetoothAdapterAndroid, ChromeBluetoothAdapter caller, boolean powered);
+    }
 }
diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java
index da726ec8..e134b66 100644
--- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java
+++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java
@@ -13,6 +13,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNIAdditionalImport;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.base.metrics.RecordHistogram;
 
 import java.util.HashMap;
@@ -149,7 +150,8 @@
                                 status);
                     }
                     if (mNativeBluetoothDeviceAndroid != 0) {
-                        nativeOnConnectionStateChange(mNativeBluetoothDeviceAndroid, status,
+                        ChromeBluetoothDeviceJni.get().onConnectionStateChange(
+                                mNativeBluetoothDeviceAndroid, ChromeBluetoothDevice.this, status,
                                 newState == android.bluetooth.BluetoothProfile.STATE_CONNECTED);
                     }
                 }
@@ -185,10 +187,12 @@
                             // between service instances with the same UUID on this device.
                             String serviceInstanceId = getAddress() + "/"
                                     + service.getUuid().toString() + "," + service.getInstanceId();
-                            nativeCreateGattRemoteService(
-                                    mNativeBluetoothDeviceAndroid, serviceInstanceId, service);
+                            ChromeBluetoothDeviceJni.get().createGattRemoteService(
+                                    mNativeBluetoothDeviceAndroid, ChromeBluetoothDevice.this,
+                                    serviceInstanceId, service);
                         }
-                        nativeOnGattServicesDiscovered(mNativeBluetoothDeviceAndroid);
+                        ChromeBluetoothDeviceJni.get().onGattServicesDiscovered(
+                                mNativeBluetoothDeviceAndroid, ChromeBluetoothDevice.this);
                     }
                 }
             });
@@ -304,17 +308,19 @@
         }
     }
 
-    // ---------------------------------------------------------------------------------------------
-    // BluetoothAdapterDevice C++ methods declared for access from java:
+    @NativeMethods
+    interface Natives {
+        // Binds to BluetoothDeviceAndroid::OnConnectionStateChange.
+        void onConnectionStateChange(long nativeBluetoothDeviceAndroid,
+                ChromeBluetoothDevice caller, int status, boolean connected);
 
-    // Binds to BluetoothDeviceAndroid::OnConnectionStateChange.
-    private native void nativeOnConnectionStateChange(
-            long nativeBluetoothDeviceAndroid, int status, boolean connected);
+        // Binds to BluetoothDeviceAndroid::CreateGattRemoteService.
+        void createGattRemoteService(long nativeBluetoothDeviceAndroid,
+                ChromeBluetoothDevice caller, String instanceId,
+                Wrappers.BluetoothGattServiceWrapper serviceWrapper);
 
-    // Binds to BluetoothDeviceAndroid::CreateGattRemoteService.
-    private native void nativeCreateGattRemoteService(long nativeBluetoothDeviceAndroid,
-            String instanceId, Wrappers.BluetoothGattServiceWrapper serviceWrapper);
-
-    // Binds to BluetoothDeviceAndroid::GattServicesDiscovered.
-    private native void nativeOnGattServicesDiscovered(long nativeBluetoothDeviceAndroid);
+        // Binds to BluetoothDeviceAndroid::GattServicesDiscovered.
+        void onGattServicesDiscovered(
+                long nativeBluetoothDeviceAndroid, ChromeBluetoothDevice caller);
+    }
 }
diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java
index 06b63aaa..41344a1 100644
--- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java
+++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java
@@ -11,6 +11,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNIAdditionalImport;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.util.List;
 
@@ -63,7 +64,9 @@
     void onCharacteristicChanged(byte[] value) {
         Log.i(TAG, "onCharacteristicChanged");
         if (mNativeBluetoothRemoteGattCharacteristicAndroid != 0) {
-            nativeOnChanged(mNativeBluetoothRemoteGattCharacteristicAndroid, value);
+            ChromeBluetoothRemoteGattCharacteristicJni.get().onChanged(
+                    mNativeBluetoothRemoteGattCharacteristicAndroid,
+                    ChromeBluetoothRemoteGattCharacteristic.this, value);
         }
     }
 
@@ -71,7 +74,9 @@
         Log.i(TAG, "onCharacteristicRead status:%d==%s", status,
                 status == android.bluetooth.BluetoothGatt.GATT_SUCCESS ? "OK" : "Error");
         if (mNativeBluetoothRemoteGattCharacteristicAndroid != 0) {
-            nativeOnRead(mNativeBluetoothRemoteGattCharacteristicAndroid, status,
+            ChromeBluetoothRemoteGattCharacteristicJni.get().onRead(
+                    mNativeBluetoothRemoteGattCharacteristicAndroid,
+                    ChromeBluetoothRemoteGattCharacteristic.this, status,
                     mCharacteristic.getValue());
         }
     }
@@ -80,7 +85,9 @@
         Log.i(TAG, "onCharacteristicWrite status:%d==%s", status,
                 status == android.bluetooth.BluetoothGatt.GATT_SUCCESS ? "OK" : "Error");
         if (mNativeBluetoothRemoteGattCharacteristicAndroid != 0) {
-            nativeOnWrite(mNativeBluetoothRemoteGattCharacteristicAndroid, status);
+            ChromeBluetoothRemoteGattCharacteristicJni.get().onWrite(
+                    mNativeBluetoothRemoteGattCharacteristicAndroid,
+                    ChromeBluetoothRemoteGattCharacteristic.this, status);
         }
     }
 
@@ -156,27 +163,31 @@
         for (Wrappers.BluetoothGattDescriptorWrapper descriptor : descriptors) {
             String descriptorInstanceId =
                     mInstanceId + "/" + descriptor.getUuid().toString() + ";" + instanceIdCounter++;
-            nativeCreateGattRemoteDescriptor(mNativeBluetoothRemoteGattCharacteristicAndroid,
-                    descriptorInstanceId, descriptor, mChromeDevice);
+            ChromeBluetoothRemoteGattCharacteristicJni.get().createGattRemoteDescriptor(
+                    mNativeBluetoothRemoteGattCharacteristicAndroid,
+                    ChromeBluetoothRemoteGattCharacteristic.this, descriptorInstanceId, descriptor,
+                    mChromeDevice);
         }
     }
 
-    // ---------------------------------------------------------------------------------------------
-    // BluetoothAdapterDevice C++ methods declared for access from java:
+    @NativeMethods
+    interface Natives {
+        // Binds to BluetoothRemoteGattCharacteristicAndroid::OnChanged.
+        void onChanged(long nativeBluetoothRemoteGattCharacteristicAndroid,
+                ChromeBluetoothRemoteGattCharacteristic caller, byte[] value);
 
-    // Binds to BluetoothRemoteGattCharacteristicAndroid::OnChanged.
-    native void nativeOnChanged(long nativeBluetoothRemoteGattCharacteristicAndroid, byte[] value);
+        // Binds to BluetoothRemoteGattCharacteristicAndroid::OnRead.
+        void onRead(long nativeBluetoothRemoteGattCharacteristicAndroid,
+                ChromeBluetoothRemoteGattCharacteristic caller, int status, byte[] value);
 
-    // Binds to BluetoothRemoteGattCharacteristicAndroid::OnRead.
-    native void nativeOnRead(
-            long nativeBluetoothRemoteGattCharacteristicAndroid, int status, byte[] value);
+        // Binds to BluetoothRemoteGattCharacteristicAndroid::OnWrite.
+        void onWrite(long nativeBluetoothRemoteGattCharacteristicAndroid,
+                ChromeBluetoothRemoteGattCharacteristic caller, int status);
 
-    // Binds to BluetoothRemoteGattCharacteristicAndroid::OnWrite.
-    native void nativeOnWrite(long nativeBluetoothRemoteGattCharacteristicAndroid, int status);
-
-    // Binds to BluetoothRemoteGattCharacteristicAndroid::CreateGattRemoteDescriptor.
-    private native void nativeCreateGattRemoteDescriptor(
-            long nativeBluetoothRemoteGattCharacteristicAndroid, String instanceId,
-            Wrappers.BluetoothGattDescriptorWrapper descriptorWrapper,
-            ChromeBluetoothDevice chromeBluetoothDevice);
+        // Binds to BluetoothRemoteGattCharacteristicAndroid::CreateGattRemoteDescriptor.
+        void createGattRemoteDescriptor(long nativeBluetoothRemoteGattCharacteristicAndroid,
+                ChromeBluetoothRemoteGattCharacteristic caller, String instanceId,
+                Wrappers.BluetoothGattDescriptorWrapper descriptorWrapper,
+                ChromeBluetoothDevice chromeBluetoothDevice);
+    }
 }
diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java
index a90608b..4214c428 100644
--- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java
+++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java
@@ -8,6 +8,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNIAdditionalImport;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 /**
  * Exposes android.bluetooth.BluetoothGattDescriptor as necessary
@@ -50,8 +51,9 @@
         Log.i(TAG, "onDescriptorRead status:%d==%s", status,
                 status == android.bluetooth.BluetoothGatt.GATT_SUCCESS ? "OK" : "Error");
         if (mNativeBluetoothRemoteGattDescriptorAndroid != 0) {
-            nativeOnRead(
-                    mNativeBluetoothRemoteGattDescriptorAndroid, status, mDescriptor.getValue());
+            ChromeBluetoothRemoteGattDescriptorJni.get().onRead(
+                    mNativeBluetoothRemoteGattDescriptorAndroid,
+                    ChromeBluetoothRemoteGattDescriptor.this, status, mDescriptor.getValue());
         }
     }
 
@@ -59,7 +61,9 @@
         Log.i(TAG, "onDescriptorWrite status:%d==%s", status,
                 status == android.bluetooth.BluetoothGatt.GATT_SUCCESS ? "OK" : "Error");
         if (mNativeBluetoothRemoteGattDescriptorAndroid != 0) {
-            nativeOnWrite(mNativeBluetoothRemoteGattDescriptorAndroid, status);
+            ChromeBluetoothRemoteGattDescriptorJni.get().onWrite(
+                    mNativeBluetoothRemoteGattDescriptorAndroid,
+                    ChromeBluetoothRemoteGattDescriptor.this, status);
         }
     }
 
@@ -106,13 +110,14 @@
         return true;
     }
 
-    // ---------------------------------------------------------------------------------------------
-    // BluetoothAdapterDevice C++ methods declared for access from java:
+    @NativeMethods
+    interface Natives {
+        // Binds to BluetoothRemoteGattDescriptorAndroid::OnRead.
+        void onRead(long nativeBluetoothRemoteGattDescriptorAndroid,
+                ChromeBluetoothRemoteGattDescriptor caller, int status, byte[] value);
 
-    // Binds to BluetoothRemoteGattDescriptorAndroid::OnRead.
-    native void nativeOnRead(
-            long nativeBluetoothRemoteGattDescriptorAndroid, int status, byte[] value);
-
-    // Binds to BluetoothRemoteGattDescriptorAndroid::OnWrite.
-    native void nativeOnWrite(long nativeBluetoothRemoteGattDescriptorAndroid, int status);
+        // Binds to BluetoothRemoteGattDescriptorAndroid::OnWrite.
+        void onWrite(long nativeBluetoothRemoteGattDescriptorAndroid,
+                ChromeBluetoothRemoteGattDescriptor caller, int status);
+    }
 }
diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattService.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattService.java
index 6f2989eb..fd6803e 100644
--- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattService.java
+++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattService.java
@@ -8,6 +8,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNIAdditionalImport;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.util.List;
 
@@ -46,9 +47,6 @@
         mNativeBluetoothRemoteGattServiceAndroid = 0;
     }
 
-    // ---------------------------------------------------------------------------------------------
-    // BluetoothRemoteGattServiceAndroid methods implemented in java:
-
     // Implements BluetoothRemoteGattServiceAndroid::Create.
     @CalledByNative
     private static ChromeBluetoothRemoteGattService create(
@@ -76,17 +74,18 @@
             // characteristic instances with the same UUID on this service.
             String characteristicInstanceId = mInstanceId + "/"
                     + characteristic.getUuid().toString() + "," + characteristic.getInstanceId();
-            nativeCreateGattRemoteCharacteristic(mNativeBluetoothRemoteGattServiceAndroid,
+            ChromeBluetoothRemoteGattServiceJni.get().createGattRemoteCharacteristic(
+                    mNativeBluetoothRemoteGattServiceAndroid, ChromeBluetoothRemoteGattService.this,
                     characteristicInstanceId, characteristic, mChromeDevice);
         }
     }
 
-    // ---------------------------------------------------------------------------------------------
-    // BluetoothAdapterDevice C++ methods declared for access from java:
-
-    // Binds to BluetoothRemoteGattServiceAndroid::CreateGattRemoteCharacteristic.
-    private native void nativeCreateGattRemoteCharacteristic(
-            long nativeBluetoothRemoteGattServiceAndroid, String instanceId,
-            Wrappers.BluetoothGattCharacteristicWrapper characteristicWrapper,
-            ChromeBluetoothDevice chromeBluetoothDevice);
+    @NativeMethods
+    interface Natives {
+        // Binds to BluetoothRemoteGattServiceAndroid::CreateGattRemoteCharacteristic.
+        void createGattRemoteCharacteristic(long nativeBluetoothRemoteGattServiceAndroid,
+                ChromeBluetoothRemoteGattService caller, String instanceId,
+                Wrappers.BluetoothGattCharacteristicWrapper characteristicWrapper,
+                ChromeBluetoothDevice chromeBluetoothDevice);
+    }
 }
diff --git a/device/gamepad/BUILD.gn b/device/gamepad/BUILD.gn
index 67d823d6..8442e95 100644
--- a/device/gamepad/BUILD.gn
+++ b/device/gamepad/BUILD.gn
@@ -184,8 +184,10 @@
     ]
     deps = [
       "//base:base_java",
+      "//base:jni_java",
       "//third_party/android_deps:androidx_annotation_annotation_java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
     srcjar_deps = [ ":java_enums_srcjar" ]
   }
 
diff --git a/device/gamepad/android/java/src/org/chromium/device/gamepad/GamepadList.java b/device/gamepad/android/java/src/org/chromium/device/gamepad/GamepadList.java
index 0991f38..eb1b9862 100644
--- a/device/gamepad/android/java/src/org/chromium/device/gamepad/GamepadList.java
+++ b/device/gamepad/android/java/src/org/chromium/device/gamepad/GamepadList.java
@@ -16,6 +16,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 /**
  * Class to manage connected gamepad devices list.
@@ -288,11 +289,12 @@
                 final GamepadDevice device = getDevice(i);
                 if (device != null) {
                     device.updateButtonsAndAxesMapping();
-                    nativeSetGamepadData(webGamepadsPtr, i, device.isStandardGamepad(), true,
-                            device.getName(), device.getTimestamp(), device.getAxes(),
-                            device.getButtons());
+                    GamepadListJni.get().setGamepadData(GamepadList.this, webGamepadsPtr, i,
+                            device.isStandardGamepad(), true, device.getName(),
+                            device.getTimestamp(), device.getAxes(), device.getButtons());
                 } else {
-                    nativeSetGamepadData(webGamepadsPtr, i, false, false, null, 0, null, null);
+                    GamepadListJni.get().setGamepadData(
+                            GamepadList.this, webGamepadsPtr, i, false, false, null, 0, null, null);
                 }
             }
         }
@@ -316,10 +318,14 @@
         }
     }
 
-    private native void nativeSetGamepadData(long webGamepadsPtr, int index, boolean mapping,
-            boolean connected, String devicename, long timestamp, float[] axes, float[] buttons);
-
     private static class LazyHolder {
         private static final GamepadList INSTANCE = new GamepadList();
     }
+
+    @NativeMethods
+    interface Natives {
+        void setGamepadData(GamepadList caller, long webGamepadsPtr, int index, boolean mapping,
+                boolean connected, String devicename, long timestamp, float[] axes,
+                float[] buttons);
+    }
 }
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn
index cf71125..775c999 100644
--- a/device/vr/BUILD.gn
+++ b/device/vr/BUILD.gn
@@ -401,8 +401,10 @@
     java_files = java_sources_needing_jni
     deps = [
       "//base:base_java",
+      "//base:jni_java",
       "//third_party/gvr-android-sdk:gvr_common_java",
       "//ui/android:ui_java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   }
 }
diff --git a/device/vr/android/java/src/org/chromium/device/vr/NonPresentingGvrContext.java b/device/vr/android/java/src/org/chromium/device/vr/NonPresentingGvrContext.java
index d7ba07e6..20acaecf 100644
--- a/device/vr/android/java/src/org/chromium/device/vr/NonPresentingGvrContext.java
+++ b/device/vr/android/java/src/org/chromium/device/vr/NonPresentingGvrContext.java
@@ -15,6 +15,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 /**
  * Creates an active GvrContext from a GvrApi created from the Application Context. This GvrContext
@@ -90,8 +91,14 @@
 
     public void onDisplayConfigurationChanged() {
         mGvrApi.refreshDisplayMetrics();
-        if (mNativeGvrDevice != 0) nativeOnDisplayConfigurationChanged(mNativeGvrDevice);
+        if (mNativeGvrDevice != 0) {
+            NonPresentingGvrContextJni.get().onDisplayConfigurationChanged(
+                    mNativeGvrDevice, NonPresentingGvrContext.this);
+        }
     }
 
-    private native void nativeOnDisplayConfigurationChanged(long nativeGvrDevice);
+    @NativeMethods
+    interface Natives {
+        void onDisplayConfigurationChanged(long nativeGvrDevice, NonPresentingGvrContext caller);
+    }
 }
diff --git a/docs/infra/cq_builders.md b/docs/infra/cq_builders.md
index 46f1562..793ff45f 100644
--- a/docs/infra/cq_builders.md
+++ b/docs/infra/cq_builders.md
@@ -91,18 +91,24 @@
 * [android_compile_x64_dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_compile_x64_dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android_compile_x64_dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_compile_x64_dbg))
 
   Path regular expressions:
+    * [`//chrome/android/java/src/org/chromium/chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser/vr/)
+    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
     * [`//sandbox/linux/seccomp-bpf/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf/)
     * [`//sandbox/linux/seccomp-bpf-helpers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf-helpers/)
     * [`//sandbox/linux/system_headers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/system_headers/)
     * [`//sandbox/linux/tests/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/tests/)
+    * [`//third_party/gvr-android-sdk/.+`](https://cs.chromium.org/chromium/src/third_party/gvr-android-sdk/)
 
 * [android_compile_x86_dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_compile_x86_dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android_compile_x86_dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_compile_x86_dbg))
 
   Path regular expressions:
+    * [`//chrome/android/java/src/org/chromium/chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser/vr/)
+    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
     * [`//sandbox/linux/seccomp-bpf/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf/)
     * [`//sandbox/linux/seccomp-bpf-helpers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf-helpers/)
     * [`//sandbox/linux/system_headers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/system_headers/)
     * [`//sandbox/linux/tests/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/tests/)
+    * [`//third_party/gvr-android-sdk/.+`](https://cs.chromium.org/chromium/src/third_party/gvr-android-sdk/)
 
 * [android_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_optional_gpu_tests_rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_optional_gpu_tests_rel))
 
@@ -312,6 +318,10 @@
 
   * Experimental percentage: 5
 
+* [fuchsia-compile-x64-dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/fuchsia-compile-x64-dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/fuchsia-compile-x64-dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+fuchsia-compile-x64-dbg))
+
+  * Experimental percentage: 5
+
 * [ios-device](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-device) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/ios-device)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-device))
 
   https://crbug.com/739556; make this non-experimental ASAP.
diff --git a/extensions/shell/test/shell_test_launcher_delegate.cc b/extensions/shell/test/shell_test_launcher_delegate.cc
index d0a9430e..2f680e4a 100644
--- a/extensions/shell/test/shell_test_launcher_delegate.cc
+++ b/extensions/shell/test/shell_test_launcher_delegate.cc
@@ -13,10 +13,8 @@
 
 int AppShellTestLauncherDelegate::RunTestSuite(int argc, char** argv) {
   base::TestSuite test_suite(argc, argv);
-  // Browser tests are expected not to tear-down various globals and may
-  // complete with the thread priority being above NORMAL.
+  // Browser tests are expected not to tear-down various globals.
   test_suite.DisableCheckForLeakedGlobals();
-  test_suite.DisableCheckForThreadPriorityAtTestEnd();
   return test_suite.Run();
 }
 
diff --git a/fuchsia/engine/renderer/web_engine_content_renderer_client.cc b/fuchsia/engine/renderer/web_engine_content_renderer_client.cc
index 0137a81..17c53ae9 100644
--- a/fuchsia/engine/renderer/web_engine_content_renderer_client.cc
+++ b/fuchsia/engine/renderer/web_engine_content_renderer_client.cc
@@ -18,6 +18,23 @@
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
 #include "third_party/widevine/cdm/widevine_cdm_common.h"
 
+namespace {
+
+// Returns true if the specified video format can be decoded on hardware.
+bool IsSupportedHardwareVideoCodec(const media::VideoType& type) {
+  // TODO(fxb/36000): Replace these hardcoded checks with a query to the
+  // fuchsia.mediacodec FIDL service.
+  if (type.codec == media::kCodecH264 && type.level <= 41)
+    return true;
+
+  if (type.codec == media::kCodecVP9 && type.level <= 40)
+    return true;
+
+  return false;
+}
+
+}  // namespace
+
 WebEngineContentRendererClient::WebEngineContentRendererClient() = default;
 
 WebEngineContentRendererClient::~WebEngineContentRendererClient() = default;
@@ -67,8 +84,25 @@
     std::vector<std::unique_ptr<media::KeySystemProperties>>* key_systems) {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableWidevine)) {
-    // TODO(yucliu): Check supported hw video decoders.
-    media::SupportedCodecs supported_video_codecs = media::EME_CODEC_NONE;
+    media::SupportedCodecs supported_video_codecs = 0;
+    constexpr uint8_t kUnknownCodecLevel = 0;
+    if (IsSupportedHardwareVideoCodec(media::VideoType{
+            media::kCodecVP9, media::VP9PROFILE_PROFILE0, kUnknownCodecLevel,
+            media::VideoColorSpace::REC709()})) {
+      supported_video_codecs |= media::EME_CODEC_VP9_PROFILE0;
+    }
+
+    if (IsSupportedHardwareVideoCodec(media::VideoType{
+            media::kCodecVP9, media::VP9PROFILE_PROFILE2, kUnknownCodecLevel,
+            media::VideoColorSpace::REC709()})) {
+      supported_video_codecs |= media::EME_CODEC_VP9_PROFILE2;
+    }
+
+    if (IsSupportedHardwareVideoCodec(media::VideoType{
+            media::kCodecH264, media::H264PROFILE_MAIN, kUnknownCodecLevel,
+            media::VideoColorSpace::REC709()})) {
+      supported_video_codecs |= media::EME_CODEC_AVC1;
+    }
 
     base::flat_set<media::EncryptionMode> encryption_schemes{
         media::EncryptionMode::kCenc, media::EncryptionMode::kCbcs};
@@ -101,13 +135,5 @@
     return ContentRendererClient::IsSupportedVideoType(type);
   }
 
-  // TODO(fxb/36000): Replace these hardcoded checks with a query to the
-  // fuchsia.mediacodec FIDL service.
-  if (type.codec == media::kCodecH264 && type.level <= 41)
-    return true;
-
-  if (type.codec == media::kCodecVP9 && type.level <= 40)
-    return true;
-
-  return false;
+  return IsSupportedHardwareVideoCodec(type);
 }
diff --git a/fuchsia/engine/test/web_engine_test_launcher.cc b/fuchsia/engine/test/web_engine_test_launcher.cc
index 9789ec4..d989d0a 100644
--- a/fuchsia/engine/test/web_engine_test_launcher.cc
+++ b/fuchsia/engine/test/web_engine_test_launcher.cc
@@ -25,10 +25,8 @@
   // content::TestLauncherDelegate implementation:
   int RunTestSuite(int argc, char** argv) override {
     base::TestSuite test_suite(argc, argv);
-    // Browser tests are expected not to tear-down various globals and may
-    // complete with the thread priority being above NORMAL.
+    // Browser tests are expected not to tear-down various globals.
     test_suite.DisableCheckForLeakedGlobals();
-    test_suite.DisableCheckForThreadPriorityAtTestEnd();
     return test_suite.Run();
   }
 
diff --git a/fuchsia/runners/cast/cast_component.cc b/fuchsia/runners/cast/cast_component.cc
index 9aed46f..c78f06a 100644
--- a/fuchsia/runners/cast/cast_component.cc
+++ b/fuchsia/runners/cast/cast_component.cc
@@ -21,6 +21,7 @@
 namespace {
 
 constexpr int kBindingsFailureExitCode = 129;
+constexpr int kRewriteRulesProviderDisconnectExitCode = 130;
 
 constexpr char kStubBindingsPath[] =
     FILE_PATH_LITERAL("fuchsia/runners/cast/not_implemented_api_bindings.js");
@@ -50,6 +51,7 @@
                    std::move(params.controller_request)),
       agent_manager_(std::move(params.agent_manager)),
       application_config_(std::move(params.app_config)),
+      rewrite_rules_provider_(std::move(params.rewrite_rules_provider)),
       touch_input_policy_(
           TouchInputPolicyFromApplicationConfig(application_config_)),
       connector_(frame()),
@@ -57,6 +59,15 @@
       navigation_listener_binding_(this) {
   base::AutoReset<bool> constructor_active_reset(&constructor_active_, true);
 
+  rewrite_rules_provider_.set_error_handler([this](zx_status_t status) {
+    ZX_LOG(ERROR, status) << "UrlRequestRewriteRulesProvider disconnected.";
+    DestroyComponent(kRewriteRulesProviderDisconnectExitCode,
+                     fuchsia::sys::TerminationReason::INTERNAL_ERROR);
+  });
+
+  DCHECK(params.rewrite_rules);
+  OnRewriteRulesReceived(std::move(params.rewrite_rules.value()));
+
   frame()->SetEnableInput(false);
   frame()->SetNavigationEventListener(
       navigation_listener_binding_.NewBinding());
@@ -83,6 +94,14 @@
   WebComponent::DestroyComponent(termination_exit_code, reason);
 }
 
+void CastComponent::OnRewriteRulesReceived(
+    std::vector<fuchsia::web::UrlRequestRewriteRule> rewrite_rules) {
+  frame()->SetUrlRequestRewriteRules(std::move(rewrite_rules), [this]() {
+    rewrite_rules_provider_->GetUrlRequestRewriteRules(
+        fit::bind_member(this, &CastComponent::OnRewriteRulesReceived));
+  });
+}
+
 void CastComponent::OnNavigationStateChanged(
     fuchsia::web::NavigationState change,
     OnNavigationStateChangedCallback callback) {
diff --git a/fuchsia/runners/cast/cast_component.h b/fuchsia/runners/cast/cast_component.h
index 9b1fe7d..cbb3859 100644
--- a/fuchsia/runners/cast/cast_component.h
+++ b/fuchsia/runners/cast/cast_component.h
@@ -36,8 +36,9 @@
     fidl::InterfaceRequest<fuchsia::sys::ComponentController>
         controller_request;
     chromium::cast::ApplicationConfig app_config;
-    fuchsia::web::AdditionalHeadersProviderPtr headers_provider;
-    base::Optional<std::vector<fuchsia::net::http::Header>> headers;
+    chromium::cast::UrlRequestRewriteRulesProviderPtr rewrite_rules_provider;
+    base::Optional<std::vector<fuchsia::web::UrlRequestRewriteRule>>
+        rewrite_rules;
   };
 
   CastComponent(CastRunner* runner, CastComponentParams params);
@@ -51,6 +52,9 @@
   void DestroyComponent(int termination_exit_code,
                         fuchsia::sys::TerminationReason reason) override;
 
+  void OnRewriteRulesReceived(
+      std::vector<fuchsia::web::UrlRequestRewriteRule> rewrite_rules);
+
   // fuchsia::web::NavigationEventListener implementation.
   // Triggers the injection of API channels into the page content.
   void OnNavigationStateChanged(
@@ -59,6 +63,7 @@
 
   std::unique_ptr<cr_fuchsia::AgentManager> agent_manager_;
   chromium::cast::ApplicationConfig application_config_;
+  chromium::cast::UrlRequestRewriteRulesProviderPtr rewrite_rules_provider_;
 
   bool constructor_active_ = false;
   TouchInputPolicy touch_input_policy_;
diff --git a/fuchsia/runners/cast/cast_runner.cc b/fuchsia/runners/cast/cast_runner.cc
index a6d9368..0d8ba3bf 100644
--- a/fuchsia/runners/cast/cast_runner.cc
+++ b/fuchsia/runners/cast/cast_runner.cc
@@ -4,7 +4,6 @@
 
 #include "fuchsia/runners/cast/cast_runner.h"
 
-#include <fuchsia/sys/cpp/fidl.h>
 #include <memory>
 #include <string>
 #include <utility>
@@ -67,25 +66,26 @@
       base::BindOnce(&CastRunner::MaybeStartComponent, base::Unretained(this),
                      base::Unretained(pending_component.get())));
 
-  // Get AdditionalHeadersProvider from the Agent.
-  fidl::InterfaceHandle<fuchsia::web::AdditionalHeadersProvider>
-      additional_headers_provider;
+  // Get UrlRequestRewriteRulesProvider from the Agent.
+  fidl::InterfaceHandle<chromium::cast::UrlRequestRewriteRulesProvider>
+      url_request_rules_provider;
   pending_component->agent_manager->ConnectToAgentService(
-      kAgentComponentUrl, additional_headers_provider.NewRequest());
-  pending_component->headers_provider = additional_headers_provider.Bind();
-  pending_component->headers_provider.set_error_handler(
-      [this, pending_component = pending_component.get()](zx_status_t error) {
-        if (pending_component->headers.has_value())
-          return;
-        pending_component->headers = {};
-        MaybeStartComponent(pending_component);
+      kAgentComponentUrl, url_request_rules_provider.NewRequest());
+  pending_component->rewrite_rules_provider = url_request_rules_provider.Bind();
+  pending_component->rewrite_rules_provider.set_error_handler(
+      [this, pending_component = pending_component.get()](zx_status_t status) {
+        ZX_LOG(ERROR, status) << "UrlRequestRewriteRulesProvider disconnected.";
+
+        // The rules provider disconnected, cancel the component launch.
+        size_t count = pending_components_.erase(pending_component);
+        DCHECK_EQ(count, 1u);
       });
-  pending_component->headers_provider->GetHeaders(
+  pending_component->rewrite_rules_provider->GetUrlRequestRewriteRules(
       [this, pending_component = pending_component.get()](
-          std::vector<fuchsia::net::http::Header> headers, zx_time_t expiry) {
-        pending_component->headers =
-            base::Optional<std::vector<fuchsia::net::http::Header>>(
-                std::move(headers));
+          std::vector<fuchsia::web::UrlRequestRewriteRule> rewrite_rules) {
+        pending_component->rewrite_rules =
+            base::Optional<std::vector<fuchsia::web::UrlRequestRewriteRule>>(
+                std::move(rewrite_rules));
         MaybeStartComponent(pending_component);
       });
 
@@ -126,19 +126,17 @@
     return;
   if (!pending_component->api_bindings_client->HasBindings())
     return;
-  if (!pending_component->headers.has_value())
+  if (!pending_component->rewrite_rules.has_value())
     return;
 
   // Create a component based on the returned configuration, and pass it the
   // |pending_component|.
-  std::vector<fuchsia::net::http::Header> additional_headers =
-      pending_component->headers.value();
-
   GURL cast_app_url(pending_component->app_config.web_url());
   auto component =
       std::make_unique<CastComponent>(this, std::move(*pending_component));
   pending_components_.erase(pending_component);
 
-  component->LoadUrl(std::move(cast_app_url), std::move(additional_headers));
+  component->LoadUrl(std::move(cast_app_url),
+                     std::vector<fuchsia::net::http::Header>());
   RegisterComponent(std::move(component));
 }
diff --git a/fuchsia/runners/cast/cast_runner.h b/fuchsia/runners/cast/cast_runner.h
index afbcaae6..f2ca037 100644
--- a/fuchsia/runners/cast/cast_runner.h
+++ b/fuchsia/runners/cast/cast_runner.h
@@ -12,7 +12,9 @@
 #include "base/callback.h"
 #include "base/containers/flat_set.h"
 #include "base/containers/unique_ptr_adapters.h"
+#include "base/fuchsia/startup_context.h"
 #include "base/macros.h"
+#include "fuchsia/base/agent_manager.h"
 #include "fuchsia/fidl/chromium/cast/cpp/fidl.h"
 #include "fuchsia/runners/cast/cast_component.h"
 #include "fuchsia/runners/common/web_content_runner.h"
diff --git a/fuchsia/runners/cast/cast_runner_integration_test.cc b/fuchsia/runners/cast/cast_runner_integration_test.cc
index 37c9676..2de8956 100644
--- a/fuchsia/runners/cast/cast_runner_integration_test.cc
+++ b/fuchsia/runners/cast/cast_runner_integration_test.cc
@@ -23,6 +23,7 @@
 #include "fuchsia/base/result_receiver.h"
 #include "fuchsia/base/string_util.h"
 #include "fuchsia/base/test_navigation_listener.h"
+#include "fuchsia/base/url_request_rewrite_test_util.h"
 #include "fuchsia/runners/cast/cast_runner.h"
 #include "fuchsia/runners/cast/fake_application_config_manager.h"
 #include "fuchsia/runners/cast/test_api_bindings.h"
@@ -45,28 +46,36 @@
   ADD_FAILURE();
 }
 
-class FakeAdditionalHeadersProvider
-    : public fuchsia::web::AdditionalHeadersProvider {
+class FakeUrlRequestRewriteRulesProvider
+    : public chromium::cast::UrlRequestRewriteRulesProvider {
  public:
-  explicit FakeAdditionalHeadersProvider(sys::OutgoingDirectory* directory)
+  explicit FakeUrlRequestRewriteRulesProvider(sys::OutgoingDirectory* directory)
       : binding_(directory, this) {}
-  ~FakeAdditionalHeadersProvider() override = default;
+  ~FakeUrlRequestRewriteRulesProvider() override = default;
 
  private:
-  void GetHeaders(GetHeadersCallback callback) override {
-    std::vector<fuchsia::net::http::Header> headers;
-    fuchsia::net::http::Header header;
-    header.name = cr_fuchsia::StringToBytes("Test");
-    header.value = cr_fuchsia::StringToBytes("Value");
-    headers.push_back(std::move(header));
-    callback(std::move(headers), 0);
+  void GetUrlRequestRewriteRules(
+      GetUrlRequestRewriteRulesCallback callback) override {
+    // Only send the rules once. They do not expire
+    if (rules_sent_)
+      return;
+    rules_sent_ = true;
+
+    std::vector<fuchsia::web::UrlRequestRewrite> rewrites;
+    rewrites.push_back(cr_fuchsia::CreateRewriteAddHeaders("Test", "Value"));
+    fuchsia::web::UrlRequestRewriteRule rule;
+    rule.set_rewrites(std::move(rewrites));
+    std::vector<fuchsia::web::UrlRequestRewriteRule> rules;
+    rules.push_back(std::move(rule));
+    callback(std::move(rules));
   }
 
+  bool rules_sent_ = false;
   const base::fuchsia::ScopedServiceBinding<
-      fuchsia::web::AdditionalHeadersProvider>
+      chromium::cast::UrlRequestRewriteRulesProvider>
       binding_;
 
-  DISALLOW_COPY_AND_ASSIGN(FakeAdditionalHeadersProvider);
+  DISALLOW_COPY_AND_ASSIGN(FakeUrlRequestRewriteRulesProvider);
 };
 
 class FakeApplicationControllerReceiver
@@ -104,8 +113,8 @@
       bool provide_controller_receiver)
       : ComponentStateBase(component_url),
         app_config_binding_(outgoing_directory(), app_config_manager),
-        additional_headers_provider_(
-            std::make_unique<FakeAdditionalHeadersProvider>(
+        url_request_rules_provider_(
+            std::make_unique<FakeUrlRequestRewriteRulesProvider>(
                 outgoing_directory())) {
     if (bindings_manager) {
       bindings_manager_binding_ = std::make_unique<
@@ -138,7 +147,8 @@
   std::unique_ptr<
       base::fuchsia::ScopedServiceBinding<chromium::cast::ApiBindings>>
       bindings_manager_binding_;
-  std::unique_ptr<FakeAdditionalHeadersProvider> additional_headers_provider_;
+  std::unique_ptr<FakeUrlRequestRewriteRulesProvider>
+      url_request_rules_provider_;
   FakeApplicationControllerReceiver controller_receiver_;
   base::Optional<base::fuchsia::ScopedServiceBinding<
       chromium::cast::ApplicationControllerReceiver>>
@@ -394,7 +404,7 @@
   EXPECT_FALSE(web_component.has_value());
 }
 
-TEST_F(CastRunnerIntegrationTest, AdditionalHeadersProvider) {
+TEST_F(CastRunnerIntegrationTest, UrlRequestRewriteRulesProvider) {
   const char kEchoAppId[] = "00000000";
   const char kEchoAppPath[] = "/echoheader?Test";
   const GURL echo_app_url = test_server_.GetURL(kEchoAppPath);
diff --git a/headless/test/headless_test_launcher.cc b/headless/test/headless_test_launcher.cc
index d0b1430..6d8ee1c 100644
--- a/headless/test/headless_test_launcher.cc
+++ b/headless/test/headless_test_launcher.cc
@@ -41,10 +41,8 @@
   // content::TestLauncherDelegate implementation:
   int RunTestSuite(int argc, char** argv) override {
     base::TestSuite test_suite(argc, argv);
-    // Browser tests are expected not to tear-down various globals and may
-    // complete with the thread priority being above NORMAL.
+    // Browser tests are expected not to tear-down various globals.
     test_suite.DisableCheckForLeakedGlobals();
-    test_suite.DisableCheckForThreadPriorityAtTestEnd();
     return test_suite.Run();
   }
 
diff --git a/infra/config/commit-queue.cfg b/infra/config/commit-queue.cfg
index bd0c4e5..ea03def8 100644
--- a/infra/config/commit-queue.cfg
+++ b/infra/config/commit-queue.cfg
@@ -159,17 +159,23 @@
       }
       builders {
         name: "chromium/try/android_compile_x64_dbg"
+        location_regexp: ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+"
+        location_regexp: ".+/[+]/chrome/browser/vr/.+"
         location_regexp: ".+/[+]/sandbox/linux/seccomp-bpf/.+"
         location_regexp: ".+/[+]/sandbox/linux/seccomp-bpf-helpers/.+"
         location_regexp: ".+/[+]/sandbox/linux/system_headers/.+"
         location_regexp: ".+/[+]/sandbox/linux/tests/.+"
+        location_regexp: ".+/[+]/third_party/gvr-android-sdk/.+"
       }
       builders {
         name: "chromium/try/android_compile_x86_dbg"
+        location_regexp: ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+"
+        location_regexp: ".+/[+]/chrome/browser/vr/.+"
         location_regexp: ".+/[+]/sandbox/linux/seccomp-bpf/.+"
         location_regexp: ".+/[+]/sandbox/linux/seccomp-bpf-helpers/.+"
         location_regexp: ".+/[+]/sandbox/linux/system_headers/.+"
         location_regexp: ".+/[+]/sandbox/linux/tests/.+"
+        location_regexp: ".+/[+]/third_party/gvr-android-sdk/.+"
       }
       builders {
         name: "chromium/try/android_optional_gpu_tests_rel"
@@ -363,6 +369,10 @@
         name: "chromium/try/chromeos-kevin-experimental-rel"
         experiment_percentage: 5
       }
+      builders {
+        name: "chromium/try/fuchsia-compile-x64-dbg"
+        experiment_percentage: 5
+      }
       # https://crbug.com/739556; make this non-experimental ASAP.
       builders {
         name: "chromium/try/ios-device"
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index 422ab62..5d1db2e3 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -1209,6 +1209,21 @@
     }
 
     builders {
+      name: "android-avd-packager"
+      mixins: "linux-xenial"
+      mixins: "builderless"
+      service_account: "chromium-cipd-builder@chops-service-accounts.iam.gserviceaccount.com"
+      recipe {
+        name: "android/avd_packager"
+        properties_j: <<END
+          avd_configs: [
+            "tools/android/avd/proto/generic_android28.textpb"
+          ]
+        END
+      }
+    }
+
+    builders {
       name: "android-code-coverage"
       mixins: "code-coverage"
       mixins: "java-coverage"
@@ -1609,6 +1624,12 @@
     }
 
     builders {
+      name: "fuchsia-x64-dbg"
+      mixins: "linux-ci"
+      mixins: "builderless"
+    }
+
+    builders {
       name: "Fuchsia x64"
       mixins: "linux-ci-goma-rbe-prod"
       mixins: "builderless"
@@ -4392,10 +4413,15 @@
       name: "fuchsia-fyi-x64-rel"
     }
     builders {
+      mixins: "builderless"
       mixins: "linux-try"
-      name: "fuchsia_x64"
+      name: "fuchsia-compile-x64-dbg"
+    }
+    builders {
+      mixins: "linux-try"
       mixins: "builderless"
       mixins: "goma-rbe-prod"
+      name: "fuchsia_x64"
     }
     builders {
       mixins: "builderless"
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index 724f744..d80d5029 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -571,6 +571,10 @@
     short_name: "aud"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Fuchsia ARM64"
+    category: "chromium.linux|fuchsia|a64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/fuchsia-arm64-cast"
     category: "chromium.linux|fuchsia|cast"
     short_name: "a64"
@@ -581,21 +585,21 @@
     short_name: "x64"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Fuchsia ARM64"
-    category: "chromium.linux|fuchsia|misc"
-    short_name: "a64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Fuchsia x64"
-    category: "chromium.linux|fuchsia|misc"
-    short_name: "x64"
+    name: "buildbucket/luci.chromium.ci/fuchsia-x64-dbg"
+    category: "chromium.linux|fuchsia|x64"
+    short_name: "dbg"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Deterministic Fuchsia (dbg)"
-    category: "chromium.linux|fuchsia|misc"
+    category: "chromium.linux|fuchsia|x64"
     short_name: "det"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Fuchsia x64"
+    category: "chromium.linux|fuchsia|x64"
+    short_name: "rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Linux ChromiumOS Full"
     category: "chromium.chromiumos|default"
     short_name: "ful"
@@ -994,6 +998,10 @@
     short_name: "aud"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Fuchsia ARM64"
+    category: "fuchsia|a64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/fuchsia-arm64-cast"
     category: "fuchsia|cast"
     short_name: "a64"
@@ -1004,20 +1012,20 @@
     short_name: "x64"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Fuchsia ARM64"
-    category: "fuchsia|misc"
-    short_name: "a64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Fuchsia x64"
-    category: "fuchsia|misc"
-    short_name: "x64"
+    name: "buildbucket/luci.chromium.ci/fuchsia-x64-dbg"
+    category: "fuchsia|x64"
+    short_name: "dbg"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Deterministic Fuchsia (dbg)"
-    category: "fuchsia|misc"
+    category: "fuchsia|x64"
     short_name: "det"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Fuchsia x64"
+    category: "fuchsia|x64"
+    short_name: "rel"
+  }
 }
 
 consoles {
@@ -4586,6 +4594,9 @@
     name: "buildbucket/luci.chromium.try/fuchsia-arm64-cast"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/fuchsia-compile-x64-dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/fuchsia-fyi-arm64-rel"
   }
   builders {
@@ -5194,6 +5205,9 @@
     name: "buildbucket/luci.chromium.try/fuchsia-angle-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/fuchsia-compile-x64-dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-l-nexus-5-32"
   }
   builders {
diff --git a/infra/config/luci-notify.cfg b/infra/config/luci-notify.cfg
index cba5cb9..7e5000b 100644
--- a/infra/config/luci-notify.cfg
+++ b/infra/config/luci-notify.cfg
@@ -196,6 +196,10 @@
     name: "fuchsia-x64-cast"
     bucket: "ci"
   }
+  builders {
+    name: "fuchsia-x64-dbg"
+    bucket: "ci"
+  }
 }
 
 notifiers {
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg
index db876c7..c95f6f73 100644
--- a/infra/config/luci-scheduler.cfg
+++ b/infra/config/luci-scheduler.cfg
@@ -153,6 +153,7 @@
   triggers: "fuchsia-arm64-cast"
   triggers: "Fuchsia ARM64"
   triggers: "fuchsia-x64-cast"
+  triggers: "fuchsia-x64-dbg"
   triggers: "Fuchsia x64"
   triggers: "GPU FYI Linux Builder (dbg)"
   triggers: "GPU FYI Linux Builder"
@@ -730,6 +731,18 @@
 }
 
 job {
+  id: "android-avd-packager"
+  acl_sets: "default"
+  # Run weekly, on Sunday morning at midnight.
+  schedule: "0 7 * * 0 *"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-avd-packager"
+  }
+}
+
+job {
   id: "android-code-coverage"
   acl_sets: "default"
   buildbucket: {
@@ -1531,6 +1544,16 @@
 }
 
 job {
+  id: "fuchsia-x64-dbg"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "fuchsia-x64-dbg"
+  }
+}
+
+job {
   id: "GPU FYI Linux Builder"
   acl_sets: "default"
   buildbucket: {
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 4747f36..36a5e887 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -2169,61 +2169,41 @@
     disableWebUsageDuringRemoval = NO;
   }
 
-  ProceduralBlock removeBrowsingDataBlock = ^{
-    if (disableWebUsageDuringRemoval) {
-      // Disables browsing and purges web views.
-      // Must be called only on the main thread.
-      DCHECK([NSThread isMainThread]);
-      self.interfaceProvider.mainInterface.userInteractionEnabled = NO;
-      self.interfaceProvider.incognitoInterface.userInteractionEnabled = NO;
-    } else if (showActivityIndicator) {
-      // Show activity overlay so users know that clear browsing data is in
-      // progress.
-      [self.mainBVC.dispatcher showActivityOverlay:YES];
-    }
-
-    BrowsingDataRemoverFactory::GetForBrowserState(browserState)
-        ->Remove(timePeriod, removeMask, base::BindOnce(^{
-                   // Activates browsing and enables web views.
-                   // Must be called only on the main thread.
-                   DCHECK([NSThread isMainThread]);
-                   if (showActivityIndicator) {
-                     // User interaction still needs to be disabled as a way to
-                     // force reload all the web states and to reset NTPs.
-                     self.interfaceProvider.mainInterface
-                         .userInteractionEnabled = NO;
-                     self.interfaceProvider.incognitoInterface
-                         .userInteractionEnabled = NO;
-
-                     [self.mainBVC.dispatcher showActivityOverlay:NO];
-                   }
-                   self.interfaceProvider.mainInterface.userInteractionEnabled =
-                       YES;
-                   self.interfaceProvider.incognitoInterface
-                       .userInteractionEnabled = YES;
-                   [self.currentBVC setPrimary:YES];
-
-                   if (completionBlock)
-                     completionBlock();
-                 }));
-  };
-
-  // Removing browsing data triggers session restore in navigation manager. If
-  // there is an in-progress session restore, wait for it to finish before
-  // attempting to clear browsing data again.
-  web::WebState* webState =
-      self.currentBVC.tabModel
-          ? self.currentBVC.tabModel.webStateList->GetActiveWebState()
-          : nullptr;
-  if (webState && webState->GetNavigationManager()) {
-    webState->GetNavigationManager()->AddRestoreCompletionCallback(
-        base::BindOnce(^{
-          removeBrowsingDataBlock();
-        }));
-    return;
+  if (disableWebUsageDuringRemoval) {
+    // Disables browsing and purges web views.
+    // Must be called only on the main thread.
+    DCHECK([NSThread isMainThread]);
+    self.interfaceProvider.mainInterface.userInteractionEnabled = NO;
+    self.interfaceProvider.incognitoInterface.userInteractionEnabled = NO;
+  } else if (showActivityIndicator) {
+    // Show activity overlay so users know that clear browsing data is in
+    // progress.
+    [self.mainBVC.dispatcher showActivityOverlay:YES];
   }
 
-  removeBrowsingDataBlock();
+  BrowsingDataRemoverFactory::GetForBrowserState(browserState)
+      ->Remove(
+          timePeriod, removeMask, base::BindOnce(^{
+            // Activates browsing and enables web views.
+            // Must be called only on the main thread.
+            DCHECK([NSThread isMainThread]);
+            if (showActivityIndicator) {
+              // User interaction still needs to be disabled as a way to
+              // force reload all the web states and to reset NTPs.
+              self.interfaceProvider.mainInterface.userInteractionEnabled = NO;
+              self.interfaceProvider.incognitoInterface.userInteractionEnabled =
+                  NO;
+
+              [self.mainBVC.dispatcher showActivityOverlay:NO];
+            }
+            self.interfaceProvider.mainInterface.userInteractionEnabled = YES;
+            self.interfaceProvider.incognitoInterface.userInteractionEnabled =
+                YES;
+            [self.currentBVC setPrimary:YES];
+
+            if (completionBlock)
+              completionBlock();
+          }));
 }
 
 #pragma mark - Navigation Controllers
diff --git a/ios/chrome/browser/ui/tab_grid/grid/BUILD.gn b/ios/chrome/browser/ui/tab_grid/grid/BUILD.gn
index 3de2a01..e22d4c1 100644
--- a/ios/chrome/browser/ui/tab_grid/grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_grid/grid/BUILD.gn
@@ -34,6 +34,8 @@
   deps = [
     ":grid_ui_constants",
     "resources:grid_cell_close_button",
+    "resources:grid_theme_dark_selection_tint_color",
+    "resources:grid_theme_selection_tint_color",
     "//base",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
index 44fb0515..9855f33 100644
--- a/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
@@ -214,17 +214,14 @@
 
   // When iOS 12 is dropped, only the next switch statement is needed for
   // styling.
-  // TODO (crbug.com/995746): New colors (|kGridThemeSelectionTintColor|
-  // and |kGridThemeDarkSelectionTintColor|), which are only used in tab
-  // grid, should be local colors instead of global.
   switch (theme) {
     case GridThemeLight:
       self.border.layer.borderColor =
-          [UIColor colorNamed:kGridThemeSelectionTintColor].CGColor;
+          [UIColor colorNamed:@"grid_theme_selection_tint_color"].CGColor;
       break;
     case GridThemeDark:
       self.border.layer.borderColor =
-          [UIColor colorNamed:kGridThemeDarkSelectionTintColor].CGColor;
+          [UIColor colorNamed:@"grid_theme_dark_selection_tint_color"].CGColor;
       break;
   }
   _theme = theme;
diff --git a/ios/chrome/browser/ui/tab_grid/grid/resources/BUILD.gn b/ios/chrome/browser/ui/tab_grid/grid/resources/BUILD.gn
index 3bffe21..0cd6645 100644
--- a/ios/chrome/browser/ui/tab_grid/grid/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_grid/grid/resources/BUILD.gn
@@ -11,3 +11,15 @@
     "grid_cell_close_button.imageset/grid_cell_close_button@3x.png",
   ]
 }
+
+colorset("grid_theme_selection_tint_color") {
+  sources = [
+    "grid_theme_selection_tint_color.colorset/Contents.json",
+  ]
+}
+
+colorset("grid_theme_dark_selection_tint_color") {
+  sources = [
+    "grid_theme_dark_selection_tint_color.colorset/Contents.json",
+  ]
+}
diff --git a/ios/chrome/common/colors/resources/grid_theme_dark_selection_tint_color.colorset/Contents.json b/ios/chrome/browser/ui/tab_grid/grid/resources/grid_theme_dark_selection_tint_color.colorset/Contents.json
similarity index 100%
rename from ios/chrome/common/colors/resources/grid_theme_dark_selection_tint_color.colorset/Contents.json
rename to ios/chrome/browser/ui/tab_grid/grid/resources/grid_theme_dark_selection_tint_color.colorset/Contents.json
diff --git a/ios/chrome/common/colors/resources/grid_theme_selection_tint_color.colorset/Contents.json b/ios/chrome/browser/ui/tab_grid/grid/resources/grid_theme_selection_tint_color.colorset/Contents.json
similarity index 100%
rename from ios/chrome/common/colors/resources/grid_theme_selection_tint_color.colorset/Contents.json
rename to ios/chrome/browser/ui/tab_grid/grid/resources/grid_theme_selection_tint_color.colorset/Contents.json
diff --git a/ios/chrome/common/colors/resources/BUILD.gn b/ios/chrome/common/colors/resources/BUILD.gn
index b250ff0e..90dcbb7 100644
--- a/ios/chrome/common/colors/resources/BUILD.gn
+++ b/ios/chrome/common/colors/resources/BUILD.gn
@@ -27,8 +27,6 @@
     ":grey_700_color",
     ":grey_800_color",
     ":grey_900_color",
-    ":grid_theme_dark_selection_tint_color",
-    ":grid_theme_selection_tint_color",
     ":mdc_ink_color",
     ":mdc_secondary_ink_color",
     ":placeholder_image_tint_color",
@@ -173,18 +171,6 @@
   ]
 }
 
-colorset("grid_theme_selection_tint_color") {
-  sources = [
-    "grid_theme_selection_tint_color.colorset/Contents.json",
-  ]
-}
-
-colorset("grid_theme_dark_selection_tint_color") {
-  sources = [
-    "grid_theme_dark_selection_tint_color.colorset/Contents.json",
-  ]
-}
-
 colorset("grey_900_color") {
   sources = [
     "grey_900_color.colorset/Contents.json",
diff --git a/ios/chrome/common/colors/semantic_color_names.h b/ios/chrome/common/colors/semantic_color_names.h
index 257b2fd..c09eab1 100644
--- a/ios/chrome/common/colors/semantic_color_names.h
+++ b/ios/chrome/common/colors/semantic_color_names.h
@@ -12,7 +12,6 @@
 extern NSString* const kBackgroundColor;
 extern NSString* const kCloseButtonColor;
 extern NSString* const kDisabledTintColor;
-extern NSString* const kGridThemeSelectionTintColor;
 // Background color used in the rounded squares behind favicons.
 extern NSString* const kFaviconBackgroundColor;
 // Ink color for an MDC button.
@@ -69,7 +68,6 @@
 
 extern NSString* const kBackgroundDarkColor;
 extern NSString* const kCloseButtonDarkColor;
-extern NSString* const kGridThemeDarkSelectionTintColor;
 extern NSString* const kTableViewRowHighlightDarkColor;
 extern NSString* const kTextPrimaryDarkColor;
 extern NSString* const kTextSecondaryDarkColor;
diff --git a/ios/chrome/common/colors/semantic_color_names.mm b/ios/chrome/common/colors/semantic_color_names.mm
index 8ad3832c..e1a632b 100644
--- a/ios/chrome/common/colors/semantic_color_names.mm
+++ b/ios/chrome/common/colors/semantic_color_names.mm
@@ -12,8 +12,6 @@
 NSString* const kBackgroundColor = @"background_color";
 NSString* const kCloseButtonColor = @"close_button_color";
 NSString* const kDisabledTintColor = @"disabled_tint_color";
-NSString* const kGridThemeSelectionTintColor =
-    @"grid_theme_selection_tint_color";
 NSString* const kFaviconBackgroundColor = @"favicon_background_color";
 NSString* const kMDCInkColor = @"mdc_ink_color";
 NSString* const kMDCSecondaryInkColor = @"mdc_secondary_ink_color";
@@ -51,8 +49,6 @@
 
 NSString* const kBackgroundDarkColor = @"background_dark_color";
 NSString* const kCloseButtonDarkColor = @"close_button_dark_color";
-NSString* const kGridThemeDarkSelectionTintColor =
-    @"grid_theme_dark_selection_tint_color";
 NSString* const kTableViewRowHighlightDarkColor =
     @"table_view_row_highlight_dark_color";
 NSString* const kTextPrimaryDarkColor = @"text_primary_dark_color";
diff --git a/ios/third_party/material_components_ios/BUILD.gn b/ios/third_party/material_components_ios/BUILD.gn
index 94aa1ce..1e182c2 100644
--- a/ios/third_party/material_components_ios/BUILD.gn
+++ b/ios/third_party/material_components_ios/BUILD.gn
@@ -380,6 +380,8 @@
     "src/components/private/Application/src/MaterialApplication.h",
     "src/components/private/Application/src/UIApplication+AppExtensions.h",
     "src/components/private/Application/src/UIApplication+AppExtensions.m",
+    "src/components/private/Color/src/UIColor+MaterialBlending.h",
+    "src/components/private/Color/src/UIColor+MaterialBlending.m",
     "src/components/private/Color/src/UIColor+MaterialDynamic.h",
     "src/components/private/Color/src/UIColor+MaterialDynamic.m",
     "src/components/private/Icons/icons/ic_arrow_back/src/MaterialIcons+ic_arrow_back.h",
diff --git a/media/base/android/BUILD.gn b/media/base/android/BUILD.gn
index 01c6322c..10726cb 100644
--- a/media/base/android/BUILD.gn
+++ b/media/base/android/BUILD.gn
@@ -168,8 +168,10 @@
     deps = [
       ":media_java_resources",
       "//base:base_java",
+      "//base:jni_java",
       "//third_party/android_deps:androidx_annotation_annotation_java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
     srcjar_deps = [
       ":java_enums",
       ":java_switches",
diff --git a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
index 63bb172..621f27ea 100644
--- a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
+++ b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
@@ -33,6 +33,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -1118,8 +1119,9 @@
                     // slider all the way down in communication mode but the callback
                     // implementation can ensure that the volume is completely muted.
                     int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
-                    if (DEBUG) logd("nativeSetMute: " + (volume == 0));
-                    nativeSetMute(mNativeAudioManagerAndroid, (volume == 0));
+                    if (DEBUG) logd("AudioManagerAndroidJni.get().setMute: " + (volume == 0));
+                    AudioManagerAndroidJni.get().setMute(
+                            mNativeAudioManagerAndroid, AudioManagerAndroid.this, (volume == 0));
                 }
         };
 
@@ -1234,5 +1236,8 @@
         mUsbAudioReceiver = null;
     }
 
-    private native void nativeSetMute(long nativeAudioManagerAndroid, boolean muted);
+    @NativeMethods
+    interface Natives {
+        void setMute(long nativeAudioManagerAndroid, AudioManagerAndroid caller, boolean muted);
+    }
 }
diff --git a/media/base/android/java/src/org/chromium/media/AudioTrackOutputStream.java b/media/base/android/java/src/org/chromium/media/AudioTrackOutputStream.java
index 2189f27..9b976a1c 100644
--- a/media/base/android/java/src/org/chromium/media/AudioTrackOutputStream.java
+++ b/media/base/android/java/src/org/chromium/media/AudioTrackOutputStream.java
@@ -14,6 +14,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.nio.ByteBuffer;
 
@@ -122,17 +123,20 @@
 
             @Override
             public AudioBufferInfo onMoreData(ByteBuffer audioData, long delayInFrames) {
-                return nativeOnMoreData(mNativeAudioTrackOutputStream, audioData, delayInFrames);
+                return AudioTrackOutputStreamJni.get().onMoreData(mNativeAudioTrackOutputStream,
+                        AudioTrackOutputStream.this, audioData, delayInFrames);
             }
 
             @Override
             public long getAddress(ByteBuffer byteBuffer) {
-                return nativeGetAddress(mNativeAudioTrackOutputStream, byteBuffer);
+                return AudioTrackOutputStreamJni.get().getAddress(
+                        mNativeAudioTrackOutputStream, AudioTrackOutputStream.this, byteBuffer);
             }
 
             @Override
             public void onError() {
-                nativeOnError(mNativeAudioTrackOutputStream);
+                AudioTrackOutputStreamJni.get().onError(
+                        mNativeAudioTrackOutputStream, AudioTrackOutputStream.this);
             }
         };
     }
@@ -313,8 +317,12 @@
         return mAudioTrack.write(mWriteBuffer, mLeftSize, AudioTrack.WRITE_BLOCKING);
     }
 
-    private native AudioBufferInfo nativeOnMoreData(
-            long nativeAudioTrackOutputStream, ByteBuffer audioData, long delayInFrames);
-    private native void nativeOnError(long nativeAudioTrackOutputStream);
-    private native long nativeGetAddress(long nativeAudioTrackOutputStream, ByteBuffer byteBuffer);
+    @NativeMethods
+    interface Natives {
+        AudioBufferInfo onMoreData(long nativeAudioTrackOutputStream, AudioTrackOutputStream caller,
+                ByteBuffer audioData, long delayInFrames);
+        void onError(long nativeAudioTrackOutputStream, AudioTrackOutputStream caller);
+        long getAddress(long nativeAudioTrackOutputStream, AudioTrackOutputStream caller,
+                ByteBuffer byteBuffer);
+    }
 }
diff --git a/media/base/android/java/src/org/chromium/media/HdrMetadata.java b/media/base/android/java/src/org/chromium/media/HdrMetadata.java
index d51daed..add144d 100644
--- a/media/base/android/java/src/org/chromium/media/HdrMetadata.java
+++ b/media/base/android/java/src/org/chromium/media/HdrMetadata.java
@@ -14,6 +14,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.MainDex;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -61,13 +62,13 @@
 
             // TODO(sandv): Use color space matrix when android has support for it.
             int colorStandard = getColorStandard();
-            if (colorStandard != -1)
+            if (colorStandard != -1) {
                 format.setInteger(MediaFormat.KEY_COLOR_STANDARD, colorStandard);
-
+            }
             int colorTransfer = getColorTransfer();
-            if (colorTransfer != -1)
+            if (colorTransfer != -1) {
                 format.setInteger(MediaFormat.KEY_COLOR_TRANSFER, colorTransfer);
-
+            }
             int colorRange = getColorRange();
             if (colorRange != -1) format.setInteger(MediaFormat.KEY_COLOR_RANGE, colorRange);
 
@@ -92,10 +93,9 @@
         }
     }
 
-    private native int nativePrimaries(long nativeJniHdrMetadata);
     private int getColorStandard() {
         // media/base/video_color_space.h
-        switch (nativePrimaries(mNativeJniHdrMetadata)) {
+        switch (HdrMetadataJni.get().primaries(mNativeJniHdrMetadata, HdrMetadata.this)) {
             case 1:
                 return MediaFormat.COLOR_STANDARD_BT709;
             case 4: // BT.470M.
@@ -110,10 +110,9 @@
         }
     }
 
-    private native int nativeColorTransfer(long nativeJniHdrMetadata);
     private int getColorTransfer() {
         // media/base/video_color_space.h
-        switch (nativeColorTransfer(mNativeJniHdrMetadata)) {
+        switch (HdrMetadataJni.get().colorTransfer(mNativeJniHdrMetadata, HdrMetadata.this)) {
             case 1: // BT.709.
             case 6: // SMPTE 170M.
             case 7: // SMPTE 240M.
@@ -129,10 +128,9 @@
         }
     }
 
-    private native int nativeRange(long nativeJniHdrMetadata);
     private int getColorRange() {
         // media/base/video_color_space.h
-        switch (nativeRange(mNativeJniHdrMetadata)) {
+        switch (HdrMetadataJni.get().range(mNativeJniHdrMetadata, HdrMetadata.this)) {
             case 1:
                 return MediaFormat.COLOR_RANGE_LIMITED;
             case 2:
@@ -142,63 +140,73 @@
         }
     }
 
-    private native float nativePrimaryRChromaticityX(long nativeJniHdrMetadata);
     private float primaryRChromaticityX() {
-        return nativePrimaryRChromaticityX(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().primaryRChromaticityX(mNativeJniHdrMetadata, HdrMetadata.this);
     }
 
-    private native float nativePrimaryRChromaticityY(long nativeJniHdrMetadata);
     private float primaryRChromaticityY() {
-        return nativePrimaryRChromaticityY(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().primaryRChromaticityY(mNativeJniHdrMetadata, HdrMetadata.this);
     }
 
-    private native float nativePrimaryGChromaticityX(long nativeJniHdrMetadata);
     private float primaryGChromaticityX() {
-        return nativePrimaryGChromaticityX(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().primaryGChromaticityX(mNativeJniHdrMetadata, HdrMetadata.this);
     }
 
-    private native float nativePrimaryGChromaticityY(long nativeJniHdrMetadata);
     private float primaryGChromaticityY() {
-        return nativePrimaryGChromaticityY(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().primaryGChromaticityY(mNativeJniHdrMetadata, HdrMetadata.this);
     }
 
-    private native float nativePrimaryBChromaticityX(long nativeJniHdrMetadata);
     private float primaryBChromaticityX() {
-        return nativePrimaryBChromaticityX(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().primaryBChromaticityX(mNativeJniHdrMetadata, HdrMetadata.this);
     }
 
-    private native float nativePrimaryBChromaticityY(long nativeJniHdrMetadata);
     private float primaryBChromaticityY() {
-        return nativePrimaryBChromaticityY(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().primaryBChromaticityY(mNativeJniHdrMetadata, HdrMetadata.this);
     }
 
-    private native float nativeWhitePointChromaticityX(long nativeJniHdrMetadata);
     private float whitePointChromaticityX() {
-        return nativeWhitePointChromaticityX(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().whitePointChromaticityX(
+                mNativeJniHdrMetadata, HdrMetadata.this);
     }
 
-    private native float nativeWhitePointChromaticityY(long nativeJniHdrMetadata);
     private float whitePointChromaticityY() {
-        return nativeWhitePointChromaticityY(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().whitePointChromaticityY(
+                mNativeJniHdrMetadata, HdrMetadata.this);
     }
 
-    private native float nativeMaxMasteringLuminance(long nativeJniHdrMetadata);
     private float maxMasteringLuminance() {
-        return nativeMaxMasteringLuminance(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().maxMasteringLuminance(mNativeJniHdrMetadata, HdrMetadata.this);
     }
 
-    private native float nativeMinMasteringLuminance(long nativeJniHdrMetadata);
     private float minMasteringLuminance() {
-        return nativeMinMasteringLuminance(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().minMasteringLuminance(mNativeJniHdrMetadata, HdrMetadata.this);
     }
 
-    private native int nativeMaxContentLuminance(long nativeJniHdrMetadata);
     private int maxContentLuminance() {
-        return nativeMaxContentLuminance(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().maxContentLuminance(mNativeJniHdrMetadata, HdrMetadata.this);
     }
 
-    private native int nativeMaxFrameAverageLuminance(long nativeJniHdrMetadata);
     private int maxFrameAverageLuminance() {
-        return nativeMaxFrameAverageLuminance(mNativeJniHdrMetadata);
+        return HdrMetadataJni.get().maxFrameAverageLuminance(
+                mNativeJniHdrMetadata, HdrMetadata.this);
+    }
+
+    @NativeMethods
+    interface Natives {
+        int primaries(long nativeJniHdrMetadata, HdrMetadata caller);
+        int colorTransfer(long nativeJniHdrMetadata, HdrMetadata caller);
+        int range(long nativeJniHdrMetadata, HdrMetadata caller);
+        float primaryRChromaticityX(long nativeJniHdrMetadata, HdrMetadata caller);
+        float primaryRChromaticityY(long nativeJniHdrMetadata, HdrMetadata caller);
+        float primaryGChromaticityX(long nativeJniHdrMetadata, HdrMetadata caller);
+        float primaryGChromaticityY(long nativeJniHdrMetadata, HdrMetadata caller);
+        float primaryBChromaticityX(long nativeJniHdrMetadata, HdrMetadata caller);
+        float primaryBChromaticityY(long nativeJniHdrMetadata, HdrMetadata caller);
+        float whitePointChromaticityX(long nativeJniHdrMetadata, HdrMetadata caller);
+        float whitePointChromaticityY(long nativeJniHdrMetadata, HdrMetadata caller);
+        float maxMasteringLuminance(long nativeJniHdrMetadata, HdrMetadata caller);
+        float minMasteringLuminance(long nativeJniHdrMetadata, HdrMetadata caller);
+        int maxContentLuminance(long nativeJniHdrMetadata, HdrMetadata caller);
+        int maxFrameAverageLuminance(long nativeJniHdrMetadata, HdrMetadata caller);
     }
 }
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
index 4a8050d..8543b7e 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
@@ -22,6 +22,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.MainDex;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.nio.ByteBuffer;
 import java.util.LinkedList;
@@ -280,12 +281,16 @@
         mNativeMediaCodecBridge = nativeMediaCodecBridge;
 
         // If any buffers or errors occurred before this, trigger the callback now.
-        if (!mPendingInputBuffers.isEmpty() || !mPendingOutputBuffers.isEmpty() || mPendingError)
+        if (!mPendingInputBuffers.isEmpty() || !mPendingOutputBuffers.isEmpty() || mPendingError) {
             notifyBuffersAvailable();
+        }
     }
 
     private synchronized void notifyBuffersAvailable() {
-        if (mNativeMediaCodecBridge != 0) nativeOnBuffersAvailable(mNativeMediaCodecBridge);
+        if (mNativeMediaCodecBridge != 0) {
+            MediaCodecBridgeJni.get().onBuffersAvailable(
+                    mNativeMediaCodecBridge, MediaCodecBridge.this);
+        }
     }
 
     public synchronized void onError(MediaCodec.CodecException e) {
@@ -396,8 +401,9 @@
         if (mUseAsyncApi) {
             synchronized (this) {
                 if (mPendingError) return new DequeueInputResult(MediaCodecStatus.ERROR, -1);
-                if (mPendingStart || mPendingInputBuffers.isEmpty())
+                if (mPendingStart || mPendingInputBuffers.isEmpty()) {
                     return new DequeueInputResult(MediaCodecStatus.TRY_AGAIN_LATER, -1);
+                }
                 return mPendingInputBuffers.remove();
             }
         }
@@ -631,8 +637,9 @@
     private DequeueOutputResult dequeueOutputBuffer(long timeoutUs) {
         if (mUseAsyncApi) {
             synchronized (this) {
-                if (mPendingError)
+                if (mPendingError) {
                     return new DequeueOutputResult(MediaCodecStatus.ERROR, -1, 0, 0, 0, 0);
+                }
                 if (mPendingOutputBuffers.isEmpty()) {
                     return new DequeueOutputResult(
                             MediaCodecStatus.TRY_AGAIN_LATER, -1, 0, 0, 0, 0);
@@ -757,5 +764,8 @@
         sCallbackHandler = new Handler(sCallbackHandlerThread.getLooper());
     }
 
-    private native void nativeOnBuffersAvailable(long nativeMediaCodecBridge);
+    @NativeMethods
+    interface Natives {
+        void onBuffersAvailable(long nativeMediaCodecBridge, MediaCodecBridge caller);
+    }
 }
diff --git a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
index 95cd4cf..14128647 100644
--- a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
@@ -16,6 +16,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.MainDex;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.media.MediaDrmSessionManager.SessionId;
 import org.chromium.media.MediaDrmSessionManager.SessionInfo;
 
@@ -582,7 +583,7 @@
      * Provision the current origin. Normally provisioning will be triggered
      * automatically when MediaCrypto is needed (in the constructor).
      * However, this is available to preprovision an origin separately.
-     * nativeOnProvisioningComplete() will be called indicating success/failure.
+     * MediaDrmBridgeJni.get().onProvisioningComplete() will be called indicating success/failure.
      */
     @CalledByNative
     private void provision() {
@@ -594,7 +595,8 @@
         // Provision only works for origin isolated storage.
         if (!mOriginSet) {
             Log.e(TAG, "Calling provision() without an origin.");
-            nativeOnProvisioningComplete(mNativeMediaDrmBridge, false);
+            MediaDrmBridgeJni.get().onProvisioningComplete(
+                    mNativeMediaDrmBridge, MediaDrmBridge.this, false);
             return;
         }
 
@@ -615,12 +617,14 @@
             }
 
             // Indicate that provisioning succeeded.
-            nativeOnProvisioningComplete(mNativeMediaDrmBridge, true);
+            MediaDrmBridgeJni.get().onProvisioningComplete(
+                    mNativeMediaDrmBridge, MediaDrmBridge.this, true);
 
         } catch (android.media.NotProvisionedException e) {
             if (!startProvisioning()) {
                 // Indicate that provisioning failed.
-                nativeOnProvisioningComplete(mNativeMediaDrmBridge, false);
+                MediaDrmBridgeJni.get().onProvisioningComplete(
+                        mNativeMediaDrmBridge, MediaDrmBridge.this, false);
             }
         }
     }
@@ -1200,7 +1204,8 @@
             return false;
         }
 
-        nativeOnProvisionRequest(mNativeMediaDrmBridge, request.getDefaultUrl(), request.getData());
+        MediaDrmBridgeJni.get().onProvisionRequest(mNativeMediaDrmBridge, MediaDrmBridge.this,
+                request.getDefaultUrl(), request.getData());
         return true;
     }
 
@@ -1266,7 +1271,8 @@
     void onProvisioned(boolean success) {
         if (!mRequiresMediaCrypto) {
             // No MediaCrypto required, so notify provisioning complete.
-            nativeOnProvisioningComplete(mNativeMediaDrmBridge, success);
+            MediaDrmBridgeJni.get().onProvisioningComplete(
+                    mNativeMediaDrmBridge, MediaDrmBridge.this, success);
             if (!success) {
                 release();
             }
@@ -1318,26 +1324,30 @@
 
     private void onMediaCryptoReady(MediaCrypto mediaCrypto) {
         if (isNativeMediaDrmBridgeValid()) {
-            nativeOnMediaCryptoReady(mNativeMediaDrmBridge, mediaCrypto);
+            MediaDrmBridgeJni.get().onMediaCryptoReady(
+                    mNativeMediaDrmBridge, MediaDrmBridge.this, mediaCrypto);
         }
     }
 
     private void onPromiseResolved(final long promiseId) {
         if (isNativeMediaDrmBridgeValid()) {
-            nativeOnPromiseResolved(mNativeMediaDrmBridge, promiseId);
+            MediaDrmBridgeJni.get().onPromiseResolved(
+                    mNativeMediaDrmBridge, MediaDrmBridge.this, promiseId);
         }
     }
 
     private void onPromiseResolvedWithSession(final long promiseId, final SessionId sessionId) {
         if (isNativeMediaDrmBridgeValid()) {
-            nativeOnPromiseResolvedWithSession(mNativeMediaDrmBridge, promiseId, sessionId.emeId());
+            MediaDrmBridgeJni.get().onPromiseResolvedWithSession(
+                    mNativeMediaDrmBridge, MediaDrmBridge.this, promiseId, sessionId.emeId());
         }
     }
 
     private void onPromiseRejected(final long promiseId, final String errorMessage) {
         Log.e(TAG, "onPromiseRejected: %s", errorMessage);
         if (isNativeMediaDrmBridgeValid()) {
-            nativeOnPromiseRejected(mNativeMediaDrmBridge, promiseId, errorMessage);
+            MediaDrmBridgeJni.get().onPromiseRejected(
+                    mNativeMediaDrmBridge, MediaDrmBridge.this, promiseId, errorMessage);
         }
     }
 
@@ -1356,28 +1366,29 @@
                     : MediaDrm.KeyRequest.REQUEST_TYPE_RENEWAL;
         }
 
-        nativeOnSessionMessage(
-                mNativeMediaDrmBridge, sessionId.emeId(), requestType, request.getData());
+        MediaDrmBridgeJni.get().onSessionMessage(mNativeMediaDrmBridge, MediaDrmBridge.this,
+                sessionId.emeId(), requestType, request.getData());
     }
 
     private void onSessionClosed(final SessionId sessionId) {
         if (isNativeMediaDrmBridgeValid()) {
-            nativeOnSessionClosed(mNativeMediaDrmBridge, sessionId.emeId());
+            MediaDrmBridgeJni.get().onSessionClosed(
+                    mNativeMediaDrmBridge, MediaDrmBridge.this, sessionId.emeId());
         }
     }
 
     private void onSessionKeysChange(final SessionId sessionId, final Object[] keysInfo,
             final boolean hasAdditionalUsableKey, final boolean isKeyRelease) {
         if (isNativeMediaDrmBridgeValid()) {
-            nativeOnSessionKeysChange(mNativeMediaDrmBridge, sessionId.emeId(), keysInfo,
-                    hasAdditionalUsableKey, isKeyRelease);
+            MediaDrmBridgeJni.get().onSessionKeysChange(mNativeMediaDrmBridge, MediaDrmBridge.this,
+                    sessionId.emeId(), keysInfo, hasAdditionalUsableKey, isKeyRelease);
         }
     }
 
     private void onSessionExpirationUpdate(final SessionId sessionId, final long expirationTime) {
         if (isNativeMediaDrmBridgeValid()) {
-            nativeOnSessionExpirationUpdate(
-                    mNativeMediaDrmBridge, sessionId.emeId(), expirationTime);
+            MediaDrmBridgeJni.get().onSessionExpirationUpdate(
+                    mNativeMediaDrmBridge, MediaDrmBridge.this, sessionId.emeId(), expirationTime);
         }
     }
 
@@ -1529,26 +1540,30 @@
         }
     }
 
-    // Native functions. At the native side, must post the task immediately to
-    // avoid reentrancy issues.
-    private native void nativeOnMediaCryptoReady(
-            long nativeMediaDrmBridge, MediaCrypto mediaCrypto);
+    // At the native side, must post the task immediately to avoid reentrancy issues.
+    @NativeMethods
+    interface Natives {
+        void onMediaCryptoReady(
+                long nativeMediaDrmBridge, MediaDrmBridge caller, MediaCrypto mediaCrypto);
 
-    private native void nativeOnProvisionRequest(
-            long nativeMediaDrmBridge, String defaultUrl, byte[] requestData);
-    private native void nativeOnProvisioningComplete(long nativeMediaDrmBridge, boolean success);
+        void onProvisionRequest(long nativeMediaDrmBridge, MediaDrmBridge caller, String defaultUrl,
+                byte[] requestData);
+        void onProvisioningComplete(
+                long nativeMediaDrmBridge, MediaDrmBridge caller, boolean success);
 
-    private native void nativeOnPromiseResolved(long nativeMediaDrmBridge, long promiseId);
-    private native void nativeOnPromiseResolvedWithSession(
-            long nativeMediaDrmBridge, long promiseId, byte[] emeSessionId);
-    private native void nativeOnPromiseRejected(
-            long nativeMediaDrmBridge, long promiseId, String errorMessage);
+        void onPromiseResolved(long nativeMediaDrmBridge, MediaDrmBridge caller, long promiseId);
+        void onPromiseResolvedWithSession(long nativeMediaDrmBridge, MediaDrmBridge caller,
+                long promiseId, byte[] emeSessionId);
+        void onPromiseRejected(long nativeMediaDrmBridge, MediaDrmBridge caller, long promiseId,
+                String errorMessage);
 
-    private native void nativeOnSessionMessage(
-            long nativeMediaDrmBridge, byte[] emeSessionId, int requestType, byte[] message);
-    private native void nativeOnSessionClosed(long nativeMediaDrmBridge, byte[] emeSessionId);
-    private native void nativeOnSessionKeysChange(long nativeMediaDrmBridge, byte[] emeSessionId,
-            Object[] keysInfo, boolean hasAdditionalUsableKey, boolean isKeyRelease);
-    private native void nativeOnSessionExpirationUpdate(
-            long nativeMediaDrmBridge, byte[] emeSessionId, long expirationTime);
+        void onSessionMessage(long nativeMediaDrmBridge, MediaDrmBridge caller, byte[] emeSessionId,
+                int requestType, byte[] message);
+        void onSessionClosed(long nativeMediaDrmBridge, MediaDrmBridge caller, byte[] emeSessionId);
+        void onSessionKeysChange(long nativeMediaDrmBridge, MediaDrmBridge caller,
+                byte[] emeSessionId, Object[] keysInfo, boolean hasAdditionalUsableKey,
+                boolean isKeyRelease);
+        void onSessionExpirationUpdate(long nativeMediaDrmBridge, MediaDrmBridge caller,
+                byte[] emeSessionId, long expirationTime);
+    }
 }
diff --git a/media/base/android/java/src/org/chromium/media/MediaDrmStorageBridge.java b/media/base/android/java/src/org/chromium/media/MediaDrmStorageBridge.java
index 8cfe7a5..13ced310 100644
--- a/media/base/android/java/src/org/chromium/media/MediaDrmStorageBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaDrmStorageBridge.java
@@ -11,6 +11,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.MainDex;
+import org.chromium.base.annotations.NativeMethods;
 
 /**
  * Origin isolated media drm scope id storage. Isolated origin is guranteed by native
@@ -85,7 +86,8 @@
      */
     void onProvisioned(Callback<Boolean> cb) {
         if (isNativeMediaDrmStorageValid()) {
-            nativeOnProvisioned(mNativeMediaDrmStorageBridge, cb);
+            MediaDrmStorageBridgeJni.get().onProvisioned(
+                    mNativeMediaDrmStorageBridge, MediaDrmStorageBridge.this, cb);
         } else {
             cb.onResult(true);
         }
@@ -96,7 +98,8 @@
      */
     void loadInfo(byte[] emeId, Callback<PersistentInfo> cb) {
         if (isNativeMediaDrmStorageValid()) {
-            nativeOnLoadInfo(mNativeMediaDrmStorageBridge, emeId, cb);
+            MediaDrmStorageBridgeJni.get().onLoadInfo(
+                    mNativeMediaDrmStorageBridge, MediaDrmStorageBridge.this, emeId, cb);
         } else {
             cb.onResult(null);
         }
@@ -107,7 +110,8 @@
      */
     void saveInfo(PersistentInfo info, Callback<Boolean> cb) {
         if (isNativeMediaDrmStorageValid()) {
-            nativeOnSaveInfo(mNativeMediaDrmStorageBridge, info, cb);
+            MediaDrmStorageBridgeJni.get().onSaveInfo(
+                    mNativeMediaDrmStorageBridge, MediaDrmStorageBridge.this, info, cb);
         } else {
             cb.onResult(false);
         }
@@ -118,7 +122,8 @@
      */
     void clearInfo(byte[] emeId, Callback<Boolean> cb) {
         if (isNativeMediaDrmStorageValid()) {
-            nativeOnClearInfo(mNativeMediaDrmStorageBridge, emeId, cb);
+            MediaDrmStorageBridgeJni.get().onClearInfo(
+                    mNativeMediaDrmStorageBridge, MediaDrmStorageBridge.this, emeId, cb);
         } else {
             cb.onResult(true);
         }
@@ -128,11 +133,15 @@
         return mNativeMediaDrmStorageBridge != INVALID_NATIVE_MEDIA_DRM_STORAGE_BRIDGE;
     }
 
-    private native void nativeOnProvisioned(long nativeMediaDrmStorageBridge, Callback<Boolean> cb);
-    private native void nativeOnLoadInfo(
-            long nativeMediaDrmStorageBridge, byte[] sessionId, Callback<PersistentInfo> cb);
-    private native void nativeOnSaveInfo(
-            long nativeMediaDrmStorageBridge, PersistentInfo info, Callback<Boolean> cb);
-    private native void nativeOnClearInfo(
-            long nativeMediaDrmStorageBridge, byte[] sessionId, Callback<Boolean> cb);
+    @NativeMethods
+    interface Natives {
+        void onProvisioned(long nativeMediaDrmStorageBridge, MediaDrmStorageBridge caller,
+                Callback<Boolean> cb);
+        void onLoadInfo(long nativeMediaDrmStorageBridge, MediaDrmStorageBridge caller,
+                byte[] sessionId, Callback<PersistentInfo> cb);
+        void onSaveInfo(long nativeMediaDrmStorageBridge, MediaDrmStorageBridge caller,
+                PersistentInfo info, Callback<Boolean> cb);
+        void onClearInfo(long nativeMediaDrmStorageBridge, MediaDrmStorageBridge caller,
+                byte[] sessionId, Callback<Boolean> cb);
+    }
 }
diff --git a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
index aaeef434..79b54374 100644
--- a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
@@ -20,6 +20,7 @@
 import org.chromium.base.StreamUtil;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.base.task.AsyncTask;
 
 import java.io.ByteArrayInputStream;
@@ -236,7 +237,8 @@
 
             deleteFile();
             assert (mNativeMediaPlayerBridge != 0);
-            nativeOnDidSetDataUriDataSource(mNativeMediaPlayerBridge, result);
+            MediaPlayerBridgeJni.get().onDidSetDataUriDataSource(
+                    mNativeMediaPlayerBridge, MediaPlayerBridge.this, result);
         }
 
         private void deleteFile() {
@@ -328,13 +330,16 @@
         return new AllowedOperations(canSeekForward, canSeekBackward);
     }
 
-    private native void nativeOnDidSetDataUriDataSource(long nativeMediaPlayerBridge,
-                                                        boolean success);
-
     private void cancelLoadDataUriTask() {
         if (mLoadDataUriTask != null) {
             mLoadDataUriTask.cancel(true);
             mLoadDataUriTask = null;
         }
     }
+
+    @NativeMethods
+    interface Natives {
+        void onDidSetDataUriDataSource(
+                long nativeMediaPlayerBridge, MediaPlayerBridge caller, boolean success);
+    }
 }
diff --git a/media/base/android/java/src/org/chromium/media/MediaPlayerListener.java b/media/base/android/java/src/org/chromium/media/MediaPlayerListener.java
index 9c4e601..86426ff 100644
--- a/media/base/android/java/src/org/chromium/media/MediaPlayerListener.java
+++ b/media/base/android/java/src/org/chromium/media/MediaPlayerListener.java
@@ -8,6 +8,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 // This class implements all the listener interface for android mediaplayer.
 // Callbacks will be sent to the native class for processing.
@@ -66,23 +67,27 @@
                 errorType = MEDIA_ERROR_INVALID_CODE;
                 break;
         }
-        nativeOnMediaError(mNativeMediaPlayerListener, errorType);
+        MediaPlayerListenerJni.get().onMediaError(
+                mNativeMediaPlayerListener, MediaPlayerListener.this, errorType);
         return true;
     }
 
     @Override
     public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
-        nativeOnVideoSizeChanged(mNativeMediaPlayerListener, width, height);
+        MediaPlayerListenerJni.get().onVideoSizeChanged(
+                mNativeMediaPlayerListener, MediaPlayerListener.this, width, height);
     }
 
     @Override
     public void onCompletion(MediaPlayer mp) {
-        nativeOnPlaybackComplete(mNativeMediaPlayerListener);
+        MediaPlayerListenerJni.get().onPlaybackComplete(
+                mNativeMediaPlayerListener, MediaPlayerListener.this);
     }
 
     @Override
     public void onPrepared(MediaPlayer mp) {
-        nativeOnMediaPrepared(mNativeMediaPlayerListener);
+        MediaPlayerListenerJni.get().onMediaPrepared(
+                mNativeMediaPlayerListener, MediaPlayerListener.this);
     }
 
     @CalledByNative
@@ -101,15 +106,14 @@
     /**
      * See media/base/android/media_player_listener.cc for all the following functions.
      */
-    private native void nativeOnMediaError(
-            long nativeMediaPlayerListener,
-            int errorType);
 
-    private native void nativeOnVideoSizeChanged(
-            long nativeMediaPlayerListener,
-            int width, int height);
-
-    private native void nativeOnMediaPrepared(long nativeMediaPlayerListener);
-
-    private native void nativeOnPlaybackComplete(long nativeMediaPlayerListener);
+    @NativeMethods
+    interface Natives {
+        void onMediaError(
+                long nativeMediaPlayerListener, MediaPlayerListener caller, int errorType);
+        void onVideoSizeChanged(
+                long nativeMediaPlayerListener, MediaPlayerListener caller, int width, int height);
+        void onMediaPrepared(long nativeMediaPlayerListener, MediaPlayerListener caller);
+        void onPlaybackComplete(long nativeMediaPlayerListener, MediaPlayerListener caller);
+    }
 }
diff --git a/media/base/android/java/src/org/chromium/media/MediaServerCrashListener.java b/media/base/android/java/src/org/chromium/media/MediaServerCrashListener.java
index 04c5787..409eef9 100644
--- a/media/base/android/java/src/org/chromium/media/MediaServerCrashListener.java
+++ b/media/base/android/java/src/org/chromium/media/MediaServerCrashListener.java
@@ -11,6 +11,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 /**
  * Class for listening to Android MediaServer crashes to throttle media decoding
@@ -83,7 +84,8 @@
                 || (currentTime - mLastReportedWatchdogCreationFailure)
                         > APPROX_MEDIA_SERVER_RESTART_TIME_IN_MS) {
             Log.e(TAG, "Unable to create watchdog player, treating it as server crash.");
-            nativeOnMediaServerCrashDetected(mNativeMediaServerCrashListener, false);
+            MediaServerCrashListenerJni.get().onMediaServerCrashDetected(
+                    mNativeMediaServerCrashListener, MediaServerCrashListener.this, false);
             mLastReportedWatchdogCreationFailure = currentTime;
         }
         return false;
@@ -92,12 +94,16 @@
     @Override
     public boolean onError(MediaPlayer mp, int what, int extra) {
         if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
-            nativeOnMediaServerCrashDetected(mNativeMediaServerCrashListener, true);
+            MediaServerCrashListenerJni.get().onMediaServerCrashDetected(
+                    mNativeMediaServerCrashListener, MediaServerCrashListener.this, true);
             releaseWatchdog();
         }
         return true;
     }
 
-    private native void nativeOnMediaServerCrashDetected(
-            long nativeMediaServerCrashListener, boolean watchdogNeedsRelease);
+    @NativeMethods
+    interface Natives {
+        void onMediaServerCrashDetected(long nativeMediaServerCrashListener,
+                MediaServerCrashListener caller, boolean watchdogNeedsRelease);
+    }
 }
diff --git a/media/base/cdm_context.cc b/media/base/cdm_context.cc
index 6f8fadb..90e69383 100644
--- a/media/base/cdm_context.cc
+++ b/media/base/cdm_context.cc
@@ -37,6 +37,12 @@
 }
 #endif
 
+#if defined(OS_FUCHSIA)
+FuchsiaCdmContext* CdmContext::GetFuchsiaCdmContext() {
+  return nullptr;
+}
+#endif
+
 void IgnoreCdmAttached(bool /* success */) {}
 
 }  // namespace media
diff --git a/media/base/cdm_context.h b/media/base/cdm_context.h
index cd59098..eaa4d15a 100644
--- a/media/base/cdm_context.h
+++ b/media/base/cdm_context.h
@@ -18,6 +18,10 @@
 class Decryptor;
 class MediaCryptoContext;
 
+#if defined(OS_FUCHSIA)
+class FuchsiaCdmContext;
+#endif
+
 // An interface representing the context that a media player needs from a
 // content decryption module (CDM) to decrypt (and decode) encrypted buffers.
 // Typically this will be passed to the media player (e.g. using SetCdm()).
@@ -90,6 +94,12 @@
   virtual MediaCryptoContext* GetMediaCryptoContext();
 #endif
 
+#if defined(OS_FUCHSIA)
+  // Returns FuchsiaCdmContext interface when the context is backed by Fuchsia
+  // CDM. Otherwise returns nullptr.
+  virtual FuchsiaCdmContext* GetFuchsiaCdmContext();
+#endif
+
  protected:
   CdmContext();
 
diff --git a/media/capture/content/android/BUILD.gn b/media/capture/content/android/BUILD.gn
index 6c2530e..6f4eb569 100644
--- a/media/capture/content/android/BUILD.gn
+++ b/media/capture/content/android/BUILD.gn
@@ -38,6 +38,8 @@
 android_library("screen_capture_java") {
   deps = [
     "//base:base_java",
+    "//base:jni_java",
   ]
   java_files = [ "java/src/org/chromium/media/ScreenCapture.java" ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 }
diff --git a/media/capture/content/android/java/src/org/chromium/media/ScreenCapture.java b/media/capture/content/android/java/src/org/chromium/media/ScreenCapture.java
index 73789db..5b53ede5 100644
--- a/media/capture/content/android/java/src/org/chromium/media/ScreenCapture.java
+++ b/media/capture/content/android/java/src/org/chromium/media/ScreenCapture.java
@@ -34,6 +34,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -143,7 +144,8 @@
                             throw new IllegalStateException();
                         }
 
-                        nativeOnRGBAFrameAvailable(mNativeScreenCaptureMachineAndroid,
+                        ScreenCaptureJni.get().onRGBAFrameAvailable(
+                                mNativeScreenCaptureMachineAndroid, ScreenCapture.this,
                                 image.getPlanes()[0].getBuffer(),
                                 image.getPlanes()[0].getRowStride(), image.getCropRect().left,
                                 image.getCropRect().top, image.getCropRect().width(),
@@ -158,7 +160,8 @@
 
                         // The pixel stride of Y plane is always 1. The U/V planes are guaranteed
                         // to have the same row stride and pixel stride.
-                        nativeOnI420FrameAvailable(mNativeScreenCaptureMachineAndroid,
+                        ScreenCaptureJni.get().onI420FrameAvailable(
+                                mNativeScreenCaptureMachineAndroid, ScreenCapture.this,
                                 image.getPlanes()[0].getBuffer(),
                                 image.getPlanes()[0].getRowStride(),
                                 image.getPlanes()[1].getBuffer(), image.getPlanes()[2].getBuffer(),
@@ -294,8 +297,8 @@
             mResultData = data;
             changeCaptureStateAndNotify(CaptureState.ALLOWED);
         }
-        nativeOnActivityResult(
-                mNativeScreenCaptureMachineAndroid, resultCode == Activity.RESULT_OK);
+        ScreenCaptureJni.get().onActivityResult(mNativeScreenCaptureMachineAndroid,
+                ScreenCapture.this, resultCode == Activity.RESULT_OK);
     }
 
     @CalledByNative
@@ -423,7 +426,8 @@
 
         mCurrentOrientation = orientation;
         rotateCaptureOrientation(orientation);
-        nativeOnOrientationChange(mNativeScreenCaptureMachineAndroid, rotation);
+        ScreenCaptureJni.get().onOrientationChange(
+                mNativeScreenCaptureMachineAndroid, ScreenCapture.this, rotation);
         return true;
     }
 
@@ -434,21 +438,23 @@
         }
     }
 
-    // Method for ScreenCapture implementations to call back native code.
-    private native void nativeOnRGBAFrameAvailable(long nativeScreenCaptureMachineAndroid,
-            ByteBuffer buf, int rowStride, int left, int top, int width, int height,
-            long timestamp);
+    @NativeMethods
+    interface Natives {
+        // Method for ScreenCapture implementations to call back native code.
+        void onRGBAFrameAvailable(long nativeScreenCaptureMachineAndroid, ScreenCapture caller,
+                ByteBuffer buf, int rowStride, int left, int top, int width, int height,
+                long timestamp);
 
-    private native void nativeOnI420FrameAvailable(long nativeScreenCaptureMachineAndroid,
-            ByteBuffer yBuffer, int yStride, ByteBuffer uBuffer, ByteBuffer vBuffer,
-            int uvRowStride, int uvPixelStride, int left, int top, int width, int height,
-            long timestamp);
+        void onI420FrameAvailable(long nativeScreenCaptureMachineAndroid, ScreenCapture caller,
+                ByteBuffer yBuffer, int yStride, ByteBuffer uBuffer, ByteBuffer vBuffer,
+                int uvRowStride, int uvPixelStride, int left, int top, int width, int height,
+                long timestamp);
+        // Method for ScreenCapture implementations to notify activity result.
+        void onActivityResult(
+                long nativeScreenCaptureMachineAndroid, ScreenCapture caller, boolean result);
 
-    // Method for ScreenCapture implementations to notify activity result.
-    private native void nativeOnActivityResult(
-            long nativeScreenCaptureMachineAndroid, boolean result);
-
-    // Method for ScreenCapture implementations to notify orientation change.
-    private native void nativeOnOrientationChange(
-            long nativeScreenCaptureMachineAndroid, int rotation);
+        // Method for ScreenCapture implementations to notify orientation change.
+        void onOrientationChange(
+                long nativeScreenCaptureMachineAndroid, ScreenCapture caller, int rotation);
+    }
 }
diff --git a/media/capture/video/android/BUILD.gn b/media/capture/video/android/BUILD.gn
index bf3297e..8c8d366 100644
--- a/media/capture/video/android/BUILD.gn
+++ b/media/capture/video/android/BUILD.gn
@@ -51,8 +51,10 @@
 android_library("capture_java") {
   deps = [
     "//base:base_java",
+    "//base:jni_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
   ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 
   srcjar_deps = [ ":media_java_enums_srcjar" ]
 
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCapture.java b/media/capture/video/android/java/src/org/chromium/media/VideoCapture.java
index 9853d9c..e35e102 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCapture.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCapture.java
@@ -12,6 +12,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
@@ -64,11 +65,11 @@
             int width, int height, int frameRate, boolean enableFaceDetection);
 
     // Success is indicated by returning true and a callback to
-    // nativeOnStarted(), which may occur synchronously or asynchronously.
-    // Failure can be indicated by one of the following:
-    // * Returning false. In this case no callback to nativeOnStarted() is made.
-    // * Returning true, and asynchronously invoking nativeOnError. In this case
-    //   also no callback to nativeOnStarted() is made.
+    // VideoCaptureJni.get().onStarted(,  VideoCapture.this), which may occur synchronously or
+    // asynchronously. Failure can be indicated by one of the following:
+    // * Returning false. In this case no callback to VideoCaptureJni.get().onStarted() is made.
+    // * Returning true, and asynchronously invoking VideoCaptureJni.get().onError. In this case
+    //   also no callback to VideoCaptureJni.get().onStarted() is made.
     @CalledByNative
     public abstract boolean startCaptureMaybeAsync();
 
@@ -76,7 +77,7 @@
     @CalledByNative
     public abstract boolean stopCaptureAndBlockUntilStopped();
 
-    // Replies by calling nativeOnGetPhotoCapabilitiesReply(). Will pass |null|
+    // Replies by calling VideoCaptureJni.get().onGetPhotoCapabilitiesReply(). Will pass |null|
     // for parameter |result| to indicate failure.
     @CalledByNative
     public abstract void getPhotoCapabilitiesAsync(long callbackId);
@@ -107,7 +108,7 @@
             int whiteBalanceMode, double iso, boolean hasRedEyeReduction, boolean redEyeReduction,
             int fillLightMode, boolean hasTorch, boolean torch, double colorTemperature);
 
-    // Replies by calling nativeOnPhotoTaken().
+    // Replies by calling VideoCaptureJni.get().onPhotoTaken().
     @CalledByNative
     public abstract void takePhotoAsync(long callbackId);
 
@@ -177,10 +178,11 @@
         return orientation;
     }
 
-    // {@link nativeOnPhotoTaken()} needs to be called back if there's any
+    // {@link VideoCaptureJni.get().onPhotoTaken()} needs to be called back if there's any
     // problem after {@link takePhotoAsync()} has returned true.
     protected void notifyTakePhotoError(long callbackId) {
-        nativeOnPhotoTaken(mNativeVideoCaptureDeviceAndroid, callbackId, null);
+        VideoCaptureJni.get().onPhotoTaken(
+                mNativeVideoCaptureDeviceAndroid, VideoCapture.this, callbackId, null);
     }
 
     /**
@@ -237,34 +239,35 @@
         return intArray;
     }
 
-    // Method for VideoCapture implementations to call back native code.
-    public native void nativeOnFrameAvailable(
-            long nativeVideoCaptureDeviceAndroid, byte[] data, int length, int rotation);
+    @NativeMethods
+    interface Natives {
+        // Method for VideoCapture implementations to call back native code.
+        void onFrameAvailable(long nativeVideoCaptureDeviceAndroid, VideoCapture caller,
+                byte[] data, int length, int rotation);
 
-    public native void nativeOnI420FrameAvailable(long nativeVideoCaptureDeviceAndroid,
-            ByteBuffer yBuffer, int yStride, ByteBuffer uBuffer, ByteBuffer vBuffer,
-            int uvRowStride, int uvPixelStride, int width, int height, int rotation,
-            long timestamp);
+        void onI420FrameAvailable(long nativeVideoCaptureDeviceAndroid, VideoCapture caller,
+                ByteBuffer yBuffer, int yStride, ByteBuffer uBuffer, ByteBuffer vBuffer,
+                int uvRowStride, int uvPixelStride, int width, int height, int rotation,
+                long timestamp);
+        // Method for VideoCapture implementations to signal an asynchronous error.
+        void onError(long nativeVideoCaptureDeviceAndroid, VideoCapture caller,
+                int androidVideoCaptureError, String message);
 
-    // Method for VideoCapture implementations to signal an asynchronous error.
-    public native void nativeOnError(
-            long nativeVideoCaptureDeviceAndroid, int androidVideoCaptureError, String message);
+        // Method for VideoCapture implementations to signal that a frame was dropped.
+        void onFrameDropped(long nativeVideoCaptureDeviceAndroid, VideoCapture caller,
+                int androidVideoCaptureFrameDropReason);
 
-    // Method for VideoCapture implementations to signal that a frame was dropped.
-    public native void nativeOnFrameDropped(
-            long nativeVideoCaptureDeviceAndroid, int androidVideoCaptureFrameDropReason);
+        void onGetPhotoCapabilitiesReply(long nativeVideoCaptureDeviceAndroid, VideoCapture caller,
+                long callbackId, PhotoCapabilities result);
+        // Callback for calls to takePhoto(). This can indicate both success and
+        // failure. Failure is indicated by |data| being null.
+        void onPhotoTaken(long nativeVideoCaptureDeviceAndroid, VideoCapture caller,
+                long callbackId, byte[] data);
 
-    public native void nativeOnGetPhotoCapabilitiesReply(
-            long nativeVideoCaptureDeviceAndroid, long callbackId, PhotoCapabilities result);
+        // Method for VideoCapture implementations to report device started event.
+        void onStarted(long nativeVideoCaptureDeviceAndroid, VideoCapture caller);
 
-    // Callback for calls to takePhoto(). This can indicate both success and
-    // failure. Failure is indicated by |data| being null.
-    public native void nativeOnPhotoTaken(
-            long nativeVideoCaptureDeviceAndroid, long callbackId, byte[] data);
-
-    // Method for VideoCapture implementations to report device started event.
-    public native void nativeOnStarted(long nativeVideoCaptureDeviceAndroid);
-
-    public native void nativeDCheckCurrentlyOnIncomingTaskRunner(
-            long nativeVideoCaptureDeviceAndroid);
+        void dCheckCurrentlyOnIncomingTaskRunner(
+                long nativeVideoCaptureDeviceAndroid, VideoCapture caller);
+    }
 }
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
index d541593d..95ede71 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
@@ -142,7 +142,7 @@
     private class CrErrorCallback implements android.hardware.Camera.ErrorCallback {
         @Override
         public void onError(int error, android.hardware.Camera camera) {
-            nativeOnError(mNativeVideoCaptureDeviceAndroid,
+            VideoCaptureJni.get().onError(mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera.this,
                     AndroidVideoCaptureError.ANDROID_API_1_CAMERA_ERROR_CALLBACK_RECEIVED,
                     "Error id: " + error);
 
@@ -170,8 +170,8 @@
             }
             synchronized (mPhotoTakenCallbackLock) {
                 if (mPhotoTakenCallbackId != 0) {
-                    nativeOnPhotoTaken(
-                            mNativeVideoCaptureDeviceAndroid, mPhotoTakenCallbackId, data);
+                    VideoCaptureJni.get().onPhotoTaken(mNativeVideoCaptureDeviceAndroid,
+                            VideoCaptureCamera.this, mPhotoTakenCallbackId, data);
                 }
                 mPhotoTakenCallbackId = 0;
             }
@@ -451,7 +451,8 @@
 
         mPreviewBufferLock.lock();
         try {
-            nativeOnStarted(mNativeVideoCaptureDeviceAndroid);
+            VideoCaptureJni.get().onStarted(
+                    mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera.this);
             mIsRunning = true;
         } finally {
             mPreviewBufferLock.unlock();
@@ -485,7 +486,8 @@
     public void getPhotoCapabilitiesAsync(long callbackId) {
         final android.hardware.Camera.Parameters parameters = getCameraParameters(mCamera);
         if (parameters == null) {
-            nativeOnGetPhotoCapabilitiesReply(mNativeVideoCaptureDeviceAndroid, callbackId, null);
+            VideoCaptureJni.get().onGetPhotoCapabilitiesReply(
+                    mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera.this, callbackId, null);
             return;
         }
         PhotoCapabilities.Builder builder = new PhotoCapabilities.Builder();
@@ -628,9 +630,10 @@
                 .setInt(PhotoCapabilityInt.STEP_COLOR_TEMPERATURE, 50);
         if (jniWhiteBalanceMode == AndroidMeteringMode.FIXED) {
             final int index = COLOR_TEMPERATURES_MAP.indexOfValue(parameters.getWhiteBalance());
-            if (index >= 0)
+            if (index >= 0) {
                 builder.setInt(PhotoCapabilityInt.CURRENT_COLOR_TEMPERATURE,
                         COLOR_TEMPERATURES_MAP.keyAt(index));
+            }
         }
 
         final List<String> flashModes = parameters.getSupportedFlashModes();
@@ -657,8 +660,8 @@
             builder.setFillLightModeArray(integerArrayListToArray(modes));
         }
 
-        nativeOnGetPhotoCapabilitiesReply(
-                mNativeVideoCaptureDeviceAndroid, callbackId, builder.build());
+        VideoCaptureJni.get().onGetPhotoCapabilitiesReply(mNativeVideoCaptureDeviceAndroid,
+                VideoCaptureCamera.this, callbackId, builder.build());
     }
 
     @Override
@@ -892,10 +895,11 @@
                 return;
             }
             if (data.length == mExpectedFrameSize) {
-                nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid, data, mExpectedFrameSize,
-                        getCameraRotation());
+                VideoCaptureJni.get().onFrameAvailable(mNativeVideoCaptureDeviceAndroid,
+                        VideoCaptureCamera.this, data, mExpectedFrameSize, getCameraRotation());
             } else {
-                nativeOnFrameDropped(mNativeVideoCaptureDeviceAndroid,
+                VideoCaptureJni.get().onFrameDropped(mNativeVideoCaptureDeviceAndroid,
+                        VideoCaptureCamera.this,
                         AndroidVideoCaptureFrameDropReason.ANDROID_API_1_UNEXPECTED_DATA_LENGTH);
             }
         } finally {
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
index 25049fa..bd68edca 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
@@ -87,7 +87,8 @@
             cameraDevice.close();
             mCameraDevice = null;
             changeCameraStateAndNotify(CameraState.STOPPED);
-            nativeOnError(mNativeVideoCaptureDeviceAndroid,
+            VideoCaptureJni.get().onError(mNativeVideoCaptureDeviceAndroid,
+                    VideoCaptureCamera2.this,
                     AndroidVideoCaptureError.ANDROID_API_2_CAMERA_DEVICE_ERROR_RECEIVED,
                     "Camera device error " + Integer.toString(error));
         }
@@ -146,7 +147,8 @@
             }
 
             changeCameraStateAndNotify(CameraState.STARTED);
-            nativeOnStarted(mNativeVideoCaptureDeviceAndroid);
+            VideoCaptureJni.get().onStarted(
+                    mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera2.this);
 
             // Frames will be arriving at CrPreviewReaderListener.onImageAvailable();
         }
@@ -160,7 +162,8 @@
             // cleanup?
             changeCameraStateAndNotify(CameraState.STOPPED);
             mPreviewSession = null;
-            nativeOnError(mNativeVideoCaptureDeviceAndroid,
+            VideoCaptureJni.get().onError(mNativeVideoCaptureDeviceAndroid,
+                    VideoCaptureCamera2.this,
                     AndroidVideoCaptureError.ANDROID_API_2_CAPTURE_SESSION_CONFIGURE_FAILED,
                     "Camera session configuration error");
         }
@@ -186,14 +189,16 @@
 
             try (Image image = reader.acquireLatestImage()) {
                 if (image == null) {
-                    nativeOnFrameDropped(mNativeVideoCaptureDeviceAndroid,
+                    VideoCaptureJni.get().onFrameDropped(mNativeVideoCaptureDeviceAndroid,
+                            VideoCaptureCamera2.this,
                             AndroidVideoCaptureFrameDropReason
                                     .ANDROID_API_2_ACQUIRED_IMAGE_IS_NULL);
                     return;
                 }
 
                 if (image.getFormat() != ImageFormat.YUV_420_888 || image.getPlanes().length != 3) {
-                    nativeOnError(mNativeVideoCaptureDeviceAndroid,
+                    VideoCaptureJni.get().onError(mNativeVideoCaptureDeviceAndroid,
+                            VideoCaptureCamera2.this,
                             AndroidVideoCaptureError
                                     .ANDROID_API_2_IMAGE_READER_UNEXPECTED_IMAGE_FORMAT,
                             "Unexpected image format: " + image.getFormat()
@@ -203,7 +208,8 @@
 
                 if (reader.getWidth() != image.getWidth()
                         || reader.getHeight() != image.getHeight()) {
-                    nativeOnError(mNativeVideoCaptureDeviceAndroid,
+                    VideoCaptureJni.get().onError(mNativeVideoCaptureDeviceAndroid,
+                            VideoCaptureCamera2.this,
                             AndroidVideoCaptureError
                                     .ANDROID_API_2_IMAGE_READER_SIZE_DID_NOT_MATCH_IMAGE_SIZE,
                             "ImageReader size (" + reader.getWidth() + "x" + reader.getHeight()
@@ -212,12 +218,12 @@
                     throw new IllegalStateException();
                 }
 
-                nativeOnI420FrameAvailable(mNativeVideoCaptureDeviceAndroid,
-                        image.getPlanes()[0].getBuffer(), image.getPlanes()[0].getRowStride(),
-                        image.getPlanes()[1].getBuffer(), image.getPlanes()[2].getBuffer(),
-                        image.getPlanes()[1].getRowStride(), image.getPlanes()[1].getPixelStride(),
-                        image.getWidth(), image.getHeight(), getCameraRotation(),
-                        image.getTimestamp());
+                VideoCaptureJni.get().onI420FrameAvailable(mNativeVideoCaptureDeviceAndroid,
+                        VideoCaptureCamera2.this, image.getPlanes()[0].getBuffer(),
+                        image.getPlanes()[0].getRowStride(), image.getPlanes()[1].getBuffer(),
+                        image.getPlanes()[2].getBuffer(), image.getPlanes()[1].getRowStride(),
+                        image.getPlanes()[1].getPixelStride(), image.getWidth(), image.getHeight(),
+                        getCameraRotation(), image.getTimestamp());
             } catch (IllegalStateException ex) {
                 Log.e(TAG, "acquireLatestImage():", ex);
             }
@@ -315,7 +321,8 @@
                 }
 
                 final byte[] capturedData = readCapturedData(image);
-                nativeOnPhotoTaken(mNativeVideoCaptureDeviceAndroid, mCallbackId, capturedData);
+                VideoCaptureJni.get().onPhotoTaken(mNativeVideoCaptureDeviceAndroid,
+                        VideoCaptureCamera2.this, mCallbackId, capturedData);
 
             } catch (IllegalStateException ex) {
                 notifyTakePhotoError(mCallbackId);
@@ -464,9 +471,10 @@
                 if (mCurrentFocusDistance == 0) {
                     Log.d(TAG, "infinity focus.");
                     mCurrentFocusDistance = (long) Double.POSITIVE_INFINITY;
-                } else if (mCurrentFocusDistance > 0)
+                } else if (mCurrentFocusDistance > 0) {
                     builder.setDouble(PhotoCapabilityDouble.CURRENT_FOCUS_DISTANCE,
                             1 / mCurrentFocusDistance);
+                }
             } else { //  null value
                 Log.d(TAG, "LENS_FOCUS_DISTANCE is null");
             }
@@ -510,9 +518,10 @@
                 } else if (focusMode == CameraMetadata.CONTROL_AF_MODE_OFF) {
                     jniFocusMode = AndroidMeteringMode.FIXED;
                     // Set focus distance here.
-                    if (mCurrentFocusDistance > 0)
+                    if (mCurrentFocusDistance > 0) {
                         builder.setDouble(PhotoCapabilityDouble.CURRENT_FOCUS_DISTANCE,
                                 1 / mCurrentFocusDistance);
+                    }
                 } else {
                     assert jniFocusMode == CameraMetadata.CONTROL_AF_MODE_EDOF;
                 }
@@ -675,8 +684,8 @@
                 builder.setFillLightModeArray(integerArrayListToArray(modes));
             }
 
-            nativeOnGetPhotoCapabilitiesReply(
-                    mNativeVideoCaptureDeviceAndroid, mCallbackId, builder.build());
+            VideoCaptureJni.get().onGetPhotoCapabilitiesReply(mNativeVideoCaptureDeviceAndroid,
+                    VideoCaptureCamera2.this, mCallbackId, builder.build());
         }
     }
 
@@ -755,14 +764,16 @@
             }
 
             if (mOptions.focusMode != AndroidMeteringMode.NOT_SET) mFocusMode = mOptions.focusMode;
-            if (mOptions.currentFocusDistance != 0)
+            if (mOptions.currentFocusDistance != 0) {
                 mCurrentFocusDistance = (float) mOptions.currentFocusDistance;
-            if (mOptions.exposureMode != AndroidMeteringMode.NOT_SET)
+            }
+            if (mOptions.exposureMode != AndroidMeteringMode.NOT_SET) {
                 mExposureMode = mOptions.exposureMode;
+            }
             if (mOptions.exposureTime != 0) mLastExposureTimeNs = (long) mOptions.exposureTime;
-            if (mOptions.whiteBalanceMode != AndroidMeteringMode.NOT_SET)
+            if (mOptions.whiteBalanceMode != AndroidMeteringMode.NOT_SET) {
                 mWhiteBalanceMode = mOptions.whiteBalanceMode;
-
+            }
             if (mOptions.width > 0) mPhotoWidth = (int) Math.round(mOptions.width);
             if (mOptions.height > 0) mPhotoHeight = (int) Math.round(mOptions.height);
 
@@ -819,12 +830,13 @@
                                   .floatValue());
             }
             if (mOptions.iso > 0) mIso = (int) Math.round(mOptions.iso);
-            if (mOptions.colorTemperature > 0)
+            if (mOptions.colorTemperature > 0) {
                 mColorTemperature = (int) Math.round(mOptions.colorTemperature);
-
+            }
             if (mOptions.hasRedEyeReduction) mRedEyeReduction = mOptions.redEyeReduction;
-            if (mOptions.fillLightMode != AndroidFillLightMode.NOT_SET)
+            if (mOptions.fillLightMode != AndroidFillLightMode.NOT_SET) {
                 mFillLightMode = mOptions.fillLightMode;
+            }
             if (mOptions.hasTorch) mTorch = mOptions.torch;
 
             if (mPreviewSession != null) {
@@ -1016,8 +1028,8 @@
         if (createPreviewObjectsAndStartPreview()) return;
 
         changeCameraStateAndNotify(CameraState.STOPPED);
-        nativeOnError(mNativeVideoCaptureDeviceAndroid, androidVideoCaptureError,
-                "Error starting or restarting preview");
+        VideoCaptureJni.get().onError(mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera2.this,
+                androidVideoCaptureError, "Error starting or restarting preview");
     }
 
     private boolean createPreviewObjectsAndStartPreview() {
@@ -1410,7 +1422,8 @@
     VideoCaptureCamera2(int id, long nativeVideoCaptureDeviceAndroid) {
         super(id, nativeVideoCaptureDeviceAndroid);
 
-        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+        VideoCaptureJni.get().dCheckCurrentlyOnIncomingTaskRunner(
+                mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera2.this);
 
         HandlerThread thread = new HandlerThread("VideoCaptureCamera2_CameraThread");
         thread.start();
@@ -1431,7 +1444,8 @@
     @Override
     public boolean allocate(int width, int height, int frameRate, boolean enableFaceDetection) {
         Log.d(TAG, "allocate: requested (%d x %d) @%dfps", width, height, frameRate);
-        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+        VideoCaptureJni.get().dCheckCurrentlyOnIncomingTaskRunner(
+                mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera2.this);
         synchronized (mCameraStateLock) {
             if (mCameraState == CameraState.OPENING || mCameraState == CameraState.CONFIGURING) {
                 Log.e(TAG, "allocate() invoked while Camera is busy opening/configuring.");
@@ -1489,7 +1503,8 @@
 
     @Override
     public boolean startCaptureMaybeAsync() {
-        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+        VideoCaptureJni.get().dCheckCurrentlyOnIncomingTaskRunner(
+                mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera2.this);
 
         changeCameraStateAndNotify(CameraState.OPENING);
         final CameraManager manager =
@@ -1511,7 +1526,8 @@
 
     @Override
     public boolean stopCaptureAndBlockUntilStopped() {
-        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+        VideoCaptureJni.get().dCheckCurrentlyOnIncomingTaskRunner(
+                mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera2.this);
         try (TraceEvent trace_event =
                         TraceEvent.scoped("VideoCaptureCamera2.stopCaptureAndBlockUntilStopped")) {
             // With Camera2 API, the capture is started asynchronously, which will cause problem if
@@ -1538,7 +1554,8 @@
 
     @Override
     public void getPhotoCapabilitiesAsync(long callbackId) {
-        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+        VideoCaptureJni.get().dCheckCurrentlyOnIncomingTaskRunner(
+                mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera2.this);
         mCameraThreadHandler.post(new GetPhotoCapabilitiesTask(callbackId));
     }
 
@@ -1548,7 +1565,8 @@
             boolean hasExposureCompensation, double exposureCompensation, double exposureTime,
             int whiteBalanceMode, double iso, boolean hasRedEyeReduction, boolean redEyeReduction,
             int fillLightMode, boolean hasTorch, boolean torch, double colorTemperature) {
-        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+        VideoCaptureJni.get().dCheckCurrentlyOnIncomingTaskRunner(
+                mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera2.this);
         mCameraThreadHandler.post(new SetPhotoOptionsTask(
                 new PhotoOptions(zoom, focusMode, currentFocusDistance, exposureMode, width, height,
                         pointsOfInterest2D, hasExposureCompensation, exposureCompensation,
@@ -1558,7 +1576,8 @@
 
     @Override
     public void takePhotoAsync(long callbackId) {
-        nativeDCheckCurrentlyOnIncomingTaskRunner(mNativeVideoCaptureDeviceAndroid);
+        VideoCaptureJni.get().dCheckCurrentlyOnIncomingTaskRunner(
+                mNativeVideoCaptureDeviceAndroid, VideoCaptureCamera2.this);
         TraceEvent.instant("VideoCaptureCamera2.java", "takePhotoAsync");
 
         mCameraThreadHandler.post(new TakePhotoTask(callbackId));
diff --git a/media/filters/BUILD.gn b/media/filters/BUILD.gn
index 9022a76..4cb86a2 100644
--- a/media/filters/BUILD.gn
+++ b/media/filters/BUILD.gn
@@ -212,6 +212,7 @@
       "//gpu/command_buffer/client",
       "//gpu/command_buffer/common",
       "//gpu/ipc/common",
+      "//media/fuchsia/cdm",
       "//media/fuchsia/common",
       "//third_party/fuchsia-sdk/sdk:media",
       "//third_party/fuchsia-sdk/sdk:mediacodec",
diff --git a/media/filters/fuchsia/fuchsia_video_decoder.cc b/media/filters/fuchsia/fuchsia_video_decoder.cc
index 2c8e526..e5be008 100644
--- a/media/filters/fuchsia/fuchsia_video_decoder.cc
+++ b/media/filters/fuchsia/fuchsia_video_decoder.cc
@@ -33,6 +33,9 @@
 #include "media/base/video_decoder_config.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
+#include "media/fuchsia/cdm/fuchsia_cdm_context.h"
+#include "media/fuchsia/cdm/fuchsia_decryptor.h"
+#include "media/fuchsia/cdm/fuchsia_stream_decryptor.h"
 #include "media/fuchsia/common/stream_processor_helper.h"
 #include "media/fuchsia/common/sysmem_buffer_pool.h"
 #include "media/fuchsia/common/sysmem_buffer_writer_queue.h"
@@ -152,7 +155,8 @@
 
 }  // namespace
 
-class FuchsiaVideoDecoder : public VideoDecoder {
+class FuchsiaVideoDecoder : public VideoDecoder,
+                            public FuchsiaSecureStreamDecryptor::Client {
  public:
   FuchsiaVideoDecoder(gpu::SharedImageInterface* shared_image_interface,
                       gpu::ContextSupport* gpu_context_support,
@@ -174,6 +178,12 @@
   int GetMaxDecodeRequests() const override;
 
  private:
+  // FuchsiaSecureStreamDecryptor::Client implementation.
+  void OnDecryptorOutputPacket(StreamProcessorHelper::IoPacket packet) override;
+  void OnDecryptorEndOfStreamPacket() override;
+  void OnDecryptorError() override;
+  void OnDecryptorNoKey() override;
+
   // Event handlers for |codec_|.
   void OnStreamFailed(uint64_t stream_lifetime_ordinal,
                       fuchsia::media::StreamError error);
@@ -227,6 +237,9 @@
   // value is used only if the aspect ratio is not specified in the bitstream.
   float container_pixel_aspect_ratio_ = 1.0;
 
+  // Decryptor is allocated only for encrypted streams.
+  std::unique_ptr<FuchsiaSecureStreamDecryptor> decryptor_;
+
   // TODO(sergeyu): Use StreamProcessorHelper.
   fuchsia::media::StreamProcessorPtr codec_;
   BufferAllocator sysmem_allocator_;
@@ -238,16 +251,22 @@
   // stream_lifetime_ordinal_.
   bool active_stream_ = false;
 
-  // Input buffers.
-  uint64_t input_buffer_lifetime_ordinal_ = 1;
+  // Callbacks for pending Decode() request.
+  std::deque<DecodeCB> decode_callbacks_;
+
+  // Buffer queue for |codec_|. Used only for clear streams, i.e. when there
+  // is no |decryptor_|.
   SysmemBufferWriterQueue input_writer_queue_;
+
+  // Input buffers for |codec_|.
+  uint64_t input_buffer_lifetime_ordinal_ = 1;
   std::unique_ptr<SysmemBufferPool::Creator> input_buffer_collection_creator_;
+  size_t num_input_buffers_ = 0;
   std::unique_ptr<SysmemBufferPool> input_buffer_collection_;
   base::flat_map<size_t, StreamProcessorHelper::IoPacket>
       in_flight_input_packets_;
-  std::deque<DecodeCB> decode_callbacks_;
 
-  // Output buffers.
+  // Output buffers for |codec_|.
   fuchsia::media::VideoUncompressedFormat output_format_;
   uint64_t output_buffer_lifetime_ordinal_ = 1;
   fuchsia::sysmem::BufferCollectionPtr output_buffer_collection_;
@@ -303,15 +322,17 @@
       return;
     }
 
-    // If decryptor is decrypt only, return false here to allow decoder selector
-    // to choose DecryptingDemuxerStream, which will handle the decryption and
-    // pass the clear stream to this decoder.
-    Decryptor* decryptor = cdm_context->GetDecryptor();
-    if (decryptor && decryptor->CanAlwaysDecrypt()) {
-      DVLOG(1) << "Decryptor can always decrypt, return false.";
+    // If Cdm is not FuchsiaCdm then fail initialization to allow decoder
+    // selector to choose DecryptingDemuxerStream, which will handle the
+    // decryption and pass the clear stream to this decoder.
+    FuchsiaCdmContext* fuchsia_cdm = cdm_context->GetFuchsiaCdmContext();
+    if (!fuchsia_cdm) {
+      DVLOG(1) << "FuchsiaVideoDecoder is compatible only with Fuchsia CDM.";
       std::move(done_callback).Run(false);
       return;
     }
+
+    decryptor_ = fuchsia_cdm->CreateSecureDecryptor(this);
   }
 
   output_cb_ = output_cb;
@@ -387,7 +408,12 @@
   }
 
   decode_callbacks_.push_back(std::move(decode_cb));
-  input_writer_queue_.EnqueueBuffer(buffer);
+
+  if (decryptor_) {
+    decryptor_->Decrypt(std::move(buffer));
+  } else {
+    input_writer_queue_.EnqueueBuffer(buffer);
+  }
 }
 
 void FuchsiaVideoDecoder::Reset(base::OnceClosure closure) {
@@ -420,7 +446,24 @@
 int FuchsiaVideoDecoder::GetMaxDecodeRequests() const {
   // Add one extra request to be able to send new InputBuffer immediately after
   // OnFreeInputPacket().
-  return input_writer_queue_.num_buffers() + 1;
+  return num_input_buffers_ + 1;
+}
+
+void FuchsiaVideoDecoder::OnDecryptorOutputPacket(
+    StreamProcessorHelper::IoPacket packet) {
+  SendInputPacket(nullptr, std::move(packet));
+}
+
+void FuchsiaVideoDecoder::OnDecryptorEndOfStreamPacket() {
+  ProcessEndOfStream();
+}
+
+void FuchsiaVideoDecoder::OnDecryptorError() {
+  OnError();
+}
+void FuchsiaVideoDecoder::OnDecryptorNoKey() {
+  NOTIMPLEMENTED();
+  OnError();
 }
 
 void FuchsiaVideoDecoder::OnStreamFailed(uint64_t stream_lifetime_ordinal,
@@ -440,21 +483,32 @@
 
   input_buffer_collection_.reset();
   input_writer_queue_.ResetBuffers();
+  num_input_buffers_ = 0;
 
   // Create buffer constrains for the input buffer collection.
-  base::Optional<fuchsia::sysmem::BufferCollectionConstraints>
-      buffer_constraints =
-          SysmemBufferWriter::GetRecommendedConstraints(stream_constraints);
-  if (!buffer_constraints.has_value()) {
-    OnError();
-    return;
+  size_t num_tokens;
+  fuchsia::sysmem::BufferCollectionConstraints buffer_constraints;
+
+  if (decryptor_) {
+    // For encrypted streams the sysmem buffer collection is used for decryptor
+    // output and decoder input. It is not used directly.
+    num_tokens = 2;
+    buffer_constraints.usage.none = fuchsia::sysmem::noneUsage;
+  } else {
+    num_tokens = 1;
+    auto writer_constraints =
+        SysmemBufferWriter::GetRecommendedConstraints(stream_constraints);
+    if (!writer_constraints.has_value()) {
+      OnError();
+      return;
+    }
+    buffer_constraints = std::move(writer_constraints).value();
   }
 
-  // Request SysmemBufferPool with one token to share with the codec.
   input_buffer_collection_creator_ =
-      sysmem_allocator_.MakeBufferPoolCreator(1 /* num_shared_token */);
+      sysmem_allocator_.MakeBufferPoolCreator(num_tokens);
   input_buffer_collection_creator_->Create(
-      std::move(buffer_constraints).value(),
+      std::move(buffer_constraints),
       base::BindOnce(&FuchsiaVideoDecoder::OnInputBufferPoolCreated,
                      base::Unretained(this), std::move(stream_constraints)));
 }
@@ -469,6 +523,9 @@
   }
 
   input_buffer_collection_ = std::move(pool);
+  num_input_buffers_ =
+      constraints.default_settings().packet_count_for_server() +
+      constraints.default_settings().packet_count_for_client();
 
   fuchsia::media::StreamBufferPartialSettings settings;
   settings.set_buffer_lifetime_ordinal(input_buffer_lifetime_ordinal_);
@@ -479,11 +536,18 @@
       constraints.default_settings().packet_count_for_server());
   settings.set_packet_count_for_client(
       constraints.default_settings().packet_count_for_client());
-  settings.set_sysmem_token(std::move(input_buffer_collection_->TakeToken()));
+  settings.set_sysmem_token(input_buffer_collection_->TakeToken());
   codec_->SetInputBufferPartialSettings(std::move(settings));
 
-  input_buffer_collection_->CreateWriter(base::BindOnce(
-      &FuchsiaVideoDecoder::OnWriterCreated, base::Unretained(this)));
+  if (decryptor_) {
+    decryptor_->SetOutputBufferCollectionToken(
+        input_buffer_collection_->TakeToken(),
+        constraints.default_settings().packet_count_for_client(),
+        constraints.default_settings().packet_count_for_server());
+  } else {
+    input_buffer_collection_->CreateWriter(base::BindOnce(
+        &FuchsiaVideoDecoder::OnWriterCreated, base::Unretained(this)));
+  }
 }
 
 void FuchsiaVideoDecoder::OnWriterCreated(
@@ -550,15 +614,6 @@
     return;
   }
 
-  if (free_input_packet.packet_index() >= input_writer_queue_.num_buffers()) {
-    DLOG(ERROR) << "fuchsia.mediacodec sent OnFreeInputPacket() for an unknown "
-                   "packet: buffer_lifetime_ordinal="
-                << free_input_packet.buffer_lifetime_ordinal()
-                << " packet_index=" << free_input_packet.packet_index();
-    OnError();
-    return;
-  }
-
   auto it = in_flight_input_packets_.find(free_input_packet.packet_index());
   if (it == in_flight_input_packets_.end()) {
     DLOG(ERROR) << "Received OnFreeInputPacket() with invalid packet index.";
@@ -828,6 +883,8 @@
   }
   decode_callbacks_.clear();
 
+  decryptor_.reset();
+
   input_writer_queue_.ResetBuffers();
   input_writer_queue_.ResetQueue();
 
diff --git a/media/fuchsia/cdm/BUILD.gn b/media/fuchsia/cdm/BUILD.gn
index 93f868a..bc242ec 100644
--- a/media/fuchsia/cdm/BUILD.gn
+++ b/media/fuchsia/cdm/BUILD.gn
@@ -8,6 +8,7 @@
   sources = [
     "fuchsia_cdm.cc",
     "fuchsia_cdm.h",
+    "fuchsia_cdm_context.h",
     "fuchsia_cdm_factory.cc",
     "fuchsia_cdm_factory.h",
     "fuchsia_cdm_provider.h",
diff --git a/media/fuchsia/cdm/fuchsia_cdm.cc b/media/fuchsia/cdm/fuchsia_cdm.cc
index df3ed2e..afa0769 100644
--- a/media/fuchsia/cdm/fuchsia_cdm.cc
+++ b/media/fuchsia/cdm/fuchsia_cdm.cc
@@ -244,6 +244,11 @@
 
 FuchsiaCdm::~FuchsiaCdm() = default;
 
+std::unique_ptr<FuchsiaSecureStreamDecryptor> FuchsiaCdm::CreateSecureDecryptor(
+    FuchsiaSecureStreamDecryptor::Client* client) {
+  return FuchsiaSecureStreamDecryptor::Create(cdm_.get(), client);
+}
+
 void FuchsiaCdm::SetServerCertificate(
     const std::vector<uint8_t>& certificate,
     std::unique_ptr<SimpleCdmPromise> promise) {
@@ -422,4 +427,8 @@
   return kInvalidCdmId;
 }
 
+FuchsiaCdmContext* FuchsiaCdm::GetFuchsiaCdmContext() {
+  return this;
+}
+
 }  // namespace media
diff --git a/media/fuchsia/cdm/fuchsia_cdm.h b/media/fuchsia/cdm/fuchsia_cdm.h
index b12e527..c9ac613 100644
--- a/media/fuchsia/cdm/fuchsia_cdm.h
+++ b/media/fuchsia/cdm/fuchsia_cdm.h
@@ -13,11 +13,14 @@
 #include "media/base/cdm_context.h"
 #include "media/base/cdm_promise_adapter.h"
 #include "media/base/content_decryption_module.h"
+#include "media/fuchsia/cdm/fuchsia_cdm_context.h"
 
 namespace media {
 class FuchsiaDecryptor;
 
-class FuchsiaCdm : public ContentDecryptionModule, public CdmContext {
+class FuchsiaCdm : public ContentDecryptionModule,
+                   public CdmContext,
+                   public FuchsiaCdmContext {
  public:
   struct SessionCallbacks {
     SessionCallbacks();
@@ -62,6 +65,11 @@
       EventCB event_cb) override;
   Decryptor* GetDecryptor() override;
   int GetCdmId() const override;
+  FuchsiaCdmContext* GetFuchsiaCdmContext() override;
+
+  // FuchsiaCdmContext implementation:
+  std::unique_ptr<FuchsiaSecureStreamDecryptor> CreateSecureDecryptor(
+      FuchsiaSecureStreamDecryptor::Client* client) override;
 
  private:
   class CdmSession;
diff --git a/media/fuchsia/cdm/fuchsia_cdm_context.h b/media/fuchsia/cdm/fuchsia_cdm_context.h
new file mode 100644
index 0000000..dd614ff
--- /dev/null
+++ b/media/fuchsia/cdm/fuchsia_cdm_context.h
@@ -0,0 +1,27 @@
+// Copyright 2019 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_FUCHSIA_CDM_FUCHSIA_CDM_CONTEXT_H_
+#define MEDIA_FUCHSIA_CDM_FUCHSIA_CDM_CONTEXT_H_
+
+#include "media/fuchsia/cdm/fuchsia_stream_decryptor.h"
+
+namespace media {
+
+// Interface for Fuchsia-specific extensions to the CdmContext interface.
+class FuchsiaCdmContext {
+ public:
+  FuchsiaCdmContext() = default;
+
+  // Creates FuchsiaSecureStreamDecryptor instance for the CDM context.
+  virtual std::unique_ptr<FuchsiaSecureStreamDecryptor> CreateSecureDecryptor(
+      FuchsiaSecureStreamDecryptor::Client* client) = 0;
+
+ protected:
+  virtual ~FuchsiaCdmContext() = default;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_FUCHSIA_CDM_FUCHSIA_CDM_CONTEXT_H_
diff --git a/media/fuchsia/cdm/fuchsia_decryptor.cc b/media/fuchsia/cdm/fuchsia_decryptor.cc
index 17b946b..e1c8ba79 100644
--- a/media/fuchsia/cdm/fuchsia_decryptor.cc
+++ b/media/fuchsia/cdm/fuchsia_decryptor.cc
@@ -29,7 +29,6 @@
                                scoped_refptr<DecoderBuffer> encrypted,
                                const DecryptCB& decrypt_cb) {
   if (stream_type != StreamType::kAudio) {
-    NOTREACHED();
     decrypt_cb.Run(Status::kError, nullptr);
     return;
   }
@@ -41,26 +40,26 @@
 }
 
 void FuchsiaDecryptor::CancelDecrypt(StreamType stream_type) {
-  DCHECK_EQ(stream_type, StreamType::kAudio);
-  audio_decryptor_->CancelDecrypt();
+  if (stream_type == StreamType::kAudio) {
+    audio_decryptor_->CancelDecrypt();
+  }
 }
 
 void FuchsiaDecryptor::InitializeAudioDecoder(const AudioDecoderConfig& config,
                                               const DecoderInitCB& init_cb) {
-  // Only supports decrypt for audio stream.
-  NOTREACHED();
+  // Only decryption is supported.
   init_cb.Run(false);
 }
 
 void FuchsiaDecryptor::InitializeVideoDecoder(const VideoDecoderConfig& config,
                                               const DecoderInitCB& init_cb) {
+  // Only decryption is supported.
   init_cb.Run(false);
 }
 
 void FuchsiaDecryptor::DecryptAndDecodeAudio(
     scoped_refptr<DecoderBuffer> encrypted,
     const AudioDecodeCB& audio_decode_cb) {
-  // Only supports decrypt for audio stream.
   NOTREACHED();
   audio_decode_cb.Run(Status::kError, AudioFrames());
 }
diff --git a/media/fuchsia/cdm/fuchsia_decryptor.h b/media/fuchsia/cdm/fuchsia_decryptor.h
index cc15dd9..5a84135a 100644
--- a/media/fuchsia/cdm/fuchsia_decryptor.h
+++ b/media/fuchsia/cdm/fuchsia_decryptor.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "media/base/decryptor.h"
+#include "media/fuchsia/cdm/fuchsia_stream_decryptor.h"
 
 namespace fuchsia {
 namespace media {
diff --git a/media/fuchsia/cdm/fuchsia_stream_decryptor.cc b/media/fuchsia/cdm/fuchsia_stream_decryptor.cc
index 3eb847b3..79e8ed8 100644
--- a/media/fuchsia/cdm/fuchsia_stream_decryptor.cc
+++ b/media/fuchsia/cdm/fuchsia_stream_decryptor.cc
@@ -95,7 +95,6 @@
 
 void FuchsiaStreamDecryptorBase::DecryptInternal(
     scoped_refptr<DecoderBuffer> encrypted) {
-  DCHECK(!encrypted->end_of_stream()) << "EOS frame is always clear.";
   input_writer_queue_.EnqueueBuffer(std::move(encrypted));
 }
 
@@ -156,7 +155,8 @@
       std::move(writer),
       base::BindRepeating(&FuchsiaStreamDecryptorBase::SendInputPacket,
                           base::Unretained(this)),
-      SysmemBufferWriterQueue::EndOfStreamCB());
+      base::BindRepeating(&FuchsiaStreamDecryptorBase::ProcessEndOfStream,
+                          base::Unretained(this)));
 }
 
 void FuchsiaStreamDecryptorBase::SendInputPacket(
@@ -176,6 +176,10 @@
   processor_.Process(std::move(packet));
 }
 
+void FuchsiaStreamDecryptorBase::ProcessEndOfStream() {
+  processor_.ProcessEos();
+}
+
 std::unique_ptr<FuchsiaClearStreamDecryptor>
 FuchsiaClearStreamDecryptor::Create(
     fuchsia::media::drm::ContentDecryptionModule* cdm) {
@@ -223,17 +227,19 @@
     return;
   }
 
-  size_t max_used_output_buffers = std::max(
+  size_t num_buffers_for_client = std::max(
       kMinClearStreamOutputFrames,
       static_cast<size_t>(stream_constraints.packet_count_for_client_min()));
+  size_t num_buffers_for_server =
+      stream_constraints.default_settings().packet_count_for_server();
 
   output_pool_creator_ =
       allocator_.MakeBufferPoolCreator(1 /* num_shared_token */);
-
   output_pool_creator_->Create(
-      SysmemBufferReader::GetRecommendedConstraints(max_used_output_buffers),
+      SysmemBufferReader::GetRecommendedConstraints(num_buffers_for_client),
       base::BindOnce(&FuchsiaClearStreamDecryptor::OnOutputBufferPoolCreated,
-                     base::Unretained(this), max_used_output_buffers));
+                     base::Unretained(this), num_buffers_for_client,
+                     num_buffers_for_server));
 }
 
 void FuchsiaClearStreamDecryptor::OnProcessEos() {
@@ -316,7 +322,8 @@
 }
 
 void FuchsiaClearStreamDecryptor::OnOutputBufferPoolCreated(
-    size_t max_used_output_buffers,
+    size_t num_buffers_for_client,
+    size_t num_buffers_for_server,
     std::unique_ptr<SysmemBufferPool> pool) {
   if (!pool) {
     LOG(ERROR) << "Fail to allocate output buffer.";
@@ -328,7 +335,8 @@
 
   // Provide token before enabling reader. Tokens must be provided to
   // StreamProcessor before getting the allocated buffers.
-  processor_.CompleteOutputBuffersAllocation(max_used_output_buffers,
+  processor_.CompleteOutputBuffersAllocation(num_buffers_for_client,
+                                             num_buffers_for_server,
                                              output_pool_->TakeToken());
 
   output_pool_->CreateReader(base::BindOnce(
@@ -348,4 +356,81 @@
   output_reader_ = std::move(reader);
 }
 
+// static
+std::unique_ptr<FuchsiaSecureStreamDecryptor>
+FuchsiaSecureStreamDecryptor::Create(
+    fuchsia::media::drm::ContentDecryptionModule* cdm,
+    Client* client) {
+  DCHECK(cdm);
+
+  fuchsia::media::drm::DecryptorParams params;
+
+  // TODO(crbug.com/997853): Enable secure mode when it's implemented in sysmem.
+  params.set_require_secure_mode(false);
+
+  params.mutable_input_details()->set_format_details_version_ordinal(0);
+  fuchsia::media::StreamProcessorPtr stream_processor;
+  cdm->CreateDecryptor(std::move(params), stream_processor.NewRequest());
+
+  return std::make_unique<FuchsiaSecureStreamDecryptor>(
+      std::move(stream_processor), client);
+}
+
+FuchsiaSecureStreamDecryptor::FuchsiaSecureStreamDecryptor(
+    fuchsia::media::StreamProcessorPtr processor,
+    Client* client)
+    : FuchsiaStreamDecryptorBase(std::move(processor)), client_(client) {}
+
+FuchsiaSecureStreamDecryptor::~FuchsiaSecureStreamDecryptor() = default;
+
+void FuchsiaSecureStreamDecryptor::SetOutputBufferCollectionToken(
+    fuchsia::sysmem::BufferCollectionTokenPtr token,
+    size_t num_buffers_for_decryptor,
+    size_t num_buffers_for_codec) {
+  DCHECK(!complete_buffer_allocation_callback_);
+  complete_buffer_allocation_callback_ =
+      base::BindOnce(&StreamProcessorHelper::CompleteOutputBuffersAllocation,
+                     base::Unretained(&processor_), num_buffers_for_decryptor,
+                     num_buffers_for_codec, std::move(token));
+  if (waiting_output_buffers_) {
+    std::move(complete_buffer_allocation_callback_).Run();
+    waiting_output_buffers_ = false;
+  }
+}
+
+void FuchsiaSecureStreamDecryptor::Decrypt(
+    scoped_refptr<DecoderBuffer> encrypted) {
+  DecryptInternal(std::move(encrypted));
+}
+
+void FuchsiaSecureStreamDecryptor::AllocateOutputBuffers(
+    const fuchsia::media::StreamBufferConstraints& stream_constraints) {
+  if (complete_buffer_allocation_callback_) {
+    std::move(complete_buffer_allocation_callback_).Run();
+  } else {
+    waiting_output_buffers_ = true;
+  }
+}
+
+void FuchsiaSecureStreamDecryptor::OnProcessEos() {
+  client_->OnDecryptorEndOfStreamPacket();
+}
+
+void FuchsiaSecureStreamDecryptor::OnOutputFormat(
+    fuchsia::media::StreamOutputFormat format) {}
+
+void FuchsiaSecureStreamDecryptor::OnOutputPacket(
+    StreamProcessorHelper::IoPacket packet) {
+  client_->OnDecryptorOutputPacket(std::move(packet));
+}
+
+void FuchsiaSecureStreamDecryptor::OnNoKey() {
+  client_->OnDecryptorNoKey();
+}
+
+void FuchsiaSecureStreamDecryptor::OnError() {
+  ResetStream();
+  client_->OnDecryptorError();
+}
+
 }  // namespace media
diff --git a/media/fuchsia/cdm/fuchsia_stream_decryptor.h b/media/fuchsia/cdm/fuchsia_stream_decryptor.h
index 8679521..0d07690 100644
--- a/media/fuchsia/cdm/fuchsia_stream_decryptor.h
+++ b/media/fuchsia/cdm/fuchsia_stream_decryptor.h
@@ -41,6 +41,7 @@
   void OnWriterCreated(std::unique_ptr<SysmemBufferWriter> writer);
   void SendInputPacket(const DecoderBuffer* buffer,
                        StreamProcessorHelper::IoPacket packet);
+  void ProcessEndOfStream();
 
   SysmemBufferWriterQueue input_writer_queue_;
   std::unique_ptr<SysmemBufferPool::Creator> input_pool_creator_;
@@ -49,7 +50,8 @@
   DISALLOW_COPY_AND_ASSIGN(FuchsiaStreamDecryptorBase);
 };
 
-// Stream decryptor used by FuchsiaDecryptor for audio streams.
+// Stream decryptor that copies output to clear DecodeBuffer's. Used for audio
+// streams.
 class FuchsiaClearStreamDecryptor : public FuchsiaStreamDecryptorBase {
  public:
   static std::unique_ptr<FuchsiaClearStreamDecryptor> Create(
@@ -73,7 +75,8 @@
   void OnNoKey() override;
   void OnError() override;
 
-  void OnOutputBufferPoolCreated(size_t max_used_output_buffers,
+  void OnOutputBufferPoolCreated(size_t num_buffers_for_client,
+                                 size_t num_buffers_for_server,
                                  std::unique_ptr<SysmemBufferPool> pool);
   void OnOutputBufferPoolReaderCreated(
       std::unique_ptr<SysmemBufferReader> reader);
@@ -92,6 +95,55 @@
   DISALLOW_COPY_AND_ASSIGN(FuchsiaClearStreamDecryptor);
 };
 
+// Stream decryptor that decrypts data to secure sysmem buffers. Used for video
+// stream.
+class FuchsiaSecureStreamDecryptor : public FuchsiaStreamDecryptorBase {
+ public:
+  class Client {
+   public:
+    virtual void OnDecryptorOutputPacket(
+        StreamProcessorHelper::IoPacket packet) = 0;
+    virtual void OnDecryptorEndOfStreamPacket() = 0;
+    virtual void OnDecryptorError() = 0;
+    virtual void OnDecryptorNoKey() = 0;
+
+   protected:
+    virtual ~Client() = default;
+  };
+
+  static std::unique_ptr<FuchsiaSecureStreamDecryptor> Create(
+      fuchsia::media::drm::ContentDecryptionModule* cdm,
+      Client* client);
+
+  FuchsiaSecureStreamDecryptor(fuchsia::media::StreamProcessorPtr processor,
+                               Client* client);
+  ~FuchsiaSecureStreamDecryptor() override;
+
+  void SetOutputBufferCollectionToken(
+      fuchsia::sysmem::BufferCollectionTokenPtr token,
+      size_t num_buffers_for_decryptor,
+      size_t num_buffers_for_codec);
+
+  void Decrypt(scoped_refptr<DecoderBuffer> encrypted);
+
+ private:
+  // StreamProcessorHelper::Client overrides.
+  void AllocateOutputBuffers(const fuchsia::media::StreamBufferConstraints&
+                                 stream_constraints) override;
+  void OnProcessEos() override;
+  void OnOutputFormat(fuchsia::media::StreamOutputFormat format) override;
+  void OnOutputPacket(StreamProcessorHelper::IoPacket packet) override;
+  void OnNoKey() override;
+  void OnError() override;
+
+  Client* const client_;
+
+  bool waiting_output_buffers_ = false;
+  base::OnceClosure complete_buffer_allocation_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(FuchsiaSecureStreamDecryptor);
+};
+
 }  // namespace media
 
 #endif  // MEDIA_FUCHSIA_CDM_FUCHSIA_STREAM_DECRYPTOR_H_
diff --git a/media/fuchsia/common/stream_processor_helper.cc b/media/fuchsia/common/stream_processor_helper.cc
index c403017..ba9f7bcc 100644
--- a/media/fuchsia/common/stream_processor_helper.cc
+++ b/media/fuchsia/common/stream_processor_helper.cc
@@ -317,10 +317,11 @@
 }
 
 void StreamProcessorHelper::CompleteOutputBuffersAllocation(
-    size_t max_used_output_buffers,
+    size_t num_buffers_for_client,
+    size_t num_buffers_for_server,
     fuchsia::sysmem::BufferCollectionTokenPtr collection_token) {
   DCHECK(!output_buffer_constraints_.IsEmpty());
-  DCHECK_LE(max_used_output_buffers,
+  DCHECK_LE(num_buffers_for_client,
             output_buffer_constraints_.packet_count_for_client_max());
 
   // Pass new output buffer settings to the stream processor.
@@ -328,9 +329,8 @@
   settings.set_buffer_lifetime_ordinal(output_buffer_lifetime_ordinal_);
   settings.set_buffer_constraints_version_ordinal(
       output_buffer_constraints_.buffer_constraints_version_ordinal());
-  settings.set_packet_count_for_client(max_used_output_buffers);
-  settings.set_packet_count_for_server(
-      output_buffer_constraints_.packet_count_for_server_recommended());
+  settings.set_packet_count_for_client(num_buffers_for_client);
+  settings.set_packet_count_for_server(num_buffers_for_server);
   settings.set_sysmem_token(std::move(collection_token));
   processor_->SetOutputBufferPartialSettings(std::move(settings));
   processor_->CompleteOutputBufferPartialSettings(
diff --git a/media/fuchsia/common/stream_processor_helper.h b/media/fuchsia/common/stream_processor_helper.h
index babbaac6..87411f2 100644
--- a/media/fuchsia/common/stream_processor_helper.h
+++ b/media/fuchsia/common/stream_processor_helper.h
@@ -121,7 +121,8 @@
   void CompleteInputBuffersAllocation(
       fuchsia::sysmem::BufferCollectionTokenPtr token);
   void CompleteOutputBuffersAllocation(
-      size_t max_used_output_buffers,
+      size_t num_buffers_for_client,
+      size_t num_buffers_for_server,
       fuchsia::sysmem::BufferCollectionTokenPtr token);
 
   void Reset();
diff --git a/media/midi/BUILD.gn b/media/midi/BUILD.gn
index 69ee4260..60005a4 100644
--- a/media/midi/BUILD.gn
+++ b/media/midi/BUILD.gn
@@ -49,7 +49,9 @@
   android_library("midi_java") {
     deps = [
       "//base:base_java",
+      "//base:jni_java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 
     java_files = [
       "java/src/org/chromium/midi/MidiDeviceAndroid.java",
diff --git a/media/midi/java/src/org/chromium/midi/MidiInputPortAndroid.java b/media/midi/java/src/org/chromium/midi/MidiInputPortAndroid.java
index f866c51e..2b0e001 100644
--- a/media/midi/java/src/org/chromium/midi/MidiInputPortAndroid.java
+++ b/media/midi/java/src/org/chromium/midi/MidiInputPortAndroid.java
@@ -12,6 +12,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.io.IOException;
 
@@ -73,7 +74,8 @@
                     if (mPort == null) {
                         return;
                     }
-                    nativeOnData(mNativeReceiverPointer, bs, offset, count, timestamp);
+                    MidiInputPortAndroidJni.get().onData(
+                            mNativeReceiverPointer, bs, offset, count, timestamp);
                 }
             }
         });
@@ -97,6 +99,9 @@
         mPort = null;
     }
 
-    private static native void nativeOnData(
-            long nativeMidiInputPortAndroid, byte[] bs, int offset, int count, long timestamp);
+    @NativeMethods
+    interface Natives {
+        void onData(
+                long nativeMidiInputPortAndroid, byte[] bs, int offset, int count, long timestamp);
+    }
 }
diff --git a/media/midi/java/src/org/chromium/midi/MidiManagerAndroid.java b/media/midi/java/src/org/chromium/midi/MidiManagerAndroid.java
index e0a4cbbd..953571c1 100644
--- a/media/midi/java/src/org/chromium/midi/MidiManagerAndroid.java
+++ b/media/midi/java/src/org/chromium/midi/MidiManagerAndroid.java
@@ -17,6 +17,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -106,7 +107,7 @@
                         if (mStopped) {
                             return;
                         }
-                        nativeOnInitializationFailed(mNativeManagerPointer);
+                        MidiManagerAndroidJni.get().onInitializationFailed(mNativeManagerPointer);
                     }
                 }
             });
@@ -137,7 +138,7 @@
                         return;
                     }
                     if (mPendingDevices.isEmpty() && !mIsInitialized) {
-                        nativeOnInitialized(
+                        MidiManagerAndroidJni.get().onInitialized(
                                 mNativeManagerPointer, mDevices.toArray(new MidiDeviceAndroid[0]));
                         mIsInitialized = true;
                     }
@@ -185,7 +186,7 @@
         for (MidiDeviceAndroid device : mDevices) {
             if (device.isOpen() && device.getInfo().getId() == info.getId()) {
                 device.close();
-                nativeOnDetached(mNativeManagerPointer, device);
+                MidiManagerAndroidJni.get().onDetached(mNativeManagerPointer, device);
             }
         }
     }
@@ -199,18 +200,21 @@
             MidiDeviceAndroid xdevice = new MidiDeviceAndroid(device);
             mDevices.add(xdevice);
             if (mIsInitialized) {
-                nativeOnAttached(mNativeManagerPointer, xdevice);
+                MidiManagerAndroidJni.get().onAttached(mNativeManagerPointer, xdevice);
             }
         }
         if (!mIsInitialized && mPendingDevices.isEmpty()) {
-            nativeOnInitialized(mNativeManagerPointer, mDevices.toArray(new MidiDeviceAndroid[0]));
+            MidiManagerAndroidJni.get().onInitialized(
+                    mNativeManagerPointer, mDevices.toArray(new MidiDeviceAndroid[0]));
             mIsInitialized = true;
         }
     }
 
-    static native void nativeOnInitialized(
-            long nativeMidiManagerAndroid, MidiDeviceAndroid[] devices);
-    static native void nativeOnInitializationFailed(long nativeMidiManagerAndroid);
-    static native void nativeOnAttached(long nativeMidiManagerAndroid, MidiDeviceAndroid device);
-    static native void nativeOnDetached(long nativeMidiManagerAndroid, MidiDeviceAndroid device);
+    @NativeMethods
+    interface Natives {
+        void onInitialized(long nativeMidiManagerAndroid, MidiDeviceAndroid[] devices);
+        void onInitializationFailed(long nativeMidiManagerAndroid);
+        void onAttached(long nativeMidiManagerAndroid, MidiDeviceAndroid device);
+        void onDetached(long nativeMidiManagerAndroid, MidiDeviceAndroid device);
+    }
 }
diff --git a/media/midi/java/src/org/chromium/midi/UsbMidiDeviceAndroid.java b/media/midi/java/src/org/chromium/midi/UsbMidiDeviceAndroid.java
index 323b0da..6f19d11 100644
--- a/media/midi/java/src/org/chromium/midi/UsbMidiDeviceAndroid.java
+++ b/media/midi/java/src/org/chromium/midi/UsbMidiDeviceAndroid.java
@@ -16,6 +16,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
@@ -187,7 +188,7 @@
                 if (mIsClosed) {
                     return;
                 }
-                nativeOnData(mNativePointer, endpointNumber, bs);
+                UsbMidiDeviceAndroidJni.get().onData(mNativePointer, endpointNumber, bs);
             }
         });
     }
@@ -318,6 +319,8 @@
         return position;
     }
 
-    private static native void nativeOnData(
-            long nativeUsbMidiDeviceAndroid, int endpointNumber, byte[] data);
+    @NativeMethods
+    interface Natives {
+        void onData(long nativeUsbMidiDeviceAndroid, int endpointNumber, byte[] data);
+    }
 }
diff --git a/media/midi/java/src/org/chromium/midi/UsbMidiDeviceFactoryAndroid.java b/media/midi/java/src/org/chromium/midi/UsbMidiDeviceFactoryAndroid.java
index 4f64b01c..9511ab1 100644
--- a/media/midi/java/src/org/chromium/midi/UsbMidiDeviceFactoryAndroid.java
+++ b/media/midi/java/src/org/chromium/midi/UsbMidiDeviceFactoryAndroid.java
@@ -18,6 +18,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -107,8 +108,8 @@
      * Enumerates USB-MIDI devices.
      * If there are devices having USB-MIDI interfaces, this function requests permission for
      * accessing the device to the user.
-     * When the permission request is accepted or rejected,  nativeOnUsbMidiDeviceRequestDone
-     * will be called.
+     * When the permission request is accepted or rejected,
+     * UsbMidiDeviceFactoryAndroidJni.get().onUsbMidiDeviceRequestDone will be called.
      *
      * If there are no USB-MIDI interfaces, this function returns false.
      * @return true if some permission requests are in progress.
@@ -184,7 +185,7 @@
                     return;
                 }
                 if (mNativePointer != 0) {
-                    nativeOnUsbMidiDeviceDetached(mNativePointer, i);
+                    UsbMidiDeviceFactoryAndroidJni.get().onUsbMidiDeviceDetached(mNativePointer, i);
                 }
                 return;
             }
@@ -236,10 +237,12 @@
         }
 
         if (mIsEnumeratingDevices) {
-            nativeOnUsbMidiDeviceRequestDone(mNativePointer, mDevices.toArray());
+            UsbMidiDeviceFactoryAndroidJni.get().onUsbMidiDeviceRequestDone(
+                    mNativePointer, mDevices.toArray());
             mIsEnumeratingDevices = false;
         } else if (midiDevice != null) {
-            nativeOnUsbMidiDeviceAttached(mNativePointer, midiDevice);
+            UsbMidiDeviceFactoryAndroidJni.get().onUsbMidiDeviceAttached(
+                    mNativePointer, midiDevice);
         }
     }
 
@@ -252,10 +255,10 @@
         ContextUtils.getApplicationContext().unregisterReceiver(mReceiver);
     }
 
-    private static native void nativeOnUsbMidiDeviceRequestDone(
-            long nativeUsbMidiDeviceFactoryAndroid, Object[] devices);
-    private static native void nativeOnUsbMidiDeviceAttached(
-            long nativeUsbMidiDeviceFactoryAndroid, Object device);
-    private static native void nativeOnUsbMidiDeviceDetached(
-            long nativeUsbMidiDeviceFactoryAndroid, int index);
+    @NativeMethods
+    interface Natives {
+        void onUsbMidiDeviceRequestDone(long nativeUsbMidiDeviceFactoryAndroid, Object[] devices);
+        void onUsbMidiDeviceAttached(long nativeUsbMidiDeviceFactoryAndroid, Object device);
+        void onUsbMidiDeviceDetached(long nativeUsbMidiDeviceFactoryAndroid, int index);
+    }
 }
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index 6636834..dfe949f 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -634,25 +634,13 @@
                       -SkFloatToScalar(image.height() * 0.5f));
   }
 
-  // This is a workaround for crbug.com/524717. A texture backed image is not
-  // safe to access on another thread or GL context. So if we're drawing into a
-  // recording canvas we read the texture back into CPU memory and record that
-  // sw image into the SkPicture. The long term solution is for Skia to provide
-  // a SkPicture filter that makes a picture safe for multiple CPU raster
-  // threads. (skbug.com/4321).
-  if (canvas->imageInfo().colorType() == kUnknown_SkColorType &&
-      image.IsTextureBacked()) {
-    sk_sp<SkImage> non_texture_image =
-        image.GetSkImage()->makeNonTextureImage();
-    image = cc::PaintImageBuilder::WithProperties(image)
-                .set_image(std::move(non_texture_image), image.content_id())
-                .TakePaintImage();
-  }
-
   SkImageInfo info;
   size_t row_bytes;
   SkIPoint origin;
   void* pixels = nullptr;
+  // This if is a special handling of video for SkiaPaintcanvas backend, where
+  // the video does not need any transform and it is enough to draw the frame
+  // directly into the skia canvas
   if (!need_transform && video_frame->IsMappable() &&
       flags.getAlpha() == SK_AlphaOPAQUE &&
       flags.getBlendMode() == SkBlendMode::kSrc &&
@@ -1469,7 +1457,8 @@
   DCHECK(!source_mailbox.IsZero());
   DCHECK(source_texture);
   auto* gl = context_provider->ContextGL();
-  gl->DeleteTextures(1, &source_texture);
+  if (!texture_ownership_in_skia)
+    gl->DeleteTextures(1, &source_texture);
   if (!wraps_video_frame_texture) {
     gpu::SyncToken sync_token;
     gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
@@ -1478,6 +1467,26 @@
   }
 }
 
+bool PaintCanvasVideoRenderer::Cache::Recycle() {
+  if (!texture_ownership_in_skia)
+    return true;
+
+  auto sk_image = paint_image.GetSkImage();
+  paint_image = cc::PaintImage();
+  if (!sk_image->unique())
+    return false;
+
+  // Flush any pending GPU work using this texture.
+  sk_image->flush(context_provider->GrContext());
+
+  // We need a new texture ID because skia will destroy the previous one with
+  // the SkImage.
+  texture_ownership_in_skia = false;
+  source_texture = SynchronizeAndImportMailbox(
+      context_provider->ContextGL(), gpu::SyncToken(), source_mailbox);
+  return true;
+}
+
 bool PaintCanvasVideoRenderer::UpdateLastImage(
     scoped_refptr<VideoFrame> video_frame,
     viz::ContextProvider* context_provider,
@@ -1515,7 +1524,8 @@
                           video_frame->ColorSpace(), context_provider);
       } else {
         if (cache_ && cache_->context_provider == context_provider &&
-            cache_->coded_size == video_frame->coded_size()) {
+            cache_->coded_size == video_frame->coded_size() &&
+            cache_->Recycle()) {
           // We can reuse the shared image from the previous cache.
           cache_->frame_id = video_frame->unique_id();
         } else {
@@ -1527,6 +1537,8 @@
           cache_->source_texture = SynchronizeAndImportMailbox(
               gl, sii->GenUnverifiedSyncToken(), cache_->source_mailbox);
         }
+
+        DCHECK(!cache_->texture_ownership_in_skia);
         ScopedSharedImageAccess dest_access(
             gl, cache_->source_texture, cache_->source_mailbox,
             GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
@@ -1561,9 +1573,32 @@
       cache_->context_provider = context_provider;
       cache_->coded_size = video_frame->coded_size();
       cache_->visible_rect = video_frame->visible_rect();
-      paint_image_builder.set_image(
-          source_image->makeSubset(gfx::RectToSkIRect(cache_->visible_rect)),
-          cc::PaintImage::GetNextContentId());
+      sk_sp<SkImage> source_subset =
+          source_image->makeSubset(gfx::RectToSkIRect(cache_->visible_rect));
+      if (source_subset) {
+        // We use the flushPendingGrContextIO = true so we can flush any pending
+        // GPU work on the GrContext to ensure that skia exectues the work for
+        // generating the subset and it can be safely destroyed.
+        GrBackendTexture image_backend =
+            source_image->getBackendTexture(/*flushPendingGrContextIO*/ true);
+        GrBackendTexture subset_backend =
+            source_subset->getBackendTexture(/*flushPendingGrContextIO*/ true);
+#if DCHECK_IS_ON()
+        GrGLTextureInfo backend_info;
+        if (image_backend.getGLTextureInfo(&backend_info))
+          DCHECK_EQ(backend_info.fID, cache_->source_texture);
+#endif
+        if (subset_backend.isValid() &&
+            subset_backend.isSameTexture(image_backend)) {
+          cache_->texture_ownership_in_skia = true;
+          source_subset = SkImage::MakeFromAdoptedTexture(
+              cache_->context_provider->GrContext(), image_backend,
+              kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType,
+              kPremul_SkAlphaType, source_image->imageInfo().refColorSpace());
+        }
+      }
+      paint_image_builder.set_image(source_subset,
+                                    cc::PaintImage::GetNextContentId());
     } else {
       cache_.emplace(video_frame->unique_id());
       paint_image_builder.set_paint_image_generator(
diff --git a/media/renderers/paint_canvas_video_renderer.h b/media/renderers/paint_canvas_video_renderer.h
index 2628f28..02c277cf 100644
--- a/media/renderers/paint_canvas_video_renderer.h
+++ b/media/renderers/paint_canvas_video_renderer.h
@@ -226,6 +226,14 @@
     // Whether |source_mailbox| directly points to a texture of the VideoFrame
     // (if true), or to an allocated shared image (if false).
     bool wraps_video_frame_texture = false;
+
+    // Whether the texture pointed by |paint_image| is owned by skia or not.
+    bool texture_ownership_in_skia = false;
+
+    // Used to allow recycling of the previous shared image. This requires that
+    // no external users have access to this resource via SkImage. Returns true
+    // if the existing resource can be recycled.
+    bool Recycle();
   };
 
   // Update the cache holding the most-recently-painted frame. Returns false
diff --git a/media/renderers/paint_canvas_video_renderer_unittest.cc b/media/renderers/paint_canvas_video_renderer_unittest.cc
index e18311b..3876b63 100644
--- a/media/renderers/paint_canvas_video_renderer_unittest.cc
+++ b/media/renderers/paint_canvas_video_renderer_unittest.cc
@@ -1174,6 +1174,14 @@
                                       gfx::Rect(2, 2, 12, 4),
                                       std::move(closure));
   }
+  // Creates a cropped I420 VideoFrame. |closure| is run once the shared images
+  // backing the VideoFrame have been destroyed.
+  scoped_refptr<VideoFrame> CreateTestI420FrameNotSubset(
+      base::OnceClosure closure) {
+    return CreateSharedImageI420Frame(media_context_, gfx::Size(16, 8),
+                                      gfx::Rect(0, 0, 16, 8),
+                                      std::move(closure));
+  }
 
   // Checks that the contents of a texture/canvas match the expectations for the
   // cropped I420 frame above. |get_color| is a callback that returns the actual
@@ -1192,6 +1200,23 @@
     EXPECT_EQ(SK_ColorWHITE, get_color.Run(11, 3));
   }
 
+  // Checks that the contents of a texture/canvas match the expectations for the
+  // cropped I420 frame above. |get_color| is a callback that returns the actual
+  // color at a given pixel location.
+  static void CheckI420FramePixelsNotSubset(GetColorCallback get_color) {
+    // Avoid checking around the "seams" where subsamples may be interpolated.
+    EXPECT_EQ(SK_ColorBLACK, get_color.Run(2, 2));
+    EXPECT_EQ(SK_ColorRED, get_color.Run(5, 2));
+    EXPECT_EQ(SK_ColorRED, get_color.Run(6, 2));
+    EXPECT_EQ(SK_ColorGREEN, get_color.Run(9, 2));
+    EXPECT_EQ(SK_ColorGREEN, get_color.Run(10, 2));
+    EXPECT_EQ(SK_ColorYELLOW, get_color.Run(13, 2));
+    EXPECT_EQ(SK_ColorBLUE, get_color.Run(2, 5));
+    EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(5, 5));
+    EXPECT_EQ(SK_ColorCYAN, get_color.Run(9, 5));
+    EXPECT_EQ(SK_ColorWHITE, get_color.Run(13, 5));
+  }
+
   // Creates a cropped NV12 VideoFrame, or nullptr if the needed extension is
   // not available. |closure| is run once the shared images backing the
   // VideoFrame have been destroyed.
@@ -1344,6 +1369,19 @@
   run_loop.Run();
 }
 
+// Checks that we correctly paint a I420 shared image VideoFrame, including
+// correct cropping.
+TEST_F(PaintCanvasVideoRendererWithGLTest, PaintI420NotSubset) {
+  base::RunLoop run_loop;
+  scoped_refptr<VideoFrame> frame =
+      CreateTestI420FrameNotSubset(run_loop.QuitClosure());
+
+  PaintVideoFrameAndCheckPixels(frame, &CheckI420FramePixelsNotSubset);
+
+  frame.reset();
+  run_loop.Run();
+}
+
 // Checks that we correctly copy a NV12 shared image VideoFrame when using
 // CopyVideoFrameYUVDataToGLTexture, including correct cropping.
 TEST_F(PaintCanvasVideoRendererWithGLTest,
diff --git a/mojo/core/node_controller.cc b/mojo/core/node_controller.cc
index 9a37558..b36b5d40 100644
--- a/mojo/core/node_controller.cc
+++ b/mojo/core/node_controller.cc
@@ -14,7 +14,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop_current.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/process/process_handle.h"
 #include "base/rand_util.h"
 #include "base/time/time.h"
diff --git a/mojo/core/user_message_impl.cc b/mojo/core/user_message_impl.cc
index fb59eba76..2f1665e 100644
--- a/mojo/core/user_message_impl.cc
+++ b/mojo/core/user_message_impl.cc
@@ -9,7 +9,6 @@
 
 #include "base/atomicops.h"
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros_local.h"
 #include "base/no_destructor.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/numerics/safe_math.h"
diff --git a/mojo/public/cpp/base/big_buffer.cc b/mojo/public/cpp/base/big_buffer.cc
index 85ba9c96..9d44b13 100644
--- a/mojo/public/cpp/base/big_buffer.cc
+++ b/mojo/public/cpp/base/big_buffer.cc
@@ -81,7 +81,15 @@
 
 BigBuffer::BigBuffer() : storage_type_(StorageType::kBytes), bytes_size_(0) {}
 
-BigBuffer::BigBuffer(BigBuffer&& other) = default;
+BigBuffer::BigBuffer(BigBuffer&& other)
+    : storage_type_(other.storage_type_),
+      bytes_(std::move(other.bytes_)),
+      bytes_size_(other.bytes_size_),
+      shared_memory_(std::move(other.shared_memory_)) {
+  // Make sure |other| looks empty.
+  other.storage_type_ = StorageType::kInvalidBuffer;
+  other.bytes_size_ = 0;
+}
 
 BigBuffer::BigBuffer(base::span<const uint8_t> data) {
   *this = BigBufferView::ToBigBuffer(BigBufferView(data));
@@ -106,7 +114,16 @@
 
 BigBuffer::~BigBuffer() = default;
 
-BigBuffer& BigBuffer::operator=(BigBuffer&& other) = default;
+BigBuffer& BigBuffer::operator=(BigBuffer&& other) {
+  storage_type_ = other.storage_type_;
+  bytes_ = std::move(other.bytes_);
+  bytes_size_ = other.bytes_size_;
+  shared_memory_ = std::move(other.shared_memory_);
+  // Make sure |other| looks empty.
+  other.storage_type_ = StorageType::kInvalidBuffer;
+  other.bytes_size_ = 0;
+  return *this;
+}
 
 uint8_t* BigBuffer::data() {
   return const_cast<uint8_t*>(const_cast<const BigBuffer*>(this)->data());
diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h
index c2cf962..f97cfcd1 100644
--- a/mojo/public/cpp/bindings/connector.h
+++ b/mojo/public/cpp/bindings/connector.h
@@ -18,6 +18,7 @@
 #include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
+#include "mojo/public/c/system/quota.h"
 #include "mojo/public/cpp/bindings/connection_group.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/sequence_local_sync_event_watcher.h"
@@ -307,6 +308,11 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
+  // If this instance was selected for unread message measurement, contains
+  // the max send quota usage seen so far. If this instance was not selected
+  // contains MOJO_QUOTA_LIMIT_NONE as a sentinel value.
+  uint64_t max_unread_message_quota_used_ = MOJO_QUOTA_LIMIT_NONE;
+
   base::Lock connected_lock_;
   bool connected_ = true;
 
diff --git a/mojo/public/cpp/bindings/features.cc b/mojo/public/cpp/bindings/features.cc
index 9026ba7..29f1226 100644
--- a/mojo/public/cpp/bindings/features.cc
+++ b/mojo/public/cpp/bindings/features.cc
@@ -22,5 +22,12 @@
 const base::Feature kTaskPerMessage{"MojoTaskPerMessage",
                                     base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables measurement of MessageChannel unread message counts. When enabled, a
+// small random selection of Connectors enable the unread message count quota
+// on their associated message pipe, and record the highest unread message count
+// seen during the Connector's lifetime.
+const base::Feature kMojoRecordUnreadMessageCount{
+    "MojoRecordUnreadMessageCount", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/features.h b/mojo/public/cpp/bindings/features.h
index 5d58f0d7..8684f5a 100644
--- a/mojo/public/cpp/bindings/features.h
+++ b/mojo/public/cpp/bindings/features.h
@@ -14,6 +14,9 @@
 COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE)
 extern const base::Feature kTaskPerMessage;
 
+COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE)
+extern const base::Feature kMojoRecordUnreadMessageCount;
+
 }  // namespace features
 }  // namespace mojo
 
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index d0739dd..d8bea67 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -12,7 +12,10 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop_current.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
+#include "base/rand_util.h"
 #include "base/run_loop.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/sequence_local_storage_slot.h"
@@ -50,6 +53,30 @@
   return enable;
 }
 
+const base::FeatureParam<int> kMojoRecordUnreadMessageCountSampleRate = {
+    &features::kMojoRecordUnreadMessageCount, "SampleRate",
+    100  // Sample 1% of Connectors by default.
+};
+
+const base::FeatureParam<int> kMojoRecordUnreadMessageCountQuotaValue = {
+    &features::kMojoRecordUnreadMessageCount, "QuotaValue",
+    100  // Use a 100 message quote by default.
+};
+
+int UnreadMessageCountQuota() {
+  static const bool enabled =
+      base::FeatureList::IsEnabled(features::kMojoRecordUnreadMessageCount);
+  if (!enabled)
+    return 0;
+
+  static const int sample_rate = kMojoRecordUnreadMessageCountSampleRate.Get();
+  if (base::RandInt(0, sample_rate - 1) != 0)
+    return 0;
+
+  static const int quota = kMojoRecordUnreadMessageCountQuotaValue.Get();
+  return quota;
+}
+
 }  // namespace
 
 // Used to efficiently maintain a doubly-linked list of all Connectors
@@ -158,9 +185,27 @@
   // Even though we don't have an incoming receiver, we still want to monitor
   // the message pipe to know if is closed or encounters an error.
   WaitToReadMore();
+
+  int unread_message_count_quota = UnreadMessageCountQuota();
+  if (unread_message_count_quota != 0) {
+    // This connector has been sampled to record the max unread message count.
+    // Note that setting the quota to N results in over-counting usage by up to
+    // N/2, in addition to overcounting due to message transit delays. As result
+    // it's best to treat the resulting metric as N-granular.
+    MojoResult rv = MojoSetQuota(message_pipe_.get().value(),
+                                 MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT,
+                                 unread_message_count_quota, nullptr);
+    if (rv == MOJO_RESULT_OK)
+      max_unread_message_quota_used_ = 0U;
+  }
 }
 
 Connector::~Connector() {
+  if (max_unread_message_quota_used_ != MOJO_QUOTA_LIMIT_NONE) {
+    UMA_HISTOGRAM_COUNTS_1M("Mojo.Connector.MaxUnreadMessageQuotaUsed",
+                            max_unread_message_quota_used_);
+  }
+
   {
     // Allow for quick destruction on any sequence if the pipe is already
     // closed.
@@ -317,6 +362,15 @@
     DCHECK(dump_result);
   }
 #endif
+  if (max_unread_message_quota_used_ != MOJO_QUOTA_LIMIT_NONE) {
+    uint64_t limit = 0;
+    uint64_t usage = 0;
+    MojoResult rv = MojoQueryQuota(message_pipe_.get().value(),
+                                   MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT,
+                                   nullptr, &limit, &usage);
+    if (rv == MOJO_RESULT_OK && usage > max_unread_message_quota_used_)
+      max_unread_message_quota_used_ = usage;
+  }
 
   MojoResult rv =
       WriteMessageNew(message_pipe_.get(), message->TakeMojoMessage(),
diff --git a/printing/BUILD.gn b/printing/BUILD.gn
index cef76da6f..67e0e0c 100644
--- a/printing/BUILD.gn
+++ b/printing/BUILD.gn
@@ -382,6 +382,7 @@
   android_library("printing_java") {
     deps = [
       "//base:base_java",
+      "//base:jni_java",
       "//ui/android:ui_java",
     ]
     java_files = [
@@ -393,5 +394,6 @@
       "android/java/src/org/chromium/printing/PrintingController.java",
       "android/java/src/org/chromium/printing/PrintingControllerImpl.java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   }
 }
diff --git a/printing/android/java/src/org/chromium/printing/PrintingContext.java b/printing/android/java/src/org/chromium/printing/PrintingContext.java
index 6924e36b..7d52e71 100644
--- a/printing/android/java/src/org/chromium/printing/PrintingContext.java
+++ b/printing/android/java/src/org/chromium/printing/PrintingContext.java
@@ -10,6 +10,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.ui.base.WindowAndroid;
 
 /**
@@ -106,8 +107,8 @@
         ThreadUtils.assertOnUiThread();
         // If the printing dialog has already finished, tell Chromium that operation is cancelled.
         if (mController.hasPrintingFinished()) {
-            // NOTE: We don't call nativeAskUserForSettingsReply (hence Chromium callback in
-            // AskUserForSettings callback) twice.
+            // NOTE: We don't call PrintingContextJni.get().askUserForSettingsReply (hence Chromium
+            // callback in AskUserForSettings callback) twice.
             askUserForSettingsReply(false);
         } else {
             mController.setPrintingContext(this);
@@ -117,16 +118,19 @@
 
     private void askUserForSettingsReply(boolean success) {
         assert mNativeObject != 0;
-        nativeAskUserForSettingsReply(mNativeObject, success);
+        PrintingContextJni.get().askUserForSettingsReply(
+                mNativeObject, PrintingContext.this, success);
     }
 
     private void showSystemDialogDone() {
         assert mNativeObject != 0;
-        nativeShowSystemDialogDone(mNativeObject);
+        PrintingContextJni.get().showSystemDialogDone(mNativeObject, PrintingContext.this);
     }
 
-    private native void nativeAskUserForSettingsReply(
-            long nativePrintingContextAndroid, boolean success);
-
-    private native void nativeShowSystemDialogDone(long nativePrintingContextAndroid);
+    @NativeMethods
+    interface Natives {
+        void askUserForSettingsReply(
+                long nativePrintingContextAndroid, PrintingContext caller, boolean success);
+        void showSystemDialogDone(long nativePrintingContextAndroid, PrintingContext caller);
+    }
 }
diff --git a/remoting/host/installer/linux/debian/postinst b/remoting/host/installer/linux/debian/postinst
index b0f9f146..682fc88b 100755
--- a/remoting/host/installer/linux/debian/postinst
+++ b/remoting/host/installer/linux/debian/postinst
@@ -18,7 +18,9 @@
 NOTIFIER_DIR="/var/lib/update-notifier/user.d"
 VAR_DIR="/var/lib/chrome-remote-desktop"
 HASHES_FILE="$VAR_DIR/hashes"
-USER_SESSION_PATH="/opt/google/chrome-remote-desktop/user-session"
+INSTALL_DIR="/opt/google/chrome-remote-desktop"
+USER_SESSION_PATH="$INSTALL_DIR/user-session"
+HOST_PATH="$INSTALL_DIR/chrome-remote-desktop-host"
 CRD_GROUP="chrome-remote-desktop"
 
 case "$1" in
@@ -35,11 +37,12 @@
       chown root:"$CRD_GROUP" "$USER_SESSION_PATH"
       chmod u=srwx,g=rx,o=r "$USER_SESSION_PATH"
     fi
-    # Kill host processes. The wrapper script will restart them.
-    # TODO(lambroslambrou): Remove the '-9' when the underlying problem with
-    # hosts not responding to SIGTERM has been fixed - http://crbug.com/420090
+    # Kill host processes. SIGKILL is used because the wrapper script will
+    # restart the host immediately (so any cleanup that might otherwise happen
+    # is not useful), and this ensures that hosts restart even if they are
+    # deadlocked.
     echo "Shutting down Chrome Remote Desktop hosts (they will restart automatically)..."
-    killall -9 -q chrome-remote-desktop-host || true
+    killall -9 -q "$HOST_PATH" || true
     # If any files have changed that require the user to restart their virtual
     # desktops (eg, the wrapper script itself) then notify them but don't do
     # anything that would result in them losing state.
diff --git a/sandbox/win/src/acl.cc b/sandbox/win/src/acl.cc
index 2f78c164..bd0b181 100644
--- a/sandbox/win/src/acl.cc
+++ b/sandbox/win/src/acl.cc
@@ -151,4 +151,21 @@
   return true;
 }
 
+bool ReplacePackageSidInDacl(HANDLE object,
+                             SE_OBJECT_TYPE object_type,
+                             const Sid& package_sid,
+                             ACCESS_MASK access) {
+  if (!AddKnownSidToObject(object, object_type, package_sid, REVOKE_ACCESS,
+                           0)) {
+    return false;
+  }
+
+  Sid any_package_sid(::WinBuiltinAnyPackageSid);
+  if (!AddKnownSidToObject(object, object_type, any_package_sid, GRANT_ACCESS,
+                           access)) {
+    return false;
+  }
+  return true;
+}
+
 }  // namespace sandbox
diff --git a/sandbox/win/src/acl.h b/sandbox/win/src/acl.h
index 86bb92f9..194edb0 100644
--- a/sandbox/win/src/acl.h
+++ b/sandbox/win/src/acl.h
@@ -51,6 +51,14 @@
                          ACCESS_MODE access_mode,
                          ACCESS_MASK access);
 
+// Replace package SID in DACL to the "any package" SID. It allows Low-IL
+// tokens to open the object which is important for warm up when using renderer
+// AppContainer.
+bool ReplacePackageSidInDacl(HANDLE object,
+                             SE_OBJECT_TYPE object_type,
+                             const Sid& package_sid,
+                             ACCESS_MASK access);
+
 }  // namespace sandbox
 
 #endif  // SANDBOX_SRC_ACL_H_
diff --git a/sandbox/win/src/restricted_token_unittest.cc b/sandbox/win/src/restricted_token_unittest.cc
index d8798e9..88cae12 100644
--- a/sandbox/win/src/restricted_token_unittest.cc
+++ b/sandbox/win/src/restricted_token_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/win/atl.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/windows_version.h"
+#include "sandbox/win/src/acl.h"
 #include "sandbox/win/src/security_capabilities.h"
 #include "sandbox/win/src/sid.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -84,6 +85,46 @@
                                      information);
 }
 
+void CheckDaclForPackageSid(const base::win::ScopedHandle& token,
+                            PSECURITY_CAPABILITIES security_capabilities,
+                            bool package_sid_required) {
+  DWORD length_needed = 0;
+  ::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION, nullptr, 0,
+                            &length_needed);
+  ASSERT_EQ(::GetLastError(), DWORD{ERROR_INSUFFICIENT_BUFFER});
+
+  std::vector<char> security_desc_buffer(length_needed);
+  SECURITY_DESCRIPTOR* security_desc =
+      reinterpret_cast<SECURITY_DESCRIPTOR*>(security_desc_buffer.data());
+
+  ASSERT_TRUE(::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION,
+                                        security_desc, length_needed,
+                                        &length_needed));
+
+  ATL::CSecurityDesc token_sd(*security_desc);
+  ATL::CDacl dacl;
+  ASSERT_TRUE(token_sd.GetDacl(&dacl));
+
+  ATL::CSid package_sid(
+      static_cast<SID*>(security_capabilities->AppContainerSid));
+  ATL::CSid all_package_sid(
+      static_cast<SID*>(sandbox::Sid(::WinBuiltinAnyPackageSid).GetPSID()));
+
+  unsigned int ace_count = dacl.GetAceCount();
+  for (unsigned int i = 0; i < ace_count; ++i) {
+    ATL::CSid sid;
+    ACCESS_MASK mask = 0;
+    BYTE type = 0;
+    dacl.GetAclEntry(i, &sid, &mask, &type);
+    if (mask != TOKEN_ALL_ACCESS || type != ACCESS_ALLOWED_ACE_TYPE)
+      continue;
+    if (sid == package_sid)
+      EXPECT_TRUE(package_sid_required);
+    else if (sid == all_package_sid)
+      EXPECT_FALSE(package_sid_required);
+  }
+}
+
 void CheckLowBoxToken(const base::win::ScopedHandle& token,
                       TOKEN_TYPE token_type,
                       PSECURITY_CAPABILITIES security_capabilities) {
@@ -126,38 +167,7 @@
                            security_capabilities->Capabilities[index].Sid));
   }
 
-  DWORD length_needed = 0;
-  ::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION, nullptr, 0,
-                            &length_needed);
-  ASSERT_EQ(::GetLastError(), DWORD{ERROR_INSUFFICIENT_BUFFER});
-
-  std::vector<char> security_desc_buffer(length_needed);
-  SECURITY_DESCRIPTOR* security_desc =
-      reinterpret_cast<SECURITY_DESCRIPTOR*>(security_desc_buffer.data());
-
-  ASSERT_TRUE(::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION,
-                                        security_desc, length_needed,
-                                        &length_needed));
-
-  ATL::CSecurityDesc token_sd(*security_desc);
-  ATL::CSid check_sid(
-      static_cast<SID*>(security_capabilities->AppContainerSid));
-  bool package_sid_found = false;
-
-  ATL::CDacl dacl;
-  ASSERT_TRUE(token_sd.GetDacl(&dacl));
-  unsigned int ace_count = dacl.GetAceCount();
-  for (unsigned int i = 0; i < ace_count; ++i) {
-    ATL::CSid sid;
-    ACCESS_MASK mask = 0;
-    BYTE type = 0;
-    dacl.GetAclEntry(i, &sid, &mask, &type);
-    if (sid == check_sid && mask == TOKEN_ALL_ACCESS &&
-        type == ACCESS_ALLOWED_ACE_TYPE) {
-      package_sid_found = true;
-    }
-  }
-  ASSERT_TRUE(package_sid_found);
+  CheckDaclForPackageSid(token, security_capabilities, true);
 }
 
 // Checks if a sid is in the restricting list of the restricted token.
@@ -761,6 +771,11 @@
   ASSERT_TRUE(token.IsValid());
   CheckLowBoxToken(token, ::TokenPrimary, &caps_no_capabilities);
 
+  ASSERT_TRUE(ReplacePackageSidInDacl(token.Get(), SE_KERNEL_OBJECT,
+                                      Sid(caps_no_capabilities.AppContainerSid),
+                                      TOKEN_ALL_ACCESS));
+  CheckDaclForPackageSid(token, &caps_no_capabilities, false);
+
   ASSERT_EQ(DWORD{ERROR_SUCCESS},
             CreateLowBoxToken(nullptr, IMPERSONATION, &caps_no_capabilities,
                               nullptr, 0, &token));
diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc
index dc4375b..d53d074 100644
--- a/sandbox/win/src/sandbox_policy_base.cc
+++ b/sandbox/win/src/sandbox_policy_base.cc
@@ -15,6 +15,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/win/win_util.h"
 #include "base/win/windows_version.h"
+#include "sandbox/win/src/acl.h"
 #include "sandbox/win/src/filesystem_policy.h"
 #include "sandbox/win/src/interception.h"
 #include "sandbox/win/src/job.h"
@@ -473,6 +474,11 @@
                           saved_handles_count, lowbox) != ERROR_SUCCESS) {
       return SBOX_ERROR_GENERIC;
     }
+
+    if (!ReplacePackageSidInDacl(lowbox->Get(), SE_KERNEL_OBJECT, package_sid,
+                                 TOKEN_ALL_ACCESS)) {
+      return SBOX_ERROR_GENERIC;
+    }
   }
 
   // Create the 'better' token. We use this token as the one that the main
diff --git a/services/data_decoder/public/cpp/android/BUILD.gn b/services/data_decoder/public/cpp/android/BUILD.gn
index 3474ecf2..8db1e96c 100644
--- a/services/data_decoder/public/cpp/android/BUILD.gn
+++ b/services/data_decoder/public/cpp/android/BUILD.gn
@@ -15,7 +15,9 @@
   android_library("safe_json_java") {
     deps = [
       "//base:base_java",
+      "//base:jni_java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
     java_files = [] + _jni_sources
   }
 }
diff --git a/services/data_decoder/public/cpp/android/java/src/org/chromium/services/data_decoder/JsonSanitizer.java b/services/data_decoder/public/cpp/android/java/src/org/chromium/services/data_decoder/JsonSanitizer.java
index b3c4bfa..f398f41 100644
--- a/services/data_decoder/public/cpp/android/java/src/org/chromium/services/data_decoder/JsonSanitizer.java
+++ b/services/data_decoder/public/cpp/android/java/src/org/chromium/services/data_decoder/JsonSanitizer.java
@@ -12,6 +12,7 @@
 import org.chromium.base.StreamUtil;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.io.IOException;
 import java.io.StringReader;
@@ -115,10 +116,10 @@
         try {
             result = sanitize(unsafeJson);
         } catch (IOException | IllegalStateException e) {
-            nativeOnError(nativePtr, e.getMessage());
+            JsonSanitizerJni.get().onError(nativePtr, e.getMessage());
             return;
         }
-        nativeOnSuccess(nativePtr, result);
+        JsonSanitizerJni.get().onSuccess(nativePtr, result);
     }
 
     /**
@@ -189,7 +190,9 @@
                 || (codePoint > 0xFDEF && codePoint <= 0x10FFFF && (codePoint & 0xFFFE) != 0xFFFE);
     }
 
-    private static native void nativeOnSuccess(long id, String json);
-
-    private static native void nativeOnError(long id, String error);
+    @NativeMethods
+    interface Natives {
+        void onSuccess(long id, String json);
+        void onError(long id, String error);
+    }
 }
diff --git a/services/device/generic_sensor/BUILD.gn b/services/device/generic_sensor/BUILD.gn
index 3fea2ec2..6e0ae74d 100644
--- a/services/device/generic_sensor/BUILD.gn
+++ b/services/device/generic_sensor/BUILD.gn
@@ -140,7 +140,9 @@
     java_files = device_sensors_jni_sources
     deps = [
       "//base:base_java",
+      "//base:jni_java",
       "//services/device/public/mojom:generic_sensor_java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   }
 }
diff --git a/services/device/generic_sensor/android/java/src/org/chromium/device/sensors/PlatformSensor.java b/services/device/generic_sensor/android/java/src/org/chromium/device/sensors/PlatformSensor.java
index 85d7c7d..d16d43d 100644
--- a/services/device/generic_sensor/android/java/src/org/chromium/device/sensors/PlatformSensor.java
+++ b/services/device/generic_sensor/android/java/src/org/chromium/device/sensors/PlatformSensor.java
@@ -12,6 +12,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.device.mojom.ReportingMode;
 
 import java.util.List;
@@ -218,7 +219,8 @@
      * Notifies native device::PlatformSensorAndroid when there is an error.
      */
     protected void sensorError() {
-        nativeNotifyPlatformSensorError(mNativePlatformSensorAndroid);
+        PlatformSensorJni.get().notifyPlatformSensorError(
+                mNativePlatformSensorAndroid, PlatformSensor.this);
     }
 
     /**
@@ -226,8 +228,8 @@
      */
     protected void updateSensorReading(
             double timestamp, double value1, double value2, double value3, double value4) {
-        nativeUpdatePlatformSensorReading(
-                mNativePlatformSensorAndroid, timestamp, value1, value2, value3, value4);
+        PlatformSensorJni.get().updatePlatformSensorReading(mNativePlatformSensorAndroid,
+                PlatformSensor.this, timestamp, value1, value2, value3, value4);
     }
 
     @Override
@@ -264,7 +266,10 @@
         }
     }
 
-    private native void nativeNotifyPlatformSensorError(long nativePlatformSensorAndroid);
-    private native void nativeUpdatePlatformSensorReading(long nativePlatformSensorAndroid,
-            double timestamp, double value1, double value2, double value3, double value4);
+    @NativeMethods
+    interface Natives {
+        void notifyPlatformSensorError(long nativePlatformSensorAndroid, PlatformSensor caller);
+        void updatePlatformSensorReading(long nativePlatformSensorAndroid, PlatformSensor caller,
+                double timestamp, double value1, double value2, double value3, double value4);
+    }
 }
diff --git a/services/device/geolocation/BUILD.gn b/services/device/geolocation/BUILD.gn
index 03a2fa7e..be8abf4 100644
--- a/services/device/geolocation/BUILD.gn
+++ b/services/device/geolocation/BUILD.gn
@@ -146,9 +146,11 @@
       "$google_play_services_package:google_play_services_basement_java",
       "$google_play_services_package:google_play_services_location_java",
       "//base:base_java",
+      "//base:jni_java",
       "//components/location/android:location_java",
       "//services/device/public/java:geolocation_java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   }
 }
 
diff --git a/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java b/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java
index 442ec64..52c501c 100644
--- a/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java
+++ b/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java
@@ -10,6 +10,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.util.concurrent.FutureTask;
 
@@ -76,20 +77,24 @@
     }
 
     public static void onNewLocationAvailable(Location location) {
-        nativeNewLocationAvailable(location.getLatitude(), location.getLongitude(),
-                location.getTime() / 1000.0, location.hasAltitude(), location.getAltitude(),
-                location.hasAccuracy(), location.getAccuracy(), location.hasBearing(),
-                location.getBearing(), location.hasSpeed(), location.getSpeed());
+        LocationProviderAdapterJni.get().newLocationAvailable(location.getLatitude(),
+                location.getLongitude(), location.getTime() / 1000.0, location.hasAltitude(),
+                location.getAltitude(), location.hasAccuracy(), location.getAccuracy(),
+                location.hasBearing(), location.getBearing(), location.hasSpeed(),
+                location.getSpeed());
     }
 
     public static void newErrorAvailable(String message) {
         Log.e(TAG, "newErrorAvailable %s", message);
-        nativeNewErrorAvailable(message);
+        LocationProviderAdapterJni.get().newErrorAvailable(message);
     }
 
-    // Native functions
-    private static native void nativeNewLocationAvailable(double latitude, double longitude,
-            double timeStamp, boolean hasAltitude, double altitude, boolean hasAccuracy,
-            double accuracy, boolean hasHeading, double heading, boolean hasSpeed, double speed);
-    private static native void nativeNewErrorAvailable(String message);
+    @NativeMethods
+    interface Natives {
+        void newLocationAvailable(double latitude, double longitude, double timeStamp,
+                boolean hasAltitude, double altitude, boolean hasAccuracy, double accuracy,
+                boolean hasHeading, double heading, boolean hasSpeed, double speed);
+
+        void newErrorAvailable(String message);
+    }
 }
diff --git a/services/device/time_zone_monitor/BUILD.gn b/services/device/time_zone_monitor/BUILD.gn
index 1f56c9f..58b31ed 100644
--- a/services/device/time_zone_monitor/BUILD.gn
+++ b/services/device/time_zone_monitor/BUILD.gn
@@ -73,6 +73,8 @@
     java_files = [ "android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java" ]
     deps = [
       "//base:base_java",
+      "//base:jni_java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   }
 }
diff --git a/services/device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java b/services/device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java
index f927cd8..2214843 100644
--- a/services/device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java
+++ b/services/device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java
@@ -13,6 +13,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 /**
  * Android implementation details for device::TimeZoneMonitorAndroid.
@@ -30,7 +31,7 @@
                 return;
             }
 
-            nativeTimeZoneChangedFromJava(mNativePtr);
+            TimeZoneMonitorJni.get().timeZoneChangedFromJava(mNativePtr, TimeZoneMonitor.this);
         }
     };
 
@@ -59,9 +60,12 @@
         mNativePtr = 0;
     }
 
-    /**
-     * Native JNI call to device::TimeZoneMonitorAndroid::TimeZoneChanged.
-     * See device/time_zone_monitor/time_zone_monitor_android.cc.
-     */
-    private native void nativeTimeZoneChangedFromJava(long nativeTimeZoneMonitorAndroid);
+    @NativeMethods
+    interface Natives {
+        /**
+         * Native JNI call to device::TimeZoneMonitorAndroid::TimeZoneChanged. See
+         * device/time_zone_monitor/time_zone_monitor_android.cc.
+         */
+        void timeZoneChangedFromJava(long nativeTimeZoneMonitorAndroid, TimeZoneMonitor caller);
+    }
 }
diff --git a/services/device/usb/BUILD.gn b/services/device/usb/BUILD.gn
index b2ba8073..a28fdc7d 100644
--- a/services/device/usb/BUILD.gn
+++ b/services/device/usb/BUILD.gn
@@ -209,6 +209,8 @@
     java_files = java_sources_needing_jni
     deps = [
       "//base:base_java",
+      "//base:jni_java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   }
 }
diff --git a/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbService.java b/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbService.java
index be304a7f..d4587b7 100644
--- a/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbService.java
+++ b/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbService.java
@@ -17,6 +17,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 
 import java.util.HashMap;
 
@@ -70,7 +71,8 @@
     private void requestDevicePermission(ChromeUsbDevice wrapper) {
         UsbDevice device = wrapper.getDevice();
         if (mUsbManager.hasPermission(device)) {
-            nativeDevicePermissionRequestComplete(mUsbServiceAndroid, device.getDeviceId(), true);
+            ChromeUsbServiceJni.get().devicePermissionRequestComplete(
+                    mUsbServiceAndroid, ChromeUsbService.this, device.getDeviceId(), true);
         } else {
             PendingIntent intent = PendingIntent.getBroadcast(
                     ContextUtils.getApplicationContext(), 0, new Intent(ACTION_USB_PERMISSION), 0);
@@ -83,24 +85,20 @@
         unregisterForUsbDeviceIntentBroadcast();
     }
 
-    private native void nativeDeviceAttached(long nativeUsbServiceAndroid, UsbDevice device);
-
-    private native void nativeDeviceDetached(long nativeUsbServiceAndroid, int deviceId);
-
-    private native void nativeDevicePermissionRequestComplete(
-            long nativeUsbServiceAndroid, int deviceId, boolean granted);
-
     private void registerForUsbDeviceIntentBroadcast() {
         mUsbDeviceReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                 if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
-                    nativeDeviceAttached(mUsbServiceAndroid, device);
+                    ChromeUsbServiceJni.get().deviceAttached(
+                            mUsbServiceAndroid, ChromeUsbService.this, device);
                 } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
-                    nativeDeviceDetached(mUsbServiceAndroid, device.getDeviceId());
+                    ChromeUsbServiceJni.get().deviceDetached(
+                            mUsbServiceAndroid, ChromeUsbService.this, device.getDeviceId());
                 } else if (ACTION_USB_PERMISSION.equals(intent.getAction())) {
-                    nativeDevicePermissionRequestComplete(mUsbServiceAndroid, device.getDeviceId(),
+                    ChromeUsbServiceJni.get().devicePermissionRequestComplete(mUsbServiceAndroid,
+                            ChromeUsbService.this, device.getDeviceId(),
                             intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
                 }
             }
@@ -117,4 +115,13 @@
         ContextUtils.getApplicationContext().unregisterReceiver(mUsbDeviceReceiver);
         mUsbDeviceReceiver = null;
     }
+
+    @NativeMethods
+    interface Natives {
+        void deviceAttached(
+                long nativeUsbServiceAndroid, ChromeUsbService caller, UsbDevice device);
+        void deviceDetached(long nativeUsbServiceAndroid, ChromeUsbService caller, int deviceId);
+        void devicePermissionRequestComplete(long nativeUsbServiceAndroid, ChromeUsbService caller,
+                int deviceId, boolean granted);
+    }
 }
diff --git a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
index 2eee226d..b167ef914 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
@@ -939,8 +939,8 @@
   RenderPassDrawQuad* render_pass_quad =
       render_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   render_pass_quad->SetNew(sqs, rect4, rect4, render_pass_id, resource_id4,
-                           mask_uv_rect, mask_texture_size, false,
-                           filters_scale, filters_origin, tex_coord_rect,
+                           mask_uv_rect, mask_texture_size, filters_scale,
+                           filters_origin, tex_coord_rect,
                            force_anti_aliasing_off, backdrop_filter_quality);
 
   const gfx::Rect rect5(123, 567, 91011, 131415);
@@ -1016,7 +1016,6 @@
   EXPECT_EQ(render_pass_id, out_render_pass_draw_quad->render_pass_id);
   EXPECT_EQ(resource_id4, out_render_pass_draw_quad->mask_resource_id());
   EXPECT_EQ(mask_texture_size, out_render_pass_draw_quad->mask_texture_size);
-  EXPECT_FALSE(out_render_pass_draw_quad->mask_applies_to_backdrop);
   EXPECT_EQ(filters_scale, out_render_pass_draw_quad->filters_scale);
   EXPECT_EQ(force_anti_aliasing_off,
             out_render_pass_draw_quad->force_anti_aliasing_off);
diff --git a/services/viz/public/cpp/compositing/quads_mojom_traits.cc b/services/viz/public/cpp/compositing/quads_mojom_traits.cc
index 3d35e2fd..e41f086 100644
--- a/services/viz/public/cpp/compositing/quads_mojom_traits.cc
+++ b/services/viz/public/cpp/compositing/quads_mojom_traits.cc
@@ -72,7 +72,6 @@
   quad->resources.ids[viz::RenderPassDrawQuad::kMaskResourceIdIndex] =
       data.mask_resource_id();
   quad->resources.count = data.mask_resource_id() ? 1 : 0;
-  quad->mask_applies_to_backdrop = data.mask_applies_to_backdrop();
   quad->render_pass_id = data.render_pass_id();
   // RenderPass ids are never zero.
   if (!quad->render_pass_id)
diff --git a/services/viz/public/cpp/compositing/quads_mojom_traits.h b/services/viz/public/cpp/compositing/quads_mojom_traits.h
index b62fcf4..b1cc3237 100644
--- a/services/viz/public/cpp/compositing/quads_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/quads_mojom_traits.h
@@ -224,12 +224,6 @@
     return quad->mask_texture_size;
   }
 
-  static bool mask_applies_to_backdrop(const viz::DrawQuad& input) {
-    const viz::RenderPassDrawQuad* quad =
-        viz::RenderPassDrawQuad::MaterialCast(&input);
-    return quad->mask_applies_to_backdrop;
-  }
-
   static const gfx::Vector2dF& filters_scale(const viz::DrawQuad& input) {
     const viz::RenderPassDrawQuad* quad =
         viz::RenderPassDrawQuad::MaterialCast(&input);
diff --git a/services/viz/public/mojom/compositing/quads.mojom b/services/viz/public/mojom/compositing/quads.mojom
index cdfaa7b0..438c869 100644
--- a/services/viz/public/mojom/compositing/quads.mojom
+++ b/services/viz/public/mojom/compositing/quads.mojom
@@ -35,10 +35,6 @@
   gfx.mojom.RectF mask_uv_rect;
   gfx.mojom.Size mask_texture_size;
 
-  // If true, the mask described by mask_resource_id should be applied to only
-  // the backdrop-filtered image.
-  bool mask_applies_to_backdrop;
-
   // The scale from layer space of the root layer of the render pass to
   // the render pass physical pixels. This scale is applied to the filter
   // parameters for pixel-moving filters. This scale should include
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index aaf6a62..a19a93da 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -25400,90 +25400,112 @@
     "junit_tests": [
       {
         "name": "android_webview_junit_tests",
+        "swarming": {},
         "test": "android_webview_junit_tests"
       },
       {
         "name": "base_junit_tests",
+        "swarming": {},
         "test": "base_junit_tests"
       },
       {
         "name": "chrome_junit_tests",
+        "swarming": {},
         "test": "chrome_junit_tests"
       },
       {
         "name": "components_background_task_scheduler_junit_tests",
+        "swarming": {},
         "test": "components_background_task_scheduler_junit_tests"
       },
       {
         "name": "components_gcm_driver_junit_tests",
+        "swarming": {},
         "test": "components_gcm_driver_junit_tests"
       },
       {
         "name": "components_invalidation_impl_junit_tests",
+        "swarming": {},
         "test": "components_invalidation_impl_junit_tests"
       },
       {
         "name": "components_policy_junit_tests",
+        "swarming": {},
         "test": "components_policy_junit_tests"
       },
       {
         "name": "components_signin_junit_tests",
+        "swarming": {},
         "test": "components_signin_junit_tests"
       },
       {
         "name": "components_variations_junit_tests",
+        "swarming": {},
         "test": "components_variations_junit_tests"
       },
       {
         "name": "content_junit_tests",
+        "swarming": {},
         "test": "content_junit_tests"
       },
       {
         "name": "device_junit_tests",
+        "swarming": {},
         "test": "device_junit_tests"
       },
       {
         "name": "junit_unit_tests",
+        "swarming": {},
         "test": "junit_unit_tests"
       },
       {
         "name": "keyboard_accessory_junit_tests",
+        "swarming": {},
         "test": "keyboard_accessory_junit_tests"
       },
       {
         "name": "media_base_junit_tests",
+        "swarming": {},
         "test": "media_base_junit_tests"
       },
       {
         "name": "media_router_junit_tests",
+        "swarming": {},
         "test": "media_router_junit_tests"
       },
       {
         "name": "module_installer_junit_tests",
+        "swarming": {},
         "test": "module_installer_junit_tests"
       },
       {
         "name": "net_junit_tests",
+        "swarming": {},
         "test": "net_junit_tests"
       },
       {
         "name": "service_junit_tests",
+        "swarming": {},
         "test": "service_junit_tests"
       },
       {
         "name": "ui_junit_tests",
+        "swarming": {},
         "test": "ui_junit_tests"
       },
       {
         "name": "webapk_client_junit_tests",
+        "swarming": {},
         "test": "webapk_client_junit_tests"
       },
       {
         "name": "webapk_shell_apk_h2o_junit_tests",
+        "swarming": {},
         "test": "webapk_shell_apk_h2o_junit_tests"
       },
       {
         "name": "webapk_shell_apk_junit_tests",
+        "swarming": {},
         "test": "webapk_shell_apk_junit_tests"
       }
     ]
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index d83cccf4..f58d98c9 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -7542,90 +7542,112 @@
     "junit_tests": [
       {
         "name": "android_webview_junit_tests",
+        "swarming": {},
         "test": "android_webview_junit_tests"
       },
       {
         "name": "base_junit_tests",
+        "swarming": {},
         "test": "base_junit_tests"
       },
       {
         "name": "chrome_junit_tests",
+        "swarming": {},
         "test": "chrome_junit_tests"
       },
       {
         "name": "components_background_task_scheduler_junit_tests",
+        "swarming": {},
         "test": "components_background_task_scheduler_junit_tests"
       },
       {
         "name": "components_gcm_driver_junit_tests",
+        "swarming": {},
         "test": "components_gcm_driver_junit_tests"
       },
       {
         "name": "components_invalidation_impl_junit_tests",
+        "swarming": {},
         "test": "components_invalidation_impl_junit_tests"
       },
       {
         "name": "components_policy_junit_tests",
+        "swarming": {},
         "test": "components_policy_junit_tests"
       },
       {
         "name": "components_signin_junit_tests",
+        "swarming": {},
         "test": "components_signin_junit_tests"
       },
       {
         "name": "components_variations_junit_tests",
+        "swarming": {},
         "test": "components_variations_junit_tests"
       },
       {
         "name": "content_junit_tests",
+        "swarming": {},
         "test": "content_junit_tests"
       },
       {
         "name": "device_junit_tests",
+        "swarming": {},
         "test": "device_junit_tests"
       },
       {
         "name": "junit_unit_tests",
+        "swarming": {},
         "test": "junit_unit_tests"
       },
       {
         "name": "keyboard_accessory_junit_tests",
+        "swarming": {},
         "test": "keyboard_accessory_junit_tests"
       },
       {
         "name": "media_base_junit_tests",
+        "swarming": {},
         "test": "media_base_junit_tests"
       },
       {
         "name": "media_router_junit_tests",
+        "swarming": {},
         "test": "media_router_junit_tests"
       },
       {
         "name": "module_installer_junit_tests",
+        "swarming": {},
         "test": "module_installer_junit_tests"
       },
       {
         "name": "net_junit_tests",
+        "swarming": {},
         "test": "net_junit_tests"
       },
       {
         "name": "service_junit_tests",
+        "swarming": {},
         "test": "service_junit_tests"
       },
       {
         "name": "ui_junit_tests",
+        "swarming": {},
         "test": "ui_junit_tests"
       },
       {
         "name": "webapk_client_junit_tests",
+        "swarming": {},
         "test": "webapk_client_junit_tests"
       },
       {
         "name": "webapk_shell_apk_h2o_junit_tests",
+        "swarming": {},
         "test": "webapk_shell_apk_h2o_junit_tests"
       },
       {
         "name": "webapk_shell_apk_junit_tests",
+        "swarming": {},
         "test": "webapk_shell_apk_junit_tests"
       }
     ]
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index c4c0e09..6412dfe 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -7494,91 +7494,135 @@
     ],
     "junit_tests": [
       {
+        "isolate_coverage_data": true,
         "name": "android_webview_junit_tests",
+        "swarming": {},
         "test": "android_webview_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "base_junit_tests",
+        "swarming": {},
         "test": "base_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "chrome_junit_tests",
+        "swarming": {},
         "test": "chrome_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_background_task_scheduler_junit_tests",
+        "swarming": {},
         "test": "components_background_task_scheduler_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_gcm_driver_junit_tests",
+        "swarming": {},
         "test": "components_gcm_driver_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_invalidation_impl_junit_tests",
+        "swarming": {},
         "test": "components_invalidation_impl_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_policy_junit_tests",
+        "swarming": {},
         "test": "components_policy_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_signin_junit_tests",
+        "swarming": {},
         "test": "components_signin_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_variations_junit_tests",
+        "swarming": {},
         "test": "components_variations_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "content_junit_tests",
+        "swarming": {},
         "test": "content_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "device_junit_tests",
+        "swarming": {},
         "test": "device_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "junit_unit_tests",
+        "swarming": {},
         "test": "junit_unit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "keyboard_accessory_junit_tests",
+        "swarming": {},
         "test": "keyboard_accessory_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "media_base_junit_tests",
+        "swarming": {},
         "test": "media_base_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "media_router_junit_tests",
+        "swarming": {},
         "test": "media_router_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "module_installer_junit_tests",
+        "swarming": {},
         "test": "module_installer_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "net_junit_tests",
+        "swarming": {},
         "test": "net_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "service_junit_tests",
+        "swarming": {},
         "test": "service_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "ui_junit_tests",
+        "swarming": {},
         "test": "ui_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "webapk_client_junit_tests",
+        "swarming": {},
         "test": "webapk_client_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "webapk_shell_apk_h2o_junit_tests",
+        "swarming": {},
         "test": "webapk_shell_apk_h2o_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "webapk_shell_apk_junit_tests",
+        "swarming": {},
         "test": "webapk_shell_apk_junit_tests"
       }
     ]
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 0490998..d750df34 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -10807,6 +10807,7 @@
           "--driver-logging",
           "--test-list=../../testing/buildbot/filters/gpu.skiarenderer_vulkan_blink_web_tests.filter",
           "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer",
+          "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization",
           "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/use-vulkan=native"
         ],
         "isolate_name": "blink_web_tests_exparchive",
@@ -11353,6 +11354,7 @@
           "--driver-logging",
           "--test-list=../../testing/buildbot/filters/gpu.skiarenderer_vulkan_blink_web_tests.filter",
           "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer",
+          "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization",
           "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/use-vulkan=native"
         ],
         "isolate_name": "blink_web_tests_exparchive",
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index 49cfea3..50c23016 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -633,14 +633,17 @@
 
   def generate_junit_test(self, waterfall, tester_name, tester_config,
                           test_name, test_config):
-    del tester_config
     if not self.should_run_on_tester(waterfall, tester_name, test_name,
                                      test_config):
       return None
-    result = {
+    result = copy.deepcopy(test_config)
+    result.update({
       'name': test_name,
       'test': test_config.get('test', test_name),
-    }
+    })
+    self.initialize_args_for_test(result, tester_config)
+    result = self.update_and_cleanup_test(
+        result, test_name, tester_name, tester_config, waterfall)
     return result
 
   def generate_instrumentation_test(self, waterfall, tester_name, tester_config,
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index d89479a8..8eb5b9a 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3340,6 +3340,7 @@
           '--driver-logging',
           '--test-list=../../testing/buildbot/filters/gpu.skiarenderer_vulkan_blink_web_tests.filter',
           '--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer',
+          '--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization',
           '--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/use-vulkan=native',
         ],
         'isolate_name': 'blink_web_tests_exparchive',
diff --git a/testing/buildbot/tryserver.chromium.android.json b/testing/buildbot/tryserver.chromium.android.json
index fdbb364..666075a92 100644
--- a/testing/buildbot/tryserver.chromium.android.json
+++ b/testing/buildbot/tryserver.chromium.android.json
@@ -2883,91 +2883,135 @@
     ],
     "junit_tests": [
       {
+        "isolate_coverage_data": true,
         "name": "android_webview_junit_tests",
+        "swarming": {},
         "test": "android_webview_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "base_junit_tests",
+        "swarming": {},
         "test": "base_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "chrome_junit_tests",
+        "swarming": {},
         "test": "chrome_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_background_task_scheduler_junit_tests",
+        "swarming": {},
         "test": "components_background_task_scheduler_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_gcm_driver_junit_tests",
+        "swarming": {},
         "test": "components_gcm_driver_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_invalidation_impl_junit_tests",
+        "swarming": {},
         "test": "components_invalidation_impl_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_policy_junit_tests",
+        "swarming": {},
         "test": "components_policy_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_signin_junit_tests",
+        "swarming": {},
         "test": "components_signin_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "components_variations_junit_tests",
+        "swarming": {},
         "test": "components_variations_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "content_junit_tests",
+        "swarming": {},
         "test": "content_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "device_junit_tests",
+        "swarming": {},
         "test": "device_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "junit_unit_tests",
+        "swarming": {},
         "test": "junit_unit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "keyboard_accessory_junit_tests",
+        "swarming": {},
         "test": "keyboard_accessory_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "media_base_junit_tests",
+        "swarming": {},
         "test": "media_base_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "media_router_junit_tests",
+        "swarming": {},
         "test": "media_router_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "module_installer_junit_tests",
+        "swarming": {},
         "test": "module_installer_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "net_junit_tests",
+        "swarming": {},
         "test": "net_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "service_junit_tests",
+        "swarming": {},
         "test": "service_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "ui_junit_tests",
+        "swarming": {},
         "test": "ui_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "webapk_client_junit_tests",
+        "swarming": {},
         "test": "webapk_client_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "webapk_shell_apk_h2o_junit_tests",
+        "swarming": {},
         "test": "webapk_shell_apk_h2o_junit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "name": "webapk_shell_apk_junit_tests",
+        "swarming": {},
         "test": "webapk_shell_apk_junit_tests"
       }
     ]
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index f266b50..64e963c 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1517,6 +1517,21 @@
             ]
         }
     ],
+    "CertVerifierBuiltin": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "CertVerifierBuiltin"
+                    ]
+                }
+            ]
+        }
+    ],
     "ChromeCleanupDistribution": [
         {
             "platforms": [
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
index aeaa0c8..722bc55 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
@@ -107,9 +107,11 @@
       }
     }
 
-    // If child has the same style as parent, parent will compute relative
-    // offset.
-    if (&child->Style() != container_style) {
+    // For implementation reasons, text nodes inherit computed style from their
+    // container, including everything, also non-inherited properties. So, if
+    // the container has a relative offset, this will be falsely reflected on
+    // text children. We need to guard against this.
+    if (!child->IsText()) {
       child_scroll_overflow.offset +=
           ComputeRelativeOffset(child->Style(), container_writing_mode,
                                 container_direction, container_physical_size);
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index ab9781059..fc9b820 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -1730,10 +1730,12 @@
       mask_layer_ = CreateGraphicsLayer(CompositingReason::kLayerForMask);
       mask_layer_->SetPaintingPhase(kGraphicsLayerPaintMask);
       CompositorElementId element_id = CompositorElementIdFromUniqueObjectId(
-          owning_layer_.GetLayoutObject().UniqueId(),
+          GetLayoutObject().UniqueId(),
           CompositorElementIdNamespace::kEffectMask);
       mask_layer_->SetElementId(element_id);
-      mask_layer_->CcLayer()->set_is_mask();
+      mask_layer_->CcLayer()->SetIsMask(true);
+      if (GetLayoutObject().HasBackdropFilter())
+        mask_layer_->CcLayer()->SetIsBackdropFilterMask(true);
       layer_changed = true;
     }
   } else if (mask_layer_) {
diff --git a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
index 0a1dbd7..c54b6c8 100644
--- a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
+++ b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
@@ -888,38 +888,18 @@
   if (interval_.BeginsAfter(presentation_time)) {
     next_interesting_interval_time = interval_.begin;
   } else if (interval_.EndsAfter(presentation_time)) {
-    next_interesting_interval_time = interval_.end;
-    SMILTime simple_duration = SimpleDuration();
-    if (simple_duration) {
+    if (SMILTime simple_duration = SimpleDuration()) {
       SMILTime next_repeat_time = ComputeNextRepeatTime(
           interval_.begin, simple_duration, presentation_time);
       DCHECK(next_repeat_time.IsFinite());
-      // Because of floating point issues we can end up getting a
-      // |next_repeat_time| == |presentation_time|, which would fail our
-      // requirement, meaning we wouldn't make progress. Integers FTW!
-      if (presentation_time < next_repeat_time) {
-        next_interesting_interval_time =
-            std::min(next_interesting_interval_time, next_repeat_time);
-      }
+      next_interesting_interval_time =
+          std::min(next_repeat_time, interval_.end);
+    } else {
+      next_interesting_interval_time = interval_.end;
     }
   }
-  // TODO(edvardt): This is a vile hack.
-  // Removing 0.5ms, which is less than the minimum precision time.
-  //
-  // Removing this will break most repeatn-* tests, and
-  // most tests with onmouse syncbases. These are a few:
-  //    svg/animations/reapeatn-*
-  //    external/wpt/svg/animations/scripted/onhover-syncbases.html
-  //    external/wpt/svg/animations/slider-switch.html
-  // These break because the updates land ON THE SAME TIMES as the
-  // instance times. And the animation cannot then get a new interval
-  // from that instance time.
-  const SMILTime half_ms = SMILTime::FromSecondsD(0.0005);
-  const SMILTime instance_time =
-      FindInstanceTime(kBegin, presentation_time, false) - half_ms;
-  if (presentation_time < instance_time)
-    return std::min(next_interesting_interval_time, instance_time);
-  return next_interesting_interval_time;
+  return std::min(next_interesting_interval_time,
+                  FindInstanceTime(kBegin, presentation_time, false));
 }
 
 void SVGSMILElement::BeginListChanged(SMILTime event_time) {
diff --git a/third_party/blink/renderer/devtools/.eslintrc.js b/third_party/blink/renderer/devtools/.eslintrc.js
index 401bb011..10cc7cb 100644
--- a/third_party/blink/renderer/devtools/.eslintrc.js
+++ b/third_party/blink/renderer/devtools/.eslintrc.js
@@ -7,8 +7,7 @@
     },
 
     "parserOptions": {
-        "ecmaVersion": 9,
-        "sourceType": "module"
+        "ecmaVersion": 9
     },
 
     /**
diff --git a/third_party/blink/renderer/devtools/BUILD.gn b/third_party/blink/renderer/devtools/BUILD.gn
index 3b9d6d3..917c44c 100644
--- a/third_party/blink/renderer/devtools/BUILD.gn
+++ b/third_party/blink/renderer/devtools/BUILD.gn
@@ -786,6 +786,7 @@
   "front_end/toolbox.js",
   "front_end/toolbox.json",
   "front_end/ui/ActionRegistry.js",
+  "front_end/ui/ARIAUtils.js",
   "front_end/ui/checkboxTextLabel.css",
   "front_end/ui/closeButton.css",
   "front_end/ui/confirmDialog.css",
@@ -957,12 +958,6 @@
 
 all_devtools_files += lighthouse_locale_files
 
-all_devtools_modules = [
-  "front_end/root.js",
-  "front_end/ui/ARIAUtils.js",
-  "front_end/ui/UI.js",
-]
-
 devtools_test_files = [
   "//third_party/axe-core/axe.js",
   "front_end/accessibility_test_runner/AccessibilityPaneTestRunner.js",
@@ -1154,12 +1149,6 @@
   "front_end/worker_app.html",
 ]
 
-copied_devtools_modules = [
-  "$resources_out_dir/root.js",
-  "$resources_out_dir/ui/ARIAUtils.js",
-  "$resources_out_dir/ui/UI.js",
-]
-
 generated_applications = [
   "$resources_out_dir/audits_worker.js",
   "$resources_out_dir/devtools_app.html",
@@ -1278,7 +1267,7 @@
 visibility = [ "//third_party/blink/*" ]
 
 group("devtools_all_files") {
-  data = all_devtools_files + all_devtools_modules
+  data = all_devtools_files
   deps = [
     ":devtools_frontend_resources_data",
   ]
@@ -1295,7 +1284,6 @@
   ":devtools_extension_api",
   ":frontend_protocol_sources",
   ":supported_css_properties",
-  ":copy_devtools_modules",
 ]
 
 if (debug_devtools) {
@@ -1360,8 +1348,8 @@
   ]
 
   grd_files =
-      all_devtools_modules + generated_applications +
-      generated_non_autostart_non_remote_modules + devtools_embedder_scripts +
+      generated_applications + generated_non_autostart_non_remote_modules +
+      devtools_embedder_scripts +
       [ "$resources_out_dir/devtools_extension_api.js" ]
 
   # Bundle remote modules in ChromeOS.
@@ -1480,24 +1468,6 @@
          ]
 }
 
-action("copy_devtools_modules") {
-  script = "scripts/build/copy_devtools_modules.py"
-
-  deps = [
-    ":build_release_devtools",
-  ]
-
-  inputs = all_devtools_modules
-  outputs = copied_devtools_modules
-
-  args = all_devtools_modules + [
-           "--input_path",
-           rebase_path(".", root_build_dir),
-           "--output_path",
-           rebase_path(resources_out_dir, root_build_dir),
-         ]
-}
-
 if (debug_devtools) {
   resources_out_debug_dir = "$root_out_dir/resources/inspector/debug"
 
diff --git a/third_party/blink/renderer/devtools/PRESUBMIT.py b/third_party/blink/renderer/devtools/PRESUBMIT.py
index 4768b66..1166bdb 100644
--- a/third_party/blink/renderer/devtools/PRESUBMIT.py
+++ b/third_party/blink/renderer/devtools/PRESUBMIT.py
@@ -78,8 +78,8 @@
     # Use eslint to autofix the braces.
     # Also fix semicolon to avoid confusing clang-format.
     eslint_process = popen([
-        local_node.node_path(),
-        local_node.eslint_path(), '--no-eslintrc', '--fix', '--env=es6', '--parser-options=ecmaVersion:9,sourceType:module',
+        local_node.node_path(), local_node.eslint_path(),
+        '--no-eslintrc', '--fix', '--env=es6', '--parser-options=ecmaVersion:9',
         '--rule={"curly": [2, "multi-or-nest", "consistent"], "semi": 2}'
     ] + affected_files)
     eslint_process.communicate()
diff --git a/third_party/blink/renderer/devtools/front_end/devtools_app.html b/third_party/blink/renderer/devtools/front_end/devtools_app.html
index 2460c329..81b6645 100644
--- a/third_party/blink/renderer/devtools/front_end/devtools_app.html
+++ b/third_party/blink/renderer/devtools/front_end/devtools_app.html
@@ -9,9 +9,8 @@
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="module" src="root.js"></script>
-    <script defer src="Runtime.js"></script>
-    <script defer src="devtools_app.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="devtools_app.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/inspector.html b/third_party/blink/renderer/devtools/front_end/inspector.html
index 1ab4d319..b5d6f04e 100644
--- a/third_party/blink/renderer/devtools/front_end/inspector.html
+++ b/third_party/blink/renderer/devtools/front_end/inspector.html
@@ -9,9 +9,8 @@
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="module" src="root.js"></script>
-    <script defer src="Runtime.js"></script>
-    <script defer src="inspector.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="inspector.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/integration_test_runner.html b/third_party/blink/renderer/devtools/front_end/integration_test_runner.html
index 073b301..ca7cc521 100644
--- a/third_party/blink/renderer/devtools/front_end/integration_test_runner.html
+++ b/third_party/blink/renderer/devtools/front_end/integration_test_runner.html
@@ -8,9 +8,8 @@
 <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
-    <script type="module" src="root.js"></script>
-    <script defer src="Runtime.js"></script>
-    <script defer src="integration_test_runner.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="integration_test_runner.js"></script>
 </head>
 <body id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/js_app.html b/third_party/blink/renderer/devtools/front_end/js_app.html
index 8cc144c..7105918 100644
--- a/third_party/blink/renderer/devtools/front_end/js_app.html
+++ b/third_party/blink/renderer/devtools/front_end/js_app.html
@@ -9,9 +9,8 @@
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="module" src="root.js"></script>
-    <script defer src="Runtime.js"></script>
-    <script defer src="js_app.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="js_app.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/ndb_app.html b/third_party/blink/renderer/devtools/front_end/ndb_app.html
index d6f17486..ac0dee1 100644
--- a/third_party/blink/renderer/devtools/front_end/ndb_app.html
+++ b/third_party/blink/renderer/devtools/front_end/ndb_app.html
@@ -9,9 +9,8 @@
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="module" src="root.js"></script>
-    <script defer src="Runtime.js"></script>
-    <script defer src="ndb_app.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="ndb_app.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/node_app.html b/third_party/blink/renderer/devtools/front_end/node_app.html
index 5b475a11..05aa910 100644
--- a/third_party/blink/renderer/devtools/front_end/node_app.html
+++ b/third_party/blink/renderer/devtools/front_end/node_app.html
@@ -9,9 +9,8 @@
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="module" src="root.js"></script>
-    <script defer src="Runtime.js"></script>
-    <script defer src="node_app.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="node_app.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/root.js b/third_party/blink/renderer/devtools/front_end/root.js
deleted file mode 100644
index 90ae39a..0000000
--- a/third_party/blink/renderer/devtools/front_end/root.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2019 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 './ui/UI.js';
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/sources/SourcesPanel.js b/third_party/blink/renderer/devtools/front_end/sources/SourcesPanel.js
index 8c846ad8e..3c79383 100644
--- a/third_party/blink/renderer/devtools/front_end/sources/SourcesPanel.js
+++ b/third_party/blink/renderer/devtools/front_end/sources/SourcesPanel.js
@@ -65,7 +65,6 @@
     const initialNavigatorWidth = 225;
     this.editorView = new UI.SplitWidget(true, false, 'sourcesPanelNavigatorSplitViewState', initialNavigatorWidth);
     this.editorView.enableShowModeSaving();
-    this.editorView.element.tabIndex = 0;
     this._splitWidget.setMainWidget(this.editorView);
 
     // Create navigator tabbed pane with toolbar.
diff --git a/third_party/blink/renderer/devtools/front_end/toolbox.html b/third_party/blink/renderer/devtools/front_end/toolbox.html
index ab892052..c48ad36 100644
--- a/third_party/blink/renderer/devtools/front_end/toolbox.html
+++ b/third_party/blink/renderer/devtools/front_end/toolbox.html
@@ -8,9 +8,8 @@
 <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' ">
-    <script type="module" src="root.js"></script>
-    <script defer src="Runtime.js"></script>
-    <script defer src="toolbox.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="toolbox.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js b/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js
index 088e6ee..fd1aad50 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js
@@ -2,326 +2,327 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-let _id = 0;
-
-/**
- * @param {string} prefix
- * @return {string}
- */
-export function nextId(prefix) {
-  return (prefix || '') + ++_id;
-}
+UI.ARIAUtils = {};
+UI.ARIAUtils._id = 0;
 
 /**
  * @param {!Element} label
  * @param {!Element} control
  */
-export function bindLabelToControl(label, control) {
-  const controlId = nextId('labelledControl');
+UI.ARIAUtils.bindLabelToControl = function(label, control) {
+  const controlId = UI.ARIAUtils.nextId('labelledControl');
   control.id = controlId;
   label.setAttribute('for', controlId);
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsAlert(element) {
+UI.ARIAUtils.markAsAlert = function(element) {
   element.setAttribute('role', 'alert');
   element.setAttribute('aria-live', 'polite');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsButton(element) {
+UI.ARIAUtils.markAsButton = function(element) {
   element.setAttribute('role', 'button');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsCheckbox(element) {
+UI.ARIAUtils.markAsCheckbox = function(element) {
   element.setAttribute('role', 'checkbox');
-}
+};
 
 /**
  * @param {!Element} element
  * @param {boolean=} modal
  */
-export function markAsDialog(element, modal) {
+UI.ARIAUtils.markAsDialog = function(element, modal) {
   element.setAttribute('role', 'dialog');
   if (modal)
     element.setAttribute('aria-modal', 'true');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsGroup(element) {
+UI.ARIAUtils.markAsGroup = function(element) {
   element.setAttribute('role', 'group');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsLink(element) {
+UI.ARIAUtils.markAsLink = function(element) {
   element.setAttribute('role', 'link');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsMenuButton(element) {
-  markAsButton(element);
+UI.ARIAUtils.markAsMenuButton = function(element) {
+  UI.ARIAUtils.markAsButton(element);
   element.setAttribute('aria-haspopup', true);
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsProgressBar(element) {
+UI.ARIAUtils.markAsProgressBar = function(element) {
   element.setAttribute('role', 'progressbar');
   element.setAttribute('aria-valuemin', 0);
   element.setAttribute('aria-valuemax', 100);
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsTab(element) {
+UI.ARIAUtils.markAsTab = function(element) {
   element.setAttribute('role', 'tab');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsTree(element) {
+UI.ARIAUtils.markAsTree = function(element) {
   element.setAttribute('role', 'tree');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsTreeitem(element) {
+UI.ARIAUtils.markAsTreeitem = function(element) {
   element.setAttribute('role', 'treeitem');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsTextBox(element) {
+UI.ARIAUtils.markAsTextBox = function(element) {
   element.setAttribute('role', 'textbox');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsMenu(element) {
+UI.ARIAUtils.markAsMenu = function(element) {
   element.setAttribute('role', 'menu');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsMenuItem(element) {
+UI.ARIAUtils.markAsMenuItem = function(element) {
   element.setAttribute('role', 'menuitem');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsMenuItemSubMenu(element) {
-  markAsMenuItem(element);
+UI.ARIAUtils.markAsMenuItemSubMenu = function(element) {
+  UI.ARIAUtils.markAsMenuItem(element);
   element.setAttribute('aria-haspopup', true);
-}
+};
 
 /**
  * Must contain children whose role is option.
  * @param {!Element} element
  */
-export function markAsListBox(element) {
+UI.ARIAUtils.markAsListBox = function(element) {
   element.setAttribute('role', 'listbox');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsMultiSelectable(element) {
+UI.ARIAUtils.markAsMultiSelectable = function(element) {
   element.setAttribute('aria-multiselectable', 'true');
-}
+};
 
 /**
  * Must be contained in, or owned by, an element with the role listbox.
  * @param {!Element} element
  */
-export function markAsOption(element) {
+UI.ARIAUtils.markAsOption = function(element) {
   element.setAttribute('role', 'option');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsRadioGroup(element) {
+UI.ARIAUtils.markAsRadioGroup = function(element) {
   element.setAttribute('role', 'radiogroup');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsHidden(element) {
+UI.ARIAUtils.markAsHidden = function(element) {
   element.setAttribute('aria-hidden', 'true');
-}
+};
 
 /**
  * @param {!Element} element
  * @param {number} level
  */
-export function markAsHeading(element, level) {
+UI.ARIAUtils.markAsHeading = function(element, level) {
   element.setAttribute('role', 'heading');
   element.setAttribute('aria-level', level);
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsPoliteLiveRegion(element) {
+UI.ARIAUtils.markAsPoliteLiveRegion = function(element) {
   element.setAttribute('aria-live', 'polite');
-}
+};
 
 /**
  * @param {!Element} element
  * @param {?string} placeholder
  */
-export function setPlaceholder(element, placeholder) {
+UI.ARIAUtils.setPlaceholder = function(element, placeholder) {
   if (placeholder)
     element.setAttribute('aria-placeholder', placeholder);
   else
     element.removeAttribute('aria-placeholder');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsPresentation(element) {
+UI.ARIAUtils.markAsPresentation = function(element) {
   element.setAttribute('role', 'presentation');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function markAsStatus(element) {
+UI.ARIAUtils.markAsStatus = function(element) {
   element.setAttribute('role', 'status');
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function ensureId(element) {
+UI.ARIAUtils.ensureId = function(element) {
   if (!element.id)
-    element.id = nextId('ariaElement');
-}
+    element.id = UI.ARIAUtils.nextId('ariaElement');
+};
+
+/**
+ * @param {string} prefix
+ * @return {string}
+ */
+UI.ARIAUtils.nextId = function(prefix) {
+  return (prefix || '') + ++UI.ARIAUtils._id;
+};
 
 /**
  * @param {!Element} element
  * @param {?Element} controlledElement
  */
-export function setControls(element, controlledElement) {
+UI.ARIAUtils.setControls = function(element, controlledElement) {
   if (!controlledElement) {
     element.removeAttribute('aria-controls');
     return;
   }
 
-  ensureId(controlledElement);
+  UI.ARIAUtils.ensureId(controlledElement);
   element.setAttribute('aria-controls', controlledElement.id);
-}
+};
 
 /**
  * @param {!Element} element
  * @param {boolean} value
  */
-export function setChecked(element, value) {
+UI.ARIAUtils.setChecked = function(element, value) {
   element.setAttribute('aria-checked', !!value);
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function setCheckboxAsIndeterminate(element) {
+UI.ARIAUtils.setCheckboxAsIndeterminate = function(element) {
   element.setAttribute('aria-checked', 'mixed');
-}
+};
 
 /**
  * @param {!Element} element
  * @param {boolean} value
  */
-export function setExpanded(element, value) {
+UI.ARIAUtils.setExpanded = function(element, value) {
   element.setAttribute('aria-expanded', !!value);
-}
+};
 
 /**
  * @param {!Element} element
  */
-export function unsetExpandable(element) {
+UI.ARIAUtils.unsetExpandable = function(element) {
   element.removeAttribute('aria-expanded');
-}
+};
 
 /**
  * @param {!Element} element
  * @param {boolean} value
  */
-export function setSelected(element, value) {
+UI.ARIAUtils.setSelected = function(element, value) {
   // aria-selected behaves differently for false and undefined.
   // Often times undefined values are unintentionally typed as booleans.
   // Use !! to make sure this is true or false.
   element.setAttribute('aria-selected', !!value);
-}
+};
 
 /**
  * @param {!Element} element
  * @param {boolean} value
  */
-export function setInvalid(element, value) {
+UI.ARIAUtils.setInvalid = function(element, value) {
   if (value)
     element.setAttribute('aria-invalid', value);
   else
     element.removeAttribute('aria-invalid');
-}
+};
 
 /**
  * @param {!Element} element
  * @param {boolean} value
  */
-export function setPressed(element, value) {
+UI.ARIAUtils.setPressed = function(element, value) {
   // aria-pressed behaves differently for false and undefined.
   // Often times undefined values are unintentionally typed as booleans.
   // Use !! to make sure this is true or false.
   element.setAttribute('aria-pressed', !!value);
-}
+};
 
 /**
  * @param {!Element} element
  * @param {number} value
  */
-export function setProgressBarCurrentPercentage(element, value) {
+UI.ARIAUtils.setProgressBarCurrentPercentage = function(element, value) {
   element.setAttribute('aria-valuenow', value);
-}
+};
 
 /**
  * @param {!Element} element
  * @param {string} name
  */
-export function setAccessibleName(element, name) {
+UI.ARIAUtils.setAccessibleName = function(element, name) {
   element.setAttribute('aria-label', name);
-}
+};
 
 /** @type {!WeakMap<!Element, !Element>} */
-const _descriptionMap = new WeakMap();
+UI.ARIAUtils._descriptionMap = new WeakMap();
 
 /**
  * @param {!Element} element
  * @param {string} description
  */
-export function setDescription(element, description) {
+UI.ARIAUtils.setDescription = function(element, description) {
   // Nodes in the accesesibility tree are made up of a core
   // triplet of "name", "value", "description"
   // The "description" field is taken from either
@@ -347,12 +348,12 @@
   // The rest of DevTools shouldn't have to worry about this,
   // so there is some unfortunate code below.
 
-  if (_descriptionMap.has(element))
-    _descriptionMap.get(element).remove();
+  if (UI.ARIAUtils._descriptionMap.has(element))
+    UI.ARIAUtils._descriptionMap.get(element).remove();
   element.removeAttribute('data-aria-utils-animation-hack');
 
   if (!description) {
-    _descriptionMap.delete(element);
+    UI.ARIAUtils._descriptionMap.delete(element);
     element.removeAttribute('aria-describedby');
     return;
   }
@@ -362,9 +363,9 @@
   const descriptionElement = createElement('span');
   descriptionElement.textContent = description;
   descriptionElement.style.display = 'none';
-  ensureId(descriptionElement);
+  UI.ARIAUtils.ensureId(descriptionElement);
   element.setAttribute('aria-describedby', descriptionElement.id);
-  _descriptionMap.set(element, descriptionElement);
+  UI.ARIAUtils._descriptionMap.set(element, descriptionElement);
 
   // Now we have to actually put this description element
   // somewhere in the DOM so that we can point to it.
@@ -397,20 +398,20 @@
   element.setAttribute('data-aria-utils-animation-hack', 'sorry');
   element.addEventListener('animationend', () => {
     // Someone might have made a new description in the meantime.
-    if (_descriptionMap.get(element) !== descriptionElement)
+    if (UI.ARIAUtils._descriptionMap.get(element) !== descriptionElement)
       return;
     element.removeAttribute('data-aria-utils-animation-hack');
 
     // Try it again. This time we are in the DOM, so it *should* work.
     element.insertAdjacentElement('afterend', descriptionElement);
   }, {once: true});
-}
+};
 
 /**
  * @param {!Element} element
  * @param {?Element} activedescendant
  */
-export function setActiveDescendant(element, activedescendant) {
+UI.ARIAUtils.setActiveDescendant = function(element, activedescendant) {
   if (!activedescendant) {
     element.removeAttribute('aria-activedescendant');
     return;
@@ -418,19 +419,17 @@
 
   console.assert(element.hasSameShadowRoot(activedescendant), 'elements are not in the same shadow dom');
 
-  ensureId(activedescendant);
+  UI.ARIAUtils.ensureId(activedescendant);
   element.setAttribute('aria-activedescendant', activedescendant.id);
-}
-
-const AlertElementSymbol = Symbol('AlertElementSybmol');
+};
 
 /**
  * @param {string} message
  * @param {!Element} element
  */
-export function alert(message, element) {
+UI.ARIAUtils.alert = function(message, element) {
   const document = element.ownerDocument;
-  if (!document[AlertElementSymbol]) {
+  if (!document[UI.ARIAUtils.AlertElementSymbol]) {
     const alertElement = document.body.createChild('div');
     alertElement.style.position = 'absolute';
     alertElement.style.left = '-999em';
@@ -438,54 +437,9 @@
     alertElement.style.overflow = 'hidden';
     alertElement.setAttribute('role', 'alert');
     alertElement.setAttribute('aria-atomic', 'true');
-    document[AlertElementSymbol] = alertElement;
+    document[UI.ARIAUtils.AlertElementSymbol] = alertElement;
   }
-
-  document[AlertElementSymbol].textContent = message.trimEndWithMaxLength(10000);
-}
-
-/** Legacy exported object @suppress {const} */
-self.UI = self.UI || {};
-self.UI.ARIAUtils = {
-  nextId,
-  bindLabelToControl,
-  markAsAlert,
-  markAsButton,
-  markAsCheckbox,
-  markAsDialog,
-  markAsGroup,
-  markAsLink,
-  markAsMenuButton,
-  markAsProgressBar,
-  markAsTab,
-  markAsTree,
-  markAsTreeitem,
-  markAsTextBox,
-  markAsMenu,
-  markAsMenuItem,
-  markAsMenuItemSubMenu,
-  markAsListBox,
-  markAsMultiSelectable,
-  markAsOption,
-  markAsRadioGroup,
-  markAsHidden,
-  markAsHeading,
-  markAsPoliteLiveRegion,
-  setPlaceholder,
-  markAsPresentation,
-  markAsStatus,
-  ensureId,
-  setControls,
-  setChecked,
-  setCheckboxAsIndeterminate,
-  setExpanded,
-  unsetExpandable,
-  setSelected,
-  setInvalid,
-  setPressed,
-  setProgressBarCurrentPercentage,
-  setAccessibleName,
-  setDescription,
-  setActiveDescendant,
-  alert,
+  document[UI.ARIAUtils.AlertElementSymbol].textContent = message.trimEndWithMaxLength(10000);
 };
+
+UI.ARIAUtils.AlertElementSymbol = Symbol('AlertElementSybmol');
diff --git a/third_party/blink/renderer/devtools/front_end/ui/UI.js b/third_party/blink/renderer/devtools/front_end/ui/UI.js
deleted file mode 100644
index 262177a9..0000000
--- a/third_party/blink/renderer/devtools/front_end/ui/UI.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import * as ARIAUtils from './ARIAUtils.js';
-
-export {
-  ARIAUtils,
-};
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/ui/module.json b/third_party/blink/renderer/devtools/front_end/ui/module.json
index 2dcde6f..bd5f5a1e 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/module.json
+++ b/third_party/blink/renderer/devtools/front_end/ui/module.json
@@ -54,16 +54,13 @@
         "SuggestBox.js",
         "TabbedPane.js",
         "UIUtils.js",
+        "ARIAUtils.js",
         "ZoomManager.js",
         "ShortcutsScreen.js",
         "Geometry.js",
         "XLink.js",
         "XWidget.js"
     ],
-    "modules": [
-        "ARIAUtils.js",
-        "UI.js"
-    ],
     "resources": [
         "checkboxTextLabel.css",
         "closeButton.css",
diff --git a/third_party/blink/renderer/devtools/front_end/worker_app.html b/third_party/blink/renderer/devtools/front_end/worker_app.html
index c041d37..e5827b5e 100644
--- a/third_party/blink/renderer/devtools/front_end/worker_app.html
+++ b/third_party/blink/renderer/devtools/front_end/worker_app.html
@@ -9,9 +9,8 @@
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://chrome-devtools-frontend.appspot.com">
     <meta name="referrer" content="no-referrer">
-    <script type="module" src="root.js"></script>
-    <script defer src="Runtime.js"></script>
-    <script defer src="worker_app.js"></script>
+    <script src="Runtime.js"></script>
+    <script src="worker_app.js"></script>
 </head>
 <body class="undocked" id="-blink-dev-tools"></body>
 </html>
diff --git a/third_party/blink/renderer/devtools/package.json b/third_party/blink/renderer/devtools/package.json
index 42d8d90..feea3f1a 100644
--- a/third_party/blink/renderer/devtools/package.json
+++ b/third_party/blink/renderer/devtools/package.json
@@ -35,10 +35,6 @@
   "bugs": {
     "url": "https://bugs.chromium.org/p/chromium/issues/list?can=2&q=component:Platform%3EDevTools%20&sort=-opened&colspec=ID%20Stars%20Owner%20Summary%20Modified%20Opened"
   },
-  "type": "module",
-  "files": [
-    "front_end/**/*.mjs"
-  ],
   "homepage": "https://devtools.chrome.com",
   "devDependencies": {
     "ajv": "^5.1.5"
diff --git a/third_party/blink/renderer/devtools/scripts/build/build_release_applications.py b/third_party/blink/renderer/devtools/scripts/build/build_release_applications.py
index db32b006..74e1c10 100755
--- a/third_party/blink/renderer/devtools/scripts/build/build_release_applications.py
+++ b/third_party/blink/renderer/devtools/scripts/build/build_release_applications.py
@@ -133,13 +133,13 @@
         output = StringIO()
         with open(join(self.application_dir, html_name), 'r') as app_input_html:
             for line in app_input_html:
-                if ('<script ' in line and 'type="module"' not in line) or '<link ' in line:
+                if '<script ' in line or '<link ' in line:
                     continue
                 if '</head>' in line:
                     self._write_include_tags(self.descriptors, output)
                     js_file = join(self.application_dir, self.app_file('js'))
                     if path.exists(js_file):
-                        output.write('    <script type="module">%s</script>\n' % minify_js(read_file(js_file)))
+                        output.write('    <script>%s</script>\n' % minify_js(read_file(js_file)))
                 output.write(line)
 
         write_file(join(self.output_dir, html_name), output.getvalue())
@@ -154,7 +154,7 @@
 
     def _generate_include_tag(self, resource_path):
         if resource_path.endswith('.js'):
-            return '    <script defer src="%s"></script>\n' % resource_path
+            return '    <script type="text/javascript" src="%s"></script>\n' % resource_path
         else:
             assert resource_path
 
diff --git a/third_party/blink/renderer/devtools/scripts/build/copy_devtools_modules.py b/third_party/blink/renderer/devtools/scripts/build/copy_devtools_modules.py
deleted file mode 100755
index 10d9ee235..0000000
--- a/third_party/blink/renderer/devtools/scripts/build/copy_devtools_modules.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: UTF-8 -*-
-#
-# Copyright 2019 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.
-"""
-Copies the modules into the resources folder
-"""
-
-from os.path import join, relpath
-import shutil
-import sys
-
-
-def main(argv):
-    try:
-        input_path_flag_index = argv.index('--input_path')
-        input_path = argv[input_path_flag_index + 1]
-        output_path_flag_index = argv.index('--output_path')
-        output_path = argv[output_path_flag_index + 1]
-        devtools_modules = argv[1:input_path_flag_index]
-    except:
-        print 'Usage: %s module_1 module_2 ... module_N --input_path <input_path> --output_path <output_path>' % argv[0]
-        raise
-
-    for file_name in devtools_modules:
-        shutil.copy(join(input_path, file_name), join(output_path, relpath(file_name, 'front_end')))
-
-
-if __name__ == '__main__':
-    sys.exit(main(sys.argv))
diff --git a/third_party/blink/renderer/devtools/scripts/build/modular_build.py b/third_party/blink/renderer/devtools/scripts/build/modular_build.py
index c818ad95..90d6697 100755
--- a/third_party/blink/renderer/devtools/scripts/build/modular_build.py
+++ b/third_party/blink/renderer/devtools/scripts/build/modular_build.py
@@ -76,7 +76,7 @@
         for name in self.sorted_modules():
             module = self.modules[name]
             skipped_files = set(module.get('skip_compilation', []))
-            for script in module.get('scripts', []) + module.get('modules', []):
+            for script in module.get('scripts', []):
                 if script not in skipped_files:
                     files[path.normpath(path.join(self.application_dir, name, script))] = True
         return files.keys()
diff --git a/third_party/blink/renderer/devtools/scripts/check_localizability.js b/third_party/blink/renderer/devtools/scripts/check_localizability.js
index 91e7158..000a294 100644
--- a/third_party/blink/renderer/devtools/scripts/check_localizability.js
+++ b/third_party/blink/renderer/devtools/scripts/check_localizability.js
@@ -284,7 +284,7 @@
   if (path.extname(filePath) === '.grdp')
     return auditGrdpFile(filePath, fileContent, errors);
 
-  const ast = esprima.parseModule(fileContent, {loc: true});
+  const ast = esprima.parse(fileContent, {loc: true});
 
   const relativeFilePath = localizationUtils.getRelativeFilePathFromSrc(filePath);
   for (const node of ast.body)
diff --git a/third_party/blink/renderer/devtools/scripts/check_localizable_resources.js b/third_party/blink/renderer/devtools/scripts/check_localizable_resources.js
index acf7e72..50a2859 100644
--- a/third_party/blink/renderer/devtools/scripts/check_localizable_resources.js
+++ b/third_party/blink/renderer/devtools/scripts/check_localizable_resources.js
@@ -32,7 +32,6 @@
     else
       await getErrors();
   } catch (e) {
-    console.log(e.stack);
     console.log(`Error: ${e.message}`);
     process.exit(1);
   }
diff --git a/third_party/blink/renderer/devtools/scripts/compile_frontend.py b/third_party/blink/renderer/devtools/scripts/compile_frontend.py
index 78a553da..53ad724f 100755
--- a/third_party/blink/renderer/devtools/scripts/compile_frontend.py
+++ b/third_party/blink/renderer/devtools/scripts/compile_frontend.py
@@ -77,7 +77,6 @@
 GLOBAL_EXTERNS_FILE = to_platform_path(path.join(DEVTOOLS_FRONTEND_PATH, 'externs.js'))
 DEFAULT_PROTOCOL_EXTERNS_FILE = path.join(DEVTOOLS_FRONTEND_PATH, 'protocol_externs.js')
 RUNTIME_FILE = to_platform_path(path.join(DEVTOOLS_FRONTEND_PATH, 'Runtime.js'))
-ROOT_MODULE_FILE = to_platform_path(path.join(DEVTOOLS_FRONTEND_PATH, 'root.js'))
 
 CLOSURE_COMPILER_JAR = to_platform_path(path.join(SCRIPTS_PATH, 'closure', 'compiler.jar'))
 CLOSURE_RUNNER_JAR = to_platform_path(path.join(SCRIPTS_PATH, 'closure', 'closure_runner', 'closure_runner.jar'))
@@ -280,8 +279,6 @@
         namespace_externs_path,
         '--js',
         RUNTIME_FILE,
-        '--js',
-        ROOT_MODULE_FILE,
     ]
 
     all_files = descriptors.all_compiled_files()
diff --git a/third_party/blink/renderer/devtools/scripts/localization_utils/check_localized_strings.js b/third_party/blink/renderer/devtools/scripts/localization_utils/check_localized_strings.js
index 4281b65c..d2ac8e67 100644
--- a/third_party/blink/renderer/devtools/scripts/localization_utils/check_localized_strings.js
+++ b/third_party/blink/renderer/devtools/scripts/localization_utils/check_localized_strings.js
@@ -93,7 +93,7 @@
   if (path.basename(filePath) === 'module.json')
     return parseLocalizableStringFromModuleJson(fileContent, filePath);
 
-  const ast = esprima.parseModule(fileContent, {loc: true});
+  const ast = esprima.parse(fileContent, {loc: true});
   for (const node of ast.body)
     parseLocalizableStringFromNode(node, filePath);
 }
diff --git a/third_party/blink/renderer/devtools/tsconfig.json b/third_party/blink/renderer/devtools/tsconfig.json
new file mode 100644
index 0000000..1dbc1cc
--- /dev/null
+++ b/third_party/blink/renderer/devtools/tsconfig.json
@@ -0,0 +1,5 @@
+{
+  "compilerOptions": {
+    "typeRoots": ["../../../devtools-node-modules/third_party/node_modules/@types"]
+  }
+}
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 359306f..a562d98 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -297,7 +297,7 @@
     return nullptr;
   }
 
-  if (layout_object_->IsAnonymousBlock() && layout_object_->ContainingBlock()) {
+  if (layout_object_->IsAnonymous() && layout_object_->ContainingBlock()) {
     return layout_object_->ContainingBlock()->GetNode();
   }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
index 96868bd..8c5c0a9 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
@@ -56,7 +56,7 @@
   ax::mojom::Role DetermineAccessibilityRole() override;
   ax::mojom::Role NativeRoleIgnoringAria() const override;
 
-  // If this is an anonymous block, returns the node of its containing layout
+  // If this is an anonymous node, returns the node of its containing layout
   // block, otherwise returns the node of this layout object.
   Node* GetNodeOrContainingBlockNode() const;
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position_test.cc b/third_party/blink/renderer/modules/accessibility/ax_position_test.cc
index f0742e5d..c571e5eb 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_position_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_position_test.cc
@@ -1236,6 +1236,36 @@
   EXPECT_EQ(12, position_after.GetPosition().OffsetInContainerNode());
 }
 
+TEST_F(AccessibilityTest, PositionInCSSImageContent) {
+  constexpr char css_content_no_text[] = R"HTML(
+   <style>
+   .heading::before {
+    content: url(data:image/gif;base64,);
+   }
+   </style>
+   <h1 id="heading" class="heading">Heading</h1>)HTML";
+  SetBodyInnerHTML(css_content_no_text);
+
+  const Node* heading = GetElementById("heading");
+  ASSERT_NE(nullptr, heading);
+
+  const AXObject* ax_heading = GetAXObjectByElementId("heading");
+  ASSERT_NE(nullptr, ax_heading);
+  ASSERT_EQ(ax::mojom::Role::kHeading, ax_heading->RoleValue());
+  ASSERT_EQ(2, ax_heading->ChildCount());
+
+  const AXObject* ax_css_before = ax_heading->FirstChild();
+  ASSERT_NE(nullptr, ax_css_before);
+  ASSERT_EQ(ax::mojom::Role::kImage, ax_css_before->RoleValue());
+
+  const auto ax_position_before =
+      AXPosition::CreateFirstPositionInObject(*ax_css_before);
+  const auto position = ax_position_before.ToPositionWithAffinity(
+      AXPositionAdjustmentBehavior::kMoveLeft);
+  EXPECT_EQ(GetDocument().body(), position.AnchorNode());
+  EXPECT_EQ(3, position.GetPosition().OffsetInContainerNode());
+}
+
 TEST_F(AccessibilityTest, PositionInTableWithCSSContent) {
   SetBodyInnerHTML(kHTMLTable);
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index ffc1af79..34d27b1 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -474,7 +474,6 @@
   // SetDrawsContent() and SetContentsVisible().
   contents_layer_->SetIsDrawable(contents_visible_);
   contents_layer_->SetHitTestable(contents_visible_);
-  contents_layer_->SetMaskLayer(nullptr);
   contents_layer_->Set3dSortingContextId(rendering_context3d_);
 }
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py b/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py
index fb2dc36..ff9d78c1 100644
--- a/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py
+++ b/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py
@@ -260,7 +260,8 @@
 
     def _collect_tests(self, args):
         return self._finder.find_tests(args, test_list=self._options.test_list,
-                                       fastest_percentile=self._options.fastest)
+                                       fastest_percentile=self._options.fastest,
+                                       filters=self._options.isolated_script_test_filter)
 
     def _is_http_test(self, test):
         return (
diff --git a/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_finder.py b/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_finder.py
index 3a2cdbc..f27532f 100644
--- a/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_finder.py
+++ b/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_finder.py
@@ -27,6 +27,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 import errno
+import fnmatch
 import json
 import logging
 import math
@@ -47,7 +48,8 @@
         self._filesystem = self._port.host.filesystem
         self.WEB_TESTS_DIRECTORIES = ('src', 'third_party', 'blink', 'web_tests')
 
-    def find_tests(self, args, test_list=None, fastest_percentile=None):
+    def find_tests(self, args, test_list=None, fastest_percentile=None, filters=None):
+        filters = filters or []
         paths = self._strip_test_dir_prefixes(args)
         if test_list:
             paths += self._strip_test_dir_prefixes(self._read_test_names_from_file(test_list, self._port.TEST_PATH_SEPARATOR))
@@ -77,6 +79,7 @@
             test_files = all_tests
             running_all_tests = True
 
+        test_files = filter_tests(test_files, [f.split('::') for f in filters])
         return (paths, test_files, running_all_tests)
 
     def _times_trie(self):
@@ -213,3 +216,57 @@
                    index, count, len(tests_to_run), len(test_names))
 
         return tests_to_run, other_tests
+
+
+def filter_tests(tests, filters):
+    """Returns a filtered list of tests to run.
+
+    The test-filtering semantics are documented in
+    https://bit.ly/chromium-test-runner-api and
+    https://bit.ly/chromium-test-list-format, but are as follows:
+
+    Each filter is a list of glob expressions, with each expression optionally
+    prefixed by a "-". If the glob starts with a "-", it is a negative glob,
+    otherwise it is a positive glob.
+
+    A test passes the filter if and only if it is explicitly matched by at
+    least one positive glob and no negative globs, or if there are no
+    positive globs and it is not matched by any negative globs.
+
+    Globbing is fairly limited; "?" is not allowed, and "*" must only appear
+    at the end of the glob. If multiple globs match a test, the longest match
+    wins. If both globs are the same length, an error is raised.
+
+    A test will be run only if it passes every filter.
+    """
+
+    def glob_sort_key(k):
+        if k and k[0] == '-':
+            return (len(k[1:]), k[1:])
+        else:
+            return (len(k), k)
+
+    for globs in filters:
+        include_by_default = all(glob.startswith('-') for glob in globs)
+        filtered_tests = []
+        for test in tests:
+            include = include_by_default
+            for glob in sorted(globs, key=glob_sort_key):
+                if not glob[:-1]:
+                    raise ValueError('Empty glob filter "%s"' % (filter,))
+                if '*' in glob[:-1]:
+                    raise ValueError('Bad test filter "%s" specified; '
+                                     'wildcards are only allowed at the end'
+                                     % (glob,))
+                if glob.startswith('-') and glob[1:] in globs:
+                    raise ValueError('Both "%s" and "%s" specified in test '
+                                     'filter' % (glob, glob[1:]))
+                if glob.startswith('-'):
+                    include = include and not fnmatch.fnmatch(test, glob[1:])
+                else:
+                    include = include or fnmatch.fnmatch(test, glob)
+            if include:
+                filtered_tests.append(test)
+        tests = filtered_tests
+
+    return tests
diff --git a/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_finder_unittest.py b/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_finder_unittest.py
index cb4de53..af79323 100644
--- a/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_finder_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_finder_unittest.py
@@ -128,3 +128,79 @@
           self.assertEqual(([3, 6], [1, 2, 4, 5]), split(tests, 0, 3))
           self.assertEqual(([1, 4], [2, 3, 5, 6]), split(tests, 1, 3))
           self.assertEqual(([2, 5], [1, 3, 4, 6]), split(tests, 2, 3))
+
+
+class FilterTestsTests(unittest.TestCase):
+    simple_test_list = ['a/a1.html', 'a/a2.html', 'b/b1.html']
+
+    def check(self, tests, filters, expected_tests):
+        self.assertEqual(expected_tests,
+                         web_test_finder.filter_tests(tests, filters))
+
+    def test_no_filters(self):
+        self.check(self.simple_test_list, [],
+                   self.simple_test_list)
+
+    def test_empty_glob_is_rejected(self):
+        self.assertRaises(ValueError, self.check,
+                          self.simple_test_list, [['']], [])
+        self.assertRaises(ValueError, self.check,
+                          self.simple_test_list, [['-']], [])
+
+    def test_one_all_positive_filter(self):
+        self.check(self.simple_test_list, [['a*']],
+                   ['a/a1.html', 'a/a2.html'])
+
+        self.check(self.simple_test_list, [['a*', 'b*']],
+                   self.simple_test_list)
+
+    def test_one_all_negative_filter(self):
+        self.check(self.simple_test_list, [['-c*']],
+                   self.simple_test_list)
+
+    def test_one_mixed_filter(self):
+        self.check(self.simple_test_list, [['a*', '-c*']],
+                   ['a/a1.html', 'a/a2.html'])
+
+    def test_two_all_positive_filters(self):
+        self.check(self.simple_test_list, [['a*'], ['b*']],
+                   [])
+
+    def test_two_all_negative_filters(self):
+        self.check(self.simple_test_list, [['-a*'], ['-b*']],
+                   [])
+
+        self.check(self.simple_test_list, [['-a*'], ['-c*']],
+                   ['b/b1.html'])
+
+    def test_two_mixed_filters(self):
+        self.check(self.simple_test_list, [['a*'], ['-b*']],
+                   ['a/a1.html', 'a/a2.html'])
+
+    def test_longest_glob_wins(self):
+        # These test that if two matching globs are specified as
+        # part of the same filter expression, the longest matching
+        # glob wins (takes precedence). The order of the two globs
+        # must not matter.
+        self.check(self.simple_test_list, [['a/a*', '-a/a2*']],
+                   ['a/a1.html'])
+        self.check(self.simple_test_list, [['-a/a*', 'a/a2*']],
+                   ['a/a2.html'])
+
+        # In this test, the positive and negative globs are in
+        # separate filter expressions, so a2 should be filtered out
+        # and nothing should run (tests should only be run if they
+        # would be run by every filter individually).
+        self.check(self.simple_test_list, [['-a/a*'], ['a/a2*']],
+                   [])
+
+    def test_only_trailing_globs_work(self):
+        self.check(self.simple_test_list, [['a*']],
+                                           ['a/a1.html', 'a/a2.html'])
+
+        # These test that if you have a glob that contains a "*" that isn't
+        # at the end, it is rejected; only globs at the end should work.
+        self.assertRaises(ValueError, self.check,
+                          self.simple_test_list, [['*1.html']], [])
+        self.assertRaises(ValueError, self.check,
+                          self.simple_test_list, [['a*.html']], [])
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py b/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py
index b221564..beef9c82 100644
--- a/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py
+++ b/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py
@@ -438,9 +438,10 @@
                 help='read list of tests to run from file'),
             optparse.make_option(
                 '--isolated-script-test-filter',
+                action='append',
                 type='string',
-                help='A list of tests to run separated by TWO colons, e.g. fast::css/test.html, '
-                     'same as listing them as positional arguments'),
+                help='A list of test globs to run or skip, separated by TWO colons, e.g. fast::css/test.html; '
+                     'prefix the glob with "-" to skip it'),
             # TODO(crbug.com/893235): Remove gtest_filter when FindIt no longer uses it.
             optparse.make_option(
                 '--gtest_filter',
@@ -594,9 +595,6 @@
     if not options.skipped:
         options.skipped = 'default'
 
-    if options.isolated_script_test_filter:
-        args.extend(options.isolated_script_test_filter.split('::'))
-
     if options.gtest_filter:
         args.extend(options.gtest_filter.split(':'))
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_web_tests_unittest.py b/third_party/blink/tools/blinkpy/web_tests/run_web_tests_unittest.py
index 5e779d1..2244f962 100644
--- a/third_party/blink/tools/blinkpy/web_tests/run_web_tests_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/run_web_tests_unittest.py
@@ -519,7 +519,26 @@
             ['--isolated-script-test-filter=passes/text.html::passes/image.html', 'passes/error.html'],
             host=host
         )
-        self.assertEqual(sorted(tests_run), ['passes/error.html', 'passes/image.html', 'passes/text.html'])
+        self.assertEqual(sorted(tests_run), [])
+
+        tests_run = get_tests_run(
+            ['--isolated-script-test-filter=passes/error.html::passes/image.html', 'passes/error.html'],
+            host=host
+        )
+        self.assertEqual(sorted(tests_run), ['passes/error.html'])
+
+        tests_run = get_tests_run(
+            ['--isolated-script-test-filter=passes/error.html::passes/image.html'],
+            host=host
+        )
+        self.assertEqual(sorted(tests_run), ['passes/error.html', 'passes/image.html'])
+
+        tests_run = get_tests_run(
+            ['--isolated-script-test-filter=passes/error.html::passes/image.html',
+             '--isolated-script-test-filter=passes/error.html'],
+            host=host
+        )
+        self.assertEqual(sorted(tests_run), ['passes/error.html'])
 
     def test_gtest_filter(self):
         host = MockHost()
diff --git a/third_party/blink/web_tests/FlagExpectations/use-vulkan=native b/third_party/blink/web_tests/FlagExpectations/use-vulkan=native
index ab7813b..4d61824 100644
--- a/third_party/blink/web_tests/FlagExpectations/use-vulkan=native
+++ b/third_party/blink/web_tests/FlagExpectations/use-vulkan=native
@@ -1,52 +1,3 @@
-# Native Failures
-# Backing Failure
-crbug.com/978628 media/remoteplayback/prompt-twice-throws.html [ Skip ]
-crbug.com/978628 media/video-controls-fullscreen.html [ Skip ]
-crbug.com/978628 media/audio-delete-while-slider-thumb-clicked.html [ Skip ]
-crbug.com/978628 media/video-controls-overflow-menu-fullscreen-button.html [ Skip ]
-crbug.com/978628 media/video-persistence.html [ Skip ]
-crbug.com/978628 media/video-src-blob.html [ Skip ]
-
-# Input timing doesn't match screenshots
-crbug.com/935970 compositing/gestures/gesture-tapHighlight-img-and-text.html [ Skip ]
-crbug.com/935970 compositing/gestures/gesture-tapHighlight-multicol.html [ Skip ]
-crbug.com/935970 compositing/gestures/gesture-tapHighlight-nested-cursor.html [ Skip ]
-crbug.com/935970 compositing/gestures/gesture-tapHighlight-nested-cursor3.html [ Skip ]
-crbug.com/935970 compositing/gestures/gesture-tapHighlight-simple-scaled-document.html [ Skip ]
-crbug.com/935970 compositing/gestures/gesture-tapHighlight-with-squashing.html [ Skip ]
-crbug.com/935970 compositing/iframes/layout-on-compositing-change.html [ Skip ]
-crbug.com/935970 compositing/squashing/squash-compositing-hover.html [ Skip ]
-crbug.com/935970 compositing/squashing/squash-transform-repainting-child.html [ Skip ]
-crbug.com/935970 compositing/squashing/squash-transform-repainting-transformed-child.html [ Skip ]
-crbug.com/935970 css3/viewport-percentage-lengths/vh-resize.html [ Skip ]
-crbug.com/935970 images/drag-image-2.html [ Skip ]
-crbug.com/935970 images/drag-image-descendant-iframe-composited.html [ Skip ]
-crbug.com/935970 images/drag-image-descendant-painting-sibling.html [ Skip ]
-crbug.com/935970 images/drag-image-transformed-child.html [ Skip ]
-crbug.com/935970 images/drag-image-transformed-parent.html [ Skip ]
-crbug.com/935970 images/drag-image.html [ Skip ]
-crbug.com/935970 images/huge-image-viewport-scale.html [ Skip ]
-crbug.com/935970 images/image-map-multiple-xhtml.xhtml [ Skip ]
-crbug.com/935970 images/image-map-multiple.html [ Skip ]
-crbug.com/935970 images/server-side-imagemap.html [ Skip ]
-crbug.com/935970 media/controls-drag-timebar.html [ Skip ]
-crbug.com/935970 media/controls-timeline.html [ Skip ]
-crbug.com/935970 media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Skip ]
-crbug.com/935970 media/controls/video-enter-exit-fullscreen-without-hovering-doesnt-show-controls.html [ Skip ]
-crbug.com/935970 media/media-controls-tap-show-controls-without-activating.html [ Skip ]
-crbug.com/935970 media/video-controls-always-visible-when-control-hovered.html [ Skip ]
-crbug.com/935970 media/video-controls-auto-hide-after-play-by-touch.html [ Skip ]
-crbug.com/935970 media/video-controls-hide-after-touch-on-control.html [ Skip ]
-crbug.com/935970 media/video-controls-hide-on-move-outside-controls.html [ Skip ]
-crbug.com/935970 media/video-controls-mouse-events-captured.html [ Skip ]
-crbug.com/935970 media/video-controls-overflow-menu-closed-captions-button.html [ Skip ]
-crbug.com/935970 media/video-controls-overflow-menu-mute-button.html [ Skip ]
-crbug.com/935970 media/video-controls-overflow-menu-play-button.html [ Skip ]
-crbug.com/935970 media/video-controls-transformed.html [ Skip ]
-crbug.com/935970 media/video-controls-visibility-multimodal-mouse-after-touch.html [ Skip ]
-crbug.com/935970 media/video-controls-visibility-multimodal-touch-after-mouse.html [ Skip ]
-crbug.com/935970 media/video-controls-visible-audio-only.html [ Skip ]
-
 # Need a Vulkan native specific baseline.
 crbug.com/993384 compositing/geometry/vertical-scroll-composited.html [ Skip ]
 crbug.com/993384 compositing/lots-of-img-layers.html [ Skip ]
diff --git a/third_party/blink/web_tests/FlagExpectations/use-vulkan=swiftshader b/third_party/blink/web_tests/FlagExpectations/use-vulkan=swiftshader
index cd3775b..294938b 100644
--- a/third_party/blink/web_tests/FlagExpectations/use-vulkan=swiftshader
+++ b/third_party/blink/web_tests/FlagExpectations/use-vulkan=swiftshader
@@ -1,16 +1,3 @@
-# SwiftShader Unimplemented Blend Mode
-crbug.com/972096 compositing/reflections/animation-inside-reflection.html [ Skip ]
-crbug.com/972096 compositing/reflections/nested-reflection-anchor-point.html [ Skip ]
-crbug.com/972096 compositing/reflections/nested-reflection-animated.html [ Skip ]
-crbug.com/972096 compositing/reflections/nested-reflection-on-overflow.html [ Skip ]
-crbug.com/972096 compositing/reflections/nested-reflection-opacity.html [ Skip ]
-crbug.com/972096 compositing/reflections/nested-reflection-transformed.html [ Skip ]
-crbug.com/972096 compositing/reflections/nested-reflection-transformed2.html [ Skip ]
-crbug.com/972096 compositing/reflections/nested-reflection.html [ Skip ]
-crbug.com/972096 compositing/reflections/transform-inside-reflection.html [ Skip ]
-crbug.com/972096 css3/filters/composited-reflected.html [ Skip ]
-crbug.com/972096 media/controls/video-enter-exit-fullscreen-without-hovering-doesnt-show-controls.html [ Skip ]
-
 # Test consistently hitting default timeout.
 Bug(none) css3/filters/effect-reference-subregion.html [ Skip ]
 
diff --git a/third_party/blink/web_tests/animations/interpolation/perspective-interpolation.html b/third_party/blink/web_tests/animations/interpolation/perspective-interpolation.html
deleted file mode 100644
index 53ec74ed..0000000
--- a/third_party/blink/web_tests/animations/interpolation/perspective-interpolation.html
+++ /dev/null
@@ -1,97 +0,0 @@
-<!DOCTYPE html>
-<meta charset="UTF-8">
-<style>
-.parent {
-  perspective: 30px;
-}
-.target {
-  display: inline-block;
-  margin-top: 50px;
-  margin-bottom: 25px;
-  perspective: 10px;
-}
-.transformed {
-  width: 50px;
-  height: 50px;
-  background: black;
-  transform: rotateY(45deg);
-}
-.expected .transformed {
-  background: green;
-}
-.expected {
-  position: relative;
-  left: -50px;
-  opacity: 0.75;
-}
-</style>
-<body>
-<template id="target-template">
-<div><div class="transformed"></div></div>
-</template>
-<script src="resources/interpolation-test.js"></script>
-<script>
-assertInterpolation({
-  property: 'perspective',
-  from: neutralKeyframe,
-  to: '20px',
-}, [
-  {at: -20, is: 'none'},
-  {at: -1, is: 'none'},
-  {at: -0.3, is: '7px'},
-  {at: 0, is: '10px'},
-  {at: 0.3, is: '13px'},
-  {at: 0.6, is: '16px'},
-  {at: 1, is: '20px'},
-  {at: 1.5, is: '25px'},
-]);
-
-assertNoInterpolation({
-  property: 'perspective',
-  from: 'initial',
-  to: '20px',
-});
-
-assertInterpolation({
-  property: 'perspective',
-  from: 'inherit',
-  to: '20px',
-}, [
-  {at: -20, is: '230px'},
-  {at: -1, is: '40px'},
-  {at: -0.3, is: '33px'},
-  {at: 0, is: '30px'},
-  {at: 0.3, is: '27px'},
-  {at: 0.6, is: '24px'},
-  {at: 1, is: '20px'},
-  {at: 1.5, is: '15px'},
-]);
-
-assertNoInterpolation({
-  property: 'perspective',
-  from: 'unset',
-  to: '20px',
-});
-
-assertInterpolation({
-  property: 'perspective',
-  from: '50px',
-  to: '100px',
-}, [
-  {at: -20, is: 'none'}, // perspective does not accept 0 or negative values
-  {at: -1, is: 'none'}, // perspective does not accept 0 or negative values
-  {at: -0.3, is: '35px'},
-  {at: 0, is: '50px'},
-  {at: 0.3, is: '65px'},
-  {at: 0.6, is: '80px'},
-  {at: 1, is: '100px'},
-  {at: 1.5, is: '125px'},
-]);
-
-assertNoInterpolation({
-  property: 'perspective',
-  from: '50px',
-  to: 'none',
-});
-</script>
-</body>
diff --git a/third_party/blink/web_tests/animations/interpolation/perspective-origin-interpolation.html b/third_party/blink/web_tests/animations/interpolation/perspective-origin-interpolation.html
index 7ff11af1..d5021aed 100644
--- a/third_party/blink/web_tests/animations/interpolation/perspective-origin-interpolation.html
+++ b/third_party/blink/web_tests/animations/interpolation/perspective-origin-interpolation.html
@@ -33,71 +33,6 @@
 </template>
 <script src="resources/interpolation-test.js"></script>
 <script>
-assertInterpolation({
-  property: 'perspective-origin',
-  from: neutralKeyframe,
-  to: '20px 20px',
-}, [
-  {at: -0.3, is: '7px 33px'},
-  {at: 0, is: '10px 30px'},
-  {at: 0.3, is: '13px 27px'},
-  {at: 0.6, is: '16px 24px'},
-  {at: 1, is: '20px 20px'},
-  {at: 1.5, is: '25px 15px'},
-]);
-
-assertInterpolation({
-  property: 'perspective-origin',
-  from: 'initial',
-  to: '20px 20px',
-}, [
-  {at: -0.3, is: '26.5px 26.5px'},
-  {at: 0, is: '25px 25px'},
-  {at: 0.3, is: '23.5px 23.5px'},
-  {at: 0.6, is: '22px 22px'},
-  {at: 1, is: '20px 20px'},
-  {at: 1.5, is: '17.5px 17.5px'},
-]);
-
-assertInterpolation({
-  property: 'perspective-origin',
-  from: 'inherit',
-  to: '20px 20px',
-}, [
-  {at: -0.3, is: '33px 7px'},
-  {at: 0, is: '30px 10px'},
-  {at: 0.3, is: '27px 13px'},
-  {at: 0.6, is: '24px 16px'},
-  {at: 1, is: '20px 20px'},
-  {at: 1.5, is: '15px 25px'},
-]);
-
-assertInterpolation({
-  property: 'perspective-origin',
-  from: 'unset',
-  to: '20px 20px',
-}, [
-  {at: -0.3, is: '26.5px 26.5px'},
-  {at: 0, is: '25px 25px'},
-  {at: 0.3, is: '23.5px 23.5px'},
-  {at: 0.6, is: '22px 22px'},
-  {at: 1, is: '20px 20px'},
-  {at: 1.5, is: '17.5px 17.5px'},
-]);
-
-assertInterpolation({
-  property: 'perspective-origin',
-  from: '0% 50%',
-  to: '100% 150%'
-}, [
-  {at: -0.3, is: '-30% 20%'},
-  {at: 0, is: '0% 50%'},
-  {at: 0.3, is: '30% 80%'},
-  {at: 0.6, is: '60% 110%'},
-  {at: 1, is: '100% 150%'},
-  {at: 1.5, is: '150% 200%'}
-]);
-
 // Regression test for https://crbug.com/984700
 assertInterpolation({
   property: 'perspective-origin',
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/positioning/relpos-percentage-left-in-scrollable-2.html b/third_party/blink/web_tests/external/wpt/css/CSS2/positioning/relpos-percentage-left-in-scrollable-2.html
new file mode 100644
index 0000000..0c7584d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/positioning/relpos-percentage-left-in-scrollable-2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/CSS22/visuren.html#propdef-left">
+<link rel="help" href="https://www.w3.org/TR/CSS22/visuren.html#relative-positioning">
+<link rel="help" href="https://www.w3.org/TR/CSS22/visuren.html#anonymous-block-level">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1002485">
+<p>There should be no red, and no scrollbar.</p>
+<div id="container" style="overflow:auto; width:500px; background:red;">
+  <div style="padding-right:90%; background:yellow;">
+    <div style="position:relative; left:900%; background:cyan;">
+      <div></div>
+      &nbsp;
+    </div>
+  </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  container.scrollLeft = 123456;
+  test(()=> {
+      assert_equals(container.scrollLeft, 0);
+  }, "Left percentage resolved correctly for overflow contribution");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/perspective-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/perspective-interpolation.html
new file mode 100644
index 0000000..a27f84a1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/perspective-interpolation.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title> perspective interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#propdef-perspective">
+<meta name="assert" content="perspective supports animation">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  perspective: 30px;
+}
+.target {
+  perspective: 10px;
+}
+.transformed {
+  width: 50px;
+  height: 50px;
+  background: black;
+  transform: rotateY(45deg);
+}
+.expected .transformed {
+  background: green;
+}
+.expected {
+  position: relative;
+  left: -50px;
+  opacity: 0.75;
+}
+</style>
+<body>
+<template id="target-template">
+<div><div class="transformed"></div></div>
+</template>
+<script>
+test_interpolation({
+  property: 'perspective',
+  from: neutralKeyframe,
+  to: '20px',
+}, [
+  {at: -20, expect: 'none'},
+  {at: -1, expect: 'none'},
+  {at: -0.3, expect: '7px'},
+  {at: 0, expect: '10px'},
+  {at: 0.3, expect: '13px'},
+  {at: 0.6, expect: '16px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '25px'},
+]);
+
+test_no_interpolation({
+  property: 'perspective',
+  from: 'initial',
+  to: '20px',
+});
+
+test_interpolation({
+  property: 'perspective',
+  from: 'inherit',
+  to: '20px',
+}, [
+  {at: -20, expect: '230px'},
+  {at: -1, expect: '40px'},
+  {at: -0.3, expect: '33px'},
+  {at: 0, expect: '30px'},
+  {at: 0.3, expect: '27px'},
+  {at: 0.6, expect: '24px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '15px'},
+]);
+
+test_no_interpolation({
+  property: 'perspective',
+  from: 'unset',
+  to: '20px',
+});
+
+test_interpolation({
+  property: 'perspective',
+  from: '50px',
+  to: '100px',
+}, [
+  {at: -20, expect: 'none'}, // perspective does not accept 0 or negative values
+  {at: -1, expect: 'none'}, // perspective does not accept 0 or negative values
+  {at: -0.3, expect: '35px'},
+  {at: 0, expect: '50px'},
+  {at: 0.3, expect: '65px'},
+  {at: 0.6, expect: '80px'},
+  {at: 1, expect: '100px'},
+  {at: 1.5, expect: '125px'},
+]);
+
+test_no_interpolation({
+  property: 'perspective',
+  from: '50px',
+  to: 'none',
+});
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/perspective-origin-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/perspective-origin-interpolation.html
new file mode 100644
index 0000000..0a1e74cb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/perspective-origin-interpolation.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>perspective-origin interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#perspective-origin-property">
+<meta name="assert" content="perspective-origin supports animation">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  perspective-origin: 30px 10px;
+}
+.target {
+  display: inline-block;
+  perspective: 50;
+  margin-top: 50px;
+  margin-bottom: 25px;
+  perspective-origin: 10px 30px;
+}
+.transformed {
+  width: 50px;
+  height: 50px;
+  background: black;
+  transform: rotateY(45deg);
+}
+.expected .transformed {
+  background: green;
+}
+.expected {
+  position: relative;
+  left: -50px;
+  opacity: 0.75;
+}
+</style>
+<body>
+<template id="target-template">
+<div><div class="transformed"></div></div>
+</template>
+<script>
+test_interpolation({
+  property: 'perspective-origin',
+  from: neutralKeyframe,
+  to: '20px 20px',
+}, [
+  {at: -0.3, expect: '7px 33px'},
+  {at: 0, expect: '10px 30px'},
+  {at: 0.3, expect: '13px 27px'},
+  {at: 0.6, expect: '16px 24px'},
+  {at: 1, expect: '20px 20px'},
+  {at: 1.5, expect: '25px 15px'},
+]);
+
+test_interpolation({
+  property: 'perspective-origin',
+  from: 'initial',
+  to: '20px 20px',
+}, [
+  {at: -0.3, expect: '26.5px 26.5px'},
+  {at: 0, expect: '25px 25px'},
+  {at: 0.3, expect: '23.5px 23.5px'},
+  {at: 0.6, expect: '22px 22px'},
+  {at: 1, expect: '20px 20px'},
+  {at: 1.5, expect: '17.5px 17.5px'},
+]);
+
+test_interpolation({
+  property: 'perspective-origin',
+  from: 'inherit',
+  to: '20px 20px',
+}, [
+  {at: -0.3, expect: '33px 7px'},
+  {at: 0, expect: '30px 10px'},
+  {at: 0.3, expect: '27px 13px'},
+  {at: 0.6, expect: '24px 16px'},
+  {at: 1, expect: '20px 20px'},
+  {at: 1.5, expect: '15px 25px'},
+]);
+
+test_interpolation({
+  property: 'perspective-origin',
+  from: 'unset',
+  to: '20px 20px',
+}, [
+  {at: -0.3, expect: '26.5px 26.5px'},
+  {at: 0, expect: '25px 25px'},
+  {at: 0.3, expect: '23.5px 23.5px'},
+  {at: 0.6, expect: '22px 22px'},
+  {at: 1, expect: '20px 20px'},
+  {at: 1.5, expect: '17.5px 17.5px'},
+]);
+
+test_interpolation({
+  property: 'perspective-origin',
+  from: '0% 50%',
+  to: '100% 150%'
+}, [
+  {at: -0.3, expect: '-30% 20%'},
+  {at: 0, expect: '0% 50%'},
+  {at: 0.3, expect: '30% 80%'},
+  {at: 0.6, expect: '60% 110%'},
+  {at: 1, expect: '100% 150%'},
+  {at: 1.5, expect: '150% 200%'}
+]);
+</script>
+</body>
diff --git a/third_party/blink/web_tests/fast/canvas/canvas-drawImage-video-reset-expected.png b/third_party/blink/web_tests/fast/canvas/canvas-drawImage-video-reset-expected.png
new file mode 100644
index 0000000..50e6f75
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/canvas-drawImage-video-reset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/canvas/canvas-drawImage-video-reset.html b/third_party/blink/web_tests/fast/canvas/canvas-drawImage-video-reset.html
new file mode 100644
index 0000000..6abd6a3
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/canvas-drawImage-video-reset.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+  <title>Ensure correct behavior of drawImage video elements.</title>
+  <style type="text/css">
+  video {
+    display: none;
+  }
+  </style>
+</head>
+<body>
+  <canvas id="canvas"></canvas>
+  <video id="video">
+    <source src="resources/canvas_video.webm" type='video/webm' />
+    <source src="resources/canvas_video.mp4"  type='video/mp4' />
+    <source src="resources/canvas_video.ogv"  type='video/ogg' />
+  </video>
+  <script>
+  var length = 150;
+  var canvas = document.getElementById("canvas");
+  canvas.setAttribute("width", length);
+  canvas.setAttribute("height", length);
+  var ctx = canvas.getContext("2d");
+
+  var video = document.getElementById("video");
+  video.addEventListener("playing", drawImageToCanvas, true);
+  video.play();
+
+  function drawImageToCanvas() {
+    video.removeEventListener("playing", drawImageToCanvas, true);
+    ctx.fillStyle = "blue";
+    ctx.fillRect(0, 0, length, length);
+    ctx.drawImage(video, 0, 0);
+    ctx.globalAlpha = 0.5;
+    ctx.drawImage(video, 0, 60);
+    video.srcObject = null;
+    if (window.testRunner)
+      testRunner.notifyDone();
+  }
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/platform/linux/fast/canvas/canvas-drawImage-video-reset-expected.png b/third_party/blink/web_tests/platform/linux/fast/canvas/canvas-drawImage-video-reset-expected.png
new file mode 100644
index 0000000..a58254b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/fast/canvas/canvas-drawImage-video-reset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/canvas-pattern-video-reset-expected.png b/third_party/blink/web_tests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/canvas-pattern-video-reset-expected.png
new file mode 100644
index 0000000..71553edf
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/canvas-pattern-video-reset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-drawImage-video-reset-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-drawImage-video-reset-expected.png
new file mode 100644
index 0000000..a58254b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-drawImage-video-reset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/canvas/canvas-drawImage-video-reset-expected.png b/third_party/blink/web_tests/platform/mac/fast/canvas/canvas-drawImage-video-reset-expected.png
new file mode 100644
index 0000000..0232744
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/fast/canvas/canvas-drawImage-video-reset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-drawImage-video-reset-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-drawImage-video-reset-expected.png
new file mode 100644
index 0000000..0232744
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-drawImage-video-reset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu/fast/canvas/canvas-drawImage-video-reset-expected.png b/third_party/blink/web_tests/virtual/gpu/fast/canvas/canvas-drawImage-video-reset-expected.png
new file mode 100644
index 0000000..d67d3cb
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/gpu/fast/canvas/canvas-drawImage-video-reset-expected.png
Binary files differ
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 110c766..cb923505 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-10-1-48-g99f23d6ff
-Revision: 99f23d6ff2203966d210bccd49eacc62a20328f9
+Version: VER-2-10-1-49-g04ebb2a00
+Revision: 04ebb2a000ee40df2a9900198ec62d79af745b1f
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
 License File: src/docs/FTL.TXT
diff --git a/third_party/protobuf/OWNERS b/third_party/protobuf/OWNERS
index 3c525a7..979e0de 100644
--- a/third_party/protobuf/OWNERS
+++ b/third_party/protobuf/OWNERS
@@ -1,5 +1,6 @@
 ajwong@chromium.org
 lgrey@chromium.org
+nyquist@chromium.org
 pkasting@chromium.org
 
 # COMPONENT: Internals
diff --git a/tools/android/avd/avd.py b/tools/android/avd/avd.py
old mode 100644
new mode 100755
index 7405601..c574e0eb
--- a/tools/android/avd/avd.py
+++ b/tools/android/avd/avd.py
@@ -1,12 +1,16 @@
+#! /usr/bin/env vpython
 # Copyright 2019 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 argparse
 import contextlib
+import json
 import logging
 import os
 import socket
 import stat
+import subprocess
 import sys
 import threading
 
@@ -23,9 +27,21 @@
 sys.path.append(
     os.path.join(_SRC_ROOT, 'third_party', 'catapult', 'devil'))
 from devil.android.sdk import adb_wrapper
+from devil.android.tools import script_common
 from devil.utils import cmd_helper
+from devil.utils import logging_common
 from devil.utils import timeout_retry
 
+sys.path.append(
+    os.path.join(_SRC_ROOT, 'build', 'android'))
+import devil_chromium
+
+
+_ALL_PACKAGES = object()
+_DEFAULT_AVDMANAGER_PATH = os.path.join(
+    _SRC_ROOT, 'third_party', 'android_sdk', 'public',
+    'tools', 'bin', 'avdmanager')
+
 
 class AvdException(Exception):
   """Raised when this module has a problem interacting with an AVD."""
@@ -58,6 +74,92 @@
     return text_format.Merge(avd_proto_file.read(), avd_pb2.Avd())
 
 
+class _AvdManagerAgent(object):
+  """Private utility for interacting with avdmanager."""
+
+  def __init__(self, avd_home, sdk_root):
+    """Create an _AvdManagerAgent.
+
+    Args:
+      avd_home: path to ANDROID_AVD_HOME directory.
+        Typically something like /path/to/dir/.android/avd
+      sdk_root: path to SDK root directory.
+    """
+    self._avd_home = avd_home
+    self._sdk_root = sdk_root
+
+    self._env = dict(os.environ)
+
+    # avdmanager, like many tools that have evolved from `android`
+    # (http://bit.ly/2m9JiTx), uses toolsdir to find the SDK root.
+    # Pass avdmanager a fake directory under the directory in which
+    # we install the system images s.t. avdmanager can find the
+    # system images.
+    fake_tools_dir = os.path.join(
+        self._sdk_root,
+        'non-existent-tools')
+    self._env.update({
+        'ANDROID_AVD_HOME': self._avd_home,
+        'AVDMANAGER_OPTS':
+            '-Dcom.android.sdkmanager.toolsdir=%s' % fake_tools_dir,
+    })
+
+  def Create(self, avd_name, system_image, force=False):
+    """Call `avdmanager create`.
+
+    Args:
+      avd_name: name of the AVD to create.
+      system_image: system image to use for the AVD.
+      force: whether to force creation, overwriting any existing
+        AVD with the same name.
+    """
+    create_cmd = [
+        _DEFAULT_AVDMANAGER_PATH,
+        '-v',
+        'create',
+        'avd',
+        '-n', avd_name,
+        '-k', system_image,
+    ]
+    if force:
+      create_cmd += ['--force']
+
+    create_proc = cmd_helper.Popen(
+        create_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE, env=self._env)
+    output, error = create_proc.communicate(input='\n')
+    if create_proc.returncode != 0:
+      raise AvdException(
+          'AVD creation failed',
+          command=create_cmd,
+          stdout=output,
+          stderr=error)
+
+    for line in output.splitlines():
+      logging.info('  %s', line)
+
+  def Delete(self, avd_name):
+    """Call `avdmanager delete`.
+
+    Args:
+      avd_name: name of the AVD to delete.
+    """
+    delete_cmd = [
+        _DEFAULT_AVDMANAGER_PATH,
+        '-v',
+        'delete',
+        'avd',
+        '-n', avd_name,
+    ]
+    try:
+      for line in cmd_helper.IterCmdOutputLines(delete_cmd, env=self._env):
+        logging.info('  %s', line)
+    except subprocess.CalledProcessError as e:
+      raise AvdException(
+          'AVD deletion failed: %s' % str(e),
+          command=delete_cmd)
+
+
 class AvdConfig(object):
   """Represents a particular AVD configuration.
 
@@ -84,16 +186,118 @@
     self._initialized = False
     self._initializer_lock = threading.Lock()
 
-  def Install(self):
+  def Create(self, force=False, snapshot=False, keep=False,
+             cipd_json_output=None):
+    """Create an instance of the AVD CIPD package.
+
+    This method:
+     - installs the requisite system image
+     - creates the AVD
+     - modifies the AVD's ini files to support running chromium tests
+       in chromium infrastructure
+     - optionally starts & stops the AVD for snapshotting (default no)
+     - creates and uploads an instance of the AVD CIPD package
+     - optionally deletes the AVD (default yes)
+
+    Args:
+      force: bool indicating whether to force create the AVD.
+      snapshot: bool indicating whether to snapshot the AVD before creating
+        the CIPD package.
+      keep: bool indicating whether to keep the AVD after creating
+        the CIPD package.
+      cipd_json_output: string path to pass to `cipd create` via -json-output.
+    """
+    logging.info('Installing required packages.')
+    self.Install(packages=[self._config.system_image_package])
+
+    android_avd_home = os.path.join(self._emulator_home, 'avd')
+
+    if not os.path.exists(android_avd_home):
+      os.makedirs(android_avd_home)
+
+    avd_manager = _AvdManagerAgent(
+        avd_home=android_avd_home,
+        sdk_root=self._emulator_sdk_root)
+
+    logging.info('Creating AVD.')
+    avd_manager.Create(
+        avd_name=self._config.avd_name,
+        system_image=self._config.system_image_name,
+        force=force)
+
+    try:
+      logging.info('Modifying AVD configuration.')
+
+      root_ini = os.path.join(
+          android_avd_home, '%s.ini' % self._config.avd_name)
+      avd_dir = os.path.join(
+          android_avd_home, '%s.avd' % self._config.avd_name)
+      config_ini = os.path.join(avd_dir, 'config.ini')
+
+      with open(root_ini, 'a') as root_ini_file:
+        root_ini_file.write('path.rel=avd/%s.avd\n' % self._config.avd_name)
+
+      with open(config_ini, 'a') as config_ini_file:
+        config_ini_file.write('disk.dataPartition.size=4G\n')
+
+      # Start & stop the AVD.
+      if snapshot:
+        # TODO(crbug.com/922145): Implement support for snapshotting.
+        raise NotImplementedError('Snapshotting is not supported yet.')
+
+      package_def_content = {
+          'package': self._config.avd_package.package_name,
+          'root': self._emulator_home,
+          'install_mode': 'copy',
+          'data': [
+              {'dir': os.path.relpath(avd_dir, self._emulator_home)},
+              {'file': os.path.relpath(root_ini, self._emulator_home)},
+          ],
+      }
+
+      logging.info('Creating AVD CIPD package.')
+      logging.debug('ensure file content: %s',
+                    json.dumps(package_def_content, indent=2))
+
+      with tempfile_ext.TemporaryFileName(suffix='.json') as package_def_path:
+        with open(package_def_path, 'w') as package_def_file:
+          json.dump(package_def_content, package_def_file)
+
+        logging.info('  %s', self._config.avd_package.package_name)
+        cipd_create_cmd = [
+            'cipd', 'create', '-pkg-def', package_def_path,
+        ]
+        if cipd_json_output:
+          cipd_create_cmd.extend([
+              '-json-output', cipd_json_output,
+          ])
+        try:
+          for line in cmd_helper.IterCmdOutputLines(cipd_create_cmd):
+            logging.info('    %s', line)
+        except subprocess.CalledProcessError as e:
+          raise AvdException(
+              'CIPD package creation failed: %s' % str(e),
+              command=cipd_create_cmd)
+
+    finally:
+      if not keep:
+        logging.info('Deleting AVD.')
+        avd_manager.Delete(avd_name=self._config.avd_name)
+
+  def Install(self, packages=_ALL_PACKAGES):
     """Installs the requested CIPD packages.
 
     Returns: None
     Raises: AvdException on failure to install.
     """
     pkgs_by_dir = {}
-    for pkg in (self._config.emulator_package,
-                self._config.system_image_package,
-                self._config.avd_package):
+    if packages is _ALL_PACKAGES:
+      packages = [
+          self._config.avd_package,
+          self._config.emulator_package,
+          self._config.system_image_package,
+      ]
+    for pkg in packages:
       if not pkg.dest_path in pkgs_by_dir:
         pkgs_by_dir[pkg.dest_path] = []
       pkgs_by_dir[pkg.dest_path].append(pkg)
@@ -114,13 +318,14 @@
       ensure_cmd = [
           'cipd', 'ensure', '-ensure-file', ensure_path, '-root', cipd_root,
       ]
-      status, output, error = cmd_helper.GetCmdStatusOutputAndError(ensure_cmd)
-      if status:
+      try:
+        for line in cmd_helper.IterCmdOutputLines(ensure_cmd):
+          logging.info('    %s', line)
+      except subprocess.CalledProcessError as e:
         raise AvdException(
-            'Failed to install CIPD package %s' % pkg.package_name,
-            command=ensure_cmd,
-            stdout=output,
-            stderr=error)
+            'Failed to install CIPD package %s: %s' % (
+                pkg.package_name, str(e)),
+            command=ensure_cmd)
 
     # The emulator requires that some files are writable.
     for dirname, _, filenames in os.walk(self._emulator_home):
@@ -247,3 +452,72 @@
   @property
   def serial(self):
     return self._emulator_serial
+
+
+def main(raw_args):
+
+  parser = argparse.ArgumentParser()
+
+  def add_common_arguments(parser):
+    logging_common.AddLoggingArguments(parser)
+    script_common.AddEnvironmentArguments(parser)
+    parser.add_argument(
+        '--avd-config',
+        type=os.path.realpath,
+        metavar='PATH',
+        required=True,
+        help='Path to an AVD config text protobuf.')
+
+  subparsers = parser.add_subparsers()
+  install_parser = subparsers.add_parser(
+      'install',
+      help='Install the CIPD packages specified in the given config.')
+  add_common_arguments(install_parser)
+
+  def install_cmd(args):
+    AvdConfig(args.avd_config).Install()
+    return 0
+
+  install_parser.set_defaults(func=install_cmd)
+
+  create_parser = subparsers.add_parser(
+      'create',
+      help='Create an AVD CIPD package according to the given config.')
+  add_common_arguments(create_parser)
+  create_parser.add_argument(
+      '--snapshot',
+      action='store_true',
+      help='Snapshot the AVD before creating the CIPD package.')
+  create_parser.add_argument(
+      '--force',
+      action='store_true',
+      help='Pass --force to AVD creation.')
+  create_parser.add_argument(
+      '--keep',
+      action='store_true',
+      help='Keep the AVD after creating the CIPD package.')
+  create_parser.add_argument(
+      '--cipd-json-output',
+      type=os.path.realpath,
+      metavar='PATH',
+      help='Path to which `cipd create` should dump json output '
+           'via -json-output.')
+
+  def create_cmd(args):
+    AvdConfig(args.avd_config).Create(
+        force=args.force, snapshot=args.snapshot, keep=args.keep,
+        cipd_json_output=args.cipd_json_output)
+    return 0
+
+  create_parser.set_defaults(func=create_cmd)
+
+  # TODO(jbudorick): Expose `start` as a subcommand.
+
+  args = parser.parse_args(raw_args)
+  logging_common.InitializeLogging(args)
+  devil_chromium.Initialize(adb_path=args.adb_path)
+  return args.func(args)
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/tools/android/avd/avd.pydeps b/tools/android/avd/avd.pydeps
index c9fd880..3aa0c24 100644
--- a/tools/android/avd/avd.pydeps
+++ b/tools/android/avd/avd.pydeps
@@ -1,5 +1,9 @@
 # Generated by running:
 #   build/print_python_deps.py --root tools/android/avd --output tools/android/avd/avd.pydeps tools/android/avd/avd.py
+../../../build/android/devil_chromium.py
+../../../build/android/pylib/__init__.py
+../../../build/android/pylib/constants/__init__.py
+../../../build/android/pylib/constants/host_paths.py
 ../../../third_party/catapult/common/py_utils/py_utils/__init__.py
 ../../../third_party/catapult/common/py_utils/py_utils/cloud_storage.py
 ../../../third_party/catapult/common/py_utils/py_utils/cloud_storage_global_lock.py
@@ -17,20 +21,50 @@
 ../../../third_party/catapult/dependency_manager/dependency_manager/uploader.py
 ../../../third_party/catapult/devil/devil/__init__.py
 ../../../third_party/catapult/devil/devil/android/__init__.py
+../../../third_party/catapult/devil/devil/android/apk_helper.py
+../../../third_party/catapult/devil/devil/android/constants/__init__.py
+../../../third_party/catapult/devil/devil/android/constants/chrome.py
+../../../third_party/catapult/devil/devil/android/constants/file_system.py
 ../../../third_party/catapult/devil/devil/android/decorators.py
+../../../third_party/catapult/devil/devil/android/device_blacklist.py
 ../../../third_party/catapult/devil/devil/android/device_errors.py
+../../../third_party/catapult/devil/devil/android/device_signal.py
+../../../third_party/catapult/devil/devil/android/device_temp_file.py
+../../../third_party/catapult/devil/devil/android/device_utils.py
+../../../third_party/catapult/devil/devil/android/install_commands.py
+../../../third_party/catapult/devil/devil/android/logcat_monitor.py
+../../../third_party/catapult/devil/devil/android/md5sum.py
+../../../third_party/catapult/devil/devil/android/ndk/__init__.py
+../../../third_party/catapult/devil/devil/android/ndk/abis.py
 ../../../third_party/catapult/devil/devil/android/sdk/__init__.py
+../../../third_party/catapult/devil/devil/android/sdk/aapt.py
 ../../../third_party/catapult/devil/devil/android/sdk/adb_wrapper.py
+../../../third_party/catapult/devil/devil/android/sdk/build_tools.py
+../../../third_party/catapult/devil/devil/android/sdk/bundletool.py
+../../../third_party/catapult/devil/devil/android/sdk/intent.py
+../../../third_party/catapult/devil/devil/android/sdk/keyevent.py
+../../../third_party/catapult/devil/devil/android/sdk/split_select.py
+../../../third_party/catapult/devil/devil/android/sdk/version_codes.py
+../../../third_party/catapult/devil/devil/android/tools/__init__.py
+../../../third_party/catapult/devil/devil/android/tools/script_common.py
 ../../../third_party/catapult/devil/devil/base_error.py
+../../../third_party/catapult/devil/devil/constants/__init__.py
+../../../third_party/catapult/devil/devil/constants/exit_codes.py
 ../../../third_party/catapult/devil/devil/devil_env.py
 ../../../third_party/catapult/devil/devil/utils/__init__.py
 ../../../third_party/catapult/devil/devil/utils/cmd_helper.py
+../../../third_party/catapult/devil/devil/utils/host_utils.py
 ../../../third_party/catapult/devil/devil/utils/lazy/__init__.py
 ../../../third_party/catapult/devil/devil/utils/lazy/weak_constant.py
+../../../third_party/catapult/devil/devil/utils/logging_common.py
+../../../third_party/catapult/devil/devil/utils/lsusb.py
 ../../../third_party/catapult/devil/devil/utils/parallelizer.py
 ../../../third_party/catapult/devil/devil/utils/reraiser_thread.py
+../../../third_party/catapult/devil/devil/utils/reset_usb.py
+../../../third_party/catapult/devil/devil/utils/run_tests_helper.py
 ../../../third_party/catapult/devil/devil/utils/timeout_retry.py
 ../../../third_party/catapult/devil/devil/utils/watchdog_timer.py
+../../../third_party/catapult/devil/devil/utils/zip_utils.py
 ../../../third_party/catapult/third_party/zipfile/zipfile_2_7_13.py
 avd.py
 proto/__init__.py
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 083e5ec..5d41172 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -440,6 +440,7 @@
       'Linux Builder': 'gpu_tests_release_bot',
       'fuchsia-arm64-cast': 'release_bot_fuchsia_arm64_cast',
       'fuchsia-x64-cast': 'release_bot_fuchsia_cast',
+      'fuchsia-x64-dbg': 'debug_bot_fuchsia_compile_only',
       'linux-gcc-rel': 'release_bot_x86_minimal_symbols_no_clang_cxx11',
       # linux-jumbo-rel is identical to linux-rel for perf comparisons, except
       # for the jumbo part.
@@ -756,6 +757,7 @@
       'closure_compilation': 'closure_compilation',
       'fuchsia_arm64': 'release_trybot_fuchsia_arm64',
       'fuchsia-arm64-cast': 'release_trybot_fuchsia_arm64_cast',
+      'fuchsia-compile-x64-dbg': 'debug_trybot_fuchsia_compile_only',
       'fuchsia-fyi-arm64-rel': 'release_trybot_fuchsia_arm64',
       'fuchsia-fyi-x64-dbg': 'debug_trybot_fuchsia',
       'fuchsia-fyi-x64-rel': 'release_trybot_fuchsia',
@@ -1475,6 +1477,10 @@
       'debug_bot', 'fuchsia',
     ],
 
+    'debug_bot_fuchsia_compile_only': [
+      'debug_bot', 'fuchsia', 'compile_only',
+    ],
+
     'debug_bot_x86': [
       'debug_bot', 'x86',
     ],
@@ -1503,6 +1509,10 @@
       'debug_trybot', 'fuchsia',
     ],
 
+    'debug_trybot_fuchsia_compile_only': [
+      'debug_trybot', 'fuchsia', 'compile_only',
+    ],
+
     'debug_trybot_x86': [
       'debug_trybot', 'x86',
     ],
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 42a42420..01907ac2 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -17119,6 +17119,16 @@
   <int value="1" label="Cound not bind to domain controller."/>
 </enum>
 
+<enum name="EnterpriseCloudReportingResponse">
+  <summary>Result of uploading an enterprise report.</summary>
+  <int value="0" label="Success"/>
+  <int value="1" label="Network error"/>
+  <int value="2" label="Temporary server error"/>
+  <int value="3" label="DDS concurrency error"/>
+  <int value="4" label="Request too large error"/>
+  <int value="5" label="Other error"/>
+</enum>
+
 <enum name="EnterpriseDeviceManagementStatus">
   <summary>
     Status codes produced by DeviceManagementService for requests made to the
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 60d54dc..0115272 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -12880,7 +12880,20 @@
     enum="BooleanSuccess" expires_after="2020-08-01">
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
-  <summary>Records the completion status of a probe when it completes.</summary>
+  <summary>
+    Records the completion status of a probe when it completes each attempt.
+  </summary>
+</histogram>
+
+<histogram base="true" name="Availability.Prober.FinalState"
+    enum="BooleanSuccess" expires_after="2020-08-01">
+  <owner>robertogden@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
+  <summary>
+    Records the end state of a probe just before it goes inactive. This happens
+    when the prober succeeds, fails and has no more retries, or the delegate
+    stops probing.
+  </summary>
 </histogram>
 
 <histogram base="true" name="Availability.Prober.NetError" enum="NetErrorCodes"
@@ -35923,6 +35936,16 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.CloudReportingResponse"
+    enum="EnterpriseCloudReportingResponse" expires_after="M83">
+  <owner>zmin@chromium.org</owner>
+  <summary>
+    The upload result for each cloud reporting request. Note that there may be
+    multiple requests per report. Also, one request may creates multiple data
+    point due to retry.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.DevicePolicyInvalidations"
     enum="EnterprisePolicyInvalidations">
   <owner>bartfab@chromium.org</owner>
@@ -49232,7 +49255,7 @@
 </histogram>
 
 <histogram name="GoogleUpdate.Inline.AppUpdateInfo.InstallStatus"
-    enum="InlineUpdateInstallStatus" expires_after="2019-10-30">
+    enum="InlineUpdateInstallStatus" expires_after="2020-02-15">
   <owner>dtrainor@chromium.org</owner>
   <owner>nyquist@chromium.org</owner>
   <summary>
@@ -49242,7 +49265,7 @@
 </histogram>
 
 <histogram name="GoogleUpdate.Inline.AppUpdateInfo.UpdateAvailability"
-    enum="InlineUpdateAvailability" expires_after="2019-10-30">
+    enum="InlineUpdateAvailability" expires_after="2020-02-15">
   <owner>dtrainor@chromium.org</owner>
   <owner>nyquist@chromium.org</owner>
   <summary>
@@ -49252,7 +49275,7 @@
 </histogram>
 
 <histogram name="GoogleUpdate.Inline.CallFailure"
-    enum="InlineUpdateCallFailure" expires_after="2019-10-30">
+    enum="InlineUpdateCallFailure" expires_after="2020-02-15">
   <owner>dtrainor@chromium.org</owner>
   <owner>nyquist@chromium.org</owner>
   <summary>
@@ -49261,7 +49284,7 @@
 </histogram>
 
 <histogram name="GoogleUpdate.Inline.StateChange.Error"
-    enum="InlineUpdateErrorCodes" expires_after="2019-10-30">
+    enum="InlineUpdateErrorCodes" expires_after="2020-02-15">
 <!-- Name completed by histogram_suffixes name="GoogleUpdate.Inline.InstallStatus" -->
 
   <owner>dtrainor@chromium.org</owner>
@@ -49274,7 +49297,7 @@
 </histogram>
 
 <histogram name="GoogleUpdate.Inline.UI.Install.Source"
-    enum="UpdateInteractionSource" expires_after="2019-10-30">
+    enum="UpdateInteractionSource" expires_after="2020-02-15">
   <owner>dtrainor@chromium.org</owner>
   <owner>nyquist@chromium.org</owner>
   <summary>
@@ -49284,7 +49307,7 @@
 </histogram>
 
 <histogram name="GoogleUpdate.Inline.UI.Retry.Source"
-    enum="UpdateInteractionSource" expires_after="2019-10-30">
+    enum="UpdateInteractionSource" expires_after="2020-02-15">
   <owner>dtrainor@chromium.org</owner>
   <owner>nyquist@chromium.org</owner>
   <summary>
@@ -49293,7 +49316,7 @@
 </histogram>
 
 <histogram name="GoogleUpdate.Inline.UI.Start.Source"
-    enum="UpdateInteractionSource" expires_after="2019-10-30">
+    enum="UpdateInteractionSource" expires_after="2020-02-15">
   <owner>dtrainor@chromium.org</owner>
   <owner>nyquist@chromium.org</owner>
   <summary>
@@ -49388,7 +49411,7 @@
 </histogram>
 
 <histogram name="GoogleUpdate.StartUp.State" enum="UpdateState"
-    expires_after="2019-10-30">
+    expires_after="2020-02-15">
   <owner>dtrainor@chromium.org</owner>
   <owner>nyquist@chromium.org</owner>
   <summary>
@@ -69331,6 +69354,19 @@
   </summary>
 </histogram>
 
+<histogram name="Mojo.Connector.MaxUnreadMessageQuotaUsed" units="messages"
+    expires_after="2020-10-01">
+  <owner>siggi@chromium.org</owner>
+  <owner>rockot@chromium.org</owner>
+  <summary>
+    The maximal unread message quota used for the lifetime of a Connector. This
+    is sampled for a configurable percentage of Connectors only when the feature
+    MojoRecordUnreadMessageCount is enabled. By default 1% of Connectors are
+    sampled, as there's some overhead involved in enabling the unread message
+    quota on a MessagePipe. See //mojo/public/cpp/bindings/lib/connector.cc.
+  </summary>
+</histogram>
+
 <histogram name="Mojo.MachPortRelay.BrokerError"
     enum="MojoMachPortRelayBrokerError" expires_after="M77">
   <obsolete>
@@ -162909,6 +162945,7 @@
       label="Origin check for Litepage previews"/>
   <affected-histogram name="Availability.Prober.CacheEntryAge"/>
   <affected-histogram name="Availability.Prober.DidSucceed"/>
+  <affected-histogram name="Availability.Prober.FinalState"/>
   <affected-histogram name="Availability.Prober.NetError"/>
   <affected-histogram name="Availability.Prober.NumAttemptsBeforeSuccess"/>
   <affected-histogram name="Availability.Prober.ResponseCode"/>
diff --git a/tools/perf/process_perf_results.py b/tools/perf/process_perf_results.py
index 0fb87dd..1b660dd 100755
--- a/tools/perf/process_perf_results.py
+++ b/tools/perf/process_perf_results.py
@@ -56,6 +56,7 @@
   'angle_perftests',
   'cc_perftests',
   'gpu_perftests',
+  'latency_perftests',
   'media_perftests',
   'views_perftests',
   'xr.vr.common_perftests',
diff --git a/tools/traffic_annotation/scripts/extractor.py b/tools/traffic_annotation/scripts/extractor.py
index b2572af..5c45726 100755
--- a/tools/traffic_annotation/scripts/extractor.py
+++ b/tools/traffic_annotation/scripts/extractor.py
@@ -247,6 +247,11 @@
   for annotation in annotation_definitions:
     print(annotation.clang_tool_output_string())
 
+  # If all files were successfully checked for annotations but none of them had
+  # any, print something so that the traffic_annotation_auditor knows there was
+  # no error so that the files get checked for deleted annotations.
+  if not annotation_definitions:
+      print('No annotations in these files.')
   return 0
 
 
diff --git a/ui/accessibility/ax_node.h b/ui/accessibility/ax_node.h
index 25ec866..86d35fe 100644
--- a/ui/accessibility/ax_node.h
+++ b/ui/accessibility/ax_node.h
@@ -16,6 +16,7 @@
 #include "build/build_config.h"
 #include "ui/accessibility/ax_export.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_tree_id.h"
 
 namespace ui {
 
@@ -49,6 +50,8 @@
     };
 
     // See AXTree.
+    virtual AXTreeID GetAXTreeID() const = 0;
+    // See AXTree.
     virtual AXTableInfo* GetTableInfo(const AXNode* table_node) const = 0;
     // See AXTree.
     virtual AXNode* GetFromId(int32_t id) const = 0;
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index 0b2945b6..30db03f 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -670,17 +670,12 @@
     return CreateNextAnchorPosition()->IsNullPosition() && AtEndOfAnchor();
   }
 
-  // This method returns a position instead of a node because this allows us to
-  // return the corresponding text offset or child index in the ancestor that
-  // relates to the current position.
-  // Also, this method uses position instead of tree logic to traverse the tree,
-  // because positions can handle moving across multiple trees, while trees
-  // cannot.
-  AXPositionInstance LowestCommonAncestor(const AXPosition& second) const {
+  // This method finds the lowest common AXNodeType of |this| and |second|.
+  AXNodeType* LowestCommonAnchor(const AXPosition& second) const {
     if (IsNullPosition() || second.IsNullPosition())
-      return CreateNullPosition();
+      return nullptr;
     if (GetAnchor() == second.GetAnchor())
-      return Clone();
+      return GetAnchor();
 
     base::stack<AXNodeType*> our_ancestors = GetAncestorAnchors();
     base::stack<AXNodeType*> other_ancestors = second.GetAncestorAnchors();
@@ -692,6 +687,17 @@
       our_ancestors.pop();
       other_ancestors.pop();
     }
+    return common_anchor;
+  }
+
+  // This method returns a position instead of a node because this allows us to
+  // return the corresponding text offset or child index in the ancestor that
+  // relates to the current position.
+  // Also, this method uses position instead of tree logic to traverse the tree,
+  // because positions can handle moving across multiple trees, while trees
+  // cannot.
+  AXPositionInstance LowestCommonAncestor(const AXPosition& second) const {
+    AXNodeType* common_anchor = LowestCommonAnchor(second);
     if (!common_anchor)
       return CreateNullPosition();
 
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index 3eac95a..fbfca41b 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -578,6 +578,10 @@
   observers_.RemoveObserver(observer);
 }
 
+AXTreeID AXTree::GetAXTreeID() const {
+  return data().tree_id;
+}
+
 AXNode* AXTree::GetFromId(int32_t id) const {
   auto iter = id_map_.find(id);
   return iter != id_map_.end() ? iter->second : nullptr;
diff --git a/ui/accessibility/ax_tree.h b/ui/accessibility/ax_tree.h
index 9e048cb..a706758 100644
--- a/ui/accessibility/ax_tree.h
+++ b/ui/accessibility/ax_tree.h
@@ -55,6 +55,10 @@
   const AXTreeData& data() const { return data_; }
 
   // AXNode::OwnerTree override.
+  // Returns the globally unique ID of this accessibility tree.
+  AXTreeID GetAXTreeID() const override;
+
+  // AXNode::OwnerTree override.
   // Returns the AXNode with the given |id| if it is part of this AXTree.
   AXNode* GetFromId(int32_t id) const override;
 
diff --git a/ui/accessibility/platform/ax_fragment_root_win.h b/ui/accessibility/platform/ax_fragment_root_win.h
index c71af51..61743958 100644
--- a/ui/accessibility/platform/ax_fragment_root_win.h
+++ b/ui/accessibility/platform/ax_fragment_root_win.h
@@ -39,7 +39,7 @@
       gfx::AcceleratedWidget widget);
 
   // Returns the NativeViewAccessible for this fragment root.
-  gfx::NativeViewAccessible GetNativeViewAccessible();
+  gfx::NativeViewAccessible GetNativeViewAccessible() override;
 
   // Assistive technologies will typically use UI Automation's control or
   // content view rather than the raw view.
diff --git a/ui/accessibility/platform/ax_fragment_root_win_unittest.cc b/ui/accessibility/platform/ax_fragment_root_win_unittest.cc
index 304c2e4..87f2774a 100644
--- a/ui/accessibility/platform/ax_fragment_root_win_unittest.cc
+++ b/ui/accessibility/platform/ax_fragment_root_win_unittest.cc
@@ -5,10 +5,12 @@
 #include "ui/accessibility/platform/ax_fragment_root_win.h"
 #include "ui/accessibility/platform/ax_platform_node_win.h"
 #include "ui/accessibility/platform/ax_platform_node_win_unittest.h"
+#include "ui/accessibility/platform/test_ax_node_wrapper.h"
 
 #include <UIAutomationClient.h>
 #include <UIAutomationCoreApi.h>
 
+#include "base/auto_reset.h"
 #include "base/win/scoped_safearray.h"
 #include "base/win/scoped_variant.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -77,6 +79,13 @@
   EXPECT_HRESULT_SUCCEEDED(fragment_root_prov->ElementProviderFromPoint(
       47, 67, &provider_from_point));
   EXPECT_EQ(root_provider.Get(), provider_from_point.Get());
+
+  // This is on node 1 with scale factor of 1.5.
+  std::unique_ptr<base::AutoReset<float>> scale_factor_reset =
+      TestAXNodeWrapper::SetScaleFactor(1.5);
+  EXPECT_HRESULT_SUCCEEDED(fragment_root_prov->ElementProviderFromPoint(
+      60, 60, &provider_from_point));
+  EXPECT_EQ(element1_provider.Get(), provider_from_point.Get());
 }
 
 TEST_F(AXFragmentRootTest, TestUIAGetFocus) {
diff --git a/ui/accessibility/platform/ax_platform_node_delegate.h b/ui/accessibility/platform/ax_platform_node_delegate.h
index dd264149..88d346ff7 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate.h
@@ -77,6 +77,10 @@
   // method is only meaningful on macOS.
   virtual gfx::NativeViewAccessible GetNSWindow() = 0;
 
+  // Get the node for this delegate, which may be an AXPlatformNode or it may
+  // be a native accessible object implemented by another class.
+  virtual gfx::NativeViewAccessible GetNativeViewAccessible() = 0;
+
   // Get the parent of the node, which may be an AXPlatformNode or it may
   // be a native accessible object implemented by another class.
   virtual gfx::NativeViewAccessible GetParent() = 0;
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.cc b/ui/accessibility/platform/ax_platform_node_delegate_base.cc
index a07e5c145..6c420da 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_delegate_base.cc
@@ -44,6 +44,11 @@
   return nullptr;
 }
 
+gfx::NativeViewAccessible
+AXPlatformNodeDelegateBase::GetNativeViewAccessible() {
+  return nullptr;
+}
+
 gfx::NativeViewAccessible AXPlatformNodeDelegateBase::GetParent() {
   return nullptr;
 }
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.h b/ui/accessibility/platform/ax_platform_node_delegate_base.h
index 6fdcd60..6f2c452a 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate_base.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate_base.h
@@ -44,6 +44,10 @@
   // See comments in AXPlatformNodeDelegate.
   gfx::NativeViewAccessible GetNSWindow() override;
 
+  // Get the node for this delegate, which may be an AXPlatformNode or it may
+  // be a native accessible object implemented by another class.
+  gfx::NativeViewAccessible GetNativeViewAccessible() override;
+
   // Get the parent of the node, which may be an AXPlatformNode or it may
   // be a native accessible object implemented by another class.
   gfx::NativeViewAccessible GetParent() override;
diff --git a/ui/accessibility/platform/ax_platform_node_textchildprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textchildprovider_win_unittest.cc
index b8825da..ac7518e 100644
--- a/ui/accessibility/platform/ax_platform_node_textchildprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textchildprovider_win_unittest.cc
@@ -71,6 +71,8 @@
 
     AXNode* root_node = GetRootNode();
     AXNodePosition::SetTree(tree_.get());
+    AXTreeManagerMap::GetInstance().AddTreeManager(update.tree_data.tree_id,
+                                                   this);
     AXNode* nontext_child_of_root_node = root_node->children()[0];
     AXNode* text_child_of_root_node = root_node->children()[1];
     AXNode* nontext_child_of_nontext_node =
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index 55a42f7..89bfc03 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -453,17 +453,22 @@
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TEXTRANGE_GETENCLOSINGELEMENT);
   UIA_VALIDATE_TEXTRANGEPROVIDER_CALL();
 
-  AXPositionInstance common_ancestor = start_->LowestCommonAncestor(*end_);
-  AXPlatformNode* node = GetDelegate(common_ancestor.get())
-                             ->GetFromNodeID(common_ancestor->anchor_id());
-  DCHECK(node);
-  while (ui::IsIgnored(node->GetDelegate()->GetData())) {
-    node = static_cast<AXPlatformNodeWin*>(
-        AXPlatformNode::FromNativeViewAccessible(
-            node->GetDelegate()->GetParent()));
-    DCHECK(node);
+  AXNode* common_anchor = start_->LowestCommonAnchor(*end_);
+  DCHECK(common_anchor);
+  if (!common_anchor)
+    return UIA_E_ELEMENTNOTAVAILABLE;
+
+  const AXTreeID tree_id = common_anchor->tree()->GetAXTreeID();
+  const AXNode::AXID node_id = common_anchor->id();
+  AXPlatformNodeDelegate* delegate = GetDelegate(tree_id, node_id);
+  DCHECK(delegate);
+  while (ui::IsIgnored(delegate->GetData())) {
+    AXPlatformNodeWin* parent = static_cast<AXPlatformNodeWin*>(
+        AXPlatformNode::FromNativeViewAccessible(delegate->GetParent()));
+    DCHECK(parent);
+    delegate = parent->GetDelegate();
   }
-  node->GetNativeViewAccessible()->QueryInterface(IID_PPV_ARGS(element));
+  delegate->GetNativeViewAccessible()->QueryInterface(IID_PPV_ARGS(element));
 
   DCHECK(*element);
   return S_OK;
@@ -812,11 +817,15 @@
 
 AXPlatformNodeDelegate* AXPlatformNodeTextRangeProviderWin::GetDelegate(
     const AXPositionInstanceType* position) const {
-  AXTreeManager* manager =
-      AXTreeManagerMap::GetInstance().GetManager(position->tree_id());
-  return manager
-             ? manager->GetDelegate(position->tree_id(), position->anchor_id())
-             : owner()->GetDelegate();
+  return GetDelegate(position->tree_id(), position->anchor_id());
+}
+
+AXPlatformNodeDelegate* AXPlatformNodeTextRangeProviderWin::GetDelegate(
+    const AXTreeID tree_id,
+    const AXNode::AXID node_id) const {
+  AXTreeManager* manager = AXTreeManagerMap::GetInstance().GetManager(tree_id);
+  return manager ? manager->GetDelegate(tree_id, node_id)
+                 : owner()->GetDelegate();
 }
 
 AXPlatformNodeTextRangeProviderWin::AXPositionInstance
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
index 6a3ff0d..1ddc770 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
@@ -92,6 +92,8 @@
   AXPlatformNodeWin* owner() const;
   AXPlatformNodeDelegate* GetDelegate(
       const AXPositionInstanceType* position) const;
+  AXPlatformNodeDelegate* GetDelegate(const AXTreeID tree_id,
+                                      const AXNode::AXID node_id) const;
 
   template <typename AnchorIterator, typename ExpandMatchLambda>
   HRESULT FindAttributeRange(const TEXTATTRIBUTEID text_attribute_id,
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index 0e89882..2316b6ac7 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -948,8 +948,11 @@
 
 TEST_F(AXPlatformNodeTextRangeProviderTest,
        TestITextRangeProviderExpandToEnclosingCharacter) {
-  Init(BuildTextDocument({"some text", "more text"}));
+  ui::AXTreeUpdate update = BuildTextDocument({"some text", "more text"});
+  Init(update);
   AXNodePosition::SetTree(tree_.get());
+  AXTreeManagerMap::GetInstance().AddTreeManager(update.tree_data.tree_id,
+                                                 this);
   AXNode* root_node = GetRootNode();
 
   ComPtr<ITextRangeProvider> text_range_provider;
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index a79d784a..3730d65 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -9,6 +9,7 @@
 
 #include <memory>
 
+#include "base/auto_reset.h"
 #include "base/stl_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/win/atl.h"
@@ -445,7 +446,7 @@
 TEST_F(AXPlatformNodeWinTest, TestIAccessibleHitTest) {
   AXNodeData root;
   root.id = 1;
-  root.relative_bounds.bounds = gfx::RectF(0, 0, 30, 30);
+  root.relative_bounds.bounds = gfx::RectF(0, 0, 40, 40);
 
   AXNodeData node1;
   node1.id = 2;
@@ -455,7 +456,7 @@
 
   AXNodeData node2;
   node2.id = 3;
-  node2.relative_bounds.bounds = gfx::RectF(20, 20, 10, 10);
+  node2.relative_bounds.bounds = gfx::RectF(20, 20, 20, 20);
   node2.SetName("Name2");
   root.child_ids.push_back(node2.id);
 
@@ -463,17 +464,23 @@
 
   ComPtr<IAccessible> root_obj(GetRootIAccessible());
 
-  ScopedVariant obj;
+  // This is way outside of the root node.
+  ScopedVariant obj_1;
+  EXPECT_EQ(S_FALSE, root_obj->accHitTest(50, 50, obj_1.Receive()));
+  EXPECT_EQ(VT_EMPTY, obj_1.type());
 
-  // This is way outside of the root node
-  EXPECT_EQ(S_FALSE, root_obj->accHitTest(50, 50, obj.Receive()));
-  EXPECT_EQ(VT_EMPTY, obj.type());
+  // This is directly on node 1.
+  EXPECT_EQ(S_OK, root_obj->accHitTest(5, 5, obj_1.Receive()));
+  ASSERT_NE(nullptr, obj_1.ptr());
+  CheckVariantHasName(obj_1, L"Name1");
 
-  // this is directly on node 1.
-  EXPECT_EQ(S_OK, root_obj->accHitTest(5, 5, obj.Receive()));
-  ASSERT_NE(nullptr, obj.ptr());
-
-  CheckVariantHasName(obj, L"Name1");
+  // This is directly on node 2 with a scale factor of 1.5.
+  ScopedVariant obj_2;
+  std::unique_ptr<base::AutoReset<float>> scale_factor_reset =
+      TestAXNodeWrapper::SetScaleFactor(1.5);
+  EXPECT_EQ(S_OK, root_obj->accHitTest(38, 38, obj_2.Receive()));
+  ASSERT_NE(nullptr, obj_2.ptr());
+  CheckVariantHasName(obj_2, L"Name2");
 }
 
 TEST_F(AXPlatformNodeWinTest, TestIAccessibleName) {
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.cc b/ui/accessibility/platform/test_ax_node_wrapper.cc
index 764241fb..2b289f04 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.cc
+++ b/ui/accessibility/platform/test_ax_node_wrapper.cc
@@ -26,6 +26,9 @@
 // A global coordinate offset.
 gfx::Vector2d g_offset;
 
+// A global scale factor.
+float g_scale_factor = 1.0;
+
 // A global map that stores which node is focused on a determined tree.
 //   - If a tree has no node being focused, there shouldn't be any entry on the
 //     map associated with such tree, i.e. a pair {tree, nullptr} is invalid.
@@ -88,6 +91,12 @@
   return g_node_from_last_default_action;
 }
 
+// static
+std::unique_ptr<base::AutoReset<float>> TestAXNodeWrapper::SetScaleFactor(
+    float value) {
+  return std::make_unique<base::AutoReset<float>>(&g_scale_factor, value);
+}
+
 TestAXNodeWrapper::~TestAXNodeWrapper() {
   platform_node_->Destroy();
 }
@@ -111,6 +120,10 @@
       ax::mojom::TextAffinity::kDownstream);
 }
 
+gfx::NativeViewAccessible TestAXNodeWrapper::GetNativeViewAccessible() {
+  return ax_platform_node()->GetNativeViewAccessible();
+}
+
 gfx::NativeViewAccessible TestAXNodeWrapper::GetParent() {
   TestAXNodeWrapper* parent_wrapper =
       GetOrCreate(tree_, node_->GetUnignoredParent());
@@ -224,7 +237,8 @@
 }
 
 gfx::NativeViewAccessible TestAXNodeWrapper::HitTestSync(int x, int y) {
-  TestAXNodeWrapper* wrapper = HitTestSyncInternal(x, y);
+  TestAXNodeWrapper* wrapper =
+      HitTestSyncInternal(x / g_scale_factor, y / g_scale_factor);
   return wrapper ? wrapper->ax_platform_node()->GetNativeViewAccessible()
                  : nullptr;
 }
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.h b/ui/accessibility/platform/test_ax_node_wrapper.h
index eff3778..e3fc5ae 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.h
+++ b/ui/accessibility/platform/test_ax_node_wrapper.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/auto_reset.h"
 #include "build/build_config.h"
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_tree.h"
@@ -40,6 +41,9 @@
   // called from for testing.
   static const AXNode* GetNodeFromLastDefaultAction();
 
+  // Set a global scale factor for testing.
+  static std::unique_ptr<base::AutoReset<float>> SetScaleFactor(float value);
+
   ~TestAXNodeWrapper() override;
 
   AXPlatformNode* ax_platform_node() const { return platform_node_; }
@@ -55,6 +59,7 @@
   const AXTree::Selection GetUnignoredSelection() const override;
   AXNodePosition::AXPositionInstance CreateTextPositionAt(
       int offset) const override;
+  gfx::NativeViewAccessible GetNativeViewAccessible() override;
   gfx::NativeViewAccessible GetParent() override;
   int GetChildCount() override;
   gfx::NativeViewAccessible ChildAtIndex(int index) override;
diff --git a/ui/base/cocoa/menu_controller.h b/ui/base/cocoa/menu_controller.h
index 69bea16..72f990a8 100644
--- a/ui/base/cocoa/menu_controller.h
+++ b/ui/base/cocoa/menu_controller.h
@@ -25,9 +25,6 @@
 @interface MenuControllerCocoa
     : NSObject<NSMenuDelegate, NSUserInterfaceValidations>
 
-// The Model passed in to -initWithModel:.
-@property(nonatomic, assign) ui::MenuModel* model;
-
 // Whether to activate selected menu items via a posted task. This may allow the
 // selection to be handled earlier, whilst the menu is fading out. If the posted
 // task wasn't processed by the time the action is normally sent, it will be
@@ -54,6 +51,9 @@
 // Programmatically close the constructed menu.
 - (void)cancel;
 
+- (ui::MenuModel*)model;
+- (void)setModel:(ui::MenuModel*)model;
+
 // Access to the constructed menu if the complex initializer was used. If the
 // default initializer was used, then this will create the menu on first call.
 - (NSMenu*)menu;
diff --git a/ui/base/cocoa/menu_controller.mm b/ui/base/cocoa/menu_controller.mm
index 647c8bb2..4128846 100644
--- a/ui/base/cocoa/menu_controller.mm
+++ b/ui/base/cocoa/menu_controller.mm
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/cancelable_callback.h"
 #include "base/logging.h"
+#include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "ui/base/accelerators/accelerator.h"
@@ -44,6 +45,42 @@
 
 }  // namespace
 
+// This class stores a base::WeakPtr<ui::MenuModel> as an Objective-C object,
+// which allows it to be stored in the representedObject field of an NSMenuItem.
+@interface WeakPtrToMenuModelAsNSObject : NSObject
++ (instancetype)weakPtrForModel:(ui::MenuModel*)model;
++ (ui::MenuModel*)getFrom:(id)instance;
+- (instancetype)initWithModel:(ui::MenuModel*)model;
+- (ui::MenuModel*)menuModel;
+@end
+
+@implementation WeakPtrToMenuModelAsNSObject {
+  base::WeakPtr<ui::MenuModel> model_;
+}
+
++ (instancetype)weakPtrForModel:(ui::MenuModel*)model {
+  return
+      [[[WeakPtrToMenuModelAsNSObject alloc] initWithModel:model] autorelease];
+}
+
++ (ui::MenuModel*)getFrom:(id)instance {
+  return [base::mac::ObjCCastStrict<WeakPtrToMenuModelAsNSObject>(instance)
+      menuModel];
+}
+
+- (instancetype)initWithModel:(ui::MenuModel*)model {
+  if ((self = [super init])) {
+    model_ = model->AsWeakPtr();
+  }
+  return self;
+}
+
+- (ui::MenuModel*)menuModel {
+  return model_.get();
+}
+
+@end
+
 // Internal methods.
 @interface MenuControllerCocoa ()
 // Called before the menu is to be displayed to update the state (enabled,
@@ -83,7 +120,7 @@
 @end
 
 @implementation MenuControllerCocoa {
-  ui::MenuModel* model_;  // Weak.
+  base::WeakPtr<ui::MenuModel> model_;
   base::scoped_nsobject<NSMenu> menu_;
   BOOL useWithPopUpButtonCell_;  // If YES, 0th item is blank
   BOOL isMenuOpen_;
@@ -91,10 +128,17 @@
   std::unique_ptr<base::CancelableClosure> postedItemSelectedTask_;
 }
 
-@synthesize model = model_;
 @synthesize useWithPopUpButtonCell = useWithPopUpButtonCell_;
 @synthesize postItemSelectedAsTask = postItemSelectedAsTask_;
 
+- (ui::MenuModel*)model {
+  return model_.get();
+}
+
+- (void)setModel:(ui::MenuModel*)model {
+  model_ = model->AsWeakPtr();
+}
+
 - (instancetype)init {
   self = [super init];
   return self;
@@ -103,7 +147,7 @@
 - (instancetype)initWithModel:(ui::MenuModel*)model
        useWithPopUpButtonCell:(BOOL)useWithCell {
   if ((self = [super init])) {
-    model_ = model;
+    model_ = model->AsWeakPtr();
     useWithPopUpButtonCell_ = useWithCell;
     [self menu];
   }
@@ -117,14 +161,15 @@
   // while its context menu is still open.
   [self cancel];
 
-  model_ = NULL;
+  model_ = nullptr;
   [super dealloc];
 }
 
 - (void)cancel {
   if (isMenuOpen_) {
     [menu_ cancelTracking];
-    model_->MenuWillClose();
+    if (model_)
+      model_->MenuWillClose();
     isMenuOpen_ = NO;
   }
 }
@@ -190,8 +235,8 @@
     // in validation of the menu items.
     [item setTag:index];
     [item setTarget:self];
-    NSValue* modelObject = [NSValue valueWithPointer:model];
-    [item setRepresentedObject:modelObject];  // Retains |modelObject|.
+    [item setRepresentedObject:[WeakPtrToMenuModelAsNSObject
+                                   weakPtrForModel:model]];
     // On the Mac, context menus never have accelerators. Menus constructed
     // for context use have useWithPopUpButtonCell_ set to NO.
     if (useWithPopUpButtonCell_) {
@@ -216,8 +261,7 @@
 
   NSInteger modelIndex = [item tag];
   ui::MenuModel* model =
-      static_cast<ui::MenuModel*>(
-          [[(id)item representedObject] pointerValue]);
+      [WeakPtrToMenuModelAsNSObject getFrom:[(id)item representedObject]];
   DCHECK(model);
   if (model) {
     BOOL checked = model->IsItemCheckedAt(modelIndex);
@@ -303,8 +347,7 @@
 
   NSInteger modelIndex = [sender tag];
   ui::MenuModel* model =
-      static_cast<ui::MenuModel*>(
-          [[sender representedObject] pointerValue]);
+      [WeakPtrToMenuModelAsNSObject getFrom:[sender representedObject]];
   DCHECK(model);
   if (model)
     model->ActivatedAt(modelIndex, uiEventFlags);
@@ -313,7 +356,7 @@
 
 - (NSMenu*)menu {
   if (!menu_ && model_) {
-    menu_.reset([[self menuFromModel:model_] retain]);
+    menu_.reset([[self menuFromModel:model_.get()] retain]);
     [menu_ setDelegate:self];
     // If this is to be used with a NSPopUpButtonCell, add an item at the 0th
     // position that's empty. Doing it after the menu has been constructed won't
@@ -334,13 +377,15 @@
 
 - (void)menuWillOpen:(NSMenu*)menu {
   isMenuOpen_ = YES;
-  model_->MenuWillShow();  // Note: |model_| may trigger -[self dealloc].
+  if (model_)
+    model_->MenuWillShow();  // Note: |model_| may trigger -[self dealloc].
 }
 
 - (void)menuDidClose:(NSMenu*)menu {
   if (isMenuOpen_) {
     isMenuOpen_ = NO;
-    model_->MenuWillClose();  // Note: |model_| may trigger -[self dealloc].
+    if (model_)
+      model_->MenuWillClose();  // Note: |model_| may trigger -[self dealloc].
   }
 }
 
diff --git a/ui/base/cocoa/menu_controller_unittest.mm b/ui/base/cocoa/menu_controller_unittest.mm
index 7b30ecb..6ae968c14 100644
--- a/ui/base/cocoa/menu_controller_unittest.mm
+++ b/ui/base/cocoa/menu_controller_unittest.mm
@@ -281,7 +281,6 @@
   NSString* title = [itemTwo title];
   EXPECT_EQ(ASCIIToUTF16("three"), base::SysNSStringToUTF16(title));
   EXPECT_EQ(2, [itemTwo tag]);
-  EXPECT_EQ([[itemTwo representedObject] pointerValue], &model);
 
   EXPECT_TRUE([[[menu menu] itemAtIndex:3] isSeparatorItem]);
 }
@@ -315,7 +314,6 @@
   NSString* title = [submenuItem title];
   EXPECT_EQ(ASCIIToUTF16("sub-two"), base::SysNSStringToUTF16(title));
   EXPECT_EQ(1, [submenuItem tag]);
-  EXPECT_EQ([[submenuItem representedObject] pointerValue], &submodel);
 
   // Make sure the item after the submenu is correct and its represented
   // object is back to the top model.
@@ -323,7 +321,6 @@
   title = [item title];
   EXPECT_EQ(ASCIIToUTF16("three"), base::SysNSStringToUTF16(title));
   EXPECT_EQ(2, [item tag]);
-  EXPECT_EQ([[item representedObject] pointerValue], &model);
 }
 
 TEST_F(MenuControllerTest, EmptySubmenu) {
diff --git a/ui/base/models/menu_model.h b/ui/base/models/menu_model.h
index e0324ff1..b0ae539 100644
--- a/ui/base/models/menu_model.h
+++ b/ui/base/models/menu_model.h
@@ -5,6 +5,7 @@
 #ifndef UI_BASE_MODELS_MENU_MODEL_H_
 #define UI_BASE_MODELS_MENU_MODEL_H_
 
+#include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "ui/base/models/menu_model_delegate.h"
 #include "ui/base/models/menu_separator_types.h"
@@ -24,7 +25,7 @@
 class ButtonMenuItemModel;
 
 // An interface implemented by an object that provides the content of a menu.
-class UI_BASE_EXPORT MenuModel {
+class UI_BASE_EXPORT MenuModel : public base::SupportsWeakPtr<MenuModel> {
  public:
   // The type of item.
   enum ItemType {
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index 6f4f9e4..222ac2f 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -632,19 +632,20 @@
   return base::StringPiece();
 }
 
-std::string ResourceBundle::DecompressDataResource(int resource_id) {
+std::string ResourceBundle::DecompressDataResource(int resource_id) const {
   return DecompressDataResourceScaled(resource_id, ui::SCALE_FACTOR_NONE);
 }
 
 std::string ResourceBundle::DecompressDataResourceScaled(
     int resource_id,
-    ScaleFactor scaling_factor) {
+    ScaleFactor scaling_factor) const {
   std::string output;
   Decompress(GetRawDataResourceForScale(resource_id, scaling_factor), &output);
   return output;
 }
 
-std::string ResourceBundle::DecompressLocalizedDataResource(int resource_id) {
+std::string ResourceBundle::DecompressLocalizedDataResource(
+    int resource_id) const {
   base::AutoLock lock_scope(*locale_resources_data_lock_);
   base::StringPiece data;
   if (!(locale_resources_data_.get() &&
diff --git a/ui/base/resource/resource_bundle.h b/ui/base/resource/resource_bundle.h
index 22df0e6..e987fca 100644
--- a/ui/base/resource/resource_bundle.h
+++ b/ui/base/resource/resource_bundle.h
@@ -254,16 +254,16 @@
   // into a newly allocated string given the resource id. Todo: Look into
   // introducing an Async version of this function in the future.
   // Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=973417
-  std::string DecompressDataResource(int resource_id);
+  std::string DecompressDataResource(int resource_id) const;
 
   // Return the contents of a scale dependent resource, decompressed into
   // a newly allocated string given the resource id.
   std::string DecompressDataResourceScaled(int resource_id,
-                                           ScaleFactor scaling_factor);
+                                           ScaleFactor scaling_factor) const;
 
   // Return the contents of a localized resource, decompressed into a
   // newly allocated string given the resource id.
-  std::string DecompressLocalizedDataResource(int resource_id);
+  std::string DecompressLocalizedDataResource(int resource_id) const;
 
   // Get a localized string given a message id.  Returns an empty string if the
   // resource_id is not found.
diff --git a/ui/gfx/font_fallback_win.cc b/ui/gfx/font_fallback_win.cc
index 3108d82..65d96cb0 100644
--- a/ui/gfx/font_fallback_win.cc
+++ b/ui/gfx/font_fallback_win.cc
@@ -4,15 +4,9 @@
 
 #include "ui/gfx/font_fallback_win.h"
 
-#include <dwrite_2.h>
-#include <usp10.h>
-#include <wrl.h>
-#include <wrl/client.h>
-
 #include <algorithm>
 #include <map>
 
-#include "base/i18n/rtl.h"
 #include "base/macros.h"
 #include "base/memory/singleton.h"
 #include "base/message_loop/message_loop_current.h"
@@ -23,7 +17,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/registry.h"
-#include "base/win/scoped_gdi_object.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/font_fallback.h"
 #include "ui/gfx/font_fallback_skia_impl.h"
diff --git a/ui/gl/init/gl_factory_x11.cc b/ui/gl/init/gl_factory_x11.cc
index 576f2d2..dcd3183 100644
--- a/ui/gl/init/gl_factory_x11.cc
+++ b/ui/gl/init/gl_factory_x11.cc
@@ -79,9 +79,11 @@
       return InitializeGLSurface(new GLSurfaceGLXX11(window));
     case kGLImplementationSwiftShaderGL:
     case kGLImplementationEGLGLES2:
-    case kGLImplementationEGLANGLE:
       DCHECK(window != gfx::kNullAcceleratedWidget);
       return InitializeGLSurface(new NativeViewGLSurfaceEGLX11(window));
+    case kGLImplementationEGLANGLE:
+      DCHECK(window != gfx::kNullAcceleratedWidget);
+      return InitializeGLSurface(new NativeViewGLSurfaceEGL(window, nullptr));
     case kGLImplementationMockGL:
     case kGLImplementationStubGL:
       return new GLSurfaceStub;
diff --git a/ui/latency/histograms_perftest.cc b/ui/latency/histograms_perftest.cc
index ab7480f..b05dbb84 100644
--- a/ui/latency/histograms_perftest.cc
+++ b/ui/latency/histograms_perftest.cc
@@ -10,7 +10,7 @@
 #include "base/metrics/sample_vector.h"
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/perf/perf_test.h"
+#include "testing/perf/perf_result_reporter.h"
 #include "ui/latency/fixed_point.h"
 #include "ui/latency/frame_metrics_test_common.h"
 
@@ -52,6 +52,12 @@
   DISALLOW_COPY_AND_ASSIGN(RatioHistogramBaseline);
 };
 
+perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
+  perf_test::PerfResultReporter reporter("FrameMetricsHistograms", story_name);
+  reporter.RegisterImportantMetric(".speedup", "score");
+  return reporter;
+}
+
 TEST(FrameMetricsHistogramsPerfTest, RatioEntireRange) {
   const int kStride = 0x1000;
 
@@ -90,8 +96,9 @@
     }
   }
 
-  double X = base_time.InSecondsF() / impl_time.InSecondsF();
-  perf_test::PrintResult(__FUNCTION__, "", __FUNCTION__, X, "x", true);
+  double speedup = base_time.InSecondsF() / impl_time.InSecondsF();
+  perf_test::PerfResultReporter reporter = SetUpReporter("RatioEntireRange");
+  reporter.AddResult(".speedup", speedup);
 }
 
 TEST(FrameMetricsHistogramsPerfTest, RatioCommonRange) {
@@ -132,8 +139,9 @@
     }
   }
 
-  double X = base_time.InSecondsF() / impl_time.InSecondsF();
-  perf_test::PrintResult(__FUNCTION__, "", __FUNCTION__, X, "x", true);
+  double speedup = base_time.InSecondsF() / impl_time.InSecondsF();
+  perf_test::PerfResultReporter reporter = SetUpReporter("RatioCommonRange");
+  reporter.AddResult(".speedup", speedup);
 }
 
 // A version of VSyncHistogram based on the default implementations
@@ -207,8 +215,9 @@
     }
   }
 
-  double X = base_time.InSecondsF() / impl_time.InSecondsF();
-  perf_test::PrintResult(__FUNCTION__, "", __FUNCTION__, X, "x", true);
+  double speedup = base_time.InSecondsF() / impl_time.InSecondsF();
+  perf_test::PerfResultReporter reporter = SetUpReporter("VSyncEntireRange");
+  reporter.AddResult(".speedup", speedup);
 }
 
 TEST(FrameMetricsHistogramsPerfTest, VSyncCommonRange) {
@@ -249,8 +258,9 @@
     }
   }
 
-  double X = base_time.InSecondsF() / impl_time.InSecondsF();
-  perf_test::PrintResult(__FUNCTION__, "", __FUNCTION__, X, "x", true);
+  double speedup = base_time.InSecondsF() / impl_time.InSecondsF();
+  perf_test::PerfResultReporter reporter = SetUpReporter("VSyncCommonRange");
+  reporter.AddResult(".speedup", speedup);
 }
 
 }  // namespace frame_metrics
diff --git a/ui/views/accessibility/ax_virtual_view.cc b/ui/views/accessibility/ax_virtual_view.cc
index a1096586..23d8495b 100644
--- a/ui/views/accessibility/ax_virtual_view.cc
+++ b/ui/views/accessibility/ax_virtual_view.cc
@@ -245,6 +245,10 @@
   return nullptr;
 }
 
+gfx::NativeViewAccessible AXVirtualView::GetNativeViewAccessible() {
+  return GetNativeObject();
+}
+
 gfx::NativeViewAccessible AXVirtualView::GetParent() {
   if (parent_view_)
     return parent_view_->GetNativeObject();
diff --git a/ui/views/accessibility/ax_virtual_view.h b/ui/views/accessibility/ax_virtual_view.h
index 9b808ad..799bf1b 100644
--- a/ui/views/accessibility/ax_virtual_view.h
+++ b/ui/views/accessibility/ax_virtual_view.h
@@ -127,6 +127,7 @@
   int GetChildCount() override;
   gfx::NativeViewAccessible ChildAtIndex(int index) override;
   gfx::NativeViewAccessible GetNSWindow() override;
+  gfx::NativeViewAccessible GetNativeViewAccessible() override;
   gfx::NativeViewAccessible GetParent() override;
   gfx::Rect GetBoundsRect(
       const ui::AXCoordinateSystem coordinate_system,
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate.cc b/ui/views/accessibility/view_ax_platform_node_delegate.cc
index 0290635..f05bab0e4 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate.cc
@@ -16,6 +16,7 @@
 #include "ui/accessibility/platform/ax_platform_node.h"
 #include "ui/accessibility/platform/ax_platform_node_base.h"
 #include "ui/accessibility/platform/ax_unique_id.h"
+#include "ui/base/layout.h"
 #include "ui/events/event_utils.h"
 #include "ui/views/accessibility/view_accessibility_utils.h"
 #include "ui/views/controls/native/native_view_host.h"
@@ -284,6 +285,11 @@
   return nullptr;
 }
 
+gfx::NativeViewAccessible
+ViewAXPlatformNodeDelegate::GetNativeViewAccessible() {
+  return GetNativeObject();
+}
+
 gfx::NativeViewAccessible ViewAXPlatformNodeDelegate::GetParent() {
   if (view()->parent())
     return view()->parent()->GetNativeViewAccessible();
@@ -320,6 +326,15 @@
   if (IsLeaf())
     return GetNativeObject();
 
+  gfx::NativeView native_view = view()->GetWidget()->GetNativeView();
+  float scale_factor = 1.0;
+  if (native_view) {
+    scale_factor = ui::GetScaleFactorForNativeView(native_view);
+    scale_factor = scale_factor <= 0 ? 1.0 : scale_factor;
+  }
+  x /= scale_factor;
+  y /= scale_factor;
+
   // Search child widgets first, since they're on top in the z-order.
   for (Widget* child_widget : GetChildWidgets().child_widgets) {
     View* child_root_view = child_widget->GetRootView();
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate.h b/ui/views/accessibility/view_ax_platform_node_delegate.h
index 6723ecf..c4ffb09ba2 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate.h
+++ b/ui/views/accessibility/view_ax_platform_node_delegate.h
@@ -50,6 +50,7 @@
   int GetChildCount() override;
   gfx::NativeViewAccessible ChildAtIndex(int index) override;
   gfx::NativeViewAccessible GetNSWindow() override;
+  gfx::NativeViewAccessible GetNativeViewAccessible() override;
   gfx::NativeViewAccessible GetParent() override;
   gfx::Rect GetBoundsRect(
       const ui::AXCoordinateSystem coordinate_system,
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
index c2584d93..6733d69 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
@@ -62,7 +62,7 @@
     window->AddObserver(this);
   }
 
-  gfx::NativeViewAccessible GetNativeViewAccessible() {
+  gfx::NativeViewAccessible GetNativeViewAccessible() override {
     return platform_node_->GetNativeViewAccessible();
   }
 
diff --git a/weblayer/OWNERS b/weblayer/OWNERS
index f0bb477f..ec16922 100644
--- a/weblayer/OWNERS
+++ b/weblayer/OWNERS
@@ -3,6 +3,7 @@
 darin@chromium.org
 jam@chromium.org
 rockot@chromium.org
+sky@chromium.org
 torne@chromium.org
 
 # TEAM: weblayer-dev@chromium.org
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
index 6558a644..8d26cd4 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
@@ -150,6 +150,12 @@
         return ObjectWrapper.wrap(mContentViewRenderView);
     }
 
+    @Override
+    public void setSupportsEmbedding(boolean enable) {
+        mContentViewRenderView.requestMode(enable ? ContentViewRenderView.MODE_TEXTURE_VIEW
+                                                  : ContentViewRenderView.MODE_SURFACE_VIEW);
+    }
+
     private static native long nativeCreateBrowserController(long profile);
     private native void nativeSetTopControlsContainerView(
             long nativeBrowserControllerImpl, long nativeTopControlsContainerView);
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java b/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java
index e7a96db..2de22ad2 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java
@@ -127,7 +127,6 @@
         mSurfaceView = new SurfaceView(getContext());
         mSurfaceView.setZOrderMediaOverlay(true);
         mSurfaceView.setBackgroundColor(mBackgroundColor);
-        mWindowAndroid.setAnimationPlaceholderView(mSurfaceView);
 
         mSurfaceCallback = new SurfaceHolder.Callback() {
             @Override
@@ -171,7 +170,6 @@
     private void uninitializeSurfaceView() {
         if (mSurfaceView == null) return;
         removeView(mSurfaceView);
-        mWindowAndroid.setAnimationPlaceholderView(null);
         mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
         mSurfaceCallback = null;
         mSurfaceView = null;
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl
index 778e044..d6cb2b6 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl
@@ -17,4 +17,6 @@
   void destroy() = 3;
 
   IObjectWrapper onCreateView() = 4;
+
+  void setSupportsEmbedding(in boolean enable) = 5;
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserFragmentImpl.java b/weblayer/public/java/org/chromium/weblayer/BrowserFragmentImpl.java
index 2400431..6914a11 100644
--- a/weblayer/public/java/org/chromium/weblayer/BrowserFragmentImpl.java
+++ b/weblayer/public/java/org/chromium/weblayer/BrowserFragmentImpl.java
@@ -46,6 +46,21 @@
         }
     }
 
+    /**
+     * Control support for embedding use cases such as animations. This should be enabled when the
+     * container view of the fragment is animated in any way, needs to be rotated or blended, or
+     * need to control z-order with other views or other BrowserFragmentImpls. Note embedder should
+     * keep WebLayer in the default non-embedding mode when user is interacting with the web
+     * content.
+     */
+    public void setSupportsEmbedding(boolean enable) {
+        try {
+            getIBrowserController().setSupportsEmbedding(enable);
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
     public BrowserController getBrowserController() {
         return mBrowserController;
     }
diff --git a/weblayer/shell/android/BUILD.gn b/weblayer/shell/android/BUILD.gn
index 9549b9b4f..d6749d2 100644
--- a/weblayer/shell/android/BUILD.gn
+++ b/weblayer/shell/android/BUILD.gn
@@ -59,6 +59,73 @@
   native_lib_placeholders = [ "libdummy.so" ]
 }
 
+generate_wrapper("run_weblayer_shell") {
+  testonly = true
+  wrapper_script = "$root_out_dir/bin/run_weblayer_shell"
+  executable = "//weblayer/tools/run_weblayer_shell.py"
+  executable_args = [
+    "--shell-apk-path",
+    "@WrappedPath(apks/WebLayerShell.apk)",
+    "--support-apk-path",
+    "@WrappedPath(apks/WebLayerSupport.apk)",
+  ]
+
+  deps = [
+    ":weblayer_shell_apk",
+    ":weblayer_support_apk",
+  ]
+}
+
+demo_apk_manifest = "$target_gen_dir/demo_apk_manifest/AndroidManifest.xml"
+
+jinja_template("demo_apk_manifest") {
+  input = "demo_apk/AndroidManifest.xml"
+  output = demo_apk_manifest
+}
+
+android_assets("demo_apk_assets") {
+  testonly = true
+
+  sources = [
+    "$root_out_dir/weblayer_shell.pak",
+  ]
+  disable_compression = true
+  deps = [
+    "//weblayer/shell:shell_pak",
+  ]
+}
+
+android_library("demo_apk_java") {
+  testonly = true
+
+  deps = [
+    ":demo_apk_manifest",
+    "//third_party/android_deps:android_support_v4_java",
+    "//weblayer/public/java",
+  ]
+  java_files = [ "demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java" ]
+
+  android_manifest_for_lint = demo_apk_manifest
+}
+
+android_apk("weblayer_demo_apk") {
+  testonly = true
+
+  deps = [
+    ":demo_apk_assets",
+    ":demo_apk_java",
+    ":demo_apk_manifest",
+  ]
+  apk_name = "WebLayerDemo"
+  android_manifest = demo_apk_manifest
+  min_sdk_version = 21
+  target_sdk_version = 28
+  android_manifest_dep = ":demo_apk_manifest"
+
+  # Add a native lib so the ABI is compatible with the implementation APK.
+  native_lib_placeholders = [ "libdummy.so" ]
+}
+
 weblayer_support_manifest =
     "$target_gen_dir/weblayer_support_manifest/AndroidManifest.xml"
 
@@ -117,23 +184,6 @@
   shared_libraries = [ "//weblayer:libweblayer" ]
 }
 
-generate_wrapper("run_weblayer_shell") {
-  testonly = true
-  wrapper_script = "$root_out_dir/bin/run_weblayer_shell"
-  executable = "//weblayer/tools/run_weblayer_shell.py"
-  executable_args = [
-    "--shell-apk-path",
-    "@WrappedPath(apks/WebLayerShell.apk)",
-    "--support-apk-path",
-    "@WrappedPath(apks/WebLayerSupport.apk)",
-  ]
-
-  deps = [
-    ":weblayer_shell_apk",
-    ":weblayer_support_apk",
-  ]
-}
-
 instrumentation_test_apk("weblayer_instrumentation_test_apk") {
   apk_name = "WebLayerInstrumentationTest"
   apk_under_test = "//weblayer/shell/android:weblayer_shell_apk"
@@ -146,6 +196,7 @@
   ]
   java_files = [
     "javatests/src/org/chromium/weblayer/test/NavigationTest.java",
+    "javatests/src/org/chromium/weblayer/test/SmokeTest.java",
     "javatests/src/org/chromium/weblayer/test/WebLayerShellActivityTestRule.java",
     "shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java",
   ]
diff --git a/weblayer/shell/android/demo_apk/AndroidManifest.xml b/weblayer/shell/android/demo_apk/AndroidManifest.xml
new file mode 100644
index 0000000..2ecd2b3
--- /dev/null
+++ b/weblayer/shell/android/demo_apk/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.chromium.weblayer.demo">
+
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+
+    <application android:label="Demo">
+        <activity android:name="WebLayerAnimationDemoActivity"
+                  android:launchMode="singleTask"
+                  android:theme="@android:style/Theme.Holo.Light.NoActionBar"
+                  android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
+                  android:windowSoftInputMode="adjustPan|stateUnspecified">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/weblayer/shell/android/demo_apk/DEPS b/weblayer/shell/android/demo_apk/DEPS
new file mode 100644
index 0000000..fd9e2a83
--- /dev/null
+++ b/weblayer/shell/android/demo_apk/DEPS
@@ -0,0 +1,4 @@
+noparent = True
+include_rules = [
+  "+weblayer/public/java",
+]
diff --git a/weblayer/shell/android/demo_apk/README.md b/weblayer/shell/android/demo_apk/README.md
new file mode 100644
index 0000000..5b91539
--- /dev/null
+++ b/weblayer/shell/android/demo_apk/README.md
@@ -0,0 +1,11 @@
+# WebLayer Shell
+
+This directory contains a demo app to demonstrate capabilities of WebLayer.
+
+To build and run on Android:
+```
+autoninja -C out/Default weblayer_support_apk weblayer_demo_apk
+out/Default/bin/weblayer_support_apk install
+out/Default/bin/weblayer_demo_apk install
+adb shell am start org.chromium.weblayer.demo/.WebLayerAnimationDemoActivity
+```
diff --git a/weblayer/shell/android/demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java b/weblayer/shell/android/demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java
new file mode 100644
index 0000000..a8d5cca
--- /dev/null
+++ b/weblayer/shell/android/demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java
@@ -0,0 +1,241 @@
+// Copyright 2019 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.weblayer.demo;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.text.InputType;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import org.chromium.weblayer.BrowserController;
+import org.chromium.weblayer.BrowserFragmentImpl;
+import org.chromium.weblayer.BrowserObserver;
+import org.chromium.weblayer.Profile;
+import org.chromium.weblayer.WebLayer;
+
+/**
+ * Activity for managing the Demo Shell.
+ */
+public class WebLayerAnimationDemoActivity extends FragmentActivity {
+    private static final String TAG = "AnimationDemo";
+
+    private Profile mProfile;
+    private final BrowserFragmentImpl mBrowserFragments[] = new BrowserFragmentImpl[4];
+    private final ContainerFrameLayout mContainerViews[] = new ContainerFrameLayout[4];
+    private FrameLayout mMainView;
+
+    public static class ShellFragment extends Fragment {
+        private BrowserFragmentImpl mBrowserFragment;
+
+        ShellFragment(BrowserFragmentImpl browserFragment) {
+            mBrowserFragment = browserFragment;
+        }
+
+        @Override
+        public View onCreateView(
+                LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+            return mBrowserFragment.onCreateView();
+        }
+    }
+
+    public static class ContainerFrameLayout extends FrameLayout {
+        private int mIndex;
+        private boolean mInterceptTouchEvent;
+
+        public ContainerFrameLayout(Context context, int index) {
+            super(context);
+            mIndex = index;
+        }
+
+        public void setInterceptTouchEvent(boolean intercept) {
+            mInterceptTouchEvent = intercept;
+        }
+
+        public int getIndex() {
+            return mIndex;
+        }
+
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            return mInterceptTouchEvent;
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            if (!mInterceptTouchEvent) return super.onTouchEvent(event);
+            if (event.getAction() == KeyEvent.ACTION_UP) {
+                performClick();
+            }
+            return true;
+        }
+    }
+
+    private void createNewFragment(int index) {
+        ContainerFrameLayout container = new ContainerFrameLayout(this, index);
+        mContainerViews[index] = container;
+        int viewId = View.generateViewId();
+        container.setId(viewId);
+        mMainView.addView(container);
+
+        mBrowserFragments[index] = mProfile.createBrowserFragment(this);
+        final BrowserController controller = mBrowserFragments[index].getBrowserController();
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        transaction.add(viewId, new ShellFragment(mBrowserFragments[index]));
+        transaction.commit();
+
+        EditText urlView = new EditText(this);
+        urlView.setSelectAllOnFocus(true);
+        urlView.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
+        urlView.setImeOptions(EditorInfo.IME_ACTION_GO);
+        // The background of the top-view must be opaque, otherwise it bleeds through to the
+        // cc::Layer that mirrors the contents of the top-view.
+        urlView.setBackgroundColor(0xFFa9a9a9);
+        urlView.setOnEditorActionListener(new OnEditorActionListener() {
+            @Override
+            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+                if ((actionId != EditorInfo.IME_ACTION_GO)
+                        && (event == null || event.getKeyCode() != KeyEvent.KEYCODE_ENTER
+                                || event.getAction() != KeyEvent.ACTION_DOWN)) {
+                    return false;
+                }
+                String url = urlView.getText().toString();
+                controller.getNavigationController().navigate(Uri.parse(sanitizeUrl(url)));
+                return true;
+            }
+        });
+        mBrowserFragments[index].setTopView(urlView);
+
+        controller.addObserver(new BrowserObserver() {
+            @Override
+            public void displayURLChanged(Uri uri) {
+                urlView.setText(uri.toString());
+            }
+        });
+    }
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        // Only call init for main process.
+        WebLayer.getInstance().init(getApplication());
+
+        super.onCreate(savedInstanceState);
+
+        FrameLayout mainView = new FrameLayout(this);
+        mMainView = mainView;
+        setContentView(mainView);
+
+        mProfile = WebLayer.getInstance().createProfile(null);
+
+        createNewFragment(0);
+        createNewFragment(1);
+        createNewFragment(2);
+
+        mBrowserFragments[0].getBrowserController().getNavigationController().navigate(
+                Uri.parse(sanitizeUrl("https://www.google.com")));
+        mBrowserFragments[1].getBrowserController().getNavigationController().navigate(
+                Uri.parse(sanitizeUrl("https://en.wikipedia.org")));
+        mBrowserFragments[2].getBrowserController().getNavigationController().navigate(
+                Uri.parse(sanitizeUrl("https://www.chromium.org")));
+    }
+
+    @Override
+    protected void onDestroy() {
+        for (int i = 0; i < mBrowserFragments.length; ++i) {
+            BrowserFragmentImpl fragment = mBrowserFragments[i];
+            if (fragment != null) fragment.destroy();
+            mBrowserFragments[i] = null;
+        }
+        if (mProfile != null) mProfile.destroy();
+        super.onDestroy();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        mContainerViews[0].setOnClickListener(new OnClickImpl());
+        mContainerViews[1].setOnClickListener(new OnClickImpl());
+        mContainerViews[2].setOnClickListener(new OnClickImpl());
+        mContainerViews[0].post(() -> {
+            mBrowserFragments[0].setSupportsEmbedding(true);
+            mBrowserFragments[1].setSupportsEmbedding(true);
+            mBrowserFragments[2].setSupportsEmbedding(true);
+            animateDown(mContainerViews[0]);
+            animateDown(mContainerViews[1]);
+            animateDown(mContainerViews[2]);
+        });
+    }
+
+    private ContainerFrameLayout mFullscreenContainer;
+    public class OnClickImpl implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            ContainerFrameLayout container = (ContainerFrameLayout) v;
+            if (mFullscreenContainer == container) return;
+
+            mMainView.removeView(container);
+            mMainView.addView(container, 0);
+
+            if (mFullscreenContainer != null) {
+                animateDown(mFullscreenContainer);
+            }
+            animateUp(container);
+            mFullscreenContainer = container;
+        }
+    }
+
+    private static void animateDown(ContainerFrameLayout container) {
+        int index = container.getIndex();
+        container.animate()
+                .scaleX(1.0f / 3)
+                .scaleY(1.0f / 3)
+                .translationX(-container.getWidth() / 3.0f + (container.getWidth() / 3.0f * index))
+                .translationY(container.getHeight() / 3.0f)
+                .alpha(0.8f)
+                .setDuration(500);
+        container.setInterceptTouchEvent(true);
+    }
+
+    private static void animateUp(ContainerFrameLayout container) {
+        container.animate()
+                .scaleX(1.0f)
+                .scaleY(1.0f)
+                .translationX(0)
+                .translationY(0)
+                .alpha(1.0f)
+                .setDuration(500);
+        container.setInterceptTouchEvent(false);
+    }
+
+    /**
+     * Given an URL, this performs minimal sanitizing to ensure it will be valid.
+     * @param url The url to be sanitized.
+     * @return The sanitized URL.
+     */
+    public static String sanitizeUrl(String url) {
+        if (url == null) return null;
+        if (url.startsWith("www.") || url.indexOf(":") == -1) url = "http://" + url;
+        return url;
+    }
+}
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/SmokeTest.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/SmokeTest.java
new file mode 100644
index 0000000..945087f
--- /dev/null
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/SmokeTest.java
@@ -0,0 +1,36 @@
+// Copyright 2019 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.weblayer.test;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.weblayer.shell.WebLayerShellActivity;
+
+@RunWith(BaseJUnit4ClassRunner.class)
+public class SmokeTest {
+    @Rule
+    public WebLayerShellActivityTestRule mActivityTestRule = new WebLayerShellActivityTestRule();
+
+    @Test
+    @SmallTest
+    public void testSetSupportEmbedding() {
+        WebLayerShellActivity activity = mActivityTestRule.launchShellWithUrl("about:blank");
+        Assert.assertNotNull(activity);
+
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { activity.getBrowserFragmentImpl().setSupportsEmbedding(true); });
+
+        String url = "data:text,foo";
+        mActivityTestRule.loadUrl(url);
+        mActivityTestRule.waitForNavigation(url);
+    }
+}
diff --git a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
index 10ffcab3..39fd147 100644
--- a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
+++ b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
@@ -58,6 +58,10 @@
         return mBrowserController;
     }
 
+    public BrowserFragmentImpl getBrowserFragmentImpl() {
+        return mBrowserFragment;
+    }
+
     @Override
     protected void onCreate(final Bundle savedInstanceState) {
         // Only call init for main process.