diff --git a/.gitignore b/.gitignore
index 64a33f50..41da51d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -100,6 +100,7 @@
 /chrome/browser/google/linkdoctor_internal
 /chrome/browser/internal
 /chrome/browser/media/engagement_internal
+/chrome/browser/media/kaleidoscope/internal
 /chrome/browser/performance_monitor/performance_monitor.xml
 /chrome/browser/protector/internal
 /chrome/browser/resources/chromeos/quickoffice
diff --git a/BUILD.gn b/BUILD.gn
index 595c105..90c41d93 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -144,7 +144,6 @@
       "//third_party/dawn:dawn_end2end_tests_temp_group",
       "//third_party/dawn:dawn_unittests_temp_group",
       "//third_party/pdfium/samples:pdfium_test",
-      "//third_party/webrtc/rtc_tools:frame_analyzer",
       "//tools/perf/clear_system_cache",
       "//tools/polymer:polymer_tools_python_unittests",
       "//ui/accessibility:accessibility_perftests",
diff --git a/DEPS b/DEPS
index 111369a6..aeb42d22 100644
--- a/DEPS
+++ b/DEPS
@@ -168,11 +168,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '0fd4f01b9b83f9c24e1e07ba96124ae8e3836a62',
+  'skia_revision': 'eab1e281fa5e9ade35f25ef0d3a69ffc5a2b7457',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '54b9decff6d862f67f07b41d3b1102dfbb4f35d6',
+  'v8_revision': '5a2bf4dc93b903eb71336802c03b5d2f9668495d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -180,11 +180,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '1c7097b69c50eecc7416d6aa52e87cabc25ebf17',
+  'angle_revision': 'f18ff947360d24edea57ba58b28b1f25c5d99d03',
   # 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': '31edef751a8d8428db1fc9e0f68c345ab987811f',
+  'swiftshader_revision': '3c4707d608f269ccd8ce90fa333f8ac22cd0e1a6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -203,7 +203,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528',
+  'googletest_revision': '076c46198fe1cb50160b287e51c72bd7b1194c1a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -223,7 +223,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
-  'harfbuzz_revision': '7cde68f10cdf2c3ff77c1d9077475c0fc034c75c',
+  'harfbuzz_revision': '64a45be5198f6e22c91454bda7bd9a9294552dff',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Emoji Segmenter
   # and whatever else without interference from each other.
@@ -231,7 +231,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '92338b81e5c421ea563cfdc2ef8cc583c6c208b1',
+  'catapult_revision': '3992f656421fc13c9565c22d963fbcabb3f4ab93',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -239,7 +239,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'c40495063dba2500b7970347782ee0384a69d140',
+  'devtools_frontend_revision': '0802bf500144bc9131e293f158115a9eaf346c50',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -299,7 +299,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'shaderc_revision': 'f0bfbce62d427c755b767aa09e306346189d7c22',
+  'shaderc_revision': 'f4cf10c66fbfeeba95e71d672d33b83da9ec95aa',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -867,7 +867,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '3573aac4baffe2bbc795e8bd126edfb2703eb1a6',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'fc903818f3013dfa9465f47ae359a8445c9254b6',
       'condition': 'checkout_linux',
   },
 
@@ -1294,7 +1294,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '27521bd0f067af6d75d4b80478d46fcb17314b33',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '51516f076308f8142fd43f911d5e01178017f35d',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1484,7 +1484,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'dd55f3ca8f2ea716ca917a4aaf36f0729fe902b1',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'a4c1aaad8d5fe0d412f08f084b0aa36e01393550',
+    Var('webrtc_git') + '/src.git' + '@' + '64e07f445a1009d82238e43df89c0d6bd5e15a4f',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1551,7 +1551,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@852f718b6ebefac9ba68508116d1ed0379320c66',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1d3b6135c44d96d25ec992654be7249e5455c408',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 2b5e1a07..9da44927 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1167,6 +1167,16 @@
         r'^content/renderer/.*\.(cc|h)$',
       ),
     ),
+    (
+      'CComPtr',
+      (
+        'New code should use Microsoft::WRL::ComPtr from wrl/client.h as a ',
+        'replacement for CComPtr from ATL. See http://crbug.com/5027 for more ',
+        'details.'
+      ),
+      False,
+      (),
+    ),
 )
 
 # Format: Sequence of tuples containing:
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 2417558..3bc79e22 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -613,12 +613,8 @@
   Shutdown();
 }
 
-void AppListControllerImpl::OnOverviewModeWillStart() {
+void AppListControllerImpl::OnOverviewModeStarting() {
   if (IsTabletMode()) {
-    scoped_suspend_visibility_update_ =
-        std::make_unique<ShelfLayoutManager::ScopedSuspendVisibilityUpdate>(
-            RootWindowController::ForWindow(presenter_.GetWindow())
-                ->GetShelfLayoutManager());
     const int64_t display_id = last_visible_display_id_;
     OnHomeLauncherTargetPositionChanged(false /* showing */, display_id);
     OnVisibilityWillChange(false /* visible */, display_id);
@@ -631,8 +627,6 @@
     bool canceled) {
   if (!IsTabletMode())
     return;
-  if (scoped_suspend_visibility_update_)
-    scoped_suspend_visibility_update_.reset();
   OnHomeLauncherAnimationComplete(false /* shown */, last_visible_display_id_);
 }
 
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 799d4e8a..f207c1f 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -232,7 +232,7 @@
   void OnShellDestroying() override;
 
   // OverviewObserver:
-  void OnOverviewModeWillStart() override;
+  void OnOverviewModeStarting() override;
   void OnOverviewModeStartingAnimationComplete(bool canceled) override;
   void OnOverviewModeEnding(OverviewSession* session) override;
   void OnOverviewModeEnded() override;
@@ -433,11 +433,6 @@
   // visibility animation to finish. Should only be used in tablet mode.
   HomeLauncherAnimationCallback home_launcher_animation_callback_;
 
-  // Used to prevent ShelfLayoutManager updating visibility state when overview
-  // is showing over the AppList.
-  std::unique_ptr<ShelfLayoutManager::ScopedSuspendVisibilityUpdate>
-      scoped_suspend_visibility_update_;
-
   base::ObserverList<AppListControllerObserver> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(AppListControllerImpl);
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index e42aec4..3e89b8c 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -1063,7 +1063,6 @@
       }
     }
   }
-  UpdateChildViewsYPositionAndOpacity();
   initial_drag_point_ = gfx::Point();
 }
 
diff --git a/ash/app_list/views/apps_container_view.cc b/ash/app_list/views/apps_container_view.cc
index 4fa586a..ccea4464 100644
--- a/ash/app_list/views/apps_container_view.cc
+++ b/ash/app_list/views/apps_container_view.cc
@@ -24,6 +24,9 @@
 #include "base/command_line.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/chromeos/search_box/search_box_constants.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_element.h"
+#include "ui/compositor/layer_animator.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
@@ -212,6 +215,47 @@
       app_list_state == ash::AppListViewState::kPeeking || is_in_drag);
 }
 
+void AppsContainerView::AnimateOpacity(float current_progress,
+                                       ash::AppListViewState target_view_state,
+                                       const OpacityAnimator& animator) {
+  const bool target_suggestion_chip_visibility =
+      target_view_state == ash::AppListViewState::kFullscreenAllApps ||
+      target_view_state == ash::AppListViewState::kPeeking;
+  animator.Run(suggestion_chip_container_view_,
+               target_suggestion_chip_visibility);
+
+  if (!apps_grid_view_->layer()->GetAnimator()->IsAnimatingProperty(
+          ui::LayerAnimationElement::OPACITY)) {
+    apps_grid_view_->UpdateOpacity(true /*restore_opacity*/);
+    apps_grid_view_->layer()->SetOpacity(current_progress > 1.0f ? 1.0f : 0.0f);
+  }
+
+  const bool target_grid_visibility =
+      target_view_state == ash::AppListViewState::kFullscreenAllApps ||
+      target_view_state == ash::AppListViewState::kFullscreenSearch;
+  animator.Run(apps_grid_view_, target_grid_visibility);
+
+  animator.Run(page_switcher_, target_grid_visibility);
+}
+
+void AppsContainerView::AnimateYPosition(
+    ash::AppListViewState target_view_state,
+    const TransformAnimator& animator) {
+  const int target_suggestion_chip_y = GetExpectedSuggestionChipY(
+      AppListView::GetTransitionProgressForState(target_view_state));
+
+  suggestion_chip_container_view_->SetY(target_suggestion_chip_y);
+  animator.Run(suggestion_chip_container_view_);
+
+  apps_grid_view_->SetY(suggestion_chip_container_view_->y() +
+                        chip_grid_y_distance_);
+  animator.Run(apps_grid_view_);
+
+  page_switcher_->SetY(suggestion_chip_container_view_->y() +
+                       chip_grid_y_distance_);
+  animator.Run(page_switcher_);
+}
+
 void AppsContainerView::UpdateYPositionAndOpacity(float progress,
                                                   bool restore_opacity) {
   apps_grid_view_->UpdateOpacity(restore_opacity);
diff --git a/ash/app_list/views/apps_container_view.h b/ash/app_list/views/apps_container_view.h
index 4cf0974..f20458d 100644
--- a/ash/app_list/views/apps_container_view.h
+++ b/ash/app_list/views/apps_container_view.h
@@ -70,6 +70,24 @@
   void UpdateControlVisibility(ash::AppListViewState app_list_state,
                                bool is_in_drag);
 
+  // Animates the container opacity from the opacity for the current app list
+  // transition progress to the opacity for |target_view_state|.
+  // |current_progress| - the current app list transition progress.
+  // |animator| - callback that when run starts the opacity animation.
+  using OpacityAnimator =
+      base::RepeatingCallback<void(views::View*, bool target_visibility)>;
+  void AnimateOpacity(float current_progress,
+                      ash::AppListViewState target_view_state,
+                      const OpacityAnimator& animator);
+
+  // Sets the expected y position for apps container children, and runs
+  // |animator| for each of them to run transform animation from current bounds.
+  // (This assumes that animator knows the offset between current apps container
+  // bounds and target apps container bounds).
+  using TransformAnimator = base::RepeatingCallback<void(views::View*)>;
+  void AnimateYPosition(ash::AppListViewState target_view_state,
+                        const TransformAnimator& animator);
+
   // Updates y position and opacity of the items in this view during dragging.
   void UpdateYPositionAndOpacity(float progress, bool restore_opacity);
 
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 559ddb5b..4753bda 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -1991,6 +1991,12 @@
   if (view_structure_.pages().empty())
     return;
 
+  // App list view state animations animate the apps grid view opacity rather
+  // than individual items' opacity. This method (used during app list view
+  // drag) sets up opacity for individual grid item, and assumes that the apps
+  // grid view is fully opaque.
+  layer()->SetOpacity(1.0f);
+
   // Updates the opacity of the apps in current page. The opacity of the app
   // starting at 0.f when the ceterline of the app is |kAllAppsOpacityStartPx|
   // above the bottom of work area and transitioning to 1.0f by the time the
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index a23d428..d67502f0 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -185,12 +185,12 @@
   // final state. In tablet mode, opacity of the elements is controlled by the
   // HomeLauncherGestureHandler which expects these elements to be opaque.
   // Otherwise the contents animate from 0 to 1 so set the initial opacity to 0.
-  const float initial_opacity =
-      app_list_view_->is_side_shelf() || app_list_view_->is_tablet_mode()
-          ? 1.0f
-          : 0.0f;
-  GetSearchBoxView()->layer()->SetOpacity(initial_opacity);
-  layer()->SetOpacity(initial_opacity);
+  if (app_list_view_->is_side_shelf() || app_list_view_->is_tablet_mode()) {
+    AnimateToViewState(AppListViewState::kFullscreenAllApps, base::TimeDelta());
+  } else if (last_target_view_state_.has_value() &&
+             *last_target_view_state_ != AppListViewState::kClosed) {
+    AnimateToViewState(AppListViewState::kClosed, base::TimeDelta());
+  }
 }
 
 void ContentsView::CancelDrag() {
@@ -827,10 +827,11 @@
   //     overall view state transition. Used during transition to closed state
   //     to speed up the search box and contenst view animation so the don't
   //     show under the shelf.
-  auto animate_opacity = [duration](ui::Layer* layer, bool target_visibility,
-                                    bool half_duration) {
+  auto animate_opacity = [](base::TimeDelta duration, views::View* view,
+                            bool target_visibility) {
+    ui::Layer* const layer = view->layer();
     ui::ScopedLayerAnimationSettings animation(layer->GetAnimator());
-    animation.SetTransitionDuration(duration / (half_duration ? 2 : 1));
+    animation.SetTransitionDuration(duration / (target_visibility ? 1 : 2));
     animation.SetTweenType(gfx::Tween::EASE_IN);
     animation.SetPreemptionStrategy(
         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
@@ -839,23 +840,19 @@
 
   // Fade in or out the contents view, the search box.
   const bool closing = target_view_state == ash::AppListViewState::kClosed;
-  animate_opacity(layer(), !closing /*target_visibility*/,
-                  closing /*half_duration*/);
-  animate_opacity(GetSearchBoxView()->layer(), !closing /*target_visibility*/,
-                  closing /*half_duration*/);
+  animate_opacity(duration, GetSearchBoxView(), !closing /*target_visibility*/);
 
   // Fade in or out the expand arrow.
   const bool target_arrow_visibility =
       target_page == ash::AppListState::kStateApps && !closing;
-  animate_opacity(expand_arrow_view_->layer(),
-                  target_arrow_visibility /*target_visibility*/,
-                  false /*half_duration*/);
+  animate_opacity(duration, expand_arrow_view_, target_arrow_visibility);
 
   // Animates layer's vertical position (using transform animation).
   // |layer| - The layer to transform.
   // |y_offset| - The initial vertical offset - the layer's vertical offset will
   //              be animated to 0.
-  auto animate_transform = [duration](views::View* view, int y_offset) {
+  auto animate_transform = [](base::TimeDelta duration, int y_offset,
+                              views::View* view) {
     ui::Layer* layer = view->layer();
     gfx::Transform transform;
     transform.Translate(0, y_offset);
@@ -907,11 +904,10 @@
       selected_page == ash::AppListState::kStateSearchResults
           ? AppListView::kProgressFlagSearchResults
           : AppListView::kProgressFlagNone;
+  const float progress = app_list_view_->GetAppListTransitionProgress(
+      AppListView::kProgressFlagWithTransform | progress_baseline_flag);
   const gfx::Rect current_search_box_bounds =
-      GetSearchBoxExpectedBoundsForProgress(
-          selected_page, app_list_view_->GetAppListTransitionProgress(
-                             AppListView::kProgressFlagWithTransform |
-                             progress_baseline_flag));
+      GetSearchBoxExpectedBoundsForProgress(selected_page, progress);
 
   const int y_offset =
       current_search_box_bounds.y() -
@@ -920,7 +916,7 @@
   // For search box, animate the search_box view layer instead of the widget
   // layer to avoid conflict with pagination model transitions (which update the
   // search box widget layer transform as the transition progresses).
-  animate_transform(search_box, y_offset);
+  animate_transform(duration, y_offset, search_box);
 
   // Update app list page bounds to their target values. This assumes that
   // potential in-progress pagination transition does not directly animate page
@@ -928,25 +924,25 @@
   for (AppListPage* page : app_list_pages_) {
     page->UpdatePageBoundsForState(target_page, GetContentsBounds(),
                                    target_search_box_bounds);
+    if (page != horizontal_page_container_) {
+      animate_opacity(duration, page, closing ? 0.0f : 1.0f);
+      animate_transform(duration, y_offset, page);
+    } else {
+      GetAppsContainerView()->AnimateOpacity(
+          progress, target_view_state,
+          base::BindRepeating(animate_opacity, duration));
+      GetAppsContainerView()->AnimateYPosition(
+          target_view_state,
+          base::BindRepeating(animate_transform, duration, y_offset));
+    }
   }
 
-  // Update apps container layout separately from horizontal container bounds.
-  GetAppsContainerView()->UpdateYPositionAndOpacity(
-      AppListView::GetTransitionProgressForState(target_view_state),
-      target_view_state != ash::AppListViewState::kClosed /*restore_opacity*/);
+  last_target_view_state_ = target_view_state;
   target_page_for_last_view_state_update_ = target_page;
 
   // Schedule expand arrow repaint to ensure the view picks up the new target
   // state.
   expand_arrow_view()->SchedulePaint();
-
-  // Animate contents view to the target bounds.
-  animate_transform(this, y_offset);
-
-  // The expand arrow view should stay in the same place relative to the
-  // app list bounds, so apply the inverse translation to the one set for
-  // the contents view layer.
-  animate_transform(expand_arrow_view_, -y_offset);
 }
 
 void ContentsView::SetExpandArrowViewVisibility(bool show) {
diff --git a/ash/app_list/views/contents_view.h b/ash/app_list/views/contents_view.h
index 2024422..a715c9d 100644
--- a/ash/app_list/views/contents_view.h
+++ b/ash/app_list/views/contents_view.h
@@ -337,6 +337,7 @@
   // Used primarily to determine the initial search box position when animating
   // to a new app list view state.
   base::Optional<ash::AppListState> target_page_for_last_view_state_update_;
+  base::Optional<ash::AppListViewState> last_target_view_state_;
 
   base::ObserverList<SearchBoxUpdateObserver> search_box_observers_;
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 6e79379..09ea973 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1823,6 +1823,9 @@
       <message name="IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_FAILURE_MESSAGE" desc="Message shown in the user pod when the sign in attempt via a smart card fails.">
         Couldn’t recognize your smart card. Try again.
       </message>
+      <message name="IDS_ASH_LOGIN_SCREEN_UNVERIFIED_CODE_WARNING" desc="Message shown in the user pod when the device has ran or is capable to run unverified code.">
+        This device may contain apps that haven't been verified by Google.
+      </message>
 
       <!-- Multi-profiles intro dialog -->
       <message name="IDS_ASH_MULTIPROFILES_INTRO_HEADLINE" desc="Describes which feature multi-profiles intro dialog presents.">
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 35ee82a..d2d03a10 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -30,10 +30,12 @@
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/login_types.h"
+#include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/style/ash_color_provider.h"
 #include "ash/system/power/power_button_controller.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/status_area_widget_delegate.h"
@@ -59,6 +61,7 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/text_constants.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/background.h"
@@ -103,6 +106,12 @@
 // Spacing between the auth error text and the learn more button.
 constexpr int kLearnMoreButtonVerticalSpacingDp = 6;
 
+// Spacing between the warning indicator and the shelf.
+constexpr int kWarningIndicatorBottomMarginDp = 16;
+
+// Spacing between icon and text in the warning indicator.
+constexpr int kWarningIndicatorChildSpacingDp = 8;
+
 // Blue-ish color for the "learn more" button text.
 constexpr SkColor kLearnMoreButtonTextColor =
     SkColorSetARGB(0xFF, 0x7B, 0xAA, 0xF7);
@@ -398,6 +407,10 @@
   return view_->system_info_;
 }
 
+views::View* LockContentsView::TestApi::warning_indicator() const {
+  return view_->warning_indicator_;
+}
+
 LoginExpandedPublicAccountView* LockContentsView::TestApi::expanded_view()
     const {
   return view_->expanded_view_;
@@ -468,6 +481,16 @@
   system_info_->SetVisible(false);
   top_header_->AddChildView(system_info_);
 
+  // The warning indicator view.
+  warning_indicator_ = new views::View();
+  auto warning_indicator_layout = std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
+      kWarningIndicatorChildSpacingDp);
+  warning_indicator_layout->set_main_axis_alignment(
+      views::BoxLayout::MainAxisAlignment::kEnd);
+  warning_indicator_->SetLayoutManager(std::move(warning_indicator_layout));
+  AddChildView(warning_indicator_);
+
   note_action_ = new NoteActionLaunchButton(initial_note_action_state);
   top_header_->AddChildView(note_action_);
 
@@ -602,6 +625,7 @@
   if (system_info_visible && !system_info_->GetVisible()) {
     system_info_->SetVisible(true);
     LayoutTopHeader();
+    LayoutWarningIndicator();
   }
 }
 
@@ -649,6 +673,7 @@
 void LockContentsView::Layout() {
   View::Layout();
   LayoutTopHeader();
+  LayoutWarningIndicator();
   LayoutPublicSessionView();
 
   if (users_list_)
@@ -1057,7 +1082,8 @@
     bool enforced,
     const std::string& os_version_label_text,
     const std::string& enterprise_info_text,
-    const std::string& bluetooth_name) {
+    const std::string& bluetooth_name,
+    bool adb_sideloading_enabled) {
   DCHECK(!os_version_label_text.empty() || !enterprise_info_text.empty() ||
          !bluetooth_name.empty());
 
@@ -1099,6 +1125,30 @@
   update_label(2, bluetooth_name);
 
   LayoutTopHeader();
+
+  // Initialize the warning indicator view.
+  if (adb_sideloading_enabled && warning_indicator_->children().empty()) {
+    auto* icon = new views::ImageView;
+    icon->SetImage(gfx::CreateVectorIcon(
+        kLockScreenAlertIcon, AshColorProvider::Get()->GetContentLayerColor(
+                                  AshColorProvider::ContentLayerType::kIconRed,
+                                  AshColorProvider::AshColorMode::kDark)));
+    warning_indicator_->AddChildView(icon);
+
+    auto* label = new views::Label();
+    label->SetAutoColorReadabilityEnabled(false);
+    label->SetEnabledColor(gfx::kGoogleRed300);
+    label->SetFontList(
+        views::Label::GetDefaultFontList().DeriveWithSizeDelta(1));
+    label->SetSubpixelRenderingEnabled(false);
+    label->SetText(l10n_util::GetStringUTF16(
+        IDS_ASH_LOGIN_SCREEN_UNVERIFIED_CODE_WARNING));
+
+    warning_indicator_->AddChildView(label);
+  }
+  warning_indicator_->SetVisible(adb_sideloading_enabled);
+
+  LayoutWarningIndicator();
 }
 
 void LockContentsView::OnPublicSessionDisplayNameChanged(
@@ -1549,6 +1599,18 @@
                            gfx::Vector2d(preferred_width, 0));
 }
 
+void LockContentsView::LayoutWarningIndicator() {
+  warning_indicator_->SizeToPreferredSize();
+
+  // Position the warning indicator in the middle above the shelf.
+  warning_indicator_->SetPosition(
+      GetLocalBounds().bottom_center() -
+      gfx::Vector2d(warning_indicator_->width() / 2,
+                    ShelfConfig::Get()->shelf_size() +
+                        kWarningIndicatorBottomMarginDp +
+                        warning_indicator_->height()));
+}
+
 void LockContentsView::LayoutPublicSessionView() {
   gfx::Rect bounds = GetContentsBounds();
   bounds.ClampToCenteredSize(expanded_view_->GetPreferredSize());
@@ -2059,6 +2121,7 @@
   expanded_view_->SetVisible(show_expanded_view);
   main_view_->SetVisible(!show_expanded_view);
   top_header_->SetVisible(!show_expanded_view);
+  warning_indicator_->SetVisible(!show_expanded_view);
   Layout();
 }
 
diff --git a/ash/login/ui/lock_contents_view.h b/ash/login/ui/lock_contents_view.h
index 76982c26..27007da8 100644
--- a/ash/login/ui/lock_contents_view.h
+++ b/ash/login/ui/lock_contents_view.h
@@ -89,6 +89,7 @@
     LoginErrorBubble* warning_banner_bubble() const;
     LoginErrorBubble* supervised_user_deprecation_bubble() const;
     views::View* system_info() const;
+    views::View* warning_indicator() const;
     LoginExpandedPublicAccountView* expanded_view() const;
     views::View* main_view() const;
 
@@ -166,7 +167,8 @@
                            bool enforced,
                            const std::string& os_version_label_text,
                            const std::string& enterprise_info_text,
-                           const std::string& bluetooth_name) override;
+                           const std::string& bluetooth_name,
+                           bool adb_sideloading_enabled) override;
   void OnPublicSessionDisplayNameChanged(
       const AccountId& account_id,
       const std::string& display_name) override;
@@ -267,6 +269,10 @@
   // change contents or visibility.
   void LayoutTopHeader();
 
+  // Lay out the warning indicator. This is called when system information is
+  // shown.
+  void LayoutWarningIndicator();
+
   // Lay out the expanded public session view.
   void LayoutPublicSessionView();
 
@@ -386,6 +392,9 @@
   // other views.
   views::View* top_header_ = nullptr;
 
+  // View for the warning indicator at login screen.
+  views::View* warning_indicator_ = nullptr;
+
   // View for launching a note taking action handler from the lock screen.
   NoteActionLaunchButton* note_action_ = nullptr;
 
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index a7ecd4b..d0608aa 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -693,10 +693,11 @@
 
   // Verify that the system info view becomes visible and it doesn't block the
   // note action button.
-  DataDispatcher()->SetSystemInfo(true /*show*/, false /*enforced*/,
-                                  "Best version ever", "Asset ID: 6666",
-                                  "Bluetooth adapter");
+  DataDispatcher()->SetSystemInfo(
+      true /*show*/, false /*enforced*/, "Best version ever", "Asset ID: 6666",
+      "Bluetooth adapter", false /*adb_sideloading_enabled*/);
   EXPECT_TRUE(test_api.system_info()->GetVisible());
+  EXPECT_FALSE(test_api.warning_indicator()->GetVisible());
   EXPECT_TRUE(test_api.note_action()->GetVisible());
   gfx::Size note_action_size = test_api.note_action()->GetPreferredSize();
   EXPECT_GE(widget_bounds.right() -
@@ -711,6 +712,10 @@
   EXPECT_LT(widget_bounds.right() -
                 test_api.system_info()->GetBoundsInScreen().right(),
             note_action_size.width());
+
+  // Verify that warning indicator is invisible if adb sideloading is not
+  // enabled.
+  EXPECT_FALSE(test_api.warning_indicator()->GetVisible());
 }
 
 // Alt-V toggles display of system information.
@@ -728,9 +733,9 @@
 
   // Verify that the system info view does not become visible when given data
   // but show is false.
-  DataDispatcher()->SetSystemInfo(false /*show*/, false /*enforced*/,
-                                  "Best version ever", "Asset ID: 6666",
-                                  "Bluetooth adapter");
+  DataDispatcher()->SetSystemInfo(
+      false /*show*/, false /*enforced*/, "Best version ever", "Asset ID: 6666",
+      "Bluetooth adapter", false /*adb_sideloading_enabled*/);
   EXPECT_FALSE(test_api.system_info()->GetVisible());
 
   // Alt-V shows hidden system info.
@@ -758,7 +763,8 @@
 
   auto set_system_info = [&](bool show, bool enforced) {
     DataDispatcher()->SetSystemInfo(show, enforced, "Best version ever",
-                                    "Asset ID: 6666", "Bluetooth adapter");
+                                    "Asset ID: 6666", "Bluetooth adapter",
+                                    false /*adb_sideloading_enabled*/);
   };
 
   // Start with hidden system info.
@@ -784,6 +790,25 @@
   EXPECT_TRUE(test_api.system_info()->GetVisible());
 }
 
+// Show warning indicator only if adb sideloading is enabled.
+TEST_F(LockContentsViewUnitTest, ShowWarningIndicatorIfAdbSideloadingEnabled) {
+  auto* contents = new LockContentsView(
+      mojom::TrayActionState::kAvailable, LockScreen::ScreenType::kLock,
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
+  SetUserCount(1);
+
+  std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
+  LockContentsView::TestApi test_api(contents);
+
+  // If the system starts with ADB sideloading enabled, warning_indicator should
+  // be visible.
+  DataDispatcher()->SetSystemInfo(
+      false /*show*/, false /*enforced*/, "Best version ever", "Asset ID: 6666",
+      "Bluetooth adapter", true /*adb_sideloading_enabled*/);
+  EXPECT_TRUE(test_api.warning_indicator()->GetVisible());
+}
+
 // Verifies the easy unlock tooltip is automatically displayed when requested.
 TEST_F(LockContentsViewUnitTest, EasyUnlockForceTooltipCreatesTooltipWidget) {
   auto* lock = new LockContentsView(
diff --git a/ash/login/ui/lock_debug_view.cc b/ash/login/ui/lock_debug_view.cc
index 793c6bc0..b698773 100644
--- a/ash/login/ui/lock_debug_view.cc
+++ b/ash/login/ui/lock_debug_view.cc
@@ -432,10 +432,11 @@
 
   void AddSystemInfo(const std::string& os_version,
                      const std::string& enterprise_info,
-                     const std::string& bluetooth_name) {
+                     const std::string& bluetooth_name,
+                     bool adb_sideloading_enabled) {
     debug_dispatcher_.SetSystemInfo(true /*show*/, false /*enforced*/,
-                                    os_version, enterprise_info,
-                                    bluetooth_name);
+                                    os_version, enterprise_info, bluetooth_name,
+                                    adb_sideloading_enabled);
   }
 
   void UpdateWarningMessage(const base::string16& message) {
@@ -877,8 +878,9 @@
         (num_system_info_clicks_ % 4) / 2 ? kDebugEnterpriseInfo : "";
     std::string bluetooth_name =
         num_system_info_clicks_ % 2 ? kDebugBluetoothName : "";
-    debug_data_dispatcher_->AddSystemInfo(os_version, enterprise_info,
-                                          bluetooth_name);
+    bool adb_sideloading_enabled = num_system_info_clicks_ % 3;
+    debug_data_dispatcher_->AddSystemInfo(
+        os_version, enterprise_info, bluetooth_name, adb_sideloading_enabled);
     return;
   }
 
diff --git a/ash/login/ui/login_data_dispatcher.cc b/ash/login/ui/login_data_dispatcher.cc
index 6c3485b4..22340cd 100644
--- a/ash/login/ui/login_data_dispatcher.cc
+++ b/ash/login/ui/login_data_dispatcher.cc
@@ -60,7 +60,8 @@
     bool enforced,
     const std::string& os_version_label_text,
     const std::string& enterprise_info_text,
-    const std::string& bluetooth_name) {}
+    const std::string& bluetooth_name,
+    bool adb_sideloading_enabled) {}
 
 void LoginDataDispatcher::Observer::OnPublicSessionDisplayNameChanged(
     const AccountId& account_id,
@@ -186,10 +187,12 @@
     bool enforced,
     const std::string& os_version_label_text,
     const std::string& enterprise_info_text,
-    const std::string& bluetooth_name) {
+    const std::string& bluetooth_name,
+    bool adb_sideloading_enabled) {
   for (auto& observer : observers_) {
     observer.OnSystemInfoChanged(show, enforced, os_version_label_text,
-                                 enterprise_info_text, bluetooth_name);
+                                 enterprise_info_text, bluetooth_name,
+                                 adb_sideloading_enabled);
   }
 }
 
diff --git a/ash/login/ui/login_data_dispatcher.h b/ash/login/ui/login_data_dispatcher.h
index 6e9e7c7..f9a8211 100644
--- a/ash/login/ui/login_data_dispatcher.h
+++ b/ash/login/ui/login_data_dispatcher.h
@@ -100,7 +100,8 @@
                                      bool enforced,
                                      const std::string& os_version_label_text,
                                      const std::string& enterprise_info_text,
-                                     const std::string& bluetooth_name);
+                                     const std::string& bluetooth_name,
+                                     bool adb_sideloading_enabled);
 
     // Called when public session display name is changed for user with
     // |account_id|.
@@ -177,7 +178,8 @@
                      bool enforced,
                      const std::string& os_version_label_text,
                      const std::string& enterprise_info_text,
-                     const std::string& bluetooth_name) override;
+                     const std::string& bluetooth_name,
+                     bool adb_sideloading_enabled) override;
   void SetPublicSessionDisplayName(const AccountId& account_id,
                                    const std::string& display_name) override;
   void SetPublicSessionLocales(const AccountId& account_id,
diff --git a/ash/public/cpp/login_screen_model.h b/ash/public/cpp/login_screen_model.h
index 982b958..9f72a49a5 100644
--- a/ash/public/cpp/login_screen_model.h
+++ b/ash/public/cpp/login_screen_model.h
@@ -92,14 +92,16 @@
   // |show|: Whether the system information should be displayed to user.
   // |enforced|: Whether the display of system information is enforced and
   // cannot be changed by some specific user operations (e.g., pressing alt-v).
-  // |os_version_label_text|: The OS version.
-  // |enterprise_info_text|:  The enterprise info.
-  // |bluetooth_name|:        The name of the bluetooth adapter.
+  // |os_version_label_text|:   The OS version.
+  // |enterprise_info_text|:    The enterprise info.
+  // |bluetooth_name|:          The name of the bluetooth adapter.
+  // |adb_sideloading_enabled|: The device status of adb sideoading.
   virtual void SetSystemInfo(bool show,
                              bool enforced,
                              const std::string& os_version_label_text,
                              const std::string& enterprise_info_text,
-                             const std::string& bluetooth_name) = 0;
+                             const std::string& bluetooth_name,
+                             bool adb_sideloading_enabled) = 0;
 
   // Set the public session display name for user with |account_id|.
   virtual void SetPublicSessionDisplayName(const AccountId& account_id,
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 0c199bb7..7e59fa4 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -663,7 +663,7 @@
   // Suspend unnecessary updates of the shelf visibility indefinitely since it
   // is going away.
   if (GetShelfLayoutManager())
-    GetShelfLayoutManager()->SuspendVisibilityUpdate();
+    GetShelfLayoutManager()->SuspendVisibilityUpdateForShutdown();
 
   // Clear the workspace controller to avoid a lot of unnecessary operations
   // when window are removed.
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index bf1a094..104710a 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -150,7 +150,6 @@
   auto* controller =
       GetActiveWorkspaceController(shelf_window->GetRootWindow());
   DCHECK(controller);
-
   return controller->GetWindowState();
 }
 
@@ -332,15 +331,15 @@
 
 // ShelfLayoutManager::ScopedSuspendVisibilityUpdate ---------------------------
 
-ShelfLayoutManager::ScopedSuspendVisibilityUpdate::
-    ScopedSuspendVisibilityUpdate(ShelfLayoutManager* manager)
+ShelfLayoutManager::ScopedSuspendWorkAreaUpdate::ScopedSuspendWorkAreaUpdate(
+    ShelfLayoutManager* manager)
     : manager_(manager) {
-  manager_->SuspendVisibilityUpdate();
+  manager_->SuspendWorkAreaUpdate();
 }
 
-ShelfLayoutManager::ScopedSuspendVisibilityUpdate::
-    ~ScopedSuspendVisibilityUpdate() {
-  manager_->ResumeVisiblityUpdate();
+ShelfLayoutManager::ScopedSuspendWorkAreaUpdate::
+    ~ScopedSuspendWorkAreaUpdate() {
+  manager_->ResumeWorkAreaUpdate();
 }
 
 // ShelfLayoutManager ----------------------------------------------------------
@@ -360,9 +359,9 @@
   // |hotseat_event_handler_| needs to be released before ShelfLayoutManager.
   hotseat_event_handler_.reset();
 
-  // Ensures that |overview_suspend_visibility_update_| is released before
+  // Ensures that |overview_suspend_work_area_update_| is released before
   // ShelfLayoutManager.
-  overview_suspend_visibility_update_.reset();
+  overview_suspend_work_area_update_.reset();
 
   for (auto& observer : observers_)
     observer.WillDeleteShelfLayoutManager();
@@ -429,7 +428,7 @@
 }
 
 void ShelfLayoutManager::UpdateVisibilityState() {
-  // Bail out early after shelf is destroyed or update is suspended.
+  // Bail out early after shelf is destroyed or visibility update is suspended.
   aura::Window* shelf_window = shelf_widget_->GetNativeWindow();
   if (in_shutdown_ || !shelf_window || suspend_visibility_update_)
     return;
@@ -768,26 +767,10 @@
   }
 }
 
-void ShelfLayoutManager::SuspendVisibilityUpdate() {
+void ShelfLayoutManager::SuspendVisibilityUpdateForShutdown() {
   ++suspend_visibility_update_;
 }
 
-void ShelfLayoutManager::ResumeVisiblityUpdate() {
-  --suspend_visibility_update_;
-  DCHECK_GE(suspend_visibility_update_, 0);
-
-  if (suspend_visibility_update_ || in_shutdown_)
-    return;
-
-  UpdateVisibilityState();
-
-  TargetBounds target_bounds;
-  CalculateTargetBoundsAndUpdateWorkArea(&target_bounds, hotseat_state());
-  UpdateBoundsAndOpacity(target_bounds, /*animate=*/true, nullptr);
-
-  MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
-}
-
 void ShelfLayoutManager::OnShelfItemSelected(ShelfAction action) {
   switch (action) {
     case SHELF_ACTION_NONE:
@@ -845,21 +828,26 @@
   }
 }
 
+void ShelfLayoutManager::OnOverviewModeWillStart() {
+  overview_mode_will_start_ = true;
+}
+
 void ShelfLayoutManager::OnOverviewModeStarting() {
-  overview_suspend_visibility_update_.emplace(this);
+  overview_mode_will_start_ = false;
+  overview_suspend_work_area_update_.emplace(this);
 }
 
 void ShelfLayoutManager::OnOverviewModeStartingAnimationComplete(
     bool canceled) {
-  overview_suspend_visibility_update_.reset();
+  overview_suspend_work_area_update_.reset();
 }
 
 void ShelfLayoutManager::OnOverviewModeEnding(OverviewSession* session) {
-  overview_suspend_visibility_update_.emplace(this);
+  overview_suspend_work_area_update_.emplace(this);
 }
 
 void ShelfLayoutManager::OnOverviewModeEndingAnimationComplete(bool canceled) {
-  overview_suspend_visibility_update_.reset();
+  overview_suspend_work_area_update_.reset();
 }
 
 void ShelfLayoutManager::OnOverviewModeEnded() {
@@ -954,11 +942,14 @@
 }
 
 void ShelfLayoutManager::OnDeskSwitchAnimationLaunching() {
-  SuspendVisibilityUpdate();
+  ++suspend_visibility_update_;
 }
 
 void ShelfLayoutManager::OnDeskSwitchAnimationFinished() {
-  ResumeVisiblityUpdate();
+  --suspend_visibility_update_;
+  DCHECK_GE(suspend_visibility_update_, 0);
+  if (!suspend_visibility_update_)
+    UpdateVisibilityState();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -968,6 +959,25 @@
 
 ShelfLayoutManager::TargetBounds::~TargetBounds() = default;
 
+void ShelfLayoutManager::SuspendWorkAreaUpdate() {
+  ++suspend_work_area_update_;
+}
+
+void ShelfLayoutManager::ResumeWorkAreaUpdate() {
+  --suspend_work_area_update_;
+  DCHECK_GE(suspend_work_area_update_, 0);
+
+  if (suspend_work_area_update_ || in_shutdown_)
+    return;
+
+  UpdateVisibilityState();
+
+  TargetBounds target_bounds;
+  CalculateTargetBoundsAndUpdateWorkArea(&target_bounds, hotseat_state());
+  UpdateBoundsAndOpacity(target_bounds, /*animate=*/true, nullptr);
+  MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
+}
+
 void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) {
   if (suspend_visibility_update_)
     return;
@@ -1071,7 +1081,8 @@
   auto* app_list_controller = Shell::Get()->app_list_controller();
   const auto* overview_controller = Shell::Get()->overview_controller();
   const bool in_overview =
-      overview_controller && overview_controller->InOverviewSession() &&
+      ((overview_controller && overview_controller->InOverviewSession()) ||
+       overview_mode_will_start_) &&
       !overview_controller->IsCompletingShutdownAnimations();
   const bool app_list_visible =
       app_list_controller->IsVisible() ||
@@ -1238,10 +1249,6 @@
     const TargetBounds& target_bounds,
     bool animate,
     ui::ImplicitAnimationObserver* observer) {
-  // Do not update the work area during overview animation.
-  if (suspend_visibility_update_)
-    return;
-
   hide_animation_observer_.reset();
   if (GetLayer(shelf_widget_)->opacity() != target_bounds.opacity) {
     if (target_bounds.opacity == 0) {
@@ -1333,28 +1340,33 @@
     hotseat_bounds.Offset(hotseat_offset);
     hotseat_widget->SetBounds(hotseat_bounds);
 
-    // Do not update the work area when the alignment changes to BOTTOM_LOCKED
-    // to prevent window movement when the screen is locked: crbug.com/622431
-    // The work area is initialized with BOTTOM_LOCKED insets to prevent window
-    // movement on async preference initialization in tests: crbug.com/834369
-    display_ = display::Screen::GetScreen()->GetDisplayNearestWindow(
-        shelf_widget_->GetNativeWindow());
-    bool in_overview = Shell::Get()->overview_controller()->InOverviewSession();
-    if (!in_overview && !state_.IsScreenLocked() &&
-        (shelf_->alignment() != SHELF_ALIGNMENT_BOTTOM_LOCKED ||
-         display_.work_area() == display_.bounds())) {
-      gfx::Insets insets;
-      // If user session is blocked (login to new user session or add user to
-      // the existing session - multi-profile) then give 100% of work area only
-      // if keyboard is not shown.
-      // TODO(agawronska): Could this be called from WorkAreaInsets?
-      const WorkAreaInsets* const work_area =
-          WorkAreaInsets::ForWindow(shelf_widget_->GetNativeWindow());
-      if (!state_.IsAddingSecondaryUser() || work_area->IsKeyboardShown())
-        insets = work_area->user_work_area_insets();
+    // Do not update the work area during overview animation.
+    if (!suspend_work_area_update_) {
+      // Do not update the work area when the alignment changes to BOTTOM_LOCKED
+      // to prevent window movement when the screen is locked: crbug.com/622431
+      // The work area is initialized with BOTTOM_LOCKED insets to prevent
+      // window movement on async preference initialization in tests:
+      // crbug.com/834369
+      display_ = display::Screen::GetScreen()->GetDisplayNearestWindow(
+          shelf_widget_->GetNativeWindow());
+      bool in_overview =
+          Shell::Get()->overview_controller()->InOverviewSession();
+      if (!in_overview && !state_.IsScreenLocked() &&
+          (shelf_->alignment() != SHELF_ALIGNMENT_BOTTOM_LOCKED ||
+           display_.work_area() == display_.bounds())) {
+        gfx::Insets insets;
+        // If user session is blocked (login to new user session or add user to
+        // the existing session - multi-profile) then give 100% of work area
+        // only if keyboard is not shown.
+        // TODO(agawronska): Could this be called from WorkAreaInsets?
+        const WorkAreaInsets* const work_area =
+            WorkAreaInsets::ForWindow(shelf_widget_->GetNativeWindow());
+        if (!state_.IsAddingSecondaryUser() || work_area->IsKeyboardShown())
+          insets = work_area->user_work_area_insets();
 
-      Shell::Get()->SetDisplayWorkAreaInsets(shelf_widget_->GetNativeWindow(),
-                                             insets);
+        Shell::Get()->SetDisplayWorkAreaInsets(shelf_widget_->GetNativeWindow(),
+                                               insets);
+      }
     }
   }
 
@@ -1578,11 +1590,13 @@
     TargetBounds* target_bounds,
     HotseatState hotseat_target_state) {
   CalculateTargetBounds(state_, target_bounds, hotseat_target_state);
-  WorkAreaInsets::ForWindow(shelf_widget_->GetNativeWindow())
-      ->SetShelfBoundsAndInsets(target_bounds->shelf_bounds,
-                                target_bounds->shelf_insets);
-  for (auto& observer : observers_)
-    observer.OnWorkAreaInsetsChanged();
+  if (!suspend_work_area_update_) {
+    WorkAreaInsets::ForWindow(shelf_widget_->GetNativeWindow())
+        ->SetShelfBoundsAndInsets(target_bounds->shelf_bounds,
+                                  target_bounds->shelf_insets);
+    for (auto& observer : observers_)
+      observer.OnWorkAreaInsetsChanged();
+  }
 }
 
 void ShelfLayoutManager::UpdateTargetBoundsForGesture(
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index 4e54501..ad0accf58 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -75,17 +75,17 @@
                                       public LocaleChangeObserver,
                                       public DesksController::Observer {
  public:
-  // Suspend shelf visibility update within its scope. Note that relevant
+  // Suspend work area updates within its scope. Note that relevant
   // ShelfLayoutManager must outlive this class.
-  class ScopedSuspendVisibilityUpdate {
+  class ScopedSuspendWorkAreaUpdate {
    public:
     // |manager| is the ShelfLayoutManager whose visibility update is suspended.
-    explicit ScopedSuspendVisibilityUpdate(ShelfLayoutManager* manager);
-    ~ScopedSuspendVisibilityUpdate();
+    explicit ScopedSuspendWorkAreaUpdate(ShelfLayoutManager* manager);
+    ~ScopedSuspendWorkAreaUpdate();
 
    private:
     ShelfLayoutManager* const manager_;
-    DISALLOW_COPY_AND_ASSIGN(ScopedSuspendVisibilityUpdate);
+    DISALLOW_COPY_AND_ASSIGN(ScopedSuspendWorkAreaUpdate);
   };
 
   ShelfLayoutManager(ShelfWidget* shelf_widget, Shelf* shelf);
@@ -163,10 +163,9 @@
   // Cancel the drag if the shelf is in drag progress.
   void CancelDragOnShelfIfInProgress();
 
-  // Suspends/resumes visibility update. Use ScopedSuspendVisibilityUpdate when
-  // possible to ensure there are balanced calls.
-  void SuspendVisibilityUpdate();
-  void ResumeVisiblityUpdate();
+  // Suspends shelf visibility updates, to be used during shutdown. Since there
+  // is no balanced "resume" public API, the suspension will be indefinite.
+  void SuspendVisibilityUpdateForShutdown();
 
   // Called when ShelfItems are interacted with in the shelf.
   void OnShelfItemSelected(ShelfAction action);
@@ -186,6 +185,7 @@
                                SplitViewController::State state) override;
 
   // OverviewObserver:
+  void OnOverviewModeWillStart() override;
   void OnOverviewModeStarting() override;
   void OnOverviewModeStartingAnimationComplete(bool canceled) override;
   void OnOverviewModeEnding(OverviewSession* overview_session) override;
@@ -366,6 +366,10 @@
     session_manager::SessionState session_state;
   };
 
+  // Suspends/resumes work area updates.
+  void SuspendWorkAreaUpdate();
+  void ResumeWorkAreaUpdate();
+
   // Sets the visibility of the shelf to |state|.
   void SetState(ShelfVisibilityState visibility_state);
 
@@ -530,10 +534,14 @@
   ShelfWidget* shelf_widget_;
   Shelf* shelf_;
 
-  // Count of pending visibility update suspensions. Skip updating shelf
+  // Count of pending visibility update suspensions. Skip updating the shelf
   // visibility state if it is greater than 0.
   int suspend_visibility_update_ = 0;
 
+  // Count of pending work area update suspensions. Skip updating the work
+  // area if it is greater than 0.
+  int suspend_work_area_update_ = 0;
+
   base::OneShotTimer auto_hide_timer_;
 
   // Whether the mouse was over the shelf when the auto hide timer started.
@@ -571,6 +579,10 @@
   // detects that the background has been interacted with.
   bool should_hide_hotseat_ = false;
 
+  // Whether the overview mode is about to start. This becomes false again
+  // once the overview mode has actually started.
+  bool overview_mode_will_start_ = false;
+
   // Tracks the amount of the drag. The value is only valid when
   // |drag_status_| is set to kDragInProgress.
   float drag_amount_ = 0.f;
@@ -630,12 +642,12 @@
   // state.
   bool is_auto_hide_state_locked_ = false;
 
-  // An optional ScopedSuspendVisibilityUpdate that gets created when suspend
+  // An optional ScopedSuspendWorkAreaUpdate that gets created when suspend
   // visibility update is requested for overview and resets when overview no
   // longer needs it. It is used because OnOverviewModeStarting() and
   // OnOverviewModeStartingAnimationComplete() calls are not balanced.
-  base::Optional<ScopedSuspendVisibilityUpdate>
-      overview_suspend_visibility_update_;
+  base::Optional<ScopedSuspendWorkAreaUpdate>
+      overview_suspend_work_area_update_;
 
   // The window drag controller that will be used when a window can be dragged
   // up from shelf to homescreen, overview or splitview.
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 508d0a3..3ac49964 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -3992,6 +3992,7 @@
               GetShelfLayoutManager()->hotseat_state());
   }
 }
+
 // Tests that popups don't activate the hotseat. (crbug.com/1018266)
 TEST_F(HotseatShelfLayoutManagerTest, HotseatRemainsHiddenIfPopupLaunched) {
   // Go to in-app shelf and extend the hotseat.
@@ -4019,6 +4020,213 @@
   EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state());
 }
 
+// Counts the number of times the work area changes.
+class DisplayWorkAreaChangeCounter : public display::DisplayObserver {
+ public:
+  DisplayWorkAreaChangeCounter() {
+    Shell::Get()->display_manager()->AddObserver(this);
+  }
+  ~DisplayWorkAreaChangeCounter() override {
+    Shell::Get()->display_manager()->RemoveObserver(this);
+  }
+
+  void OnDisplayMetricsChanged(const display::Display& display,
+                               uint32_t metrics) override {
+    if (metrics & display::DisplayObserver::DISPLAY_METRIC_WORK_AREA)
+      work_area_change_count_++;
+  }
+
+  int count() const { return work_area_change_count_; }
+
+ private:
+  int work_area_change_count_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplayWorkAreaChangeCounter);
+};
+
+// TODO(https:/crbug.com/1019531): Re-enable this test after the work-area
+// exhibits the desired behavior.
+// Tests that the work area does not update after going to/from tablet mode with
+// no windows open.
+TEST_F(HotseatShelfLayoutManagerTest,
+       DISABLED_WorkAreaDoesNotUpdateClamshellToFromHomeLauncherNoWindows) {
+  DisplayWorkAreaChangeCounter counter;
+  TabletModeControllerTestApi().EnterTabletMode();
+
+  EXPECT_EQ(0, counter.count());
+
+  TabletModeControllerTestApi().LeaveTabletMode();
+
+  EXPECT_EQ(0, counter.count());
+}
+
+// TODO(https:/crbug.com/1019531): Re-enable this test after the work-area
+// exhibits the desired behavior.
+// Tests that opening a window in tablet mode changes the work area.
+TEST_F(HotseatShelfLayoutManagerTest,
+       DISABLED_OpenWindowInTabletModeChangesWorkArea) {
+  DisplayWorkAreaChangeCounter counter;
+  TabletModeControllerTestApi().EnterTabletMode();
+
+  std::unique_ptr<aura::Window> window =
+      AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
+  wm::ActivateWindow(window.get());
+
+  EXPECT_EQ(1, counter.count());
+}
+
+// TODO(https:/crbug.com/1019531): Re-enable this test after the work-area
+// exhibits the desired behavior.
+// Tests that going to and from tablet mode with an open window results in a
+// work area change.
+TEST_F(HotseatShelfLayoutManagerTest,
+       DISABLED_ToFromTabletModeWithWindowChangesWorkArea) {
+  DisplayWorkAreaChangeCounter counter;
+  std::unique_ptr<aura::Window> window =
+      AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
+  wm::ActivateWindow(window.get());
+
+  TabletModeControllerTestApi().EnterTabletMode();
+  EXPECT_EQ(1, counter.count());
+
+  TabletModeControllerTestApi().LeaveTabletMode();
+  EXPECT_EQ(2, counter.count());
+}
+
+// TODO(https:/crbug.com/1019531): Re-enable this test after the work-area
+// exhibits the desired behavior.
+// Tests that going between Applist and overview in tablet mode with no windows
+// results in no work area change.
+TEST_F(HotseatShelfLayoutManagerTest,
+       DISABLED_WorkAreaDoesNotUpdateAppListToFromOverviewWithNoWindow) {
+  DisplayWorkAreaChangeCounter counter;
+  TabletModeControllerTestApi().EnterTabletMode();
+  {
+    OverviewAnimationWaiter waiter;
+    Shell::Get()->overview_controller()->StartOverview();
+    waiter.Wait();
+  }
+
+  EXPECT_EQ(0, counter.count());
+
+  {
+    OverviewAnimationWaiter waiter;
+    Shell::Get()->overview_controller()->EndOverview();
+    waiter.Wait();
+  }
+
+  EXPECT_EQ(0, counter.count());
+}
+
+// TODO(https:/crbug.com/1019531): Re-enable this test after the work-area
+// exhibits the desired behavior.
+// Tests that switching between AppList and overview with a window results in no
+// work area change.
+TEST_F(HotseatShelfLayoutManagerTest,
+       DISABLED_WorkAreaDoesNotUpdateAppListToFromOverviewWithWindow) {
+  DisplayWorkAreaChangeCounter counter;
+  TabletModeControllerTestApi().EnterTabletMode();
+  std::unique_ptr<aura::Window> window =
+      AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
+  wm::ActivateWindow(window.get());
+  ASSERT_EQ(1, counter.count());
+  views::View* home_button = GetPrimaryShelf()->shelf_widget()->GetHomeButton();
+  GetEventGenerator()->GestureTapAt(
+      home_button->GetBoundsInScreen().CenterPoint());
+
+  {
+    OverviewAnimationWaiter waiter;
+    gfx::Point overview_button_center = GetPrimaryShelf()
+                                            ->shelf_widget()
+                                            ->status_area_widget()
+                                            ->overview_button_tray()
+                                            ->GetBoundsInScreen()
+                                            .CenterPoint();
+    GetEventGenerator()->GestureTapAt(overview_button_center);
+    waiter.Wait();
+  }
+
+  EXPECT_EQ(1, counter.count());
+
+  {
+    OverviewAnimationWaiter waiter;
+    // Overview button has moved a bit now that the shelf is in-app.
+    gfx::Point overview_button_center = GetPrimaryShelf()
+                                            ->shelf_widget()
+                                            ->status_area_widget()
+                                            ->overview_button_tray()
+                                            ->GetBoundsInScreen()
+                                            .CenterPoint();
+    GetEventGenerator()->GestureTapAt(overview_button_center);
+    waiter.Wait();
+  }
+
+  EXPECT_EQ(1, counter.count());
+}
+
+// TODO(https:/crbug.com/1019531): Re-enable this test after the work-area
+// exhibits the desired behavior.
+// Tests that switching between AppList and an active window does not update the
+// work area.
+TEST_F(HotseatShelfLayoutManagerTest,
+       DISABLED_WorkAreaDoesNotUpdateOpenWindowToFromAppList) {
+  TabletModeControllerTestApi().EnterTabletMode();
+  std::unique_ptr<aura::Window> window =
+      AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
+  wm::ActivateWindow(window.get());
+  ASSERT_TRUE(ShelfConfig::Get()->is_in_app());
+
+  // Go to the home launcher, work area should not udpate.
+  DisplayWorkAreaChangeCounter counter;
+  views::View* home_button = GetPrimaryShelf()->shelf_widget()->GetHomeButton();
+  GetEventGenerator()->GestureTapAt(
+      home_button->GetBoundsInScreen().CenterPoint());
+
+  GetAppListTestHelper()->CheckVisibility(true);
+  EXPECT_EQ(0, counter.count());
+
+  // Go back to the window, work area should not update.
+  wm::ActivateWindow(window.get());
+
+  EXPECT_TRUE(ShelfConfig::Get()->is_in_app());
+  EXPECT_EQ(0, counter.count());
+}
+
+// TODO(https:/crbug.com/1019531): Re-enable this test after the work-area
+// exhibits the desired behavior.
+// Tests that switching between overview and an active window does not update
+// the work area.
+TEST_F(HotseatShelfLayoutManagerTest,
+       DISABLED_WorkAreaDoesNotUpdateOpenWindowToFromOverview) {
+  TabletModeControllerTestApi().EnterTabletMode();
+  std::unique_ptr<aura::Window> window =
+      AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
+  wm::ActivateWindow(window.get());
+  ASSERT_TRUE(ShelfConfig::Get()->is_in_app());
+
+  // Go to overview, there should not be a work area update.
+  DisplayWorkAreaChangeCounter counter;
+  {
+    OverviewAnimationWaiter waiter;
+    gfx::Point overview_button_center = GetPrimaryShelf()
+                                            ->shelf_widget()
+                                            ->status_area_widget()
+                                            ->overview_button_tray()
+                                            ->GetBoundsInScreen()
+                                            .CenterPoint();
+    GetEventGenerator()->GestureTapAt(overview_button_center);
+    waiter.Wait();
+  }
+
+  EXPECT_EQ(0, counter.count());
+
+  // Go back to the app, there should not be a work area update.
+  wm::ActivateWindow(window.get());
+
+  EXPECT_TRUE(ShelfConfig::Get()->is_in_app());
+  EXPECT_EQ(0, counter.count());
+}
+
 class ShelfLayoutManagerWindowDraggingTest : public ShelfLayoutManagerTestBase {
  public:
   ShelfLayoutManagerWindowDraggingTest() = default;
diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h
index 865675c1..b0f96b9 100644
--- a/base/strings/string_piece.h
+++ b/base/strings/string_piece.h
@@ -154,8 +154,7 @@
 // BasicStringPiece ------------------------------------------------------------
 
 // Defines the types, methods, operators, and data members common to both
-// StringPiece and StringPiece16. Do not refer to this class directly, but
-// rather to BasicStringPiece, StringPiece, or StringPiece16.
+// StringPiece and StringPiece16.
 //
 // This is templatized by string class type rather than character type, so
 // BasicStringPiece<std::string> or BasicStringPiece<base::string16>.
diff --git a/base/trace_event/OWNERS b/base/trace_event/OWNERS
index 8d95238..3e279bf 100644
--- a/base/trace_event/OWNERS
+++ b/base/trace_event/OWNERS
@@ -2,7 +2,6 @@
 oysteine@chromium.org
 primiano@chromium.org
 skyostil@chromium.org
-per-file trace_event_android.cc=wangxianzhu@chromium.org
 
 # For memory-infra related changes
 ssid@chromium.org
diff --git a/build/android/pylib/instrumentation/instrumentation_test_instance.py b/build/android/pylib/instrumentation/instrumentation_test_instance.py
index 0b9a5f8603..b4065e5 100644
--- a/build/android/pylib/instrumentation/instrumentation_test_instance.py
+++ b/build/android/pylib/instrumentation/instrumentation_test_instance.py
@@ -246,7 +246,13 @@
     if filter_av is None:
       return True
     elif isinstance(av, dict):
-      return filter_av in av['value']
+      tav_from_dict = av['value']
+      # If tav_from_dict is an int, the 'in' operator breaks, so convert
+      # filter_av and manually compare. See https://crbug.com/1019707
+      if isinstance(tav_from_dict, int):
+        return int(filter_av) == tav_from_dict
+      else:
+        return filter_av in tav_from_dict
     elif isinstance(av, list):
       return filter_av in av
     return filter_av == av
diff --git a/build/config/fuchsia/BUILD.gn b/build/config/fuchsia/BUILD.gn
index 56d79f41..e6abcc3 100644
--- a/build/config/fuchsia/BUILD.gn
+++ b/build/config/fuchsia/BUILD.gn
@@ -49,14 +49,6 @@
   # Add SDK lib dir for -lfdio above.
   lib_dirs = [ "${fuchsia_sdk}/arch/${current_cpu}/lib" ]
 
-  # TODO(crbug.com/821951): Clang enables SafeStack by default when targeting
-  # Fuchsia, but it breaks some tests, notably in V8.
-  # Force the toolchain to use shadow-call-stack instead, until that is default.
-  cflags += [
-    "-fno-sanitize=safe-stack",
-    "-fsanitize=shadow-call-stack",
-  ]
-
   libs = [ "zircon" ]
 }
 
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index f2c3471..2797cee 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8897110199606882896
\ No newline at end of file
+8897091697641008928
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 02ca646..7717cab 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8897111035204896016
\ No newline at end of file
+8897090257456483296
\ No newline at end of file
diff --git a/cc/paint/display_item_list.h b/cc/paint/display_item_list.h
index af922d37..bb297dc8 100644
--- a/cc/paint/display_item_list.h
+++ b/cc/paint/display_item_list.h
@@ -90,6 +90,8 @@
     return offset;
   }
 
+  UsageHint GetUsageHint() const { return usage_hint_; }
+
   // Called by blink::PaintChunksToCcLayer when an effect ends, to update the
   // bounds of a SaveLayer[Alpha]Op which was emitted when the effect started.
   // This is needed because blink doesn't know the bounds when an effect starts.
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index ce3dc73b..9cf23aa 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -15,6 +15,7 @@
   "junit/src/org/chromium/chrome/browser/ShadowDeviceConditions.java",
   "junit/src/org/chromium/chrome/browser/ShortcutHelperTest.java",
   "junit/src/org/chromium/chrome/browser/SSLClientCertificateRequestTest.java",
+  "junit/src/org/chromium/chrome/browser/appmenu/AppMenuPopupPositionTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java",
   "junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskSchedulerTest.java",
   "junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskTest.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java
index c357998..d7d22710 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java
@@ -366,4 +366,9 @@
     private void setThirdPartyPrivacyNoticeText(String text) {
         set(THIRDPARTY_PRIVACY_NOTICE_TEXT, text);
     }
+
+    @CalledByNative
+    private void setDefaultEmail(String email) {
+        set(DEFAULT_EMAIL, email);
+    }
 }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemTest.java
index 08f5194..ae21248 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemTest.java
@@ -22,6 +22,7 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.rotateDeviceToOrientation;
 
 import android.content.res.Configuration;
+import android.os.Build;
 import android.support.test.espresso.NoMatchingRootException;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
@@ -40,6 +41,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -114,6 +116,7 @@
 
     @Test
     @MediumTest
+    @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.N_MR1, message = "https://crbug.com/1023430")
     public void testIphItemScreenRotation() throws InterruptedException {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
index 1a77451f..5a1a891f 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
@@ -306,6 +306,12 @@
                 () -> rule.getActivity().getTabContentManager().getPendingReadbacksForTesting()));
     }
 
+    public static void verifyAllTabsHaveThumbnail(TabModel tabModel) {
+        for (int i = 0; i < tabModel.getCount(); i++) {
+            checkThumbnailsExist(tabModel.getTabAt(i));
+        }
+    }
+
     public static void checkThumbnailsExist(Tab tab) {
         File etc1File = TabContentManager.getTabThumbnailFileEtc1(tab);
         CriteriaHelper.pollInstrumentationThread(etc1File::exists,
diff --git a/chrome/android/java/res/drawable/ic_settings_nfc.xml b/chrome/android/java/res/drawable/ic_settings_nfc.xml
new file mode 100644
index 0000000..47b7f2e
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_settings_nfc.xml
@@ -0,0 +1,14 @@
+<?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. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,2L4,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM20,20L4,20L4,4h16v16zM18,6h-5c-1.1,0 -2,0.9 -2,2v2.28c-0.6,0.35 -1,0.98 -1,1.72 0,1.1 0.9,2 2,2s2,-0.9 2,-2c0,-0.74 -0.4,-1.38 -1,-1.72L13,8h3v8L8,16L8,8h2L10,6L6,6v12h12L18,6z"
+      android:fillColor="#000000"/>
+</vector>
diff --git a/chrome/android/java/res/xml/single_website_preferences.xml b/chrome/android/java/res/xml/single_website_preferences.xml
index 5a0ceac..144127c 100644
--- a/chrome/android/java/res/xml/single_website_preferences.xml
+++ b/chrome/android/java/res/xml/single_website_preferences.xml
@@ -73,6 +73,8 @@
     <ListPreference
         android:key="clipboard_permission_list" />
     <ListPreference
+        android:key="nfc_permission_list" />
+    <ListPreference
         android:key="bluetooth_scanning_permission_list" />
 
     <org.chromium.chrome.browser.preferences.ButtonPreference
diff --git a/chrome/android/java/res/xml/site_settings_preferences.xml b/chrome/android/java/res/xml/site_settings_preferences.xml
index 9e9184a0..a9617eb 100644
--- a/chrome/android/java/res/xml/site_settings_preferences.xml
+++ b/chrome/android/java/res/xml/site_settings_preferences.xml
@@ -84,6 +84,10 @@
         android:title="@string/website_settings_storage"
         android:icon="@drawable/settings_storage"
         app:iconTint="@color/default_icon_color" />
+    <!-- NFC -->
+    <org.chromium.chrome.browser.preferences.website.SiteSettingsPreference
+        android:fragment="org.chromium.chrome.browser.preferences.website.SingleCategoryPreferences"
+        android:key="nfc" />
     <!-- USB -->
     <org.chromium.chrome.browser.preferences.website.SiteSettingsPreference
         android:fragment="org.chromium.chrome.browser.preferences.website.SingleCategoryPreferences"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
index 052bdee..3617a3e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
@@ -267,8 +267,10 @@
 
         int popupHeight = setMenuHeight(menuItems.size(), visibleDisplayFrame, screenHeight,
                 sizingPadding, footerHeight, headerHeight, anchorView);
-        int[] popupPosition = getPopupPosition(mCurrentScreenRotation, visibleDisplayFrame,
-                sizingPadding, anchorView, popupWidth, popupHeight, showFromBottom);
+        int[] popupPosition = getPopupPosition(mTempLocation, mIsByPermanentButton,
+                mNegativeSoftwareVerticalOffset, mNegativeVerticalOffsetNotTopAnchored,
+                mCurrentScreenRotation, visibleDisplayFrame, sizingPadding, anchorView, popupWidth,
+                popupHeight, showFromBottom, anchorView.getRootView().getLayoutDirection());
 
         mPopup.setContentView(contentView);
         mPopup.showAtLocation(
@@ -298,16 +300,19 @@
         }
     }
 
-    private int[] getPopupPosition(int screenRotation, Rect appRect, Rect padding, View anchorView,
-            int popupWidth, int popupHeight, boolean isAnchorAtBottom) {
-        anchorView.getLocationInWindow(mTempLocation);
-        int anchorViewX = mTempLocation[0];
-        int anchorViewY = mTempLocation[1];
+    @VisibleForTesting
+    static int[] getPopupPosition(int[] tempLocation, boolean isByPermanentButton,
+            int negativeSoftwareVerticalOffset, int negativeVerticalOffsetNotTopAnchored,
+            int screenRotation, Rect appRect, Rect padding, View anchorView, int popupWidth,
+            int popupHeight, boolean isAnchorAtBottom, int viewLayoutDirection) {
+        anchorView.getLocationInWindow(tempLocation);
+        int anchorViewX = tempLocation[0];
+        int anchorViewY = tempLocation[1];
 
         int[] offsets = new int[2];
         // If we have a hardware menu button, locate the app menu closer to the estimated
         // hardware menu button location.
-        if (mIsByPermanentButton) {
+        if (isByPermanentButton) {
             int horizontalOffset = -anchorViewX;
             switch (screenRotation) {
                 case Surface.ROTATION_0:
@@ -328,21 +333,21 @@
             // padding of the background.
             offsets[1] = -padding.bottom;
         } else {
-            offsets[1] = -mNegativeSoftwareVerticalOffset;
+            offsets[1] = -negativeSoftwareVerticalOffset;
 
             // If the anchor is at the bottom of the screen, align the popup with the bottom of the
             // anchor. The anchor may not be fully visible, so
             // (appRect.bottom - anchorViewLocationOnScreenY) is used to determine the visible
             // bottom edge of the anchor view.
             if (isAnchorAtBottom) {
-                anchorView.getLocationOnScreen(mTempLocation);
-                int anchorViewLocationOnScreenY = mTempLocation[1];
+                anchorView.getLocationOnScreen(tempLocation);
+                int anchorViewLocationOnScreenY = tempLocation[1];
                 offsets[1] += appRect.bottom - anchorViewLocationOnScreenY - popupHeight;
-                offsets[1] -= mNegativeVerticalOffsetNotTopAnchored;
-                if (!mIsByPermanentButton) offsets[1] += padding.bottom;
+                offsets[1] -= negativeVerticalOffsetNotTopAnchored;
+                offsets[1] += padding.bottom;
             }
 
-            if (anchorView.getRootView().getLayoutDirection() != View.LAYOUT_DIRECTION_RTL) {
+            if (viewLayoutDirection != View.LAYOUT_DIRECTION_RTL) {
                 offsets[0] = anchorView.getWidth() - popupWidth;
             }
         }
@@ -364,10 +369,15 @@
     @Override
     public boolean onItemLongClick(MenuItem menuItem, View view) {
         if (!menuItem.isEnabled()) return false;
-        Context context = ContextUtils.getApplicationContext();
         CharSequence titleCondensed = menuItem.getTitleCondensed();
         CharSequence message =
                 TextUtils.isEmpty(titleCondensed) ? menuItem.getTitle() : titleCondensed;
+        return showToastForItem(message, view);
+    }
+
+    @VisibleForTesting
+    boolean showToastForItem(CharSequence message, View view) {
+        Context context = ContextUtils.getApplicationContext();
         return Toast.showAnchoredToast(context, view, message);
     }
 
@@ -545,4 +555,9 @@
     void finishAnimationsForTests() {
         if (mMenuItemEnterAnimator != null) mMenuItemEnterAnimator.end();
     }
+
+    @VisibleForTesting
+    AppMenuAdapter getAdapterForTests() {
+        return mAdapter;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinatorImpl.java
index d7166162..4ef6738c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinatorImpl.java
@@ -13,6 +13,8 @@
 
 /** A UI coordinator the app menu. */
 class AppMenuCoordinatorImpl implements AppMenuCoordinator {
+    private static Boolean sHasPermanentMenuKeyForTesting;
+
     /**
      * Factory which creates the AppMenuHandlerImpl.
      */
@@ -79,7 +81,9 @@
     public void showAppMenuForKeyboardEvent() {
         if (mAppMenuHandler == null || !mAppMenuHandler.shouldShowAppMenu()) return;
 
-        boolean hasPermanentMenuKey = ViewConfiguration.get(mContext).hasPermanentMenuKey();
+        boolean hasPermanentMenuKey = sHasPermanentMenuKeyForTesting != null
+                ? sHasPermanentMenuKeyForTesting.booleanValue()
+                : ViewConfiguration.get(mContext).hasPermanentMenuKey();
         mAppMenuHandler.showAppMenu(
                 hasPermanentMenuKey ? null : mButtonDelegate.getMenuButtonView(), false,
                 mButtonDelegate.isMenuFromBottom());
@@ -111,4 +115,13 @@
     AppMenuHandlerImpl getAppMenuHandlerImplForTesting() {
         return mAppMenuHandler;
     }
+
+    /**
+     * @param hasPermanentMenuKey Overrides {@link ViewConfiguration#hasPermanentMenuKey()} for
+     *         testing. Pass null to reset.
+     */
+    @VisibleForTesting
+    static void setHasPermanentMenuKeyForTesting(Boolean hasPermanentMenuKey) {
+        sHasPermanentMenuKeyForTesting = hasPermanentMenuKey;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java
index dbc7477..302ba29f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java
@@ -18,6 +18,7 @@
 
 import androidx.annotation.IntDef;
 
+import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
@@ -299,7 +300,8 @@
     /**
      * @return Visible rect in screen coordinates for the given View.
      */
-    private Rect getScreenVisibleRect(View view) {
+    @VisibleForTesting
+    Rect getScreenVisibleRect(View view) {
         view.getLocalVisibleRect(mScreenVisibleRect);
         view.getLocationOnScreen(mScreenVisiblePoint);
         mScreenVisibleRect.offset(mScreenVisiblePoint[0], mScreenVisiblePoint[1]);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
index 21ee2d4..490d034 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
@@ -162,8 +162,6 @@
                 ApiCompatibilityUtils.getColor(resources, R.color.drag_handlebar_color);
         mButtonPaddingDps =
                 (int) (mPxToDp * resources.getDimension(R.dimen.overlay_panel_button_padding));
-        mBarShadowVisible = true;
-        mPanelShadowVisible = true;
     }
 
     // ============================================================================================
@@ -412,8 +410,6 @@
     // --------------------------------------------------------------------------------------------
     private final float mBarMarginSide;
     private final float mBarMarginTop;
-    private final boolean mBarShadowVisible;
-    private final boolean mPanelShadowVisible;
 
     private float mBarHeight;
     private boolean mIsBarBorderVisible;
@@ -462,13 +458,6 @@
     }
 
     /**
-     * @return Whether the Bar shadow is visible (between the Bar and content).
-     */
-    public boolean getBarShadowVisible() {
-        return mBarShadowVisible;
-    }
-
-    /**
      * @return The background color of the Bar.
      */
     public int getBarBackgroundColor() {
@@ -476,13 +465,6 @@
     }
 
     /**
-     * @return Whether the shadow for the entire Panel & Bar is visible.
-     */
-    public boolean getPanelShadowVisible() {
-        return mPanelShadowVisible;
-    }
-
-    /**
      * @return The tint used for icons.
      */
     public int getIconColor() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
index 5a1ac80..82d249a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
@@ -112,8 +112,6 @@
         boolean searchBarBorderVisible = panel.isBarBorderVisible();
         float searchBarBorderHeight = panel.getBarBorderHeight();
 
-        boolean searchBarShadowVisible = panel.getBarShadowVisible();
-
         final int iconColor = panel.getIconColor();
         final int dragHandlebarColor = panel.getDragHandlebarColor();
         float arrowIconOpacity = panel.getArrowIconOpacity();
@@ -146,10 +144,9 @@
         // The panel shadow goes all the way around in the old layout, but in the new layout
         // the top_round resource also includes the shadow so we only need a side shadow.
         // In either case there's just one shadow-only resource needed.
-        int panelShadowResourceId = panel.getPanelShadowVisible()
-                ? (OverlayPanel.isNewLayout() ? R.drawable.overlay_side_shadow
-                                              : R.drawable.contextual_search_bar_background)
-                : INVALID_RESOURCE_ID;
+        int panelShadowResourceId = OverlayPanel.isNewLayout()
+                ? R.drawable.overlay_side_shadow
+                : R.drawable.contextual_search_bar_background;
         int closeIconResourceId = OverlayPanel.isNewLayout()
                 ? INVALID_RESOURCE_ID
                 : ContextualSearchPanel.CLOSE_ICON_DRAWABLE_ID;
@@ -174,7 +171,7 @@
                 searchBarControl.getTextLayerMinHeight(), searchTermOpacity,
                 searchBarControl.getSearchTermCaptionSpacing(), searchCaptionAnimationPercentage,
                 searchCaptionVisible, searchBarBorderVisible, searchBarBorderHeight * mDpToPx,
-                searchBarShadowVisible, quickActionIconVisible, thumbnailVisible, thumbnailUrl,
+                quickActionIconVisible, thumbnailVisible, thumbnailUrl,
                 customImageVisibilityPercentage, barImageSize, iconColor, dragHandlebarColor,
                 arrowIconOpacity, arrowIconRotation, closeIconOpacity, isProgressBarVisible,
                 progressBarHeight * mDpToPx, progressBarOpacity, progressBarCompletion,
@@ -249,8 +246,8 @@
                 float searchTextLayerMinHeight, float searchTermOpacity,
                 float searchTermCaptionSpacing, float searchCaptionAnimationPercentage,
                 boolean searchCaptionVisible, boolean searchBarBorderVisible,
-                float searchBarBorderHeight, boolean searchBarShadowVisible,
-                boolean quickActionIconVisible, boolean thumbnailVisible, String thumbnailUrl,
+                float searchBarBorderHeight, boolean quickActionIconVisible,
+                boolean thumbnailVisible, String thumbnailUrl,
                 float customImageVisibilityPercentage, int barImageSize, int iconColor,
                 int dragHandlebarColor, float arrowIconOpacity, float arrowIconRotation,
                 float closeIconOpacity, boolean isProgressBarVisible, float progressBarHeight,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/EphemeralTabSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/EphemeralTabSceneLayer.java
index 780714b..a42e8e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/EphemeralTabSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/EphemeralTabSceneLayer.java
@@ -32,8 +32,6 @@
     /** The conversion multiple from dp to px. */
     private final float mDpToPx;
 
-    private final int mFaviconSizePx;
-
     /** Interface to get notified that favicon is available. */
     interface FaviconCallback {
         /**
@@ -50,7 +48,6 @@
      */
     public EphemeralTabSceneLayer(float dpToPx, int faviconSizeDp) {
         mDpToPx = dpToPx;
-        mFaviconSizePx = (int) (faviconSizeDp * dpToPx);
     }
 
     /**
@@ -80,10 +77,9 @@
             // The panel shadow goes all the way around in the old layout, but in the new layout
             // the top_round resource also includes the shadow so we only need a side shadow.
             // In either case there's just one shadow-only resource needed.
-            int panelShadowResourceId = panel.getPanelShadowVisible()
-                    ? (OverlayPanel.isNewLayout() ? R.drawable.overlay_side_shadow
-                                                  : R.drawable.contextual_search_bar_background)
-                    : INVALID_RESOURCE_ID;
+            int panelShadowResourceId = OverlayPanel.isNewLayout()
+                    ? R.drawable.overlay_side_shadow
+                    : R.drawable.contextual_search_bar_background;
             EphemeralTabSceneLayerJni.get().setResourceIds(mNativePtr, EphemeralTabSceneLayer.this,
                     title.getViewId(), panelShadowResourceId, roundedBarTopId,
                     R.drawable.modern_toolbar_shadow, R.drawable.infobar_chrome, dragHandlebarId,
@@ -121,9 +117,9 @@
                 panel.getBarBackgroundColor(), panel.getBarMarginSide() * mDpToPx,
                 panel.getBarMarginTop() * mDpToPx, panel.getBarHeight() * mDpToPx,
                 panel.isBarBorderVisible(), panel.getBarBorderHeight() * mDpToPx,
-                panel.getBarShadowVisible(), panel.getIconColor(), panel.getDragHandlebarColor(),
-                panel.getFaviconOpacity(), isProgressBarVisible, progressBarHeight * mDpToPx,
-                progressBarOpacity, progressBarCompletion, separatorLineColor);
+                panel.getIconColor(), panel.getDragHandlebarColor(), panel.getFaviconOpacity(),
+                isProgressBarVisible, progressBarHeight * mDpToPx, progressBarOpacity,
+                progressBarCompletion, separatorLineColor);
     }
 
     @Override
@@ -179,9 +175,9 @@
                 float basePageBrightness, float basePageYOffset, WebContents webContents,
                 float panelX, float panelY, float panelWidth, float panelHeight,
                 int barBackgroundColor, float barMarginSide, float barMarginTop, float barHeight,
-                boolean barBorderVisible, float barBorderHeight, boolean barShadowVisible,
-                int iconColor, int dragHandlebarColor, float faviconOpacity,
-                boolean isProgressBarVisible, float progressBarHeight, float progressBarOpacity,
-                float progressBarCompletion, int separatorLineColor);
+                boolean barBorderVisible, float barBorderHeight, int iconColor,
+                int dragHandlebarColor, float faviconOpacity, boolean isProgressBarVisible,
+                float progressBarHeight, float progressBarOpacity, float progressBarCompletion,
+                int separatorLineColor);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index 5e09720..1c7c1fe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -223,7 +223,7 @@
         void onCanMakePaymentReturned();
         void onHasEnrolledInstrumentCalled();
         void onHasEnrolledInstrumentReturned();
-        void onShowInstrumentsReady();
+        void onShowAppsReady();
         void onNotSupportedError();
         void onConnectionTerminated();
         void onAbortCalled();
@@ -2431,7 +2431,7 @@
     private boolean disconnectIfNoPaymentMethodsSupported() {
         if (!isFinishedQueryingPaymentApps() || !mIsCurrentPaymentRequestShowing) return false;
         if (mNativeObserverForTest != null) {
-            mNativeObserverForTest.onShowInstrumentsReady();
+            mNativeObserverForTest.onShowAppsReady();
         }
 
         boolean foundPaymentMethods =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java
index 475435e..eec13edc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java
@@ -363,6 +363,9 @@
             case ContentSettingsType.MEDIASTREAM_MIC:
                 PrefServiceBridgeJni.get().setMicEnabled(allow);
                 break;
+            case ContentSettingsType.NFC:
+                PrefServiceBridgeJni.get().setNfcEnabled(allow);
+                break;
             case ContentSettingsType.NOTIFICATIONS:
                 PrefServiceBridgeJni.get().setNotificationsEnabled(allow);
                 break;
@@ -403,6 +406,8 @@
                 return PrefServiceBridgeJni.get().getCameraEnabled();
             case ContentSettingsType.MEDIASTREAM_MIC:
                 return PrefServiceBridgeJni.get().getMicEnabled();
+            case ContentSettingsType.NFC:
+                return PrefServiceBridgeJni.get().getNfcEnabled();
             case ContentSettingsType.NOTIFICATIONS:
                 return PrefServiceBridgeJni.get().getNotificationsEnabled();
             case ContentSettingsType.SENSORS:
@@ -545,6 +550,7 @@
         boolean getMicManagedByCustodian();
         boolean getIncognitoModeEnabled();
         boolean getIncognitoModeManaged();
+        boolean getNfcEnabled();
         boolean getSensorsEnabled();
         boolean getSoundEnabled();
         void setAutomaticDownloadsEnabled(boolean enabled);
@@ -556,6 +562,7 @@
         boolean getNotificationsEnabled();
         void setAllowLocationEnabled(boolean enabled);
         void setNotificationsEnabled(boolean enabled);
+        void setNfcEnabled(boolean enabled);
         void setSensorsEnabled(boolean enabled);
         void setSoundEnabled(boolean enabled);
         boolean canPrefetchAndPrerender();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java
index 089b8d7..a14bc427 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java
@@ -161,6 +161,11 @@
             localMap.put(ContentSettingsType.MIDI_SYSEX,
                     new ResourceItem(R.drawable.permission_midi, 0,
                             R.string.midi_sysex_permission_title, null, null, 0, 0));
+            localMap.put(ContentSettingsType.NFC,
+                    new ResourceItem(R.drawable.ic_settings_nfc, R.string.nfc_permission_title,
+                            R.string.nfc_permission_title, ContentSettingValues.ASK,
+                            ContentSettingValues.BLOCK, R.string.website_settings_category_nfc_ask,
+                            R.string.website_settings_category_nfc_blocked));
             localMap.put(ContentSettingsType.NOTIFICATIONS,
                     new ResourceItem(R.drawable.permission_push_notification,
                             R.string.push_notifications_permission_title,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/PermissionInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/PermissionInfo.java
index 27e7def..35062d59 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/PermissionInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/PermissionInfo.java
@@ -29,13 +29,14 @@
         int GEOLOCATION = 2;
         int MICROPHONE = 3;
         int MIDI = 4;
-        int NOTIFICATION = 5;
-        int PROTECTED_MEDIA_IDENTIFIER = 6;
-        int SENSORS = 7;
+        int NFC = 5;
+        int NOTIFICATION = 6;
+        int PROTECTED_MEDIA_IDENTIFIER = 7;
+        int SENSORS = 8;
         /**
          * Number of handled permissions used for example inside for loops.
          */
-        int NUM_ENTRIES = 8;
+        int NUM_ENTRIES = 9;
     }
 
     private final boolean mIsIncognito;
@@ -90,6 +91,9 @@
             case Type.MIDI:
                 return WebsitePreferenceBridgeJni.get().getMidiSettingForOrigin(
                         mOrigin, getEmbedderSafe(), mIsIncognito);
+            case Type.NFC:
+                return WebsitePreferenceBridgeJni.get().getNfcSettingForOrigin(
+                        mOrigin, getEmbedderSafe(), mIsIncognito);
             case Type.NOTIFICATION:
                 return WebsitePreferenceBridgeJni.get().getNotificationSettingForOrigin(
                         mOrigin, mIsIncognito);
@@ -130,6 +134,10 @@
                 WebsitePreferenceBridgeJni.get().setMidiSettingForOrigin(
                         mOrigin, getEmbedderSafe(), value, mIsIncognito);
                 break;
+            case Type.NFC:
+                WebsitePreferenceBridgeJni.get().setNfcSettingForOrigin(
+                        mOrigin, getEmbedderSafe(), value, mIsIncognito);
+                break;
             case Type.NOTIFICATION:
                 WebsitePreferenceBridgeJni.get().setNotificationSettingForOrigin(
                         mOrigin, value, mIsIncognito);
@@ -159,6 +167,8 @@
                 return ContentSettingsType.MEDIASTREAM_MIC;
             case Type.MIDI:
                 return ContentSettingsType.MIDI_SYSEX;
+            case Type.NFC:
+                return ContentSettingsType.NFC;
             case Type.NOTIFICATION:
                 return ContentSettingsType.NOTIFICATIONS;
             case Type.PROTECTED_MEDIA_IDENTIFIER:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
index 803bb28..3aa72be 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
@@ -90,6 +90,7 @@
             "location_access_list", // PermissionInfo.Type.GEOLOCATION
             "microphone_permission_list", // PermissionInfo.Type.MICROPHONE
             "midi_sysex_permission_list", // PermissionInfo.Type.MIDI
+            "nfc_permission_list", // PermissionInfo.Type.NFC
             "push_notifications_list", // PermissionInfo.Type.NOTIFICATION
             "protected_media_identifier_permission_list",
             // PermissionInfo.Type.PROTECTED_MEDIA_IDENTIFIER
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java
index 23975ccc..d86083f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java
@@ -39,37 +39,39 @@
  */
 public class SiteSettingsCategory {
     @IntDef({Type.ALL_SITES, Type.ADS, Type.AUTOMATIC_DOWNLOADS, Type.AUTOPLAY,
-            Type.BACKGROUND_SYNC, Type.CAMERA, Type.CLIPBOARD, Type.COOKIES, Type.DEVICE_LOCATION,
-            Type.JAVASCRIPT, Type.MICROPHONE, Type.NOTIFICATIONS, Type.POPUPS, Type.PROTECTED_MEDIA,
-            Type.SENSORS, Type.SOUND, Type.USE_STORAGE, Type.USB, Type.BLUETOOTH_SCANNING})
+            Type.BACKGROUND_SYNC, Type.BLUETOOTH_SCANNING, Type.CAMERA, Type.CLIPBOARD,
+            Type.COOKIES, Type.DEVICE_LOCATION, Type.JAVASCRIPT, Type.MICROPHONE, Type.NFC,
+            Type.NOTIFICATIONS, Type.POPUPS, Type.PROTECTED_MEDIA, Type.SENSORS, Type.SOUND,
+            Type.USB, Type.USE_STORAGE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Type {
         // Values used to address array index - should be enumerated from 0 and can't have gaps.
         // All updates here must also be reflected in {@link #preferenceKey(int)
         // preferenceKey} and {@link #contentSettingsType(int) contentSettingsType}.
-        int ALL_SITES = 0;
+        int ALL_SITES = 0; // Always first as it should appear in the UI at the top.
         int ADS = 1;
-        int AUTOPLAY = 2;
-        int BACKGROUND_SYNC = 3;
-        int CAMERA = 4;
-        int CLIPBOARD = 5;
-        int COOKIES = 6;
-        int DEVICE_LOCATION = 7;
-        int JAVASCRIPT = 8;
-        int MICROPHONE = 9;
-        int NOTIFICATIONS = 10;
-        int POPUPS = 11;
-        int PROTECTED_MEDIA = 12;
-        int SENSORS = 13;
-        int SOUND = 14;
-        int USE_STORAGE = 15;
-        int USB = 16;
-        int AUTOMATIC_DOWNLOADS = 17;
-        int BLUETOOTH_SCANNING = 18;
+        int AUTOMATIC_DOWNLOADS = 2;
+        int AUTOPLAY = 3;
+        int BACKGROUND_SYNC = 4;
+        int BLUETOOTH_SCANNING = 5;
+        int CAMERA = 6;
+        int CLIPBOARD = 7;
+        int COOKIES = 8;
+        int DEVICE_LOCATION = 9;
+        int JAVASCRIPT = 10;
+        int MICROPHONE = 11;
+        int NFC = 12;
+        int NOTIFICATIONS = 13;
+        int POPUPS = 14;
+        int PROTECTED_MEDIA = 15;
+        int SENSORS = 16;
+        int SOUND = 17;
+        int USB = 18;
+        int USE_STORAGE = 19; // Always last as it should appear in the UI at the bottom.
         /**
          * Number of handled categories used for calculating array sizes.
          */
-        int NUM_ENTRIES = 19;
+        int NUM_ENTRIES = 20;
     }
 
     // The id of this category.
@@ -153,6 +155,8 @@
                 return ContentSettingsType.JAVASCRIPT;
             case Type.MICROPHONE:
                 return ContentSettingsType.MEDIASTREAM_MIC;
+            case Type.NFC:
+                return ContentSettingsType.NFC;
             case Type.NOTIFICATIONS:
                 return ContentSettingsType.NOTIFICATIONS;
             case Type.POPUPS:
@@ -214,6 +218,8 @@
                 return "javascript";
             case Type.MICROPHONE:
                 return "microphone";
+            case Type.NFC:
+                return "nfc";
             case Type.NOTIFICATIONS:
                 return "notifications";
             case Type.POPUPS:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java
index 14463e8..34334840 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java
@@ -15,6 +15,7 @@
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.preferences.PreferenceUtils;
 import org.chromium.chrome.browser.preferences.website.SiteSettingsCategory.Type;
+import org.chromium.content_public.browser.ContentFeatureList;
 import org.chromium.content_public.common.ContentSwitches;
 
 import java.util.ArrayList;
@@ -91,6 +92,9 @@
             if (!commandLine.hasSwitch(ContentSwitches.ENABLE_EXPERIMENTAL_WEB_PLATFORM_FEATURES)) {
                 getPreferenceScreen().removePreference(findPreference(Type.BLUETOOTH_SCANNING));
             }
+            if (!ContentFeatureList.isEnabled(ContentFeatureList.WEB_NFC)) {
+                getPreferenceScreen().removePreference(findPreference(Type.NFC));
+            }
         }
     }
 
@@ -118,6 +122,9 @@
             websitePrefs.add(Type.JAVASCRIPT);
             websitePrefs.add(Type.DEVICE_LOCATION);
             websitePrefs.add(Type.MICROPHONE);
+            if (ContentFeatureList.isEnabled(ContentFeatureList.WEB_NFC)) {
+                websitePrefs.add(Type.NFC);
+            }
             websitePrefs.add(Type.NOTIFICATIONS);
             websitePrefs.add(Type.POPUPS);
             websitePrefs.add(Type.SENSORS);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java
index 45b54cc5..58d578a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java
@@ -10,6 +10,7 @@
 import org.chromium.base.CommandLine;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.ContentSettingsType;
+import org.chromium.content_public.browser.ContentFeatureList;
 import org.chromium.content_public.common.ContentSwitches;
 
 import java.util.ArrayList;
@@ -130,6 +131,10 @@
             // Bluetooth scanning permission is per-origin.
             queue.add(new ExceptionInfoFetcher(ContentSettingsType.BLUETOOTH_SCANNING));
         }
+        if (ContentFeatureList.isEnabled(ContentFeatureList.WEB_NFC)) {
+            // NFC permission is per-origin and per-embedder.
+            queue.add(new PermissionInfoFetcher(PermissionInfo.Type.NFC));
+        }
 
         queue.add(new PermissionsAvailableCallbackRunner(callback));
 
@@ -214,6 +219,11 @@
                 // Bluetooth scanning permission is per-origin.
                 queue.add(new ExceptionInfoFetcher(ContentSettingsType.BLUETOOTH_SCANNING));
             }
+        } else if (category.showSites(SiteSettingsCategory.Type.NFC)) {
+            if (ContentFeatureList.isEnabled(ContentFeatureList.WEB_NFC)) {
+                // NFC permission is per-origin and per-embedder.
+                queue.add(new PermissionInfoFetcher(PermissionInfo.Type.NFC));
+            }
         }
         queue.add(new PermissionsAvailableCallbackRunner(callback));
         queue.next();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferenceBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferenceBridge.java
index ce9097b8..281099c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferenceBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferenceBridge.java
@@ -48,6 +48,8 @@
             WebsitePreferenceBridgeJni.get().getMicrophoneOrigins(list, managedOnly);
         } else if (type == PermissionInfo.Type.MIDI) {
             WebsitePreferenceBridgeJni.get().getMidiOrigins(list);
+        } else if (type == PermissionInfo.Type.NFC) {
+            WebsitePreferenceBridgeJni.get().getNfcOrigins(list);
         } else if (type == PermissionInfo.Type.NOTIFICATION) {
             WebsitePreferenceBridgeJni.get().getNotificationOrigins(list);
         } else if (type == PermissionInfo.Type.PROTECTED_MEDIA_IDENTIFIER) {
@@ -103,6 +105,12 @@
     }
 
     @CalledByNative
+    private static void insertNfcInfoIntoList(
+            ArrayList<PermissionInfo> list, String origin, String embedder) {
+        insertInfoIntoList(PermissionInfo.Type.NFC, list, origin, embedder);
+    }
+
+    @CalledByNative
     private static void insertNotificationIntoList(
             ArrayList<PermissionInfo> list, String origin, String embedder) {
         insertInfoIntoList(PermissionInfo.Type.NOTIFICATION, list, origin, embedder);
@@ -223,6 +231,7 @@
         void getMicrophoneOrigins(Object list, boolean managedOnly);
         void getMidiOrigins(Object list);
         void getNotificationOrigins(Object list);
+        void getNfcOrigins(Object list);
         void getProtectedMediaIdentifierOrigins(Object list);
         void getSensorsOrigins(Object list);
         int getCameraSettingForOrigin(String origin, String embedder, boolean isIncognito);
@@ -230,6 +239,7 @@
         int getGeolocationSettingForOrigin(String origin, String embedder, boolean isIncognito);
         int getMicrophoneSettingForOrigin(String origin, String embedder, boolean isIncognito);
         int getMidiSettingForOrigin(String origin, String embedder, boolean isIncognito);
+        int getNfcSettingForOrigin(String origin, String embedder, boolean isIncognito);
         int getNotificationSettingForOrigin(String origin, boolean isIncognito);
         int getProtectedMediaIdentifierSettingForOrigin(
                 String origin, String embedder, boolean isIncognito);
@@ -241,6 +251,7 @@
         void setMicrophoneSettingForOrigin(String origin, int value, boolean isIncognito);
         void setMidiSettingForOrigin(
                 String origin, String embedder, int value, boolean isIncognito);
+        void setNfcSettingForOrigin(String origin, String embedder, int value, boolean isIncognito);
         void setNotificationSettingForOrigin(String origin, int value, boolean isIncognito);
         void reportNotificationRevokedForOrigin(
                 String origin, int newSettingValue, boolean isIncognito);
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_NFC_PERMISSION_TITLE.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_NFC_PERMISSION_TITLE.png.sha1
new file mode 100644
index 0000000..dc92935
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_NFC_PERMISSION_TITLE.png.sha1
@@ -0,0 +1 @@
+fcd51a8c5a41e713decd5b840a727b3e99939749
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_WEBSITE_SETTINGS_CATEGORY_NFC_ASK.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_WEBSITE_SETTINGS_CATEGORY_NFC_ASK.png.sha1
new file mode 100644
index 0000000..aa1a377
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_WEBSITE_SETTINGS_CATEGORY_NFC_ASK.png.sha1
@@ -0,0 +1 @@
+dcc70d2f161b724d0f133f61f351960266fced6a
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_WEBSITE_SETTINGS_CATEGORY_NFC_BLOCKED.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_WEBSITE_SETTINGS_CATEGORY_NFC_BLOCKED.png.sha1
new file mode 100644
index 0000000..0063cff8
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_WEBSITE_SETTINGS_CATEGORY_NFC_BLOCKED.png.sha1
@@ -0,0 +1 @@
+79a40a1cc62f30a32d18ca40f74761f7064d241d
\ No newline at end of file
diff --git a/chrome/android/javatests/res/drawable/test_ic_arrow_downward_black_24dp.xml b/chrome/android/javatests/res/drawable/test_ic_arrow_downward_black_24dp.xml
index 598cee0..edb4fb0 100644
--- a/chrome/android/javatests/res/drawable/test_ic_arrow_downward_black_24dp.xml
+++ b/chrome/android/javatests/res/drawable/test_ic_arrow_downward_black_24dp.xml
@@ -5,10 +5,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     tools:targetApi="21"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
     <path
         android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"
         android:fillColor="#010101"/>
diff --git a/chrome/android/javatests/res/drawable/test_ic_arrow_forward_black_24dp.xml b/chrome/android/javatests/res/drawable/test_ic_arrow_forward_black_24dp.xml
index 8eb68b5a..86338fee 100644
--- a/chrome/android/javatests/res/drawable/test_ic_arrow_forward_black_24dp.xml
+++ b/chrome/android/javatests/res/drawable/test_ic_arrow_forward_black_24dp.xml
@@ -5,10 +5,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     tools:targetApi="21"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
     <path
         android:fillColor="#FF000000"
         android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
diff --git a/chrome/android/javatests/res/drawable/test_ic_info_outline_black_24dp.xml b/chrome/android/javatests/res/drawable/test_ic_info_outline_black_24dp.xml
index 434620e8..5f801a6 100644
--- a/chrome/android/javatests/res/drawable/test_ic_info_outline_black_24dp.xml
+++ b/chrome/android/javatests/res/drawable/test_ic_info_outline_black_24dp.xml
@@ -5,10 +5,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     tools:targetApi="21"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
     <path
         android:fillColor="#FF000000"
         android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
diff --git a/chrome/android/javatests/res/drawable/test_ic_more_vert_black_24dp.xml b/chrome/android/javatests/res/drawable/test_ic_more_vert_black_24dp.xml
new file mode 100644
index 0000000..f381a3b
--- /dev/null
+++ b/chrome/android/javatests/res/drawable/test_ic_more_vert_black_24dp.xml
@@ -0,0 +1,15 @@
+<?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. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/chrome/android/javatests/res/drawable/test_ic_refresh_black_24dp.xml b/chrome/android/javatests/res/drawable/test_ic_refresh_black_24dp.xml
index e411381..88c03d04 100644
--- a/chrome/android/javatests/res/drawable/test_ic_refresh_black_24dp.xml
+++ b/chrome/android/javatests/res/drawable/test_ic_refresh_black_24dp.xml
@@ -5,10 +5,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     tools:targetApi="21"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
     <path
         android:fillColor="#FF000000"
         android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
diff --git a/chrome/android/javatests/res/drawable/test_ic_star_border_black_24dp.xml b/chrome/android/javatests/res/drawable/test_ic_star_border_black_24dp.xml
index a075fd16..be562e0 100644
--- a/chrome/android/javatests/res/drawable/test_ic_star_border_black_24dp.xml
+++ b/chrome/android/javatests/res/drawable/test_ic_star_border_black_24dp.xml
@@ -5,10 +5,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     tools:targetApi="21"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
     <path
         android:fillColor="#FF000000"
         android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
diff --git a/chrome/android/javatests/res/layout/test_app_menu_activity_layout.xml b/chrome/android/javatests/res/layout/test_app_menu_activity_layout.xml
new file mode 100644
index 0000000..df75306b
--- /dev/null
+++ b/chrome/android/javatests/res/layout/test_app_menu_activity_layout.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. -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent" >
+
+    <ImageButton
+        android:id="@+id/top_button"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        app:srcCompat="@drawable/test_ic_more_vert_black_24dp"
+        android:layout_gravity="top|end"
+        tools:ignore="ContentDescription" />
+
+    <ImageButton
+        android:id="@+id/bottom_button"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        app:srcCompat="@drawable/test_ic_more_vert_black_24dp"
+        android:layout_gravity="bottom|end"
+        tools:ignore="ContentDescription" />
+
+    <View
+        android:id="@+id/menu_anchor_stub"
+        android:layout_width="0px"
+        android:layout_height="0px"
+        android:layout_gravity="bottom|start" />
+</FrameLayout>
\ No newline at end of file
diff --git a/chrome/android/javatests/res/menu/test_menu.xml b/chrome/android/javatests/res/menu/test_menu.xml
index 8e98dd12..ebfea06c 100644
--- a/chrome/android/javatests/res/menu/test_menu.xml
+++ b/chrome/android/javatests/res/menu/test_menu.xml
@@ -18,7 +18,8 @@
             <item android:id="@+id/icon_one"
                 android:title="Icon One" />
             <item android:id="@+id/icon_two"
-                android:title="Icon Two" />
+                android:title="Icon Two"
+                android:titleCondensed="2" />
             <item android:id="@+id/icon_three"
                 android:title="Icon Three" />
         </menu>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuAdapterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuAdapterTest.java
index c25f50b..587cb93 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuAdapterTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuAdapterTest.java
@@ -310,6 +310,28 @@
                 view5.getTag(R.id.menu_item_enter_anim_id));
     }
 
+    @Test
+    @MediumTest
+    public void testTitleMenuItem_ToggleCheckbox() {
+        List<MenuItem> items = new ArrayList<>();
+        items.add(buildTitleMenuItem(1, 2, TITLE_1, 3, TITLE_2));
+
+        AppMenuAdapter adapter = new AppMenuAdapter(
+                mClickHandler, items, getActivity().getLayoutInflater(), 0, null);
+
+        ViewGroup parentView = getActivity().findViewById(android.R.id.content);
+        View view = adapter.getView(0, null, parentView);
+        AppMenuItemIcon checkbox = view.findViewById(R.id.checkbox);
+
+        Assert.assertFalse("Checkbox should be unchecked", checkbox.isChecked());
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> checkbox.toggle());
+        Assert.assertTrue("Checkbox should be checked", checkbox.isChecked());
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> checkbox.toggle());
+        Assert.assertFalse("Checkbox should be unchecked again", checkbox.isChecked());
+    }
+
     static MenuItem buildMenuItem(int id, CharSequence title) {
         return buildMenuItem(id, title, true);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java
index e38bde54..efc5b48 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java
@@ -4,16 +4,23 @@
 
 package org.chromium.chrome.browser.appmenu;
 
+import android.graphics.Rect;
 import android.support.test.filters.MediumTest;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
 
+import org.junit.AfterClass;
 import org.junit.Assert;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
@@ -23,10 +30,12 @@
 import org.chromium.chrome.browser.ui.widget.highlight.ViewHighlighterTestUtils;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.R;
+import org.chromium.chrome.test.ui.DummyUiActivity;
 import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -42,6 +51,12 @@
     private TestAppMenuDelegate mDelegate;
     private TestAppMenuObserver mMenuObserver;
     private TestActivityLifecycleDispatcher mLifecycleDispatcher;
+    private TestMenuButtonDelegate mTestMenuButtonDelegate;
+
+    @BeforeClass
+    public static void setUpBeforeActivityLaunched() {
+        DummyUiActivity.setTestLayout(R.layout.test_app_menu_activity_layout);
+    }
 
     @Override
     public void setUpTest() throws Exception {
@@ -50,12 +65,18 @@
         mLifecycleDispatcher.observerRegisteredCallbackHelper.waitForCallback(0);
     }
 
+    @AfterClass
+    public static void tearDownAfterActivityDestroyed() {
+        AppMenuCoordinatorImpl.setHasPermanentMenuKeyForTesting(null);
+    }
+
     private void setUpTestOnUiThread() {
         mLifecycleDispatcher = new TestActivityLifecycleDispatcher();
         mDelegate = new TestAppMenuDelegate();
+        mTestMenuButtonDelegate = new TestMenuButtonDelegate();
         mAppMenuCoordinator = new AppMenuCoordinatorImpl(getActivity(), mLifecycleDispatcher,
-                new TestMenuButtonDelegate(), mDelegate, getActivity().getWindow().getDecorView(),
-                new View(getActivity()));
+                mTestMenuButtonDelegate, mDelegate, getActivity().getWindow().getDecorView(),
+                getActivity().findViewById(R.id.menu_anchor_stub));
         mAppMenuHandler = mAppMenuCoordinator.getAppMenuHandlerImplForTesting();
         mMenuObserver = new TestAppMenuObserver();
         mAppMenuCoordinator.getAppMenuHandler().addObserver(mMenuObserver);
@@ -66,7 +87,7 @@
     @Test
     @MediumTest
     public void testShowHideAppMenu() throws TimeoutException {
-        showSimpleMenuAndAssert();
+        showMenuAndAssert();
 
         TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
         mMenuObserver.menuHiddenCallback.waitForCallback(0);
@@ -81,8 +102,87 @@
 
     @Test
     @MediumTest
+    public void testHideAppMenuMultiple() throws TimeoutException {
+        showMenuAndAssert();
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.getAppMenu().dismiss());
+        mMenuObserver.menuHiddenCallback.waitForCallback(0);
+
+        Assert.assertEquals("Incorrect number of calls to #onMenuDismissed after first call", 1,
+                mPropertiesDelegate.menuDismissedCallback.getCallCount());
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.getAppMenu().dismiss());
+        Assert.assertEquals("Incorrect number of calls to #onMenuDismissed after second call", 1,
+                mPropertiesDelegate.menuDismissedCallback.getCallCount());
+    }
+
+    @Test
+    @MediumTest
+    public void testShowAppMenu_AnchorTop() throws TimeoutException {
+        AppMenuCoordinatorImpl.setHasPermanentMenuKeyForTesting(false);
+        showMenuAndAssert();
+
+        View topAnchor = getActivity().findViewById(R.id.top_button);
+        Rect viewRect = getViewLocationRect(topAnchor);
+        Rect popupRect = getPopupLocationRect();
+
+        // Check that top right corner of app menu aligns with the top right corner of the anchor.
+        int alignmentSlop = viewRect.bottom - viewRect.top;
+        Assert.assertEquals("Popup should overlap top anchor. Anchor rect: " + viewRect
+                        + ", popup rect: " + popupRect,
+                viewRect.top, popupRect.top, alignmentSlop);
+        Assert.assertTrue("Popup should overlap top anchor. Anchor rect: " + viewRect
+                        + ", popup rect: " + popupRect,
+                viewRect.top <= popupRect.top);
+        Assert.assertEquals("Popup should be aligned with right of anchor. Anchor rect: " + viewRect
+                        + ", popup rect: " + popupRect,
+                viewRect.right, popupRect.right);
+    }
+
+    @Test
+    @MediumTest
+    public void testShowAppMenu_AnchorBottom() throws TimeoutException {
+        AppMenuCoordinatorImpl.setHasPermanentMenuKeyForTesting(false);
+        mTestMenuButtonDelegate.useBottomAnchor = true;
+        showMenuAndAssert();
+
+        View bottomAnchor = getActivity().findViewById(R.id.bottom_button);
+        Rect viewRect = getViewLocationRect(bottomAnchor);
+        Rect popupRect = getPopupLocationRect();
+
+        // Check that bottom right corner of app menu aligns with bottom right corner of the anchor.
+        int alignmentSlop = viewRect.bottom - viewRect.top;
+        Assert.assertEquals("Popup should be overlap bottom anchor. Anchor rect: " + viewRect
+                        + ", popup rect: " + popupRect,
+                viewRect.bottom, popupRect.bottom, alignmentSlop);
+        Assert.assertTrue("Popup should overlap bottom anchor. Anchor rect: " + viewRect
+                        + ", popup rect: " + popupRect,
+                viewRect.bottom >= popupRect.bottom);
+        Assert.assertEquals("Popup should be aligned with right of anchor. Anchor rect: " + viewRect
+                        + ", popup rect: " + popupRect,
+                viewRect.right, popupRect.right);
+    }
+
+    @Test
+    @MediumTest
+    public void testShowAppMenu_PermanentButton() throws TimeoutException {
+        AppMenuCoordinatorImpl.setHasPermanentMenuKeyForTesting(true);
+        showMenuAndAssert();
+
+        View anchorStub = getActivity().findViewById(R.id.menu_anchor_stub);
+        Rect viewRect = getViewLocationRect(anchorStub);
+        Rect popupRect = getPopupLocationRect();
+
+        // Check a basic alignment property. Full coverage checked in unit tests.
+        Assert.assertNotEquals("Popup should be offset from right of anchor."
+                        + "Anchor rect: " + viewRect + ", popup rect: " + popupRect,
+                viewRect.right, popupRect.right);
+    }
+
+    @Test
+    @MediumTest
     public void testShowDestroyAppMenu() throws TimeoutException {
-        showSimpleMenuAndAssert();
+        showMenuAndAssert();
 
         TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuCoordinator.destroy());
 
@@ -93,20 +193,96 @@
     @Test
     @MediumTest
     public void testClickMenuItem() throws TimeoutException {
-        showSimpleMenuAndAssert();
+        showMenuAndAssert();
 
         TestThreadUtils.runOnUiThreadBlocking(
                 ()
                         -> AppMenuTestSupport.callOnItemClick(
                                 mAppMenuCoordinator, R.id.menu_item_three));
-        mDelegate.itemSelectedCallbackHelper.waitForCallback(0);
 
+        Assert.assertEquals("Item selected callback should have been called.", 1,
+                mDelegate.itemSelectedCallbackHelper.getCallCount());
         Assert.assertEquals("Incorrect id for last selected item.", R.id.menu_item_three,
                 mDelegate.lastSelectedItemId);
     }
 
     @Test
     @MediumTest
+    public void testClickMenuItem_Disabled() throws TimeoutException {
+        showMenuAndAssert();
+
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> AppMenuTestSupport.callOnItemClick(mAppMenuCoordinator, R.id.menu_item_two));
+
+        Assert.assertEquals("Item selected callback should not have been called.", 0,
+                mDelegate.itemSelectedCallbackHelper.getCallCount());
+    }
+
+    @Test
+    @MediumTest
+    public void testClickMenuItem_UsingPosition() throws TimeoutException {
+        showMenuAndAssert();
+
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> mAppMenuHandler.getAppMenu().onItemClick(null, null, 0, 0));
+
+        Assert.assertEquals("Item selected callback should have been called.", 1,
+                mDelegate.itemSelectedCallbackHelper.getCallCount());
+        Assert.assertEquals("Incorrect id for last selected item.", R.id.menu_item_one,
+                mDelegate.lastSelectedItemId);
+    }
+
+    @Test
+    @MediumTest
+    public void testLongClickMenuItem_Title() throws TimeoutException {
+        mPropertiesDelegate.enableAppIconRow = true;
+        showMenuAndAssert();
+        AppMenu spiedMenu = Mockito.spy(mAppMenuHandler.getAppMenu());
+
+        View dummyView = new View(getActivity());
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            spiedMenu.onItemLongClick(
+                    mAppMenuHandler.getAppMenu().getMenu().findItem(R.id.icon_one), dummyView);
+        });
+
+        Mockito.verify(spiedMenu, Mockito.times(1)).showToastForItem("Icon One", dummyView);
+    }
+
+    @Test
+    @MediumTest
+    public void testLongClickMenuItem_TitleCondensed() throws TimeoutException {
+        mPropertiesDelegate.enableAppIconRow = true;
+        showMenuAndAssert();
+        AppMenu spiedMenu = Mockito.spy(mAppMenuHandler.getAppMenu());
+
+        View dummyView = new View(getActivity());
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            spiedMenu.onItemLongClick(
+                    mAppMenuHandler.getAppMenu().getMenu().findItem(R.id.icon_two), dummyView);
+        });
+
+        Mockito.verify(spiedMenu, Mockito.times(1)).showToastForItem("2", dummyView);
+    }
+
+    @Test
+    @MediumTest
+    public void testLongClickMenuItem_Disabled() throws TimeoutException {
+        mPropertiesDelegate.enableAppIconRow = true;
+        showMenuAndAssert();
+        AppMenu spiedMenu = Mockito.spy(mAppMenuHandler.getAppMenu());
+
+        View dummyView = new View(getActivity());
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            spiedMenu.onItemLongClick(
+                    mAppMenuHandler.getAppMenu().getMenu().findItem(R.id.icon_three), dummyView);
+        });
+
+        Mockito.verify(spiedMenu, Mockito.times(0))
+                .showToastForItem(Mockito.any(CharSequence.class), Mockito.any(View.class));
+    }
+
+    @Test
+    @MediumTest
     public void testAppMenuBlockers() throws TimeoutException {
         Assert.assertTrue("App menu should be allowed to show, no blockers registered",
                 AppMenuTestSupport.shouldShowAppMenu(mAppMenuCoordinator));
@@ -126,7 +302,7 @@
         mAppMenuCoordinator.unregisterAppMenuBlocker(blocker1);
         Assert.assertTrue("App menu should be allowed to show, only blocker2 registered",
                 AppMenuTestSupport.shouldShowAppMenu(mAppMenuCoordinator));
-        showSimpleMenuAndAssert();
+        showMenuAndAssert();
     }
 
     @Test
@@ -139,7 +315,7 @@
         mMenuObserver.menuHighlightChangedCallback.waitForCallback(0);
         Assert.assertTrue(mMenuObserver.menuHighlighting);
 
-        showSimpleMenuAndAssert();
+        showMenuAndAssert();
 
         View itemView = getViewAtPosition(0);
         ViewHighlighterTestUtils.checkHighlightOn(itemView);
@@ -162,7 +338,7 @@
         mMenuObserver.menuHighlightChangedCallback.waitForCallback(0);
         Assert.assertTrue(mMenuObserver.menuHighlighting);
 
-        showSimpleMenuAndAssert();
+        showMenuAndAssert();
 
         View itemView = ((LinearLayout) getViewAtPosition(3)).getChildAt(0);
         ViewHighlighterTestUtils.checkHighlightOn(itemView);
@@ -176,7 +352,7 @@
     @Test
     @MediumTest
     public void testMenuItemContentChanged() throws TimeoutException {
-        showSimpleMenuAndAssert();
+        showMenuAndAssert();
         View itemView = getViewAtPosition(1);
         Assert.assertEquals("Menu item text incorrect", "Menu Item Two",
                 ((TextView) itemView.findViewById(R.id.menu_item_text)).getText());
@@ -197,7 +373,7 @@
     public void testHeaderFooter() throws TimeoutException {
         mPropertiesDelegate.headerResourceId = R.layout.test_menu_header;
         mPropertiesDelegate.footerResourceId = R.layout.test_menu_footer;
-        showSimpleMenuAndAssert();
+        showMenuAndAssert();
 
         mPropertiesDelegate.headerInflatedCallback.waitForCallback(0);
         mPropertiesDelegate.footerInflatedCallback.waitForCallback(0);
@@ -212,7 +388,7 @@
     @Test
     @MediumTest
     public void testAppMenuHiddenOnStopWithNative() throws TimeoutException {
-        showSimpleMenuAndAssert();
+        showMenuAndAssert();
         TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.onStopWithNative());
         Assert.assertFalse(mAppMenuHandler.isAppMenuShowing());
     }
@@ -220,15 +396,269 @@
     @Test
     @MediumTest
     public void testAppMenuHiddenOnConfigurationChange() throws TimeoutException {
-        showSimpleMenuAndAssert();
+        showMenuAndAssert();
         TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.onConfigurationChanged(null));
         Assert.assertFalse(mAppMenuHandler.isAppMenuShowing());
     }
 
-    private void showSimpleMenuAndAssert() throws TimeoutException {
+    @Test
+    @MediumTest
+    public void testAppMenuKeyEvent_HiddenOnHardwareButtonPress() throws Exception {
+        showMenuAndAssert();
+
+        AppMenu appMenu = mAppMenuHandler.getAppMenu();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            KeyEvent down = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU);
+            KeyEvent up = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU);
+            appMenu.onKey(appMenu.getListView(), KeyEvent.KEYCODE_MENU, down);
+            appMenu.onKey(appMenu.getListView(), KeyEvent.KEYCODE_MENU, up);
+        });
+        mMenuObserver.menuHiddenCallback.waitForCallback(0);
+    }
+
+    @Test
+    @MediumTest
+    public void testAppMenuKeyEvent_IgnoreUnrelatedKeyCode() throws Exception {
+        showMenuAndAssert();
+
+        AppMenu appMenu = mAppMenuHandler.getAppMenu();
+        KeyEvent unrelated = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BOOKMARK);
+        Assert.assertFalse("#onKeyEvent should return false for unrelated codes",
+                appMenu.onKey(null, KeyEvent.KEYCODE_BOOKMARK, unrelated));
+    }
+
+    @Test
+    @MediumTest
+    public void testAppMenuKeyEvent_IgnoreUnrelatedKeyEvent() throws Exception {
+        showMenuAndAssert();
+
+        AppMenu appMenu = mAppMenuHandler.getAppMenu();
+        KeyEvent unrelated = new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_MENU);
+        Assert.assertFalse("#onKeyEvent should return false for unrelated events",
+                appMenu.onKey(null, KeyEvent.KEYCODE_MENU, unrelated));
+    }
+
+    @Test
+    @MediumTest
+    public void testAppMenuKeyEvent_IgnoreEventsWhenHidden() throws Exception {
+        // Show app menu to initialize, then hide.
+        showMenuAndAssert();
+        TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
+        mMenuObserver.menuHiddenCallback.waitForCallback(0);
+
+        AppMenu appMenu = mAppMenuHandler.getAppMenu();
+        Assert.assertNull("ListView should be null.", appMenu.getListView());
+        KeyEvent down = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU);
+        Assert.assertFalse("#onKeyEvent should return false when app menu hidden",
+                appMenu.onKey(null, KeyEvent.KEYCODE_MENU, null));
+    }
+
+    @Test
+    @MediumTest
+    public void testAppMenuButtonHelper_DownUp() throws Exception {
+        AppMenuButtonHelperImpl buttonHelper =
+                (AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
+
+        Assert.assertFalse("View should start unpressed",
+                mTestMenuButtonDelegate.getMenuButtonView().isPressed());
+        Assert.assertFalse("App menu should be not be active", buttonHelper.isAppMenuActive());
+
+        MotionEvent downMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        sendMotionEventToButtonHelper(
+                buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
+
+        waitForMenuToShow(0);
+        Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
+        Assert.assertTrue(
+                "View should be pressed", mTestMenuButtonDelegate.getMenuButtonView().isPressed());
+        Assert.assertTrue("App menu should be active", buttonHelper.isAppMenuActive());
+
+        MotionEvent upMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+        sendMotionEventToButtonHelper(
+                buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), upMotionEvent);
+
+        Assert.assertFalse("View should no longer be pressed",
+                mTestMenuButtonDelegate.getMenuButtonView().isPressed());
+        Assert.assertTrue("App menu should still be active", buttonHelper.isAppMenuActive());
+    }
+
+    @Test
+    @MediumTest
+    public void testAppMenuButtonHelper_DownCancel() throws Exception {
+        AppMenuButtonHelperImpl buttonHelper =
+                (AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
+        Assert.assertFalse("View should start unpressed",
+                mTestMenuButtonDelegate.getMenuButtonView().isPressed());
+
+        MotionEvent downMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        sendMotionEventToButtonHelper(
+                buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
+
+        waitForMenuToShow(0);
+        Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
+
+        Assert.assertTrue(
+                "View should be pressed", mTestMenuButtonDelegate.getMenuButtonView().isPressed());
+
+        MotionEvent cancelMotionEvent =
+                MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
+        sendMotionEventToButtonHelper(
+                buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), cancelMotionEvent);
+
+        Assert.assertFalse("View should no longer be pressed",
+                mTestMenuButtonDelegate.getMenuButtonView().isPressed());
+    }
+
+    @Test
+    @MediumTest
+    public void testAppMenuButtonHelper_ClickRunnable() throws Exception {
+        Assert.assertFalse("View should start unpressed",
+                mTestMenuButtonDelegate.getMenuButtonView().isPressed());
+
+        AppMenuButtonHelperImpl buttonHelper =
+                (AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
+        CallbackHelper clickCallbackHelper = new CallbackHelper();
+        Runnable clickRunnable = () -> clickCallbackHelper.notifyCalled();
+        buttonHelper.setOnClickRunnable(clickRunnable);
+
+        MotionEvent downMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        sendMotionEventToButtonHelper(
+                buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
+
+        clickCallbackHelper.waitForCallback(0);
+        waitForMenuToShow(0);
+    }
+
+    @Test
+    @MediumTest
+    public void testAppMenuButtonHelper_ShowTwice() throws Exception {
+        AppMenuButtonHelperImpl buttonHelper =
+                (AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
+
+        CallbackHelper showCallbackHelper = new CallbackHelper();
+        Runnable showListener = () -> showCallbackHelper.notifyCalled();
+        buttonHelper.setOnAppMenuShownListener(showListener);
+
+        MotionEvent downMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        sendMotionEventToButtonHelper(
+                buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
+
+        waitForMenuToShow(0);
+        Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
+        Assert.assertEquals(
+                "Runnable should have been called once", 1, showCallbackHelper.getCallCount());
+
+        sendMotionEventToButtonHelper(
+                buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
+
+        Assert.assertEquals("Runnable should still only have been called once", 1,
+                showCallbackHelper.getCallCount());
+    }
+
+    @Test
+    @MediumTest
+    public void testAppMenuButtonHelper_ShowBlocked() throws Exception {
+        AppMenuButtonHelperImpl buttonHelper =
+                (AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
+        AppMenuBlocker blocker1 = () -> false;
+        mAppMenuCoordinator.registerAppMenuBlocker(blocker1);
+
+        CallbackHelper showCallbackHelper = new CallbackHelper();
+        Runnable showListener = () -> showCallbackHelper.notifyCalled();
+        buttonHelper.setOnAppMenuShownListener(showListener);
+
+        MotionEvent downMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        sendMotionEventToButtonHelper(
+                buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
+
+        Assert.assertEquals(
+                "Runnable should not have been called once", 0, showCallbackHelper.getCallCount());
+    }
+
+    @Test
+    @MediumTest
+    public void testAppMenuButtonHelper_AccessibilityActions() throws Exception {
+        AppMenuButtonHelperImpl buttonHelper =
+                (AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
+
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> buttonHelper.performAccessibilityAction(
+                                mTestMenuButtonDelegate.getMenuButtonView(),
+                                AccessibilityNodeInfo.ACTION_CLICK, null));
+
+        waitForMenuToShow(0);
+        Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
+
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> buttonHelper.performAccessibilityAction(
+                                mTestMenuButtonDelegate.getMenuButtonView(),
+                                AccessibilityNodeInfo.ACTION_CLICK, null));
+
+        mMenuObserver.menuHiddenCallback.waitForCallback(0);
+        Assert.assertFalse("Menu should be hidden", mAppMenuHandler.isAppMenuShowing());
+    }
+
+    @Test
+    @MediumTest
+    public void testAppMenuButtonHelper_showEnterKeyPress() throws Exception {
+        AppMenuButtonHelperImpl buttonHelper =
+                (AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
+
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> buttonHelper.onEnterKeyPress(mTestMenuButtonDelegate.getMenuButtonView()));
+
+        waitForMenuToShow(0);
+        Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
+    }
+
+    @Test
+    @MediumTest
+    public void testDragHelper_ClickItem() throws Exception {
+        AppMenuButtonHelperImpl buttonHelper =
+                (AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
+
+        Assert.assertFalse("View should start unpressed",
+                mTestMenuButtonDelegate.getMenuButtonView().isPressed());
+        Assert.assertFalse("App menu should be not be active", buttonHelper.isAppMenuActive());
+
+        MotionEvent downMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        sendMotionEventToButtonHelper(
+                buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
+
+        waitForMenuToShow(0);
+        Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
+
+        View firstItem = mAppMenuHandler.getAppMenu().getListView().getChildAt(0);
+        Rect firstItemScreenRect =
+                mAppMenuHandler.getAppMenuDragHelper().getScreenVisibleRect(firstItem);
+        int eventX = firstItemScreenRect.left + (firstItemScreenRect.right / 2);
+        int eventY = firstItemScreenRect.top + (firstItemScreenRect.bottom / 2);
+        MotionEvent dragMotionEvent =
+                MotionEvent.obtain(0, 100, MotionEvent.ACTION_MOVE, eventX, eventY, 0);
+        sendMotionEventToButtonHelper(
+                buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), dragMotionEvent);
+
+        MotionEvent upMotionEvent =
+                MotionEvent.obtain(0, 150, MotionEvent.ACTION_UP, eventX, eventY, 0);
+        sendMotionEventToButtonHelper(
+                buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), upMotionEvent);
+
+        Assert.assertEquals("Item selected callback should have been called.", 1,
+                mDelegate.itemSelectedCallbackHelper.getCallCount());
+        Assert.assertEquals("Incorrect id for last selected item.", R.id.menu_item_one,
+                mDelegate.lastSelectedItemId);
+    }
+
+    private void showMenuAndAssert() throws TimeoutException {
         int currentCallCount = mMenuObserver.menuShownCallback.getCallCount();
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> mAppMenuCoordinator.showAppMenuForKeyboardEvent());
+        waitForMenuToShow(currentCallCount);
+    }
+
+    private void waitForMenuToShow(int currentCallCount) throws TimeoutException {
         mMenuObserver.menuShownCallback.waitForCallback(currentCallCount);
         Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
 
@@ -253,15 +683,18 @@
     }
 
     private class TestMenuButtonDelegate implements MenuButtonDelegate {
+        public boolean useBottomAnchor;
+
         @Nullable
         @Override
         public View getMenuButtonView() {
-            return null;
+            return getActivity().findViewById(
+                    useBottomAnchor ? R.id.bottom_button : R.id.top_button);
         }
 
         @Override
         public boolean isMenuFromBottom() {
-            return false;
+            return useBottomAnchor;
         }
     }
 
@@ -273,4 +706,36 @@
                         != null);
         return AppMenuTestSupport.getListView(mAppMenuCoordinator).getChildAt(index);
     }
+
+    private Rect getPopupLocationRect() {
+        View contentView = mAppMenuHandler.getAppMenu().getPopup().getContentView();
+        CriteriaHelper.pollUiThread(() -> contentView.getHeight() != 0);
+        Rect bgPadding = new Rect();
+        mAppMenuHandler.getAppMenu().getPopup().getBackground().getPadding(bgPadding);
+
+        Rect popupRect = new Rect();
+        int[] popupLocation = new int[2];
+        contentView.getLocationOnScreen(popupLocation);
+        popupRect.left = popupLocation[0] - bgPadding.left;
+        popupRect.top = popupLocation[1] - bgPadding.top;
+        popupRect.right = popupLocation[0] + contentView.getWidth() + bgPadding.right;
+        popupRect.bottom = popupLocation[1] + contentView.getHeight() + bgPadding.bottom;
+        return popupRect;
+    }
+
+    private Rect getViewLocationRect(View anchor) {
+        Rect viewRect = new Rect();
+        int[] viewLocation = new int[2];
+        anchor.getLocationOnScreen(viewLocation);
+        viewRect.left = viewLocation[0];
+        viewRect.top = viewLocation[1];
+        viewRect.right = viewRect.left + anchor.getWidth();
+        viewRect.bottom = viewRect.top + anchor.getHeight();
+        return viewRect;
+    }
+
+    private void sendMotionEventToButtonHelper(AppMenuButtonHelperImpl helper, View view,
+            MotionEvent event) throws ExecutionException {
+        TestThreadUtils.runOnUiThreadBlocking(() -> helper.onTouch(view, event));
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/TestAppMenuPropertiesDelegate.java b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/TestAppMenuPropertiesDelegate.java
index 6289c47b..1640603 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/TestAppMenuPropertiesDelegate.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/TestAppMenuPropertiesDelegate.java
@@ -42,6 +42,8 @@
 
     @Override
     public void prepareMenu(Menu menu, AppMenuHandler handler) {
+        menu.findItem(R.id.menu_item_two).setEnabled(false);
+
         menu.findItem(R.id.icon_row_menu_id).setVisible(enableAppIconRow);
         if (enableAppIconRow) {
             menu.findItem(R.id.icon_one)
@@ -53,6 +55,7 @@
             menu.findItem(R.id.icon_three)
                     .setIcon(AppCompatResources.getDrawable(ContextUtils.getApplicationContext(),
                             R.drawable.test_ic_arrow_forward_black_24dp));
+            menu.findItem(R.id.icon_three).setEnabled(false);
         }
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java
index 46fbb1b..34070550 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java
@@ -16,6 +16,7 @@
 
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
@@ -39,6 +40,7 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@DisabledTest(message = "https://crbug.com/1023437")
 public class TabObserverRegistrarTest {
     private static class LoadUrlTabObserver extends EmptyTabObserver {
         private List<String> mUrlLoadRequests = new ArrayList<>();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
index 504a90b6..1a4494c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
@@ -16,6 +16,7 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.chrome.browser.ui.widget.MoreProgressButton;
 import org.chromium.chrome.browser.ui.widget.MoreProgressButton.State;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
@@ -30,6 +31,7 @@
  * turned on (HistoryManager::isScrollToLoadDisabled() == true).
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@DisableIf.Build(supported_abis_includes = "arm64-v8a", message = "crbug.com/1023426")
 public class HistoryAdapterAccessibilityTest {
     public static final int PAGING = 2;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
index 020cb80..ff596ab 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
@@ -16,6 +16,7 @@
 import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.widget.MoreProgressButton;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
@@ -29,6 +30,7 @@
  * Tests for the {@link HistoryAdapter}.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@DisableIf.Build(supported_abis_includes = "arm64-v8a", message = "crbug.com/1023426")
 public class HistoryAdapterTest {
     private StubbedHistoryProvider mHistoryProvider;
     private HistoryAdapter mAdapter;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java
index cda1a5d6..c9712b2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java
@@ -411,7 +411,7 @@
         Assert.assertNotNull(bigViewIcon);
 
         // Starts from Android O MR1, large icon can be downscaled by Android platform code.
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
             Assert.assertTrue(expectedIcon.sameAs(((BitmapDrawable) bigViewIcon).getBitmap()));
         }
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
index cbef307..7b01162 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
@@ -637,7 +637,7 @@
         Bitmap generatedIcon = generator.generateIconForUrl(mPermissionTestRule.getOrigin());
         Assert.assertNotNull(generatedIcon);
         // Starts from Android O MR1, large icon can be downscaled by Android platform code.
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
             Assert.assertTrue(generatedIcon.sameAs(
                     NotificationTestUtil.getLargeIconFromNotification(context, notification)));
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java
index dc2cacae..b948eca 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java
@@ -470,7 +470,7 @@
     @Feature({"Preferences"})
     public void testOnlyExpectedPreferencesShown() {
         // If you add a category in the SiteSettings UI, please add a test for it below.
-        Assert.assertEquals(19, SiteSettingsCategory.Type.NUM_ENTRIES);
+        Assert.assertEquals(20, SiteSettingsCategory.Type.NUM_ENTRIES);
 
         String[] nullArray = new String[0];
         String[] binaryToggle = new String[] {"binary_toggle"};
@@ -497,6 +497,7 @@
         testCases.put(SiteSettingsCategory.Type.JAVASCRIPT,
                 new Pair<>(binaryToggleWithException, binaryToggleWithException));
         testCases.put(SiteSettingsCategory.Type.MICROPHONE, new Pair<>(binaryToggle, binaryToggle));
+        testCases.put(SiteSettingsCategory.Type.NFC, new Pair<>(binaryToggle, binaryToggle));
         testCases.put(SiteSettingsCategory.Type.NOTIFICATIONS,
                 new Pair<>(binaryToggleWithAllowed, binaryToggleWithAllowed));
         testCases.put(SiteSettingsCategory.Type.POPUPS, new Pair<>(binaryToggle, binaryToggle));
@@ -766,6 +767,34 @@
         doTestBluetoothScanningPermission(false);
     }
 
+    /**
+     * Helper function to test allowing and blocking NFC feature.
+     * @param enabled true to test enabling NFC feature, false to test disabling the
+     *         feature.
+     */
+    private void doTestNfcPermission(final boolean enabled) {
+        setGlobalToggleForCategory(SiteSettingsCategory.Type.NFC, enabled);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertEquals("NFC should be " + (enabled ? "enabled" : "disabled"),
+                    PrefServiceBridge.getInstance().isCategoryEnabled(ContentSettingsType.NFC),
+                    enabled);
+        });
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    public void testAllowNfc() {
+        doTestNfcPermission(true);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    public void testBlockNfc() {
+        doTestNfcPermission(false);
+    }
+
     private int getTabCount() {
         return TestThreadUtils.runOnUiThreadBlockingNoException(
                 () -> mActivityTestRule.getActivity().getTabModelSelector().getTotalTabCount());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcherTest.java
index 1756b21..b23b597 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcherTest.java
@@ -267,7 +267,7 @@
         fetcher.setWebsitePreferenceBridgeForTesting(websitePreferenceBridge);
 
         // Add permission info types.
-        Assert.assertEquals(8, PermissionInfo.Type.NUM_ENTRIES);
+        Assert.assertEquals(9, PermissionInfo.Type.NUM_ENTRIES);
         String googleOrigin = "https://google.com";
         websitePreferenceBridge.addPermissionInfo(new PermissionInfo(
                 PermissionInfo.Type.GEOLOCATION, googleOrigin, googleOrigin, false));
@@ -275,6 +275,8 @@
                 new PermissionInfo(PermissionInfo.Type.MIDI, googleOrigin, googleOrigin, false));
         websitePreferenceBridge.addPermissionInfo(new PermissionInfo(
                 PermissionInfo.Type.PROTECTED_MEDIA_IDENTIFIER, googleOrigin, googleOrigin, false));
+        websitePreferenceBridge.addPermissionInfo(
+                new PermissionInfo(PermissionInfo.Type.NFC, googleOrigin, googleOrigin, false));
         websitePreferenceBridge.addPermissionInfo(new PermissionInfo(
                 PermissionInfo.Type.NOTIFICATION, googleOrigin, googleOrigin, false));
         websitePreferenceBridge.addPermissionInfo(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java
index 5e12aee..15e41f37 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java
@@ -19,7 +19,6 @@
 
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -110,10 +109,15 @@
     @Test
     @SmallTest
     @Feature({"ReturnToChrome", "RenderTest"})
-    @FlakyTest(message = "crbug.com/1023079")
     @CommandLineFlags.Add({BASE_PARAMS + "/" + TAB_SWITCHER_ON_RETURN_MS + "/0"})
     public void testInitialScrollIndex() throws Exception {
         TabUiTestHelper.prepareTabsWithThumbnail(mActivityTestRule, 10, 0, mUrl);
+
+        // Trigger thumbnail capturing for the last tab.
+        TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
+        TabUiTestHelper.verifyAllTabsHaveThumbnail(
+                mActivityTestRule.getActivity().getCurrentTabModel());
+
         ApplicationTestUtils.finishActivity(mActivityTestRule.getActivity());
 
         mActivityTestRule.startMainActivityFromLauncher();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/appmenu/AppMenuPopupPositionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/appmenu/AppMenuPopupPositionTest.java
new file mode 100644
index 0000000..edbfa77
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/appmenu/AppMenuPopupPositionTest.java
@@ -0,0 +1,164 @@
+// 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.chrome.browser.appmenu;
+
+import android.graphics.Rect;
+import android.view.Surface;
+import android.view.View;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.test.support.DisableHistogramsRule;
+
+/**
+ * Tests AppMenu#getPopupPosition.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class AppMenuPopupPositionTest {
+    @Rule
+    public DisableHistogramsRule mDisableHistogramsRule = new DisableHistogramsRule();
+
+    private final int[] mTempLocation = new int[2];
+
+    private final int mAppWidth = 400;
+    private final int mAppHeight = 1000;
+    private final int mBgPadding = 10;
+    private final int mPopupWidth = 300;
+    private final int mPopupHeight = 500;
+    private final int mAnchorX = 100;
+    private final int mAnchorY = 300;
+    private final int mAnchorWidth = 40;
+    private final int mNegativeSoftwareVerticalOffset = 25;
+    private final int mNegativeSoftwareVerticalOffsetNotTopAnchored = 15;
+    private final View mAnchorView = Mockito.mock(View.class);
+    private final Rect mAppRect = new Rect(0, 0, mAppWidth, mAppHeight);
+    private final Rect mBgPaddingRect = new Rect(mBgPadding, mBgPadding, mBgPadding, mBgPadding);
+
+    @Before
+    public void setUp() {
+        Mockito.doAnswer((InvocationOnMock invocation) -> {
+                   mTempLocation[0] = mAnchorX;
+                   mTempLocation[1] = mAnchorY;
+                   return null;
+               })
+                .when(mAnchorView)
+                .getLocationInWindow(mTempLocation);
+
+        Mockito.doAnswer((InvocationOnMock invocation) -> {
+                   mTempLocation[0] = mAnchorX;
+                   mTempLocation[1] = mAnchorY;
+                   return null;
+               })
+                .when(mAnchorView)
+                .getLocationOnScreen(mTempLocation);
+
+        Mockito.when(mAnchorView.getWidth()).thenReturn(mAnchorWidth);
+    }
+
+    @Test
+    public void testPermanentButton_Portrait() {
+        // Popup should should be in the middle of the screen, anchored near the anchor view.
+        int expectedX = (mAppWidth - mPopupWidth) / 2;
+        int expectedY = mAnchorY - mBgPadding;
+
+        int[] results =
+                getPopupPosition(true, Surface.ROTATION_0, false, View.LAYOUT_DIRECTION_LTR);
+        Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
+        Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
+
+        results = getPopupPosition(true, Surface.ROTATION_180, false, View.LAYOUT_DIRECTION_LTR);
+        Assert.assertEquals("Incorrect popup x for rotation 180", expectedX, results[0]);
+        Assert.assertEquals("Incorrect popup y for rotation 180", expectedY, results[1]);
+    }
+
+    @Test
+    public void testPermanentButton_Landscape() {
+        int[] results =
+                getPopupPosition(true, Surface.ROTATION_90, false, View.LAYOUT_DIRECTION_LTR);
+
+        // Popup should be positioned toward the right edge of the screen, anchored near the anchor
+        // view.
+        int expectedX = mAppWidth - mPopupWidth;
+        int expectedY = mAnchorY - mBgPadding;
+        Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
+        Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
+
+        // Popup should be positioned toward the left edge of the screen, anchored near the anchor
+        // view.
+        expectedX = 0;
+        results = getPopupPosition(true, Surface.ROTATION_270, false, View.LAYOUT_DIRECTION_LTR);
+        Assert.assertEquals("Incorrect popup x for rotation 180", expectedX, results[0]);
+        Assert.assertEquals("Incorrect popup y for rotation 180", expectedY, results[1]);
+    }
+
+    @Test
+    public void testTopButton_LTR() {
+        int[] results =
+                getPopupPosition(false, Surface.ROTATION_0, false, View.LAYOUT_DIRECTION_LTR);
+
+        // The top right edge of the popup should be aligned with the top right edge of the button.
+        int expectedX = mAnchorX + mAnchorWidth - mPopupWidth;
+        int expectedY = mAnchorY - mNegativeSoftwareVerticalOffset;
+        Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
+        Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
+    }
+
+    @Test
+    public void testTopButton_RTL() {
+        int[] results =
+                getPopupPosition(false, Surface.ROTATION_0, false, View.LAYOUT_DIRECTION_RTL);
+
+        // The top left edge of the popup should be aligned with the top left edge of the button.
+        int expectedX = mAnchorX;
+        int expectedY = mAnchorY - mNegativeSoftwareVerticalOffset;
+        Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
+        Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
+    }
+
+    @Test
+    public void testBottomButton_LTR() {
+        int[] results =
+                getPopupPosition(false, Surface.ROTATION_0, true, View.LAYOUT_DIRECTION_LTR);
+
+        // The bottom popup menu positioning assumes the anchor is at the bottom of the screen. It
+        // aligns the popup based on the bottom of the screen plus offsets.
+        int expectedX = mAnchorX + mAnchorWidth - mPopupWidth;
+        int expectedY = mAppHeight - mPopupHeight - mNegativeSoftwareVerticalOffset
+                - mNegativeSoftwareVerticalOffsetNotTopAnchored + mBgPadding;
+        Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
+        Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
+    }
+
+    @Test
+    public void testBottomButton_RTL() {
+        int[] results =
+                getPopupPosition(false, Surface.ROTATION_0, true, View.LAYOUT_DIRECTION_RTL);
+
+        // The bottom popup menu positioning assumes the anchor is at the bottom of the screen. It
+        // aligns the popup based on the bottom of the screen plus offsets.
+        int expectedX = mAnchorX;
+        int expectedY = mAppHeight - mPopupHeight - mNegativeSoftwareVerticalOffset
+                - mNegativeSoftwareVerticalOffsetNotTopAnchored + mBgPadding;
+        Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
+        Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
+    }
+
+    private int[] getPopupPosition(boolean isByPermanentButton, int rotation,
+            boolean isAnchorAtBottom, int layoutDirection) {
+        return AppMenu.getPopupPosition(mTempLocation, isByPermanentButton,
+                mNegativeSoftwareVerticalOffset, mNegativeSoftwareVerticalOffsetNotTopAnchored,
+                rotation, mAppRect, mBgPaddingRect, mAnchorView, mPopupWidth, mPopupHeight,
+                isAnchorAtBottom, layoutDirection);
+    }
+}
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index f9d838d..52132ed 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2931,6 +2931,9 @@
   <message name="IDS_SETTINGS_PRIVACY" desc="Name of the settings page which allows users to modify privacy and security settings.">
     Privacy and security
   </message>
+  <message name="IDS_SETTINGS_PRIVACY_MORE" desc="Label on the expansion button to show more privacy settings.">
+    More
+  </message>
   <message name="IDS_SETTINGS_LINKDOCTOR_PREF" desc="The documentation string of the 'Use Link Doctor' preference to help with navigation errors.">
     Show suggestions for similar pages when a page can't be found
   </message>
@@ -3673,7 +3676,7 @@
     Block third-party cookies
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIE_SUBLABEL" desc="A sub-label below the Block 3rd-party cookie checkbox.">
-    When turned on, sites can't use cookies that track you across the web. Some websites might break.
+    When on, sites can’t use your browsing activity across different sites to personalize ads. Some sites may not work properly.
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_ADOBE_FLASH_SETTINGS" desc="The text for the link that points the user to the Adobe Flash Player Storage settings on the web">
     Adobe Flash Player Storage settings
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4644c026..6abcb3c9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1061,6 +1061,8 @@
     "password_manager/account_storage/account_password_store_factory.h",
     "password_manager/chrome_password_manager_client.cc",
     "password_manager/chrome_password_manager_client.h",
+    "password_manager/field_info_manager_factory.cc",
+    "password_manager/field_info_manager_factory.h",
     "password_manager/password_manager_util_mac.h",
     "password_manager/password_manager_util_mac.mm",
     "password_manager/password_manager_util_win.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index fea4019..2745d67 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3760,8 +3760,7 @@
      FEATURE_VALUE_TYPE(kClickToCallReceiver)},
 #endif  // defined(OS_ANDROID)
 
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
-    defined(OS_CHROMEOS)
+#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
     {"click-to-call-context-menu-selected-text",
      flag_descriptions::kClickToCallContextMenuForSelectedTextName,
      flag_descriptions::kClickToCallContextMenuForSelectedTextDescription,
@@ -3770,7 +3769,10 @@
     {"click-to-call-ui", flag_descriptions::kClickToCallUIName,
      flag_descriptions::kClickToCallUIDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(kClickToCallUI)},
+#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
 
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
+    defined(OS_CHROMEOS)
     {"remote-copy-receiver", flag_descriptions::kRemoteCopyReceiverName,
      flag_descriptions::kRemoteCopyReceiverDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(kRemoteCopyReceiver)},
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 3f519bd..2a3a923 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -960,6 +960,10 @@
       env, jmodel,
       CreateJavaAdditionalSections(
           env, collect_user_data_options->additional_appended_sections));
+  Java_AssistantCollectUserDataModel_setDefaultEmail(
+      env, jmodel,
+      base::android::ConvertUTF8ToJavaString(
+          env, collect_user_data_options->default_email));
 
   Java_AssistantCollectUserDataModel_setVisible(env, jmodel, true);
 }
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index e4488c7e7..f2c6f893 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -81,6 +81,7 @@
     &features::kServiceWorkerPaymentApps,
     &features::kShowTrustedPublisherURL,
     &features::kWebAuth,
+    &features::kWebNfc,
     &features::kWebPayments,
     &feed::kInterestFeedContentSuggestions,
     &kAdjustWebApkInstallationSpace,
diff --git a/chrome/browser/android/compositor/layer/contextual_search_layer.cc b/chrome/browser/android/compositor/layer/contextual_search_layer.cc
index 02b5545..f1982f4 100644
--- a/chrome/browser/android/compositor/layer/contextual_search_layer.cc
+++ b/chrome/browser/android/compositor/layer/contextual_search_layer.cc
@@ -77,7 +77,6 @@
     bool search_caption_visible,
     bool search_bar_border_visible,
     float search_bar_border_height,
-    bool search_bar_shadow_visible,
     bool quick_action_icon_visible,
     bool thumbnail_visible,
     float custom_image_visibility_percentage,
@@ -127,8 +126,8 @@
       search_panel_width, search_panel_height, search_bar_background_color,
       search_bar_margin_side, search_bar_margin_top, search_bar_height,
       search_bar_top, search_term_opacity, should_render_bar_border,
-      search_bar_border_height, search_bar_shadow_visible, icon_color,
-      drag_handlebar_color, close_icon_opacity, separator_line_color);
+      search_bar_border_height, icon_color, drag_handlebar_color,
+      close_icon_opacity, separator_line_color);
 
   // -----------------------------------------------------------------
   // Content setup, to center in space below drag handle (when present).
diff --git a/chrome/browser/android/compositor/layer/contextual_search_layer.h b/chrome/browser/android/compositor/layer/contextual_search_layer.h
index 135b612..47a1ce6 100644
--- a/chrome/browser/android/compositor/layer/contextual_search_layer.h
+++ b/chrome/browser/android/compositor/layer/contextual_search_layer.h
@@ -75,7 +75,6 @@
                      bool search_caption_visible,
                      bool search_bar_border_visible,
                      float search_bar_border_height,
-                     bool search_bar_shadow_visible,
                      bool quick_action_icon_visible,
                      bool thumbnail_visible,
                      float custom_image_visibility_percentage,
diff --git a/chrome/browser/android/compositor/layer/ephemeral_tab_layer.cc b/chrome/browser/android/compositor/layer/ephemeral_tab_layer.cc
index 016e253..a6b0c5e 100644
--- a/chrome/browser/android/compositor/layer/ephemeral_tab_layer.cc
+++ b/chrome/browser/android/compositor/layer/ephemeral_tab_layer.cc
@@ -78,7 +78,6 @@
     float bar_height,
     bool bar_border_visible,
     float bar_border_height,
-    bool bar_shadow_visible,
     int icon_color,
     int drag_handlebar_color,
     jfloat favicon_opacity,
@@ -110,8 +109,8 @@
       dp_to_px, content_layer, bar_height, panel_x, panel_y, panel_width,
       panel_height, bar_background_color, bar_margin_side, bar_margin_top,
       bar_height, 0.0f, title_opacity, bar_border_visible, bar_border_height,
-      bar_shadow_visible, icon_color, drag_handlebar_color,
-      1.0f /* icon opacity */, separator_line_color);
+      icon_color, drag_handlebar_color, 1.0f /* icon opacity */,
+      separator_line_color);
 
   // Content setup, to center in space below drag handle (when present).
   int content_top = bar_top;
diff --git a/chrome/browser/android/compositor/layer/ephemeral_tab_layer.h b/chrome/browser/android/compositor/layer/ephemeral_tab_layer.h
index a7c56e9..815afb7 100644
--- a/chrome/browser/android/compositor/layer/ephemeral_tab_layer.h
+++ b/chrome/browser/android/compositor/layer/ephemeral_tab_layer.h
@@ -70,7 +70,6 @@
                      float bar_height,
                      bool bar_border_visible,
                      float bar_border_height,
-                     bool bar_shadow_visible,
                      int icon_color,
                      int drag_handlebar_color,
                      jfloat favicon_opacity,
diff --git a/chrome/browser/android/compositor/layer/overlay_panel_layer.cc b/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
index 96e2e5f..40d3f30b 100644
--- a/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
+++ b/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
@@ -75,7 +75,6 @@
     float bar_text_opacity,
     bool bar_border_visible,
     float bar_border_height,
-    bool bar_shadow_visible,
     int icon_tint,
     int drag_handlebar_tint,
     float icon_opacity,
@@ -361,7 +360,6 @@
   // ---------------------------------------------------------------------------
   // Bar Shadow
   // ---------------------------------------------------------------------------
-  if (bar_shadow_visible) {
     ui::Resource* bar_shadow_resource = resource_manager_->GetResource(
         ui::ANDROID_RESOURCE_TYPE_STATIC, bar_shadow_resource_id_);
 
@@ -377,10 +375,6 @@
       bar_shadow_->SetPosition(gfx::PointF(0.f, bar_bottom));
       bar_shadow_->SetOpacity(1.0f);
     }
-  } else {
-    if (bar_shadow_.get() && bar_shadow_->parent())
-      bar_shadow_->RemoveFromParent();
-  }
 
   // ---------------------------------------------------------------------------
   // Panel
diff --git a/chrome/browser/android/compositor/layer/overlay_panel_layer.h b/chrome/browser/android/compositor/layer/overlay_panel_layer.h
index bfae8c41..59edd08 100644
--- a/chrome/browser/android/compositor/layer/overlay_panel_layer.h
+++ b/chrome/browser/android/compositor/layer/overlay_panel_layer.h
@@ -54,7 +54,6 @@
                      float bar_text_opacity,
                      bool bar_border_visible,
                      float bar_border_height,
-                     bool bar_shadow_visible,
                      int icon_tint,
                      int drag_handlebar_tint,
                      float icon_opacity,
diff --git a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
index 04611f4..d9b7dab6 100644
--- a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
@@ -115,7 +115,6 @@
     jboolean search_caption_visible,
     jboolean search_bar_border_visible,
     jfloat search_bar_border_height,
-    jboolean search_bar_shadow_visible,
     jboolean quick_action_icon_visible,
     jboolean thumbnail_visible,
     jstring j_thumbnail_url,
@@ -186,8 +185,7 @@
       search_text_layer_min_height, search_term_opacity,
       search_term_caption_spacing, search_caption_animation_percentage,
       search_caption_visible, search_bar_border_visible,
-      search_bar_border_height, search_bar_shadow_visible,
-      quick_action_icon_visible, thumbnail_visible,
+      search_bar_border_height, quick_action_icon_visible, thumbnail_visible,
       custom_image_visibility_percentage, bar_image_size, icon_color,
       drag_handlebar_color, arrow_icon_opacity, arrow_icon_rotation,
       close_icon_opacity, progress_bar_visible, progress_bar_height,
diff --git a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
index c679c00..c88993c 100644
--- a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
+++ b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
@@ -87,7 +87,6 @@
       jboolean search_caption_visible,
       jboolean search_bar_border_visible,
       jfloat search_bar_border_height,
-      jboolean search_bar_shadow_visible,
       jboolean quick_action_icon_visible,
       jboolean thumbnail_visible,
       jstring j_thumbnail_url,
diff --git a/chrome/browser/android/compositor/scene_layer/ephemeral_tab_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/ephemeral_tab_scene_layer.cc
index 1647d09a4..44e1c08 100644
--- a/chrome/browser/android/compositor/scene_layer/ephemeral_tab_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/ephemeral_tab_scene_layer.cc
@@ -101,7 +101,6 @@
                                     jfloat bar_height,
                                     jboolean bar_border_visible,
                                     jfloat bar_border_height,
-                                    jboolean bar_shadow_visible,
                                     jint icon_color,
                                     jint drag_handlebar_color,
                                     jfloat favicon_opacity,
@@ -137,10 +136,10 @@
       progress_bar_background_resource_id, progress_bar_resource_id, dp_to_px,
       content_layer, panel_x, panel_y, panel_width, panel_height,
       bar_background_color, bar_margin_side, bar_margin_top, bar_height,
-      bar_border_visible, bar_border_height, bar_shadow_visible, icon_color,
-      drag_handlebar_color, favicon_opacity, progress_bar_visible,
-      progress_bar_height, progress_bar_opacity, progress_bar_completion,
-      separator_line_color, is_new_layout_);
+      bar_border_visible, bar_border_height, icon_color, drag_handlebar_color,
+      favicon_opacity, progress_bar_visible, progress_bar_height,
+      progress_bar_opacity, progress_bar_completion, separator_line_color,
+      is_new_layout_);
   // Make the layer visible if it is not already.
   ephemeral_tab_layer_->layer()->SetHideLayerAndSubtree(false);
 }
diff --git a/chrome/browser/android/compositor/scene_layer/ephemeral_tab_scene_layer.h b/chrome/browser/android/compositor/scene_layer/ephemeral_tab_scene_layer.h
index 4ebfa1b..d355253 100644
--- a/chrome/browser/android/compositor/scene_layer/ephemeral_tab_scene_layer.h
+++ b/chrome/browser/android/compositor/scene_layer/ephemeral_tab_scene_layer.h
@@ -67,7 +67,6 @@
               jfloat bar_height,
               jboolean bar_border_visible,
               jfloat bar_border_height,
-              jboolean bar_shadow_visible,
               jint icon_color,
               jint drag_handlebar_color,
               jfloat favicon_opacity,
diff --git a/chrome/browser/android/preferences/pref_service_bridge.cc b/chrome/browser/android/preferences/pref_service_bridge.cc
index 7b80ffe..d26a328 100644
--- a/chrome/browser/android/preferences/pref_service_bridge.cc
+++ b/chrome/browser/android/preferences/pref_service_bridge.cc
@@ -283,6 +283,10 @@
   return GetBooleanForContentSetting(ContentSettingsType::AUTOPLAY);
 }
 
+static jboolean JNI_PrefServiceBridge_GetNfcEnabled(JNIEnv* env) {
+  return GetBooleanForContentSetting(ContentSettingsType::NFC);
+}
+
 static jboolean JNI_PrefServiceBridge_GetSensorsEnabled(JNIEnv* env) {
   return GetBooleanForContentSetting(ContentSettingsType::SENSORS);
 }
@@ -390,6 +394,14 @@
       allow ? CONTENT_SETTING_ASK : CONTENT_SETTING_BLOCK);
 }
 
+static void JNI_PrefServiceBridge_SetNfcEnabled(JNIEnv* env, jboolean allow) {
+  HostContentSettingsMap* host_content_settings_map =
+      HostContentSettingsMapFactory::GetForProfile(GetOriginalProfile());
+  host_content_settings_map->SetDefaultContentSetting(
+      ContentSettingsType::NFC,
+      allow ? CONTENT_SETTING_ASK : CONTENT_SETTING_BLOCK);
+}
+
 static void JNI_PrefServiceBridge_SetSensorsEnabled(
     JNIEnv* env,
     jboolean allow) {
diff --git a/chrome/browser/android/preferences/website_preference_bridge.cc b/chrome/browser/android/preferences/website_preference_bridge.cc
index 640ed53d..e9d0ca5 100644
--- a/chrome/browser/android/preferences/website_preference_bridge.cc
+++ b/chrome/browser/android/preferences/website_preference_bridge.cc
@@ -825,6 +825,34 @@
       url, GURL(), ContentSettingsType::ADS_DATA, std::string(), nullptr);
 }
 
+static void JNI_WebsitePreferenceBridge_GetNfcOrigins(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& list) {
+  JNI_WebsitePreferenceBridge_GetOrigins(
+      env, ContentSettingsType::NFC,
+      &Java_WebsitePreferenceBridge_insertNfcInfoIntoList, list, false);
+}
+
+static jint JNI_WebsitePreferenceBridge_GetNfcSettingForOrigin(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& origin,
+    const JavaParamRef<jstring>& embedder,
+    jboolean is_incognito) {
+  return JNI_WebsitePreferenceBridge_GetSettingForOrigin(
+      env, ContentSettingsType::NFC, origin, embedder, is_incognito);
+}
+
+static void JNI_WebsitePreferenceBridge_SetNfcSettingForOrigin(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& origin,
+    const JavaParamRef<jstring>& embedder,
+    jint value,
+    jboolean is_incognito) {
+  JNI_WebsitePreferenceBridge_SetSettingForOrigin(
+      env, ContentSettingsType::NFC, origin, embedder,
+      static_cast<ContentSetting>(value), is_incognito);
+}
+
 static void JNI_WebsitePreferenceBridge_GetSensorsOrigins(
     JNIEnv* env,
     const JavaParamRef<jobject>& list) {
diff --git a/chrome/browser/android/vr/arcore_device/arcore.h b/chrome/browser/android/vr/arcore_device/arcore.h
index 8afebe3..5f393da 100644
--- a/chrome/browser/android/vr/arcore_device/arcore.h
+++ b/chrome/browser/android/vr/arcore_device/arcore.h
@@ -63,13 +63,15 @@
       mojom::XRRayPtr ray) = 0;
 
   virtual mojom::XRHitTestSubscriptionResultsDataPtr
-  GetHitTestSubscriptionResults(const device::mojom::VRPosePtr& pose) = 0;
+  GetHitTestSubscriptionResults(
+      const gfx::Transform& mojo_from_viewer,
+      const base::Optional<std::vector<mojom::XRInputSourceStatePtr>>&
+          maybe_input_state) = 0;
 
   virtual void UnsubscribeFromHitTest(uint64_t subscription_id) = 0;
 
-  virtual base::Optional<uint64_t> CreateAnchor(
-      const mojom::VRPosePtr& pose) = 0;
-  virtual base::Optional<uint64_t> CreateAnchor(const mojom::VRPosePtr& pose,
+  virtual base::Optional<uint64_t> CreateAnchor(const mojom::PosePtr& pose) = 0;
+  virtual base::Optional<uint64_t> CreateAnchor(const mojom::PosePtr& pose,
                                                 uint64_t plane_id) = 0;
 
   virtual void DetachAnchor(uint64_t anchor_id) = 0;
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
index 399a807..e7acee2 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/android/vr/arcore_device/ar_image_transport.h"
 #include "chrome/browser/android/vr/arcore_device/arcore_impl.h"
 #include "chrome/browser/android/vr/arcore_device/arcore_session_utils.h"
+#include "chrome/browser/android/vr/arcore_device/type_converters.h"
 #include "chrome/browser/android/vr/web_xr_presentation_state.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h"
@@ -697,7 +698,7 @@
   arcore_->UnsubscribeFromHitTest(subscription_id);
 }
 
-void ArCoreGl::CreateAnchor(mojom::VRPosePtr anchor_pose,
+void ArCoreGl::CreateAnchor(mojom::PosePtr anchor_pose,
                             CreateAnchorCallback callback) {
   DVLOG(2) << __func__;
 
@@ -711,7 +712,7 @@
   }
 }
 
-void ArCoreGl::CreatePlaneAnchor(mojom::VRPosePtr anchor_pose,
+void ArCoreGl::CreatePlaneAnchor(mojom::PosePtr anchor_pose,
                                  uint64_t plane_id,
                                  CreatePlaneAnchorCallback callback) {
   DVLOG(2) << __func__;
@@ -763,7 +764,9 @@
 
     // Get results for hit test subscriptions.
     frame_data->hit_test_subscription_results =
-        arcore_->GetHitTestSubscriptionResults(frame_data->pose);
+        arcore_->GetHitTestSubscriptionResults(
+            mojo::ConvertTo<gfx::Transform>(frame_data->pose),
+            frame_data->pose->input_state);
   }
 
   // Get anchors data, including anchors created this frame.
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.h b/chrome/browser/android/vr/arcore_device/arcore_gl.h
index 34aad1f..a708420 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.h
@@ -122,9 +122,9 @@
 
   void UnsubscribeFromHitTest(uint64_t subscription_id) override;
 
-  void CreateAnchor(mojom::VRPosePtr anchor_pose,
+  void CreateAnchor(mojom::PosePtr anchor_pose,
                     CreateAnchorCallback callback) override;
-  void CreatePlaneAnchor(mojom::VRPosePtr anchor_pose,
+  void CreatePlaneAnchor(mojom::PosePtr anchor_pose,
                          uint64_t plane_id,
                          CreatePlaneAnchorCallback callback) override;
 
diff --git a/chrome/browser/android/vr/arcore_device/arcore_impl.cc b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
index 23ae5520..41bbb76 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_impl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
@@ -22,41 +22,55 @@
 
 namespace {
 
-device::mojom::VRPosePtr GetMojomPoseFromArPose(
-    ArSession* session,
-    device::internal::ScopedArCoreObject<ArPose*> pose) {
-  float pose_raw[7];  // 7 = orientation(4) + position(3).
-  ArPose_getPoseRaw(session, pose.get(), pose_raw);
+std::pair<gfx::Quaternion, gfx::Point3F> GetPositionAndOrientationFromArPose(
+    const ArSession* session,
+    const device::internal::ScopedArCoreObject<ArPose*>& pose) {
+  std::array<float, 7> pose_raw;  // 7 = orientation(4) + position(3).
+  ArPose_getPoseRaw(session, pose.get(), pose_raw.data());
 
+  return {gfx::Quaternion(pose_raw[0], pose_raw[1], pose_raw[2], pose_raw[3]),
+          gfx::Point3F(pose_raw[4], pose_raw[5], pose_raw[6])};
+}
+
+// Helper, returns new VRPosePtr with position and orientation set to match the
+// position and orientation of passed in |pose|.
+device::mojom::VRPosePtr GetMojomVRPoseFromArPose(
+    const ArSession* session,
+    const device::internal::ScopedArCoreObject<ArPose*>& pose) {
   device::mojom::VRPosePtr result = device::mojom::VRPose::New();
-
-  result->orientation =
-      gfx::Quaternion(pose_raw[0], pose_raw[1], pose_raw[2], pose_raw[3]);
-  result->position = gfx::Point3F(pose_raw[4], pose_raw[5], pose_raw[6]);
+  std::tie(result->orientation, result->position) =
+      GetPositionAndOrientationFromArPose(session, pose);
 
   return result;
 }
 
+// Helper, returns new PosePtr with position and orientation set to match the
+// position and orientation of passed in |pose|.
+device::mojom::PosePtr GetMojomPoseFromArPose(
+    const ArSession* session,
+    const device::internal::ScopedArCoreObject<ArPose*>& pose) {
+  device::mojom::PosePtr result = device::mojom::Pose::New();
+  std::tie(result->orientation, result->position) =
+      GetPositionAndOrientationFromArPose(session, pose);
+
+  return result;
+}
+
+// Helper, creates new ArPose* with position and orientation set to match the
+// position and orientation of passed in |pose|.
 device::internal::ScopedArCoreObject<ArPose*> GetArPoseFromMojomPose(
     ArSession* session,
-    const device::mojom::VRPosePtr& pose) {
+    const device::mojom::PosePtr& pose) {
   float pose_raw[7] = {};  // 7 = orientation(4) + position(3).
 
-  if (pose->orientation) {
-    pose_raw[0] = pose->orientation->x();
-    pose_raw[1] = pose->orientation->y();
-    pose_raw[2] = pose->orientation->z();
-    pose_raw[3] = pose->orientation->w();
-  } else {
-    // Only need to set the .w to 1.
-    pose_raw[3] = 1;
-  }
+  pose_raw[0] = pose->orientation.x();
+  pose_raw[1] = pose->orientation.y();
+  pose_raw[2] = pose->orientation.z();
+  pose_raw[3] = pose->orientation.w();
 
-  if (pose->position) {
-    pose_raw[4] = pose->position->x();
-    pose_raw[5] = pose->position->y();
-    pose_raw[6] = pose->position->z();
-  }
+  pose_raw[4] = pose->position.x();
+  pose_raw[5] = pose->position.y();
+  pose_raw[6] = pose->position.z();
 
   device::internal::ScopedArCoreObject<ArPose*> result;
 
@@ -230,7 +244,8 @@
   ArCamera_getDisplayOrientedPose(arcore_session_.get(), arcore_camera.get(),
                                   arcore_pose.get());
 
-  return GetMojomPoseFromArPose(arcore_session_.get(), std::move(arcore_pose));
+  return GetMojomVRPoseFromArPose(arcore_session_.get(),
+                                  std::move(arcore_pose));
 }
 
 void ArCoreImpl::EnsureArCorePlanesList() {
@@ -320,7 +335,7 @@
         arcore_session_.get(), nullptr,
         internal::ScopedArCoreObject<ArPose*>::Receiver(anchor_pose).get());
     ArAnchor_getPose(arcore_session_.get(), ar_anchor, anchor_pose.get());
-    mojom::VRPosePtr pose =
+    mojom::PosePtr pose =
         GetMojomPoseFromArPose(arcore_session_.get(), std::move(anchor_pose));
 
     // ID
@@ -411,7 +426,7 @@
         arcore_session_.get(), nullptr,
         internal::ScopedArCoreObject<ArPose*>::Receiver(plane_pose).get());
     ArPlane_getCenterPose(arcore_session_.get(), ar_plane, plane_pose.get());
-    mojom::VRPosePtr pose =
+    mojom::PosePtr pose =
         GetMojomPoseFromArPose(arcore_session_.get(), std::move(plane_pose));
 
     // polygon
@@ -615,7 +630,9 @@
 
 mojom::XRHitTestSubscriptionResultsDataPtr
 ArCoreImpl::GetHitTestSubscriptionResults(
-    const device::mojom::VRPosePtr& pose) {
+    const gfx::Transform& mojo_from_viewer,
+    const base::Optional<std::vector<mojom::XRInputSourceStatePtr>>&
+        maybe_input_state) {
   mojom::XRHitTestSubscriptionResultsDataPtr result =
       mojom::XRHitTestSubscriptionResultsData::New();
 
@@ -623,7 +640,8 @@
     // First, check if we can find the current transformation for a ray. If not,
     // skip processing this subscription.
     auto maybe_mojo_from_native_origin = GetMojoFromNativeOrigin(
-        subscription_id_and_data.second.native_origin_information, pose);
+        subscription_id_and_data.second.native_origin_information,
+        mojo_from_viewer, maybe_input_state);
 
     if (!maybe_mojo_from_native_origin) {
       continue;
@@ -661,7 +679,7 @@
 
 base::Optional<gfx::Transform> ArCoreImpl::GetMojoFromReferenceSpace(
     device::mojom::XRReferenceSpaceCategory category,
-    const device::mojom::VRPosePtr& mojo_from_viewer) {
+    const gfx::Transform& mojo_from_viewer) {
   switch (category) {
     case device::mojom::XRReferenceSpaceCategory::LOCAL:
       return gfx::Transform{};
@@ -671,7 +689,7 @@
       return result;
     }
     case device::mojom::XRReferenceSpaceCategory::VIEWER:
-      return mojo::ConvertTo<gfx::Transform>(mojo_from_viewer);
+      return mojo_from_viewer;
     case device::mojom::XRReferenceSpaceCategory::BOUNDED_FLOOR:
       return base::nullopt;
     case device::mojom::XRReferenceSpaceCategory::UNBOUNDED:
@@ -681,27 +699,27 @@
 
 base::Optional<gfx::Transform> ArCoreImpl::GetMojoFromNativeOrigin(
     const mojom::XRNativeOriginInformationPtr& native_origin_information,
-    const device::mojom::VRPosePtr& mojo_from_viewer) {
+    const gfx::Transform& mojo_from_viewer,
+    const base::Optional<std::vector<mojom::XRInputSourceStatePtr>>&
+        maybe_input_state) {
   if (native_origin_information->is_input_source_id()) {
-    if (!mojo_from_viewer->input_state) {
+    if (!maybe_input_state) {
       return base::nullopt;
     }
 
     // Linear search should be fine for ARCore device as it only has one input
     // source (for now).
-    for (auto& input_source_state : *mojo_from_viewer->input_state) {
+    for (auto& input_source_state : *maybe_input_state) {
       if (input_source_state->source_id ==
           native_origin_information->get_input_source_id()) {
         if (!input_source_state->description->input_from_pointer) {
           return base::nullopt;
         }
 
-        auto view_from_pointer =
+        auto viewer_from_pointer =
             *input_source_state->description->input_from_pointer;
 
-        auto mojo_from_view = mojo::ConvertTo<gfx::Transform>(mojo_from_viewer);
-
-        return mojo_from_view * view_from_pointer;
+        return mojo_from_viewer * viewer_from_pointer;
       }
     }
 
@@ -726,7 +744,7 @@
         arcore_session_.get(), nullptr,
         internal::ScopedArCoreObject<ArPose*>::Receiver(ar_pose).get());
     ArPlane_getCenterPose(arcore_session_.get(), plane, ar_pose.get());
-    mojom::VRPosePtr mojo_pose =
+    mojom::PosePtr mojo_pose =
         GetMojomPoseFromArPose(arcore_session_.get(), std::move(ar_pose));
 
     return mojo::ConvertTo<gfx::Transform>(mojo_pose);
@@ -744,7 +762,7 @@
 
     ArAnchor_getPose(arcore_session_.get(), anchor_it->second.get(),
                      ar_pose.get());
-    mojom::VRPosePtr mojo_pose =
+    mojom::PosePtr mojo_pose =
         GetMojomPoseFromArPose(arcore_session_.get(), std::move(ar_pose));
 
     return mojo::ConvertTo<gfx::Transform>(mojo_pose);
@@ -911,7 +929,7 @@
 }
 
 base::Optional<uint64_t> ArCoreImpl::CreateAnchor(
-    const device::mojom::VRPosePtr& pose) {
+    const device::mojom::PosePtr& pose) {
   DCHECK(pose);
 
   auto ar_pose = GetArPoseFromMojomPose(arcore_session_.get(), pose);
@@ -935,7 +953,7 @@
 }
 
 base::Optional<uint64_t> ArCoreImpl::CreateAnchor(
-    const device::mojom::VRPosePtr& pose,
+    const device::mojom::PosePtr& pose,
     uint64_t plane_id) {
   DCHECK(pose);
 
diff --git a/chrome/browser/android/vr/arcore_device/arcore_impl.h b/chrome/browser/android/vr/arcore_device/arcore_impl.h
index 9c507b5..33ba0e9 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_impl.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_impl.h
@@ -142,13 +142,15 @@
       mojom::XRRayPtr ray) override;
 
   mojom::XRHitTestSubscriptionResultsDataPtr GetHitTestSubscriptionResults(
-      const device::mojom::VRPosePtr& pose) override;
+      const gfx::Transform& mojo_from_viewer,
+      const base::Optional<std::vector<mojom::XRInputSourceStatePtr>>&
+          maybe_input_state) override;
 
   void UnsubscribeFromHitTest(uint64_t subscription_id) override;
 
   base::Optional<uint64_t> CreateAnchor(
-      const device::mojom::VRPosePtr& pose) override;
-  base::Optional<uint64_t> CreateAnchor(const device::mojom::VRPosePtr& pose,
+      const device::mojom::PosePtr& pose) override;
+  base::Optional<uint64_t> CreateAnchor(const device::mojom::PosePtr& pose,
                                         uint64_t plane_id) override;
 
   void DetachAnchor(uint64_t anchor_id) override;
@@ -231,11 +233,13 @@
 
   base::Optional<gfx::Transform> GetMojoFromNativeOrigin(
       const mojom::XRNativeOriginInformationPtr& native_origin_information,
-      const device::mojom::VRPosePtr& pose);
+      const gfx::Transform& mojo_from_viewer,
+      const base::Optional<std::vector<mojom::XRInputSourceStatePtr>>&
+          maybe_input_state);
 
   base::Optional<gfx::Transform> GetMojoFromReferenceSpace(
       device::mojom::XRReferenceSpaceCategory category,
-      const device::mojom::VRPosePtr& pose);
+      const gfx::Transform& mojo_from_viewer);
 
   // Executes |fn| for each still tracked, non-subsumed plane present in
   // |arcore_planes_|.
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.cc b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
index 0f5b7d7a..2268640 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.cc
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
@@ -220,7 +220,9 @@
 
 mojom::XRHitTestSubscriptionResultsDataPtr
 FakeArCore::GetHitTestSubscriptionResults(
-    const device::mojom::VRPosePtr& pose) {
+    const gfx::Transform& mojo_from_viewer,
+    const base::Optional<std::vector<mojom::XRInputSourceStatePtr>>&
+        maybe_input_state) {
   return nullptr;
 }
 
@@ -232,7 +234,7 @@
   std::vector<mojom::XRPlaneDataPtr> result;
 
   // 1m ahead of the origin, neutral orientation facing forward.
-  mojom::VRPosePtr pose = mojom::VRPose::New();
+  mojom::PosePtr pose = mojom::Pose::New();
   pose->position = gfx::Point3F(0.0, 0.0, -1.0);
   pose->orientation = gfx::Quaternion();
 
@@ -255,7 +257,7 @@
   std::vector<uint64_t> result_ids;
 
   for (auto& anchor_id_and_data : anchors_) {
-    mojom::VRPosePtr pose = mojom::VRPose::New();
+    mojom::PosePtr pose = mojom::Pose::New();
     pose->position = anchor_id_and_data.second.position;
     pose->orientation = anchor_id_and_data.second.orientation;
 
@@ -267,28 +269,16 @@
   return mojom::XRAnchorsData::New(std::move(result_ids), std::move(result));
 }
 
-base::Optional<uint64_t> FakeArCore::CreateAnchor(const mojom::VRPosePtr& pose,
+base::Optional<uint64_t> FakeArCore::CreateAnchor(const mojom::PosePtr& pose,
                                                   uint64_t plane_id) {
   // TODO(992035): Fix this when implementing tests.
   return CreateAnchor(pose);
 }
 
-base::Optional<uint64_t> FakeArCore::CreateAnchor(
-    const mojom::VRPosePtr& pose) {
+base::Optional<uint64_t> FakeArCore::CreateAnchor(const mojom::PosePtr& pose) {
   DCHECK(pose);
 
-  gfx::Point3F position =
-      pose->position ? gfx::Point3F(pose->position->x(), pose->position->y(),
-                                    pose->position->z())
-                     : gfx::Point3F();
-
-  gfx::Quaternion orientation =
-      pose->orientation
-          ? gfx::Quaternion(pose->orientation->x(), pose->orientation->y(),
-                            pose->orientation->z(), pose->orientation->w())
-          : gfx::Quaternion(0, 0, 0, 1);
-
-  anchors_[next_id_] = {position, orientation};
+  anchors_[next_id_] = {pose->position, pose->orientation};
   int32_t anchor_id = next_id_;
 
   next_id_++;
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.h b/chrome/browser/android/vr/arcore_device/fake_arcore.h
index 7d98974..ba1aad3 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.h
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.h
@@ -45,7 +45,9 @@
       mojom::XRRayPtr ray) override;
 
   mojom::XRHitTestSubscriptionResultsDataPtr GetHitTestSubscriptionResults(
-      const device::mojom::VRPosePtr& pose) override;
+      const gfx::Transform& mojo_from_viewer,
+      const base::Optional<std::vector<mojom::XRInputSourceStatePtr>>&
+          maybe_input_state) override;
 
   void UnsubscribeFromHitTest(uint64_t subscription_id) override;
 
@@ -53,8 +55,8 @@
   mojom::XRAnchorsDataPtr GetAnchorsData() override;
 
   base::Optional<uint64_t> CreateAnchor(
-      const device::mojom::VRPosePtr& pose) override;
-  base::Optional<uint64_t> CreateAnchor(const device::mojom::VRPosePtr& pose,
+      const device::mojom::PosePtr& pose) override;
+  base::Optional<uint64_t> CreateAnchor(const device::mojom::PosePtr& pose,
                                         uint64_t plane_id) override;
   void DetachAnchor(uint64_t anchor_id) override;
 
diff --git a/chrome/browser/android/vr/arcore_device/type_converters.cc b/chrome/browser/android/vr/arcore_device/type_converters.cc
index 535c4d6..42682d9 100644
--- a/chrome/browser/android/vr/arcore_device/type_converters.cc
+++ b/chrome/browser/android/vr/arcore_device/type_converters.cc
@@ -36,4 +36,16 @@
   return gfx::ComposeTransform(decomposed);
 }
 
+gfx::Transform TypeConverter<gfx::Transform, device::mojom::PosePtr>::Convert(
+    const device::mojom::PosePtr& pose) {
+  gfx::DecomposedTransform decomposed;
+  decomposed.quaternion = pose->orientation;
+
+  decomposed.translate[0] = pose->position.x();
+  decomposed.translate[1] = pose->position.y();
+  decomposed.translate[2] = pose->position.z();
+
+  return gfx::ComposeTransform(decomposed);
+}
+
 }  // namespace mojo
diff --git a/chrome/browser/android/vr/arcore_device/type_converters.h b/chrome/browser/android/vr/arcore_device/type_converters.h
index 3cf27d5..8e26e31 100644
--- a/chrome/browser/android/vr/arcore_device/type_converters.h
+++ b/chrome/browser/android/vr/arcore_device/type_converters.h
@@ -21,6 +21,11 @@
   static gfx::Transform Convert(const device::mojom::VRPosePtr& pose);
 };
 
+template <>
+struct TypeConverter<gfx::Transform, device::mojom::PosePtr> {
+  static gfx::Transform Convert(const device::mojom::PosePtr& pose);
+};
+
 }  // namespace mojo
 
 #endif  // CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_TYPE_CONVERTERS_H_
diff --git a/chrome/browser/chromeos/login/mojo_system_info_dispatcher.cc b/chrome/browser/chromeos/login/mojo_system_info_dispatcher.cc
index ef201f94..b6efa04 100644
--- a/chrome/browser/chromeos/login/mojo_system_info_dispatcher.cc
+++ b/chrome/browser/chromeos/login/mojo_system_info_dispatcher.cc
@@ -48,6 +48,11 @@
   OnSystemInfoUpdated();
 }
 
+void MojoSystemInfoDispatcher::OnAdbSideloadStatusUpdated(bool enabled) {
+  adb_sideloading_enabled_ = enabled;
+  OnSystemInfoUpdated();
+}
+
 void MojoSystemInfoDispatcher::OnSystemInfoUpdated() {
   const base::Optional<bool> policy_show =
       version_info_updater_.IsSystemInfoEnforced();
@@ -61,8 +66,8 @@
            channel != version_info::Channel::BETA;
   }
   ash::LoginScreen::Get()->GetModel()->SetSystemInfo(
-      show, enforced, os_version_label_text_, enterprise_info_,
-      bluetooth_name_);
+      show, enforced, os_version_label_text_, enterprise_info_, bluetooth_name_,
+      adb_sideloading_enabled_);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/mojo_system_info_dispatcher.h b/chrome/browser/chromeos/login/mojo_system_info_dispatcher.h
index 38499f2..93389f8 100644
--- a/chrome/browser/chromeos/login/mojo_system_info_dispatcher.h
+++ b/chrome/browser/chromeos/login/mojo_system_info_dispatcher.h
@@ -24,17 +24,21 @@
   void OnEnterpriseInfoUpdated(const std::string& enterprise_info,
                                const std::string& asset_id) override;
   void OnDeviceInfoUpdated(const std::string& bluetooth_name) override;
+  void OnAdbSideloadStatusUpdated(bool enabled) override;
 
  private:
   // Sends a new mojo call based on the currently stored system information.
   void OnSystemInfoUpdated();
 
+  void OnQueryAdbSideload(bool success, bool enabled);
+
   // Used to fetch the system/version information.
   VersionInfoUpdater version_info_updater_{this};
 
   std::string os_version_label_text_;
   std::string enterprise_info_;
   std::string bluetooth_name_;
+  bool adb_sideloading_enabled_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(MojoSystemInfoDispatcher);
 };
diff --git a/chrome/browser/chromeos/login/saml/in_session_password_change_manager.cc b/chrome/browser/chromeos/login/saml/in_session_password_change_manager.cc
index 43323412..8ae43ec 100644
--- a/chrome/browser/chromeos/login/saml/in_session_password_change_manager.cc
+++ b/chrome/browser/chromeos/login/saml/in_session_password_change_manager.cc
@@ -209,6 +209,18 @@
   }
 }
 
+// static
+void InSessionPasswordChangeManager::SetForTesting(
+    InSessionPasswordChangeManager* instance) {
+  CHECK(!g_test_instance);
+  g_test_instance = instance;
+}
+
+// static
+void InSessionPasswordChangeManager::ResetForTesting() {
+  g_test_instance = nullptr;
+}
+
 void InSessionPasswordChangeManager::MaybeShowExpiryNotification() {
   // We are checking password expiry now, and this function will decide if we
   // want to check again in the future, so for now, make sure there are no other
@@ -416,18 +428,6 @@
                                ->in_session_password_change_manager();
 }
 
-// static
-void InSessionPasswordChangeManager::SetForTesting(
-    InSessionPasswordChangeManager* instance) {
-  CHECK(!g_test_instance);
-  g_test_instance = instance;
-}
-
-// static
-void InSessionPasswordChangeManager::ResetForTesting() {
-  g_test_instance = nullptr;
-}
-
 void InSessionPasswordChangeManager::NotifyObservers(Event event) {
   for (auto& observer : observer_list_) {
     observer.OnEvent(event);
diff --git a/chrome/browser/chromeos/login/saml/in_session_password_change_manager.h b/chrome/browser/chromeos/login/saml/in_session_password_change_manager.h
index 32d5ff64..e0b9a97 100644
--- a/chrome/browser/chromeos/login/saml/in_session_password_change_manager.h
+++ b/chrome/browser/chromeos/login/saml/in_session_password_change_manager.h
@@ -102,6 +102,10 @@
   explicit InSessionPasswordChangeManager(Profile* primary_profile);
   ~InSessionPasswordChangeManager() override;
 
+  // Sets the given instance as the singleton for testing.
+  static void SetForTesting(InSessionPasswordChangeManager* instance);
+  static void ResetForTesting();
+
   // Checks if the primary user's password has expired or will soon expire, and
   // shows a notification if needed. If the password will expire in the distant
   // future, posts a task to check again in the distant future.
@@ -162,10 +166,6 @@
  private:
   static InSessionPasswordChangeManager* GetNullable();
 
-  // Sets the given instance as the singleton for testing.
-  static void SetForTesting(InSessionPasswordChangeManager* instance);
-  static void ResetForTesting();
-
   void NotifyObservers(Event event);
 
   Profile* primary_profile_;
diff --git a/chrome/browser/chromeos/login/saml/password_change_success_detection_browsertest.cc b/chrome/browser/chromeos/login/saml/password_change_success_detection_browsertest.cc
new file mode 100644
index 0000000..1d8824e
--- /dev/null
+++ b/chrome/browser/chromeos/login/saml/password_change_success_detection_browsertest.cc
@@ -0,0 +1,209 @@
+// 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/login/saml/in_session_password_change_manager.h"
+#include "chrome/browser/chromeos/login/test/embedded_test_server_mixin.h"
+#include "chrome/browser/chromeos/login/test/js_checker.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
+#include "chromeos/constants/chromeos_switches.h"
+#include "components/prefs/pref_service.h"
+#include "net/base/url_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+using net::test_server::BasicHttpResponse;
+using net::test_server::HttpMethod;
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+
+namespace chromeos {
+
+constexpr char kPasswordChangePageTemplate[] =
+    "<html><body onload='document.forms[0].submit();'>"
+    "<form action='{0}' method='post'>"
+    "Old password: <input name='op' type='password' /><br>"
+    "New password: <input name='np' type='password' /><br>"
+    "Confirm new password: <input name='cnp' type='password' /><br>"
+    "<input type='submit' value='Submit'>"
+    "</form></body></html>";
+
+// Simulates an IdP at where the user can change the password. Redirects the
+// user to URLs in the same way as the real IdP would, which we use to detect
+// that the password was changed successfully.
+// Unlike the real change password page, this one automatically hits submit
+// on the change password form as soon as the page loads.
+class FakeChangePasswordIdp {
+ public:
+  FakeChangePasswordIdp() = default;
+  FakeChangePasswordIdp& operator=(const FakeChangePasswordIdp&) = delete;
+  FakeChangePasswordIdp(const FakeChangePasswordIdp&) = delete;
+  ~FakeChangePasswordIdp() = default;
+
+  void SetFormSubmitAction(const std::string& url) {
+    form_submit_action_url_ = url;
+  }
+
+  void RedirectNextPostTo(const std::string& url) {
+    redirect_next_post_url_ = url;
+  }
+
+  std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request);
+
+ private:
+  std::string GetPasswordChangePageHtmlContent();
+
+  std::string form_submit_action_url_;
+  std::string redirect_next_post_url_;
+};
+
+std::unique_ptr<HttpResponse> FakeChangePasswordIdp::HandleRequest(
+    const HttpRequest& request) {
+  auto http_response = std::make_unique<BasicHttpResponse>();
+
+  if (request.method == HttpMethod::METHOD_POST) {
+    if (redirect_next_post_url_ != "") {
+      http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
+      http_response->AddCustomHeader("Location", redirect_next_post_url_);
+      redirect_next_post_url_.clear();
+      return http_response;
+    }
+    http_response->set_code(net::HTTP_OK);
+    return http_response;
+  }
+
+  http_response->set_code(net::HTTP_OK);
+  http_response->set_content(GetPasswordChangePageHtmlContent());
+  return http_response;
+}
+
+std::string FakeChangePasswordIdp::GetPasswordChangePageHtmlContent() {
+  std::string result = kPasswordChangePageTemplate;
+  std::string place_holder = "{0}";
+  result.replace(result.find(place_holder), place_holder.size(),
+                 form_submit_action_url_);
+  return result;
+}
+
+// Waits for an SAML_IDP_PASSWORD_CHANGED event from the
+// InSessionPasswordChangeManager.
+class PasswordChangeWaiter : public InSessionPasswordChangeManager::Observer {
+ public:
+  PasswordChangeWaiter() {
+    InSessionPasswordChangeManager::Get()->AddObserver(this);
+  }
+
+  PasswordChangeWaiter& operator=(const PasswordChangeWaiter&) = delete;
+  PasswordChangeWaiter(const PasswordChangeWaiter&) = delete;
+
+  ~PasswordChangeWaiter() override {
+    InSessionPasswordChangeManager::Get()->RemoveObserver(this);
+  }
+
+  void WaitForPasswordChange() {
+    run_loop_.Run();
+    ASSERT_TRUE(saml_password_changed_);
+  }
+
+  void OnEvent(InSessionPasswordChangeManager::Event event) override {
+    if (event ==
+        InSessionPasswordChangeManager::Event::SAML_IDP_PASSWORD_CHANGED) {
+      saml_password_changed_ = true;
+      run_loop_.Quit();
+    }
+  }
+
+ private:
+  bool saml_password_changed_ = false;
+  base::RunLoop run_loop_;
+};
+
+// Simulates the redirects that Adfs, Azure, and Ping do in the case of
+// password change success, and ensures that we detect each one.
+class PasswordChangeSuccessDetectionTest
+    : public MixinBasedInProcessBrowserTest {
+ protected:
+  PasswordChangeSuccessDetectionTest() = default;
+  PasswordChangeSuccessDetectionTest& operator=(
+      const PasswordChangeSuccessDetectionTest&) = delete;
+  PasswordChangeSuccessDetectionTest(
+      const PasswordChangeSuccessDetectionTest&) = delete;
+
+  void SetUp() override {
+    embedded_test_server_.RegisterRequestHandler(base::Bind(
+        &FakeChangePasswordIdp::HandleRequest, base::Unretained(&fake_idp_)));
+
+    MixinBasedInProcessBrowserTest::SetUp();
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitchASCII(switches::kSamlPasswordChangeUrl,
+                                    embedded_test_server_.base_url().spec());
+  }
+
+  void SetUpOnMainThread() override {
+    MixinBasedInProcessBrowserTest::SetUpOnMainThread();
+
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    Profile* profile =
+        Profile::FromBrowserContext(web_contents->GetBrowserContext());
+    profile->GetPrefs()->SetBoolean(prefs::kSamlInSessionPasswordChangeEnabled,
+                                    true);
+
+    password_change_manager_ =
+        std::make_unique<InSessionPasswordChangeManager>(profile);
+    InSessionPasswordChangeManager::SetForTesting(
+        password_change_manager_.get());
+  }
+
+  void WaitForPasswordChangeDetected() {
+    PasswordChangeWaiter password_change_waiter;
+    password_change_waiter.WaitForPasswordChange();
+  }
+
+  void TearDownOnMainThread() override {
+    InSessionPasswordChangeManager::ResetForTesting();
+    MixinBasedInProcessBrowserTest::TearDownOnMainThread();
+  }
+
+  net::EmbeddedTestServer embedded_test_server_{
+      net::EmbeddedTestServer::Type::TYPE_HTTPS};
+  EmbeddedTestServerSetupMixin embedded_test_server_mixin_{
+      &mixin_host_, &embedded_test_server_};
+  FakeChangePasswordIdp fake_idp_;
+
+  std::unique_ptr<InSessionPasswordChangeManager> password_change_manager_;
+};
+
+IN_PROC_BROWSER_TEST_F(PasswordChangeSuccessDetectionTest, DetectAdfsSuccess) {
+  fake_idp_.SetFormSubmitAction("/adfs/portal/updatepassword/");
+  fake_idp_.RedirectNextPostTo("/adfs/portal/updatepassword/?status=0");
+
+  InSessionPasswordChangeManager::Get()->StartInSessionPasswordChange();
+  WaitForPasswordChangeDetected();
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordChangeSuccessDetectionTest, DetectAzureSuccess) {
+  fake_idp_.SetFormSubmitAction("/ChangePassword.aspx");
+  fake_idp_.RedirectNextPostTo("/ChangePassword.aspx?ReturnCode=0");
+
+  InSessionPasswordChangeManager::Get()->StartInSessionPasswordChange();
+  WaitForPasswordChangeDetected();
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordChangeSuccessDetectionTest, DetectPingSuccess) {
+  fake_idp_.SetFormSubmitAction(
+      "/idp/directory/a/12345/password/chg/67890?returnurl=/Selection");
+  fake_idp_.RedirectNextPostTo("/Selection");
+
+  InSessionPasswordChangeManager::Get()->StartInSessionPasswordChange();
+  WaitForPasswordChangeDetected();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/fingerprint_setup_browsertest.cc b/chrome/browser/chromeos/login/screens/fingerprint_setup_browsertest.cc
index 334eeb40..c446ec7 100644
--- a/chrome/browser/chromeos/login/screens/fingerprint_setup_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/fingerprint_setup_browsertest.cc
@@ -2,15 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/login/login_wizard.h"
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/browser/chromeos/login/screens/fingerprint_setup_screen.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/login/test/oobe_base_test.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/ui/webui/chromeos/login/fingerprint_setup_screen_handler.h"
-#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
 #include "chromeos/dbus/biod/fake_biod_client.h"
 
 namespace chromeos {
@@ -21,33 +19,32 @@
 
 int kMaxAllowedFingerprints = 3;
 
-chromeos::OobeUI* GetOobeUI() {
-  auto* host = chromeos::LoginDisplayHost::default_host();
-  return host ? host->GetOobeUI() : nullptr;
-}
-
 }  // namespace
 
-class FingerprintSetupTest : public InProcessBrowserTest {
+class FingerprintSetupTest : public OobeBaseTest {
  public:
   FingerprintSetupTest() = default;
   ~FingerprintSetupTest() override = default;
 
   void SetUpOnMainThread() override {
-    ShowLoginWizard(OobeScreen::SCREEN_TEST_NO_WINDOW);
+    // Enable fingerprint for testing.
+    quick_unlock::EnabledForTesting(true);
 
-    fingerprint_setup_screen_ = std::make_unique<FingerprintSetupScreen>(
-        GetOobeUI()->GetView<FingerprintSetupScreenHandler>(),
+    // Override the screen exit callback with our own method.
+    FingerprintSetupScreen* fingerprint_screen = FingerprintSetupScreen::Get(
+        WizardController::default_controller()->screen_manager());
+    fingerprint_screen->set_exit_callback_for_testing(
         base::BindRepeating(&FingerprintSetupTest::OnFingerprintSetupScreenExit,
                             base::Unretained(this)));
 
-    InProcessBrowserTest::SetUpOnMainThread();
+    OobeBaseTest::SetUpOnMainThread();
   }
 
-  void TearDownOnMainThread() override {
-    fingerprint_setup_screen_.reset();
-
-    InProcessBrowserTest::TearDownOnMainThread();
+  // Shows the fingerprint screen and overrides its exit callback.
+  void ShowFingerprintScreen() {
+    WizardController::default_controller()->AdvanceToScreen(
+        FingerprintSetupScreenView::kScreenId);
+    OobeScreenWaiter(FingerprintSetupScreenView::kScreenId).Wait();
   }
 
   void WaitForScreenExit() {
@@ -87,18 +84,13 @@
         {"fingerprint-setup-impl", "fingerprintAddAnother"});
   }
 
-  std::unique_ptr<FingerprintSetupScreen> fingerprint_setup_screen_;
-
  private:
   bool screen_exit_ = false;
-
-  base::OnceClosure screen_exit_callback_;
+  base::RepeatingClosure screen_exit_callback_;
 };
 
 IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintEnrollHalf) {
-  quick_unlock::EnabledForTesting(true);
-  fingerprint_setup_screen_->Show();
-  OobeScreenWaiter(FingerprintSetupScreenView::kScreenId).Wait();
+  ShowFingerprintScreen();
 
   EnrollFingerprint(50);
   test::OobeJS().ExpectVisiblePath({"fingerprint-setup-impl", "arc"});
@@ -115,10 +107,8 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintEnrollFull) {
-  quick_unlock::EnabledForTesting(true);
-  fingerprint_setup_screen_->Show();
+  ShowFingerprintScreen();
 
-  OobeScreenWaiter(FingerprintSetupScreenView::kScreenId).Wait();
   EnrollFingerprint(100);
   CheckCompletedEnroll();
 
@@ -128,9 +118,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintEnrollLimit) {
-  quick_unlock::EnabledForTesting(true);
-  fingerprint_setup_screen_->Show();
-  OobeScreenWaiter(FingerprintSetupScreenView::kScreenId).Wait();
+  ShowFingerprintScreen();
 
   for (int i = 0; i < kMaxAllowedFingerprints - 1; i++) {
     EnrollFingerprint(100);
@@ -148,16 +136,17 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintDisabled) {
+  // Disable fingerprint
   quick_unlock::EnabledForTesting(false);
-  fingerprint_setup_screen_->Show();
+
+  WizardController::default_controller()->AdvanceToScreen(
+      FingerprintSetupScreenView::kScreenId);
 
   WaitForScreenExit();
 }
 
 IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintSetupScreenElements) {
-  quick_unlock::EnabledForTesting(true);
-  fingerprint_setup_screen_->Show();
-  OobeScreenWaiter(FingerprintSetupScreenView::kScreenId).Wait();
+  ShowFingerprintScreen();
 
   test::OobeJS().CreateVisibilityWaiter(true, {"fingerprint-setup"})->Wait();
   test::OobeJS().ExpectVisible("fingerprint-setup-impl");
@@ -167,32 +156,31 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintSetupCancel) {
-  quick_unlock::EnabledForTesting(true);
-  fingerprint_setup_screen_->Show();
-  OobeScreenWaiter(FingerprintSetupScreenView::kScreenId).Wait();
+  ShowFingerprintScreen();
+
   test::OobeJS().TapOnPath({"fingerprint-setup-impl", "skipFingerprintSetup"});
+
   WaitForScreenExit();
 }
 
 IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintSetupNext) {
-  quick_unlock::EnabledForTesting(true);
-  fingerprint_setup_screen_->Show();
-  OobeScreenWaiter(FingerprintSetupScreenView::kScreenId).Wait();
+  ShowFingerprintScreen();
 
   test::OobeJS().CreateVisibilityWaiter(true, {"fingerprint-setup"})->Wait();
+
   test::OobeJS().TapOnPath(
       {"fingerprint-setup-impl", "showSensorLocationButton"});
+
   test::OobeJS()
       .CreateVisibilityWaiter(true, {"fingerprint-setup-impl", "placeFinger"})
       ->Wait();
+
   test::OobeJS().ExpectHiddenPath(
       {"fingerprint-setup-impl", "setupFingerprint"});
 }
 
 IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintSetupLater) {
-  quick_unlock::EnabledForTesting(true);
-  fingerprint_setup_screen_->Show();
-  OobeScreenWaiter(FingerprintSetupScreenView::kScreenId).Wait();
+  ShowFingerprintScreen();
 
   test::OobeJS().CreateVisibilityWaiter(true, {"fingerprint-setup"})->Wait();
   test::OobeJS().TapOnPath(
diff --git a/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.cc b/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.cc
index c7ef674..07e649d 100644
--- a/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.cc
+++ b/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.cc
@@ -15,6 +15,11 @@
 
 }  // namespace
 
+FingerprintSetupScreen* FingerprintSetupScreen::Get(ScreenManager* manager) {
+  return static_cast<FingerprintSetupScreen*>(
+      manager->GetScreen(FingerprintSetupScreenView::kScreenId));
+}
+
 FingerprintSetupScreen::FingerprintSetupScreen(
     FingerprintSetupScreenView* view,
     const base::RepeatingClosure& exit_callback)
diff --git a/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.h b/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.h
index c8ab509..a954fa7 100644
--- a/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.h
+++ b/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.h
@@ -9,6 +9,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "chrome/browser/chromeos/login/screen_manager.h"
 #include "chrome/browser/chromeos/login/screens/base_screen.h"
 
 namespace chromeos {
@@ -23,6 +24,13 @@
                          const base::RepeatingClosure& exit_callback);
   ~FingerprintSetupScreen() override;
 
+  static FingerprintSetupScreen* Get(ScreenManager* manager);
+
+  void set_exit_callback_for_testing(
+      const base::RepeatingClosure& exit_callback) {
+    exit_callback_ = exit_callback;
+  }
+
   // BaseScreen:
   void Show() override;
   void Hide() override;
diff --git a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
index c6478e9d..55dd28f 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
@@ -1064,7 +1064,13 @@
 }
 
 // TODO(https://crbug.com/990844): Re-enable once flakiness is fixed.
-IN_PROC_BROWSER_TEST_P(MergeSessionTest, DISABLED_XHRNotThrottled) {
+// TODO(crbug.com/998330): The test is flaky (timeout) on Chromium OS MSAN.
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_XHRNotThrottled DISABLED_XHRNotThrottled
+#else
+#define MAYBE_XHRNotThrottled XHRNotThrottled
+#endif
+IN_PROC_BROWSER_TEST_P(MergeSessionTest, MAYBE_XHRNotThrottled) {
   StartNewUserSession(/*wait_for_merge=*/false,
                       /*is_under_advanced_protection=*/false);
 
diff --git a/chrome/browser/chromeos/login/version_info_updater.cc b/chrome/browser/chromeos/login/version_info_updater.cc
index 38c2c66..ebbed5b 100644
--- a/chrome/browser/chromeos/login/version_info_updater.cc
+++ b/chrome/browser/chromeos/login/version_info_updater.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "chromeos/dbus/util/version_loader.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
@@ -98,6 +99,12 @@
   // Update device bluetooth info.
   device::BluetoothAdapterFactory::GetAdapter(base::BindOnce(
       &VersionInfoUpdater::OnGetAdapter, weak_pointer_factory_.GetWeakPtr()));
+
+  // Get ADB sideloading status.
+  chromeos::SessionManagerClient* client =
+      chromeos::SessionManagerClient::Get();
+  client->QueryAdbSideload(base::Bind(&VersionInfoUpdater::OnQueryAdbSideload,
+                                      weak_pointer_factory_.GetWeakPtr()));
 }
 
 base::Optional<bool> VersionInfoUpdater::IsSystemInfoEnforced() const {
@@ -175,4 +182,15 @@
   UpdateEnterpriseInfo();
 }
 
+void VersionInfoUpdater::OnQueryAdbSideload(bool success, bool enabled) {
+  if (!success) {
+    LOG(ERROR) << "Failed to query adb sideload status";
+    // Pretend to be enabled to show warning at login screen conservatively.
+    enabled = true;
+  }
+
+  if (delegate_)
+    delegate_->OnAdbSideloadStatusUpdated(enabled);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/version_info_updater.h b/chrome/browser/chromeos/login/version_info_updater.h
index e1ad9446..a6495f34 100644
--- a/chrome/browser/chromeos/login/version_info_updater.h
+++ b/chrome/browser/chromeos/login/version_info_updater.h
@@ -40,6 +40,9 @@
 
     // Called when the device info should be updated.
     virtual void OnDeviceInfoUpdated(const std::string& bluetooth_name) = 0;
+
+    // Called when ADB sideloading status should be updated.
+    virtual void OnAdbSideloadStatusUpdated(bool enabled) = 0;
   };
 
   explicit VersionInfoUpdater(Delegate* delegate);
@@ -79,6 +82,9 @@
   // Callback from device::BluetoothAdapterFactory::GetAdapter.
   void OnGetAdapter(scoped_refptr<device::BluetoothAdapter> adapter);
 
+  // Callback from SessionManagerClient::QueryAdbSideload.
+  void OnQueryAdbSideload(bool success, bool enabled);
+
   // Information pieces for version label.
   std::string version_text_;
   std::string serial_number_text_;
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index f74a3db3..79458a5e 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -72,6 +72,7 @@
 #include "chrome/browser/chromeos/policy/device_policy_builder.h"
 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/updater/chromeos_extension_cache_delegate.h"
@@ -97,6 +98,7 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/constants/chromeos_paths.h"
@@ -105,6 +107,7 @@
 #include "chromeos/login/auth/mock_auth_status_consumer.h"
 #include "chromeos/login/auth/user_context.h"
 #include "chromeos/network/policy_certificate_provider.h"
+#include "chromeos/settings/timezone_settings.h"
 #include "components/crx_file/crx_verifier.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
@@ -311,30 +314,94 @@
   DISALLOW_COPY_AND_ASSIGN(TestingUpdateManifestProvider);
 };
 
-// Helper that observes the dictionary |pref| in local state and waits until the
-// value stored for |key| matches |expected_value|.
-class DictionaryPrefValueWaiter {
+// Helper base class used for waiting for a pref value stored in local state
+// to change to a particular expected value.
+class PrefValueWaiter {
  public:
-  DictionaryPrefValueWaiter(const std::string& pref,
-                            const std::string& key,
-                            const std::string& expected_value);
-  ~DictionaryPrefValueWaiter();
+  PrefValueWaiter(const std::string& pref, base::Value expected_value)
+      : pref_(pref), expected_value_(std::move(expected_value)) {
+    pref_change_registrar_.Init(g_browser_process->local_state());
+  }
+
+  PrefValueWaiter(const PrefValueWaiter&) = delete;
+  PrefValueWaiter& operator=(const PrefValueWaiter&) = delete;
+
+  virtual bool ExpectedValueFound();
+  virtual ~PrefValueWaiter() = default;
 
   void Wait();
 
- private:
-  void QuitLoopIfExpectedValueFound();
-
+ protected:
   const std::string pref_;
-  const std::string key_;
-  const std::string expected_value_;
+  const base::Value expected_value_;
 
-  base::RunLoop run_loop_;
   PrefChangeRegistrar pref_change_registrar_;
 
-  DISALLOW_COPY_AND_ASSIGN(DictionaryPrefValueWaiter);
+ private:
+  base::RunLoop run_loop_;
+  void QuitLoopIfExpectedValueFound();
 };
 
+bool PrefValueWaiter::ExpectedValueFound() {
+  const base::Value* pref_value =
+      pref_change_registrar_.prefs()->Get(pref_.c_str());
+  if (!pref_value) {
+    // Can't use ASSERT_* in non-void functions so this is the next best
+    // thing.
+    ADD_FAILURE() << "Pref " << pref_ << " not found";
+    return true;
+  }
+  return *pref_value == expected_value_;
+}
+
+void PrefValueWaiter::QuitLoopIfExpectedValueFound() {
+  if (ExpectedValueFound())
+    run_loop_.Quit();
+}
+
+void PrefValueWaiter::Wait() {
+  pref_change_registrar_.Add(
+      pref_.c_str(), base::Bind(&PrefValueWaiter::QuitLoopIfExpectedValueFound,
+                                base::Unretained(this)));
+  // Necessary if the pref value changes before the run loop is run. It is
+  // safe to call RunLoop::Quit before RunLoop::Run (in which case the call
+  // to Run will do nothing).
+  QuitLoopIfExpectedValueFound();
+  run_loop_.Run();
+}
+
+class DictionaryPrefValueWaiter : public PrefValueWaiter {
+ public:
+  DictionaryPrefValueWaiter(const std::string& pref,
+                            const std::string& expected_value,
+                            const std::string& key)
+      : PrefValueWaiter(pref, base::Value(expected_value)), key_(key) {}
+
+  DictionaryPrefValueWaiter(const DictionaryPrefValueWaiter&) = delete;
+  DictionaryPrefValueWaiter& operator=(const DictionaryPrefValueWaiter&) =
+      delete;
+  ~DictionaryPrefValueWaiter() override = default;
+
+ private:
+  bool ExpectedValueFound() override;
+
+  const std::string key_;
+};
+
+bool DictionaryPrefValueWaiter::ExpectedValueFound() {
+  const base::DictionaryValue* pref =
+      pref_change_registrar_.prefs()->GetDictionary(pref_.c_str());
+  if (!pref) {
+    // Can't use ASSERT_* in non-void functions so this is the next best
+    // thing.
+    ADD_FAILURE() << "Pref " << pref_ << " not found";
+    return true;
+  }
+  std::string actual_value;
+  return (pref->GetStringWithoutPathExpansion(key_, &actual_value) &&
+          actual_value == expected_value_.GetString());
+}
+
 TestingUpdateManifestProvider::Update::Update(const std::string& version,
                                               const GURL& crx_url)
     : version(version),
@@ -393,38 +460,6 @@
 TestingUpdateManifestProvider::~TestingUpdateManifestProvider() {
 }
 
-DictionaryPrefValueWaiter::DictionaryPrefValueWaiter(
-    const std::string& pref,
-    const std::string& key,
-    const std::string& expected_value)
-    : pref_(pref),
-      key_(key),
-      expected_value_(expected_value) {
-  pref_change_registrar_.Init(g_browser_process->local_state());
-}
-
-DictionaryPrefValueWaiter::~DictionaryPrefValueWaiter() {
-}
-
-void DictionaryPrefValueWaiter::Wait() {
-  pref_change_registrar_.Add(
-      pref_.c_str(),
-      base::Bind(&DictionaryPrefValueWaiter::QuitLoopIfExpectedValueFound,
-                 base::Unretained(this)));
-  QuitLoopIfExpectedValueFound();
-  run_loop_.Run();
-}
-
-void DictionaryPrefValueWaiter::QuitLoopIfExpectedValueFound() {
-  const base::DictionaryValue* pref =
-      pref_change_registrar_.prefs()->GetDictionary(pref_.c_str());
-  ASSERT_TRUE(pref);
-  std::string actual_value;
-  if (pref->GetStringWithoutPathExpansion(key_, &actual_value) &&
-      actual_value == expected_value_) {
-    run_loop_.Quit();
-  }
-}
 
 bool DoesInstallFailureReferToId(const std::string& id,
                                  const content::NotificationSource& source,
@@ -643,6 +678,18 @@
     EXPECT_EQ(user_manager::USER_TYPE_PUBLIC_ACCOUNT, user->GetType());
   }
 
+  void SetSystemTimezoneAutomaticDetectionPolicy(
+      em::SystemTimezoneProto_AutomaticTimezoneDetectionType policy) {
+    em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
+    proto.mutable_system_timezone()->set_timezone_detection_type(policy);
+    RefreshDevicePolicy();
+
+    PrefValueWaiter(prefs::kSystemTimezoneAutomaticDetectionPolicy,
+                    base::Value(policy))
+        .Wait();
+    ASSERT_TRUE(local_policy_mixin_.UpdateDevicePolicy(proto));
+  }
+
   base::FilePath GetExtensionCacheDirectoryForAccountID(
       const std::string& account_id) {
     base::FilePath extension_cache_root_dir;
@@ -676,9 +723,8 @@
 
   void WaitForDisplayName(const std::string& user_id,
                           const std::string& expected_display_name) {
-    DictionaryPrefValueWaiter("UserDisplayName",
-                              user_id,
-                              expected_display_name).Wait();
+    DictionaryPrefValueWaiter("UserDisplayName", expected_display_name, user_id)
+        .Wait();
   }
 
   void WaitForPolicy() {
@@ -1841,6 +1887,63 @@
                 .id());
 }
 
+// Tests whether or not managed guest session users can change the system
+// timezone, which should be possible iff the timezone automatic detection
+// policy is set to either DISABLED or USERS_DECIDE.
+IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ManagedSessionTimezoneChange) {
+  SetManagedSessionsEnabled(true);
+  UploadAndInstallDeviceLocalAccountPolicy();
+  AddPublicSessionToDevicePolicy(kAccountId1);
+  EnableAutoLogin();
+
+  WaitForPolicy();
+
+  WaitForSessionStart();
+
+  CheckPublicSessionPresent(account_id_1_);
+
+  const user_manager::User* user =
+      user_manager::UserManager::Get()->FindUser(account_id_1_);
+  ASSERT_TRUE(user);
+  ASSERT_EQ(user->GetType(), user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+
+  std::string timezone_id1("America/Los_Angeles");
+  std::string timezone_id2("Europe/Berlin");
+  base::string16 timezone_id1_utf16(base::UTF8ToUTF16(timezone_id1));
+  base::string16 timezone_id2_utf16(base::UTF8ToUTF16(timezone_id2));
+
+  chromeos::system::TimezoneSettings* timezone_settings =
+      chromeos::system::TimezoneSettings::GetInstance();
+
+  timezone_settings->SetTimezoneFromID(timezone_id1_utf16);
+  SetSystemTimezoneAutomaticDetectionPolicy(em::SystemTimezoneProto::DISABLED);
+  chromeos::system::SetSystemTimezone(user, timezone_id2);
+  EXPECT_EQ(timezone_settings->GetCurrentTimezoneID(), timezone_id2_utf16);
+
+  timezone_settings->SetTimezoneFromID(timezone_id1_utf16);
+  SetSystemTimezoneAutomaticDetectionPolicy(
+      em::SystemTimezoneProto::USERS_DECIDE);
+  chromeos::system::SetSystemTimezone(user, timezone_id2);
+  EXPECT_EQ(timezone_settings->GetCurrentTimezoneID(), timezone_id2_utf16);
+
+  timezone_settings->SetTimezoneFromID(timezone_id1_utf16);
+  SetSystemTimezoneAutomaticDetectionPolicy(em::SystemTimezoneProto::IP_ONLY);
+  chromeos::system::SetSystemTimezone(user, timezone_id2);
+  EXPECT_NE(timezone_settings->GetCurrentTimezoneID(), timezone_id2_utf16);
+
+  timezone_settings->SetTimezoneFromID(timezone_id1_utf16);
+  SetSystemTimezoneAutomaticDetectionPolicy(
+      em::SystemTimezoneProto::SEND_WIFI_ACCESS_POINTS);
+  chromeos::system::SetSystemTimezone(user, timezone_id2);
+  EXPECT_NE(timezone_settings->GetCurrentTimezoneID(), timezone_id2_utf16);
+
+  timezone_settings->SetTimezoneFromID(timezone_id1_utf16);
+  SetSystemTimezoneAutomaticDetectionPolicy(
+      em::SystemTimezoneProto::SEND_ALL_LOCATION_INFO);
+  chromeos::system::SetSystemTimezone(user, timezone_id2);
+  EXPECT_NE(timezone_settings->GetCurrentTimezoneID(), timezone_id2_utf16);
+}
+
 IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, OneRecommendedLocale) {
   // Specify a recommended locale.
   SetRecommendedLocales(kSingleRecommendedLocale,
diff --git a/chrome/browser/chromeos/system/timezone_util.cc b/chrome/browser/chromeos/system/timezone_util.cc
index ddf1e56..356f2cd8 100644
--- a/chrome/browser/chromeos/system/timezone_util.cc
+++ b/chrome/browser/chromeos/system/timezone_util.cc
@@ -151,6 +151,17 @@
   return result;
 }
 
+bool CanSetSystemTimezoneFromManagedGuestSession() {
+  const int automatic_detection_policy =
+      g_browser_process->local_state()->GetInteger(
+          prefs::kSystemTimezoneAutomaticDetectionPolicy);
+
+  return (automatic_detection_policy ==
+          enterprise_management::SystemTimezoneProto::DISABLED) ||
+         (automatic_detection_policy ==
+          enterprise_management::SystemTimezoneProto::USERS_DECIDE);
+}
+
 // Returns true if the given user is allowed to set the system timezone - that
 // is, the single timezone at TimezoneSettings::GetInstance()->GetTimezone(),
 // which is also stored in a file at /var/lib/timezone/localtime.
@@ -168,13 +179,15 @@
       return true;
 
     case user_manager::USER_TYPE_GUEST:
-    case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
       return false;
 
     case user_manager::USER_TYPE_CHILD:
       return base::FeatureList::IsEnabled(
           features::kParentAccessCodeForTimeChange);
 
+    case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
+      return CanSetSystemTimezoneFromManagedGuestSession();
+
     case user_manager::NUM_USER_TYPES:
       NOTREACHED();
 
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 2c8ff0f..e73feb8 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3863,8 +3863,7 @@
 
 #endif  // defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
 
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
-    defined(OS_CHROMEOS)
+#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
 
 const char kClickToCallContextMenuForSelectedTextName[] =
     "Enable click to call feature on desktop when a phone number is selected";
@@ -3878,6 +3877,11 @@
     "Enables click to call feature signals to be handled on desktop by showing "
     "a list of user's available devices with telephony functionality.";
 
+#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
+    defined(OS_CHROMEOS)
+
 const char kRemoteCopyReceiverName[] =
     "Enables the remote copy feature to receive messages";
 const char kRemoteCopyReceiverDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index fd171b9..c2d0d3bf 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2306,8 +2306,7 @@
 
 #endif  // defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
 
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
-    defined(OS_CHROMEOS)
+#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
 
 extern const char kClickToCallContextMenuForSelectedTextName[];
 extern const char kClickToCallContextMenuForSelectedTextDescription[];
@@ -2315,6 +2314,11 @@
 extern const char kClickToCallUIName[];
 extern const char kClickToCallUIDescription[];
 
+#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
+    defined(OS_CHROMEOS)
+
 extern const char kRemoteCopyReceiverName[];
 extern const char kRemoteCopyReceiverDescription[];
 
diff --git a/chrome/browser/lifetime/application_lifetime_browsertest.cc b/chrome/browser/lifetime/application_lifetime_browsertest.cc
new file mode 100644
index 0000000..8487b82
--- /dev/null
+++ b/chrome/browser/lifetime/application_lifetime_browsertest.cc
@@ -0,0 +1,84 @@
+// 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/lifetime/application_lifetime.h"
+
+#include "base/command_line.h"
+#include "base/test/mock_callback.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_browser_main.h"
+#include "chrome/browser/chrome_browser_main_extra_parts.h"
+#include "chrome/browser/first_run/scoped_relaunch_chrome_browser_override.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// A gMock matcher that is satisfied when its argument is a command line
+// containing a given switch.
+MATCHER_P(HasSwitch, switch_name, "") {
+  return arg.HasSwitch(switch_name);
+}
+
+}  // namespace
+
+/**
+ * Test Fixture for testing chrome::AttemptRestart method.
+ *
+ * The SetupCommandLine method takes care of initiating the browser with the
+ * incognito or guest switch. This switch is accessed via GetParam() method and
+ * the switches are passed during the testcase declaration which invokes the
+ * same test once per switch.
+ *
+ * The SetUpInProcessBrowserTestFixture method, creates a mock callback which
+ * gets hooked when chrome tries to Relaunch the browser. The hooking is thanks
+ * to the upgrade_util::ScopedRelaunchChromeBrowserOverride. Our mock callback
+ * runs the MATCHER_P.
+ *
+ * It is template parameterized on the type of switches::kIncognito and
+ * switches::kGuest which is const char*
+ */
+class AttemptRestartTest : public InProcessBrowserTest,
+                           public testing::WithParamInterface<const char*> {
+ protected:
+  AttemptRestartTest() = default;
+
+  // InProcessBrowserTest:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(GetParam());
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    // Expect a browser relaunch late in browser shutdown.
+    mock_relaunch_callback_ = std::make_unique<::testing::StrictMock<
+        base::MockCallback<upgrade_util::RelaunchChromeBrowserCallback>>>();
+    EXPECT_CALL(*mock_relaunch_callback_,
+                Run(testing::Not(HasSwitch(GetParam()))));
+    relaunch_chrome_override_ =
+        std::make_unique<upgrade_util::ScopedRelaunchChromeBrowserOverride>(
+            mock_relaunch_callback_->Get());
+  }
+
+ private:
+  std::unique_ptr<
+      base::MockCallback<upgrade_util::RelaunchChromeBrowserCallback>>
+      mock_relaunch_callback_;
+  std::unique_ptr<upgrade_util::ScopedRelaunchChromeBrowserOverride>
+      relaunch_chrome_override_;
+};
+
+INSTANTIATE_TEST_CASE_P(,
+                        AttemptRestartTest,
+                        testing::Values(switches::kIncognito,
+                                        switches::kGuest));
+
+IN_PROC_BROWSER_TEST_P(AttemptRestartTest, AttemptRestartWithOTRProfiles) {
+  // We will now attempt restart, prior to (crbug.com/999085)
+  // the new session after restart defaulted to the browser type
+  // of the last session. Now, we will restart always to regular mode.
+  chrome::AttemptRestart();
+}
diff --git a/chrome/browser/lifetime/switch_utils.cc b/chrome/browser/lifetime/switch_utils.cc
index 47f6406..64bc5d9 100644
--- a/chrome/browser/lifetime/switch_utils.cc
+++ b/chrome/browser/lifetime/switch_utils.cc
@@ -26,6 +26,8 @@
     switches::kApp,
     switches::kAppId,
     switches::kForceFirstRun,
+    switches::kGuest,
+    switches::kIncognito,
     switches::kMakeDefaultBrowser,
     switches::kNoStartupWindow,
     switches::kRestoreLastSession,
diff --git a/chrome/browser/media/kaleidoscope/OWNERS b/chrome/browser/media/kaleidoscope/OWNERS
new file mode 100644
index 0000000..18438e71d
--- /dev/null
+++ b/chrome/browser/media/kaleidoscope/OWNERS
@@ -0,0 +1,5 @@
+beccahughes@chromium.org
+steimel@chromium.org
+mlamouri@chromium.org
+
+# COMPONENT: Internals>Media>UI
diff --git a/chrome/browser/password_manager/field_info_manager_factory.cc b/chrome/browser/password_manager/field_info_manager_factory.cc
new file mode 100644
index 0000000..ac1b632
--- /dev/null
+++ b/chrome/browser/password_manager/field_info_manager_factory.cc
@@ -0,0 +1,45 @@
+// 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/password_manager/field_info_manager_factory.h"
+
+#include "base/memory/singleton.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/password_manager/core/browser/field_info_manager.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "content/public/browser/browser_context.h"
+
+using password_manager::FieldInfoManager;
+
+// static
+FieldInfoManagerFactory* FieldInfoManagerFactory::GetInstance() {
+  return base::Singleton<FieldInfoManagerFactory>::get();
+}
+
+// static
+FieldInfoManager* FieldInfoManagerFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return static_cast<FieldInfoManager*>(
+      GetInstance()->GetServiceForBrowserContext(context, true /* create */));
+}
+
+FieldInfoManagerFactory::FieldInfoManagerFactory()
+    : BrowserContextKeyedServiceFactory(
+          "FieldInfoManagerFactory",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(PasswordStoreFactory::GetInstance());
+}
+
+FieldInfoManagerFactory::~FieldInfoManagerFactory() = default;
+
+// BrowserContextKeyedServiceFactory overrides:
+KeyedService* FieldInfoManagerFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  Profile* profile = static_cast<Profile*>(context);
+  return new FieldInfoManager(PasswordStoreFactory::GetForProfile(
+      profile, ServiceAccessType::EXPLICIT_ACCESS));
+}
diff --git a/chrome/browser/password_manager/field_info_manager_factory.h b/chrome/browser/password_manager/field_info_manager_factory.h
new file mode 100644
index 0000000..7203a51
--- /dev/null
+++ b/chrome/browser/password_manager/field_info_manager_factory.h
@@ -0,0 +1,37 @@
+// 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_PASSWORD_MANAGER_FIELD_INFO_MANAGER_FACTORY_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_FIELD_INFO_MANAGER_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace password_manager {
+class FieldInfoManager;
+}
+
+class FieldInfoManagerFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  static FieldInfoManagerFactory* GetInstance();
+
+  // Returns the FieldInfoManager associated with |context|.
+  // This may be nullptr for an incognito |context|.
+  static password_manager::FieldInfoManager* GetForBrowserContext(
+      content::BrowserContext* context);
+
+ private:
+  friend struct base::DefaultSingletonTraits<FieldInfoManagerFactory>;
+
+  FieldInfoManagerFactory();
+  ~FieldInfoManagerFactory() override;
+  FieldInfoManagerFactory(const FieldInfoManagerFactory&) = delete;
+  FieldInfoManagerFactory& operator=(const FieldInfoManagerFactory&) = delete;
+
+  // BrowserContextKeyedServiceFactory overrides:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+};
+
+#endif  // CHROME_BROWSER_PASSWORD_MANAGER_FIELD_INFO_MANAGER_FACTORY_H_
diff --git a/chrome/browser/payments/journey_logger_browsertest.cc b/chrome/browser/payments/journey_logger_browsertest.cc
index 4193953..09af859 100644
--- a/chrome/browser/payments/journey_logger_browsertest.cc
+++ b/chrome/browser/payments/journey_logger_browsertest.cc
@@ -48,7 +48,7 @@
  public:
   // PaymentRequestTestObserver events that can be waited on by the EventWaiter.
   enum TestEvent : int {
-    SHOW_INSTRUMENTS_READY,
+    SHOW_APPS_READY,
   };
 
   JourneyLoggerTest()
@@ -103,9 +103,9 @@
   }
 
   // PaymentRequestTestObserver implementation.
-  void OnShowInstrumentsReady() override {
+  void OnShowAppsReady() override {
     if (event_waiter_)
-      event_waiter_->OnEvent(TestEvent::SHOW_INSTRUMENTS_READY);
+      event_waiter_->OnEvent(TestEvent::SHOW_APPS_READY);
   }
 
   void ResetEventWaiterForSequence(std::list<TestEvent> event_sequence) {
@@ -129,7 +129,7 @@
 IN_PROC_BROWSER_TEST_F(JourneyLoggerTest, NoPaymentMethodSupported) {
   base::HistogramTester histogram_tester;
 
-  ResetEventWaiterForSequence({TestEvent::SHOW_INSTRUMENTS_READY});
+  ResetEventWaiterForSequence({TestEvent::SHOW_APPS_READY});
   EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), "testBasicCard()"));
   WaitForObservedEvent();
 
@@ -153,7 +153,7 @@
 
   base::HistogramTester histogram_tester;
 
-  ResetEventWaiterForSequence({TestEvent::SHOW_INSTRUMENTS_READY});
+  ResetEventWaiterForSequence({TestEvent::SHOW_APPS_READY});
   EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), "testBasicCard()"));
   WaitForObservedEvent();
 
diff --git a/chrome/browser/policy/browser_dm_token_storage.cc b/chrome/browser/policy/browser_dm_token_storage.cc
index c9f470e..a6409781 100644
--- a/chrome/browser/policy/browser_dm_token_storage.cc
+++ b/chrome/browser/policy/browser_dm_token_storage.cc
@@ -120,29 +120,22 @@
 
 void BrowserDMTokenStorage::StoreDMToken(const std::string& dm_token,
                                          StoreCallback callback) {
-  if (dm_token.empty())
-    StoreDMToken(BrowserDMToken::CreateEmptyToken(), std::move(callback));
-  else if (dm_token == kInvalidTokenValue)
-    StoreDMToken(BrowserDMToken::CreateInvalidToken(), std::move(callback));
-  else
-    StoreDMToken(BrowserDMToken::CreateValidToken(dm_token),
-                 std::move(callback));
-}
-
-void BrowserDMTokenStorage::StoreDMToken(const BrowserDMToken& dm_token,
-                                         StoreCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!store_callback_);
   InitIfNeeded();
 
-  dm_token_ = dm_token;
-
   store_callback_ = std::move(callback);
 
-  if (dm_token_.is_invalid())
+  if (dm_token.empty()) {
+    dm_token_ = BrowserDMToken::CreateEmptyToken();
+    SaveDMToken("");
+  } else if (dm_token == kInvalidTokenValue) {
+    dm_token_ = BrowserDMToken::CreateInvalidToken();
     SaveDMToken(kInvalidTokenValue);
-  else
+  } else {
+    dm_token_ = BrowserDMToken::CreateValidToken(dm_token);
     SaveDMToken(dm_token_.value());
+  }
 }
 
 std::string BrowserDMTokenStorage::RetrieveDMToken() {
diff --git a/chrome/browser/policy/browser_dm_token_storage.h b/chrome/browser/policy/browser_dm_token_storage.h
index e9c6da3..28a4671 100644
--- a/chrome/browser/policy/browser_dm_token_storage.h
+++ b/chrome/browser/policy/browser_dm_token_storage.h
@@ -57,7 +57,6 @@
    private:
     enum class Status { kValid, kInvalid, kEmpty };
 
-    BrowserDMToken();
     BrowserDMToken(Status status, const base::StringPiece value);
 
     Status status_;
@@ -77,9 +76,7 @@
   // Asynchronously stores |dm_token| and calls |callback| with a boolean to
   // indicate success or failure. It is an error to attempt concurrent store
   // operations.
-  // TODO(domfc): Remove overload after updating callers.
   void StoreDMToken(const std::string& dm_token, StoreCallback callback);
-  void StoreDMToken(const BrowserDMToken& dm_token, StoreCallback callback);
   // Returns an already stored DM token. An empty token is returned if no DM
   // token exists on the system or an error is encountered.
   // TODO(domfc): Remove overload after updating callers. Note that the names
diff --git a/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.cc b/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.cc
index d57eeb1..c5cea45 100644
--- a/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.cc
+++ b/chrome/browser/policy/chrome_browser_cloud_management_register_watcher.cc
@@ -42,7 +42,7 @@
     return RegisterResult::kNoEnrollmentNeeded;
 
   // We are already enrolled successfully.
-  if (!token_storage->RetrieveDMToken().empty())
+  if (token_storage->RetrieveBrowserDMToken().is_valid())
     return RegisterResult::kEnrollmentSuccessBeforeDialogDisplayed;
 
   EnterpriseStartupDialog::DialogResultCallback callback = base::BindOnce(
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 1886b03..f5f84aab 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1230,6 +1230,12 @@
     extensions::pref_names::kBlockExternalExtensions,
     base::Value::Type::BOOLEAN },
 #endif
+
+#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
+  { key::kClickToCallEnabled,
+    prefs::kClickToCallEnabled,
+    base::Value::Type::BOOLEAN },
+#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
 };
 // clang-format on
 
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index e6a92498..fb2a2abe 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -262,8 +262,10 @@
     /**
      * Key for whether it allows to start in service manager only mode.
      * Default value is false.
+     * Deprecated in M77.
      */
-    public static final String ALLOW_STARTING_SERVICE_MANAGER_ONLY_KEY =
+    @Deprecated
+    private static final String ALLOW_STARTING_SERVICE_MANAGER_ONLY_KEY =
             "allow_starting_service_manager_only";
 
     /**
diff --git a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
index 1b3c39b..83929ba 100644
--- a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
+++ b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
@@ -32,6 +32,7 @@
       multidevice-setup {
         --multidevice-setup-height: 100%;
         --multidevice-setup-width: 100%;
+        --iron-pages-overflow: auto;
       }
 
       #multidevice-help-overlay-container {
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
index ec5cbd7..a9f7038 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
+++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
@@ -13,6 +13,7 @@
       multidevice-setup {
         --multidevice-setup-height: 640px;
         --multidevice-setup-width: 768px;
+        --iron-pages-overflow: visible;
       }
 
       #backward-button,
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css b/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css
index bdcd2e0..d37169216 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css
@@ -534,7 +534,6 @@
 }
 
 #centering-options {
-  display: flex;
   padding: 0;
 }
 
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/main.html b/chrome/browser/resources/chromeos/wallpaper_manager/main.html
index 3816df2..4363eb6 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/main.html
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/main.html
@@ -165,12 +165,12 @@
       <div class="divider"></div>
       <div id="wallpaper-description"></div>
       <div class="more-options">
-        <div id="centering-options">
-          <div id="center-button" class="custom-option">
+        <div id="centering-options" class="custom-option">
+          <div id="center-button">
             <div class="icon"></div>
             <div class="text" i18n-content="centerLayout"></div>
           </div>
-          <div id="center-cropped-button" class="custom-option">
+          <div id="center-cropped-button">
             <div class="icon"></div>
             <div class="text" i18n-content="centerCroppedLayout"></div>
           </div>
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index 7c40d63..8b4e1a8 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -5,6 +5,7 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="../appearance_page/appearance_page.html">
+<link rel="import" href="../privacy_page/privacy_page.html">
 <link rel="import" href="../autofill_page/autofill_page.html">
 <link rel="import" href="../change_password_page/change_password_page.html">
 <link rel="import" href="../controls/settings_idle_load.html">
@@ -175,6 +176,15 @@
             </settings-autofill-page>
           </settings-section>
         </template>
+        <template is="dom-if" if="[[showPrivacyPageAndRedesign_(
+            pageVisibility.privacy, 'BASIC')]]" restamp>
+          <settings-section page-title="$i18n{privacyPageTitle}"
+              section="privacy">
+            <settings-privacy-page prefs="{{prefs}}"
+                page-visibility="[[pageVisibility.privacy]]">
+            </settings-privacy-page>
+          </settings-section>
+        </template>
         <template is="dom-if" if="[[showPage_(pageVisibility.appearance)]]"
             restamp>
           <settings-section page-title="$i18n{appearancePageTitle}"
@@ -274,8 +284,8 @@
               </settings-section>
             </template>
 </if>
-            <template is="dom-if" if="[[showPage_(pageVisibility.privacy)]]"
-                restamp>
+            <template is="dom-if" if="[[showPrivacyPageAndRedesign_(
+                pageVisibility.privacy, 'ADVANCED')]]" restamp>
               <settings-section page-title="$i18n{privacyPageTitle}"
                   section="privacy">
                 <settings-privacy-page prefs="{{prefs}}"
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.js b/chrome/browser/resources/settings/basic_page/basic_page.js
index f12eb09..2e0fdd2 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.js
+++ b/chrome/browser/resources/settings/basic_page/basic_page.js
@@ -203,6 +203,20 @@
   },
 
   /**
+   * @param {boolean|undefined} visibility
+   * @param {string} location
+   * @return {boolean}
+   * @private
+   */
+  showPrivacyPageAndRedesign_: function(visibility, location) {
+    if (!this.showPage_(visibility)) {
+      return false;
+    }
+    return (location == 'BASIC') ==
+        loadTimeData.getBoolean('privacySettingsRedesignEnabled');
+  },
+
+  /**
    * Queues a task to search the basic sections, then another for the advanced
    * sections.
    * @param {string} query The text to search for.
diff --git a/chrome/browser/resources/settings/lazy_load.html b/chrome/browser/resources/settings/lazy_load.html
index 24e3853..90b7096 100644
--- a/chrome/browser/resources/settings/lazy_load.html
+++ b/chrome/browser/resources/settings/lazy_load.html
@@ -5,7 +5,6 @@
   <link rel="import" href="downloads_page/downloads_page.html">
   <link rel="import" href="languages_page/languages_page.html">
   <link rel="import" href="printing_page/printing_page.html">
-  <link rel="import" href="privacy_page/privacy_page.html">
   <link rel="import" href="reset_page/reset_page.html">
 
   <if expr="chromeos">
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index 19c65072..83c0a9cb 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -2,12 +2,14 @@
 
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
 <link rel="import" href="security_keys_subpage.html">
 <link rel="import" href="../clear_browsing_data_dialog/clear_browsing_data_dialog.html">
 <link rel="import" href="../controls/settings_toggle_button.html">
@@ -43,6 +45,10 @@
 <dom-module id="settings-privacy-page">
   <template>
     <style include="settings-shared">
+      .iron-collapse-indented {
+        margin-inline-start: var(--cr-section-indent-width);
+      }
+
 <if expr="not chromeos">
       #toast {
         left: 0;
@@ -82,76 +88,90 @@
     <settings-animated-pages id="pages" section="privacy"
         focus-config="[[focusConfig_]]">
       <div route-path="default">
-        <cr-link-row label="$i18n{syncAndNonPersonalizedServices}"
-            sub-label="$i18n{syncAndGoogleServicesPrivacyDescription}"
-            on-click="onSyncAndGoogleServicesClick_">
-        </cr-link-row>
-<if expr="not chromeos">
-        <settings-toggle-button id="signinAllowedToggle"
-            pref="{{prefs.signin.allowed_on_next_startup}}"
-            label="$i18n{signinAllowedTitle}"
-            sub-label="$i18n{signinAllowedDescription}"
-            on-settings-boolean-control-change="onSigninAllowedChange_"
-            no-set-pref>
-        </settings-toggle-button>
-</if><!-- not chromeos -->
-        <settings-toggle-button id="doNotTrack"
-            pref="{{prefs.enable_do_not_track}}" label="$i18n{doNotTrack}"
-            on-settings-boolean-control-change="onDoNotTrackChange_"
-            no-set-pref>
-        </settings-toggle-button>
-        <settings-toggle-button id="canMakePaymentToggle"
-            aria-label="$i18n{canMakePaymentToggleLabel}"
-            label="$i18n{canMakePaymentToggleLabel}"
-            pref="{{prefs.payments.can_make_payment_enabled}}"
-            on-settings-boolean-control-change="onCanMakePaymentChange_">
-        </settings-toggle-button>
-        <settings-toggle-button hidden="[[!pageVisibility.networkPrediction]]"
-            pref="{{prefs.net.network_prediction_options}}"
-            label="$i18n{networkPredictionEnabled}"
-            sub-label="$i18n{networkPredictionEnabledDesc}"
-            numeric-unchecked-value="[[networkPredictionUncheckedValue_]]"
-            on-settings-boolean-control-change="onNetworkPredictionChange_">
-        </settings-toggle-button>
-<if expr="chromeos">
-        <settings-toggle-button
-            hidden="[[!pageVisibility.contentProtectionAttestation]]"
-            pref="{{prefs.cros.device.attestation_for_content_protection_enabled}}"
-            label="$i18n{enableContentProtectionAttestation}">
-        </settings-toggle-button>
-        <settings-toggle-button
-            hidden="[[!pageVisibility.wakeOnWifi]]"
-            pref="{{prefs.settings.internet.wake_on_wifi_darkconnect}}"
-            label="$i18n{wakeOnWifi}">
-        </settings-toggle-button>
-</if>
-<if expr="use_nss_certs or is_win or is_macosx">
-        <cr-link-row id="manageCertificates"
-            class="hr"
-<if expr="not use_nss_certs">
-            external
-</if>
-            label="$i18n{manageCertificates}"
-            sub-label="$i18n{manageCertificatesDescription}"
-            on-click="onManageCertificatesTap_"></cr-link-row>
-</if>
-        <template is="dom-if" if="[[enableSecurityKeysSubpage_]]">
-          <cr-link-row id="security-keys-subpage-trigger"
-            class="hr"
-            label="$i18n{securityKeysTitle}"
-            sub-label="$i18n{securityKeysDesc}"
-            on-click="onSecurityKeysTap_"></cr-link-row>
-        </template>
-        <cr-link-row id="site-settings-subpage-trigger"
-            class="hr"
-            label="$i18n{siteSettings}"
-            sub-label="$i18n{siteSettingsDescription}"
-            on-click="onSiteSettingsTap_"></cr-link-row>
         <cr-link-row id="clearBrowsingData"
             class="hr"
             label="$i18n{clearBrowsingData}"
             sub-label="$i18n{clearBrowsingDataDescription}"
             on-click="onClearBrowsingDataTap_"></cr-link-row>
+        <cr-link-row id="site-settings-subpage-trigger"
+            class="hr"
+            label="$i18n{siteSettings}"
+            sub-label="$i18n{siteSettingsDescription}"
+            on-click="onSiteSettingsTap_"></cr-link-row>
+        <cr-link-row id="syncService"
+            class="hr"
+            label="$i18n{syncAndNonPersonalizedServices}"
+            sub-label="$i18n{syncAndGoogleServicesPrivacyDescription}"
+            on-click="onSyncAndGoogleServicesClick_"></cr-link-row>
+        <cr-expand-button id="moreExpansion"
+            alt="$i18n{privacyPageMore}"
+            class="settings-box"
+            expanded="{{moreOpened_}}"
+            hidden="[[!privacySettingsRedesignEnabled_]]">
+            <div>$i18n{privacyPageMore}</div>
+        </cr-expand-button>
+        <iron-collapse id="moreCollapse" opened="[[moreOpened_]]"
+            class$="[[getIronCollapseCssClass_(
+              privacySettingsRedesignEnabled_)]]">
+          <settings-toggle-button id="doNotTrack"
+              class$="[[getTopBarCssClass_(privacySettingsRedesignEnabled_)]]"
+              pref="{{prefs.enable_do_not_track}}" label="$i18n{doNotTrack}"
+              on-settings-boolean-control-change="onDoNotTrackChange_"
+              no-set-pref>
+          </settings-toggle-button>
+<if expr="not chromeos">
+          <settings-toggle-button id="signinAllowedToggle"
+              pref="{{prefs.signin.allowed_on_next_startup}}"
+              label="$i18n{signinAllowedTitle}"
+              sub-label="$i18n{signinAllowedDescription}"
+              on-settings-boolean-control-change="onSigninAllowedChange_"
+              no-set-pref>
+          </settings-toggle-button>
+</if><!-- not chromeos -->
+          <settings-toggle-button id="canMakePaymentToggle"
+              aria-label="$i18n{canMakePaymentToggleLabel}"
+              label="$i18n{canMakePaymentToggleLabel}"
+              pref="{{prefs.payments.can_make_payment_enabled}}"
+              on-settings-boolean-control-change="onCanMakePaymentChange_">
+          </settings-toggle-button>
+          <settings-toggle-button hidden="[[!pageVisibility.networkPrediction]]"
+              pref="{{prefs.net.network_prediction_options}}"
+              label="$i18n{networkPredictionEnabled}"
+              sub-label="$i18n{networkPredictionEnabledDesc}"
+              numeric-unchecked-value="[[networkPredictionUncheckedValue_]]"
+              on-settings-boolean-control-change="onNetworkPredictionChange_">
+          </settings-toggle-button>
+<if expr="chromeos">
+          <settings-toggle-button
+              hidden="[[!pageVisibility.contentProtectionAttestation]]"
+              pref=
+              "{{prefs.cros.device.attestation_for_content_protection_enabled}}"
+              label="$i18n{enableContentProtectionAttestation}">
+          </settings-toggle-button>
+          <settings-toggle-button
+              hidden="[[!pageVisibility.wakeOnWifi]]"
+              pref="{{prefs.settings.internet.wake_on_wifi_darkconnect}}"
+              label="$i18n{wakeOnWifi}">
+          </settings-toggle-button>
+</if>
+<if expr="use_nss_certs or is_win or is_macosx">
+          <cr-link-row id="manageCertificates"
+              class="hr"
+<if expr="not use_nss_certs">
+          external
+</if>
+          label="$i18n{manageCertificates}"
+          sub-label="$i18n{manageCertificatesDescription}"
+          on-click="onManageCertificatesTap_"></cr-link-row>
+</if>
+          <template is="dom-if" if="[[enableSecurityKeysSubpage_]]">
+            <cr-link-row id="security-keys-subpage-trigger"
+              class="hr"
+              label="$i18n{securityKeysTitle}"
+              sub-label="$i18n{securityKeysDesc}"
+              on-click="onSecurityKeysTap_"></cr-link-row>
+          </template>
+        </iron-collapse>
       </div>
 
 <if expr="use_nss_certs">
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.js b/chrome/browser/resources/settings/privacy_page/privacy_page.js
index 194f85eb..b1b06d71 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.js
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.js
@@ -120,6 +120,25 @@
     },
 
     /** @private */
+    privacySettingsRedesignEnabled_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('privacySettingsRedesignEnabled');
+      },
+    },
+
+    /**
+     * Whether the more settings list is opened.
+     * @private
+     */
+    moreOpened_: {
+      type: Boolean,
+      value: function() {
+        return !loadTimeData.getBoolean('privacySettingsRedesignEnabled');
+      },
+    },
+
+    /** @private */
     enableBlockAutoplayContentSetting_: {
       type: Boolean,
       value: function() {
@@ -481,5 +500,21 @@
     e.stopPropagation();
     settings.LifetimeBrowserProxyImpl.getInstance().restart();
   },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getIronCollapseCssClass_: function() {
+    return this.privacySettingsRedesignEnabled_ ? 'iron-collapse-indented' : '';
+  },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getTopBarCssClass_: function() {
+    return this.privacySettingsRedesignEnabled_ ? 'settings-box first' : '';
+  },
 });
 })();
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js
index 9cbaf6d..9323a38 100644
--- a/chrome/browser/resources/settings/route.js
+++ b/chrome/browser/resources/settings/route.js
@@ -256,6 +256,78 @@
   }
 
   /**
+   * Add all of the child routes that originate from the privacy route,
+   * regardless of whether the privacy section under basic or advanced.
+   * @param {!SettingsRoutes} r
+   */
+  function addPrivacyChildRoutes(r) {
+    r.CERTIFICATES = r.PRIVACY.createChild('/certificates');
+    r.SITE_SETTINGS = r.PRIVACY.createChild('/content');
+    if (loadTimeData.getBoolean('enableSecurityKeysSubpage')) {
+      r.SECURITY_KEYS = r.PRIVACY.createChild('/securityKeys');
+    }
+
+    r.SITE_SETTINGS_ALL = r.SITE_SETTINGS.createChild('all');
+    r.SITE_SETTINGS_SITE_DETAILS =
+        r.SITE_SETTINGS_ALL.createChild('/content/siteDetails');
+
+    r.SITE_SETTINGS_HANDLERS = r.SITE_SETTINGS.createChild('/handlers');
+
+    // TODO(tommycli): Find a way to refactor these repetitive category
+    // routes.
+    r.SITE_SETTINGS_ADS = r.SITE_SETTINGS.createChild('ads');
+    r.SITE_SETTINGS_AUTOMATIC_DOWNLOADS =
+        r.SITE_SETTINGS.createChild('automaticDownloads');
+    r.SITE_SETTINGS_BACKGROUND_SYNC =
+        r.SITE_SETTINGS.createChild('backgroundSync');
+    r.SITE_SETTINGS_CAMERA = r.SITE_SETTINGS.createChild('camera');
+    r.SITE_SETTINGS_CLIPBOARD = r.SITE_SETTINGS.createChild('clipboard');
+    r.SITE_SETTINGS_COOKIES = r.SITE_SETTINGS.createChild('cookies');
+    r.SITE_SETTINGS_SITE_DATA =
+        r.SITE_SETTINGS_COOKIES.createChild('/siteData');
+    r.SITE_SETTINGS_DATA_DETAILS =
+        r.SITE_SETTINGS_SITE_DATA.createChild('/cookies/detail');
+    r.SITE_SETTINGS_IMAGES = r.SITE_SETTINGS.createChild('images');
+    if (loadTimeData.getBoolean('enableInsecureContentContentSetting')) {
+      r.SITE_SETTINGS_MIXEDSCRIPT =
+          r.SITE_SETTINGS.createChild('insecureContent');
+    }
+    r.SITE_SETTINGS_JAVASCRIPT = r.SITE_SETTINGS.createChild('javascript');
+    r.SITE_SETTINGS_SOUND = r.SITE_SETTINGS.createChild('sound');
+    r.SITE_SETTINGS_SENSORS = r.SITE_SETTINGS.createChild('sensors');
+    r.SITE_SETTINGS_LOCATION = r.SITE_SETTINGS.createChild('location');
+    r.SITE_SETTINGS_MICROPHONE = r.SITE_SETTINGS.createChild('microphone');
+    r.SITE_SETTINGS_NOTIFICATIONS =
+        r.SITE_SETTINGS.createChild('notifications');
+    r.SITE_SETTINGS_FLASH = r.SITE_SETTINGS.createChild('flash');
+    r.SITE_SETTINGS_POPUPS = r.SITE_SETTINGS.createChild('popups');
+    r.SITE_SETTINGS_UNSANDBOXED_PLUGINS =
+        r.SITE_SETTINGS.createChild('unsandboxedPlugins');
+    r.SITE_SETTINGS_MIDI_DEVICES = r.SITE_SETTINGS.createChild('midiDevices');
+    r.SITE_SETTINGS_USB_DEVICES = r.SITE_SETTINGS.createChild('usbDevices');
+    r.SITE_SETTINGS_SERIAL_PORTS = r.SITE_SETTINGS.createChild('serialPorts');
+    r.SITE_SETTINGS_ZOOM_LEVELS = r.SITE_SETTINGS.createChild('zoomLevels');
+    r.SITE_SETTINGS_PDF_DOCUMENTS = r.SITE_SETTINGS.createChild('pdfDocuments');
+    r.SITE_SETTINGS_PROTECTED_CONTENT =
+        r.SITE_SETTINGS.createChild('protectedContent');
+    if (loadTimeData.getBoolean('enablePaymentHandlerContentSetting')) {
+      r.SITE_SETTINGS_PAYMENT_HANDLER =
+          r.SITE_SETTINGS.createChild('paymentHandler');
+    }
+    if (loadTimeData.getBoolean('enableExperimentalWebPlatformFeatures')) {
+      r.SITE_SETTINGS_BLUETOOTH_SCANNING =
+          r.SITE_SETTINGS.createChild('bluetoothScanning');
+    }
+    if (loadTimeData.getBoolean('enableNativeFileSystemWriteContentSetting')) {
+      r.SITE_SETTINGS_NATIVE_FILE_SYSTEM_WRITE =
+          r.SITE_SETTINGS.createChild('filesystem');
+    }
+
+    r.CLEAR_BROWSER_DATA = r.BASIC.createChild('/clearBrowserData');
+    r.CLEAR_BROWSER_DATA.isNavigableDialog = true;
+  }
+
+  /**
    * Adds Route objects for each path corresponding to browser-only content.
    * @param {!SettingsRoutes} r Routes to include browser-only content.
    */
@@ -283,6 +355,13 @@
       r.ADDRESSES = r.AUTOFILL.createChild('/addresses');
     }
 
+    if (loadTimeData.getBoolean('privacySettingsRedesignEnabled')) {
+      if (pageVisibility.privacy !== false) {
+        r.PRIVACY = r.BASIC.createSection('/privacy', 'privacy');
+        addPrivacyChildRoutes(r);
+      }
+    }
+
     if (pageVisibility.defaultBrowser !== false) {
       r.DEFAULT_BROWSER =
           r.BASIC.createSection('/defaultBrowser', 'defaultBrowser');
@@ -299,76 +378,13 @@
     if (pageVisibility.advancedSettings !== false) {
       r.ADVANCED = new Route('/advanced');
 
-      r.CLEAR_BROWSER_DATA = r.ADVANCED.createChild('/clearBrowserData');
-      r.CLEAR_BROWSER_DATA.isNavigableDialog = true;
-
-      if (pageVisibility.privacy !== false) {
-        r.PRIVACY = r.ADVANCED.createSection('/privacy', 'privacy');
-        r.CERTIFICATES = r.PRIVACY.createChild('/certificates');
-        r.SITE_SETTINGS = r.PRIVACY.createChild('/content');
-        if (loadTimeData.getBoolean('enableSecurityKeysSubpage')) {
-          r.SECURITY_KEYS = r.PRIVACY.createChild('/securityKeys');
+      if (!loadTimeData.getBoolean('privacySettingsRedesignEnabled')) {
+        if (pageVisibility.privacy !== false) {
+          r.PRIVACY = r.ADVANCED.createSection('/privacy', 'privacy');
+          addPrivacyChildRoutes(r);
         }
       }
 
-      r.SITE_SETTINGS_ALL = r.SITE_SETTINGS.createChild('all');
-      r.SITE_SETTINGS_SITE_DETAILS =
-          r.SITE_SETTINGS_ALL.createChild('/content/siteDetails');
-
-      r.SITE_SETTINGS_HANDLERS = r.SITE_SETTINGS.createChild('/handlers');
-
-      // TODO(tommycli): Find a way to refactor these repetitive category
-      // routes.
-      r.SITE_SETTINGS_ADS = r.SITE_SETTINGS.createChild('ads');
-      r.SITE_SETTINGS_AUTOMATIC_DOWNLOADS =
-          r.SITE_SETTINGS.createChild('automaticDownloads');
-      r.SITE_SETTINGS_BACKGROUND_SYNC =
-          r.SITE_SETTINGS.createChild('backgroundSync');
-      r.SITE_SETTINGS_CAMERA = r.SITE_SETTINGS.createChild('camera');
-      r.SITE_SETTINGS_CLIPBOARD = r.SITE_SETTINGS.createChild('clipboard');
-      r.SITE_SETTINGS_COOKIES = r.SITE_SETTINGS.createChild('cookies');
-      r.SITE_SETTINGS_SITE_DATA =
-          r.SITE_SETTINGS_COOKIES.createChild('/siteData');
-      r.SITE_SETTINGS_DATA_DETAILS =
-          r.SITE_SETTINGS_SITE_DATA.createChild('/cookies/detail');
-      r.SITE_SETTINGS_IMAGES = r.SITE_SETTINGS.createChild('images');
-      if (loadTimeData.getBoolean('enableInsecureContentContentSetting')) {
-        r.SITE_SETTINGS_MIXEDSCRIPT =
-            r.SITE_SETTINGS.createChild('insecureContent');
-      }
-      r.SITE_SETTINGS_JAVASCRIPT = r.SITE_SETTINGS.createChild('javascript');
-      r.SITE_SETTINGS_SOUND = r.SITE_SETTINGS.createChild('sound');
-      r.SITE_SETTINGS_SENSORS = r.SITE_SETTINGS.createChild('sensors');
-      r.SITE_SETTINGS_LOCATION = r.SITE_SETTINGS.createChild('location');
-      r.SITE_SETTINGS_MICROPHONE = r.SITE_SETTINGS.createChild('microphone');
-      r.SITE_SETTINGS_NOTIFICATIONS =
-          r.SITE_SETTINGS.createChild('notifications');
-      r.SITE_SETTINGS_FLASH = r.SITE_SETTINGS.createChild('flash');
-      r.SITE_SETTINGS_POPUPS = r.SITE_SETTINGS.createChild('popups');
-      r.SITE_SETTINGS_UNSANDBOXED_PLUGINS =
-          r.SITE_SETTINGS.createChild('unsandboxedPlugins');
-      r.SITE_SETTINGS_MIDI_DEVICES = r.SITE_SETTINGS.createChild('midiDevices');
-      r.SITE_SETTINGS_USB_DEVICES = r.SITE_SETTINGS.createChild('usbDevices');
-      r.SITE_SETTINGS_SERIAL_PORTS = r.SITE_SETTINGS.createChild('serialPorts');
-      r.SITE_SETTINGS_ZOOM_LEVELS = r.SITE_SETTINGS.createChild('zoomLevels');
-      r.SITE_SETTINGS_PDF_DOCUMENTS =
-          r.SITE_SETTINGS.createChild('pdfDocuments');
-      r.SITE_SETTINGS_PROTECTED_CONTENT =
-          r.SITE_SETTINGS.createChild('protectedContent');
-      if (loadTimeData.getBoolean('enablePaymentHandlerContentSetting')) {
-        r.SITE_SETTINGS_PAYMENT_HANDLER =
-            r.SITE_SETTINGS.createChild('paymentHandler');
-      }
-      if (loadTimeData.getBoolean('enableExperimentalWebPlatformFeatures')) {
-        r.SITE_SETTINGS_BLUETOOTH_SCANNING =
-            r.SITE_SETTINGS.createChild('bluetoothScanning');
-      }
-      if (loadTimeData.getBoolean(
-              'enableNativeFileSystemWriteContentSetting')) {
-        r.SITE_SETTINGS_NATIVE_FILE_SYSTEM_WRITE =
-            r.SITE_SETTINGS.createChild('filesystem');
-      }
-
       r.LANGUAGES = r.ADVANCED.createSection('/languages', 'languages');
       // <if expr="not is_macosx">
       r.EDIT_DICTIONARY = r.LANGUAGES.createChild('/editDictionary');
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.html b/chrome/browser/resources/settings/settings_menu/settings_menu.html
index 5f7a88b..d38780ea 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.html
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.html
@@ -135,6 +135,10 @@
         <iron-icon icon="settings:assignment"></iron-icon>
         $i18n{autofillPageTitle}
       </a>
+      <a href="/privacy" hidden="[[!privacySettingsRedesignEnabled_]]">
+        <iron-icon icon="cr:security"></iron-icon>
+        $i18n{privacyPageTitle}
+      </a>
       <a id="appearance" href="/appearance"
         hidden="[[!pageVisibility.appearance]]">
         <iron-icon icon="settings:palette"></iron-icon>
@@ -193,7 +197,7 @@
             $i18n{dateTimePageTitle}
           </a>
 </if>
-          <a href="/privacy">
+          <a href="/privacy" hidden="[[privacySettingsRedesignEnabled_]]">
             <iron-icon icon="cr:security"></iron-icon>
             $i18n{privacyPageTitle}
           </a>
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.js b/chrome/browser/resources/settings/settings_menu/settings_menu.js
index c604ae4b..5ace9a68 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.js
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.js
@@ -18,6 +18,14 @@
       notify: true,
     },
 
+    /** @private */
+    privacySettingsRedesignEnabled_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('privacySettingsRedesignEnabled');
+      },
+    },
+
     /**
      * Dictionary defining page visibility.
      * @type {!PageVisibility}
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_browsertest_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_browsertest_win.cc
index b3f0fd5..6f05115 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_browsertest_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_browsertest_win.cc
@@ -100,17 +100,24 @@
 }
 
 IN_PROC_BROWSER_TEST_P(ChromeCleanerPromptUserTest,
-                       DISABLED_OnInfectedBrowserNotAvailable) {
+                       OnInfectedBrowserNotAvailable) {
   browser()->window()->Minimize();
   base::RunLoop().RunUntilIdle();
-  dialog_controller_->OnInfected(false, ChromeCleanerScannerResults());
 
+  // We try to not show the prompt while minimized, but there will always be
+  // race conditions because the window manager can restore a window outside
+  // the test's control. So a prompt might show up even while minimized. That's
+  // not critical. The really important test is that a prompt always shows up
+  // after restoring.
+  //
+  // Install the expectation here so that we'll detect when the prompt shows
+  // up, even if it's too early.
   base::RunLoop run_loop;
-  // We only set the expectation here because we want to make sure that the
-  // prompt is shown only when the window is restored.
   EXPECT_CALL(mock_delegate_, ShowChromeCleanerPrompt(_, _, _))
       .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
 
+  dialog_controller_->OnInfected(false, ChromeCleanerScannerResults());
+
   browser()->window()->Restore();
   run_loop.Run();
 }
diff --git a/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc b/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc
index 8cb9101..db21bd0 100644
--- a/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc
@@ -29,6 +29,8 @@
 #include "content/public/browser/browser_thread.h"
 #include "net/http/http_status_code.h"
 
+using BrowserDMToken = policy::BrowserDMTokenStorage::BrowserDMToken;
+
 namespace safe_browsing {
 namespace {
 
@@ -38,13 +40,19 @@
 // here.
 const char kSbBinaryUploadUrl[] = "";
 
-std::string* GetTestingDMToken() {
-  static std::string dm_token;
+const char** GetTestingDMTokenStorage() {
+  static const char* dm_token = "";
   return &dm_token;
 }
 
-std::string GetDMToken() {
-  std::string dm_token = *GetTestingDMToken();
+BrowserDMToken GetTestingDMToken() {
+  const char* dm_token = *GetTestingDMTokenStorage();
+  return dm_token && dm_token[0] ? BrowserDMToken::CreateValidToken(dm_token)
+                                 : BrowserDMToken::CreateEmptyToken();
+}
+
+policy::BrowserDMTokenStorage::BrowserDMToken GetDMToken() {
+  auto dm_token = GetTestingDMToken();
 
 #if !defined(OS_CHROMEOS)
   // This is not compiled on chromeos because
@@ -52,9 +60,9 @@
   // policy::BrowserDMTokenStorage::Get()->RetrieveDMToken() does not return a
   // valid token either.  Once these are fixed the #if !defined can be removed.
 
-  if (dm_token.empty() &&
+  if (dm_token.is_empty() &&
       policy::ChromeBrowserCloudManagementController::IsEnabled()) {
-    dm_token = policy::BrowserDMTokenStorage::Get()->RetrieveDMToken();
+    dm_token = policy::BrowserDMTokenStorage::Get()->RetrieveBrowserDMToken();
   }
 #endif
 
@@ -412,8 +420,8 @@
   if (!can_upload_data_.has_value()) {
     // Send a request to check if the browser can upload data.
     if (!pending_validate_data_upload_request_) {
-      std::string dm_token = GetDMToken();
-      if (dm_token.empty()) {
+      auto dm_token = GetDMToken();
+      if (!dm_token.is_valid()) {
         std::move(callback).Run(false);
         return;
       }
@@ -422,7 +430,7 @@
       auto request = std::make_unique<ValidateDataUploadRequest>(base::BindOnce(
           &BinaryUploadService::ValidateDataUploadRequestCallback,
           weakptr_factory_.GetWeakPtr()));
-      request->set_dm_token(dm_token);
+      request->set_dm_token(dm_token.value());
       UploadForDeepScanning(std::move(request));
     }
     authorization_callbacks_.push_back(std::move(callback));
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
index ffaafd1..36cb3d2c 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -43,6 +43,7 @@
 namespace safe_browsing {
 
 using content::BrowserThread;
+using policy::BrowserDMTokenStorage;
 
 namespace {
 
@@ -359,8 +360,9 @@
     request->set_request_malware_scan(std::move(malware_request));
   }
 
-  request->set_dm_token(
-      policy::BrowserDMTokenStorage::Get()->RetrieveDMToken());
+  auto dm_token = BrowserDMTokenStorage::Get()->RetrieveBrowserDMToken();
+  DCHECK(dm_token.is_valid());
+  request->set_dm_token(dm_token.value());
 
   service()->UploadForDeepScanning(profile, std::move(request));
 }
@@ -389,8 +391,9 @@
           CheckContentComplianceValues::CHECK_UPLOADS_AND_DOWNLOADS)
     return false;
 
-  // If there's no DM token, the upload will fail, so we can skip uploading now.
-  if (policy::BrowserDMTokenStorage::Get()->RetrieveDMToken().empty())
+  // If there's no valid DM token, the upload will fail, so we can skip
+  // uploading now.
+  if (!BrowserDMTokenStorage::Get()->RetrieveBrowserDMToken().is_valid())
     return false;
 
   const base::ListValue* domains = g_browser_process->local_state()->GetList(
@@ -428,8 +431,9 @@
           SendFilesForMalwareCheckValues::SEND_UPLOADS_AND_DOWNLOADS)
     return false;
 
-  // If there's no DM token, the upload will fail, so we can skip uploading now.
-  return !policy::BrowserDMTokenStorage::Get()->RetrieveDMToken().empty();
+  // If there's no valid DM token, the upload will fail, so we can skip
+  // uploading now.
+  return BrowserDMTokenStorage::Get()->RetrieveBrowserDMToken().is_valid();
 }
 
 void CheckClientDownloadRequest::OnDeepScanningComplete(
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc b/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
index 77c2c25..accc3da 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/policy/policy_test_utils.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_utils.h"
@@ -22,6 +23,9 @@
 #include "chrome/browser/sharing/sharing_metrics.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
 #include "chrome/browser/sync/test/integration/sessions_helper.h"
+#include "chrome/common/pref_names.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
 #include "components/sync/driver/profile_sync_service.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
@@ -36,6 +40,13 @@
 const char kTextWithoutPhoneNumber[] = "abcde";
 
 const char kTestPageURL[] = "/sharing/tel.html";
+
+enum class ClickToCallPolicy {
+  kNotConfigured,
+  kFalse,
+  kTrue,
+};
+
 }  // namespace
 
 // Browser tests for the Click To Call feature.
@@ -415,3 +426,62 @@
   // Regression test for http://crbug.com/1000934.
   sessions_helper::CloseTab(/*browser_index=*/0, /*tab_index=*/0);
 }
+
+class ClickToCallPolicyTest
+    : public policy::PolicyTest,
+      public testing::WithParamInterface<ClickToCallPolicy> {
+ public:
+  ClickToCallPolicyTest() {
+    scoped_feature_list_.InitWithFeatures(
+        {kClickToCallUI, kClickToCallContextMenuForSelectedText}, {});
+  }
+  ~ClickToCallPolicyTest() override = default;
+
+  void SetUpInProcessBrowserTestFixture() override {
+    PolicyTest::SetUpInProcessBrowserTestFixture();
+    policy::PolicyMap policies;
+
+    const ClickToCallPolicy policy = GetParam();
+    if (policy == ClickToCallPolicy::kFalse ||
+        policy == ClickToCallPolicy::kTrue) {
+      const bool policy_bool = (policy == ClickToCallPolicy::kTrue);
+      policies.Set(policy::key::kClickToCallEnabled,
+                   policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+                   policy::POLICY_SOURCE_ENTERPRISE_DEFAULT,
+                   std::make_unique<base::Value>(policy_bool), nullptr);
+    }
+
+    provider_.UpdateChromePolicy(policies);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(ClickToCallPolicyTest, RunTest) {
+  const char* kPhoneNumber = "+9876543210";
+  const char* kPhoneLink = "tel:+9876543210";
+  bool expected_enabled = GetParam() != ClickToCallPolicy::kFalse;
+  bool expected_configured = GetParam() != ClickToCallPolicy::kNotConfigured;
+
+  PrefService* prefs = browser()->profile()->GetPrefs();
+  EXPECT_EQ(expected_enabled, prefs->GetBoolean(prefs::kClickToCallEnabled));
+  EXPECT_EQ(expected_configured,
+            prefs->IsManagedPreference(prefs::kClickToCallEnabled));
+
+  EXPECT_EQ(expected_enabled, ShouldOfferClickToCallForURL(browser()->profile(),
+                                                           GURL(kPhoneLink)));
+
+  base::Optional<std::string> extracted =
+      ExtractPhoneNumberForClickToCall(browser()->profile(), kPhoneNumber);
+  if (expected_enabled)
+    EXPECT_EQ(kPhoneNumber, extracted.value());
+  else
+    EXPECT_FALSE(extracted.has_value());
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         ClickToCallPolicyTest,
+                         ::testing::Values(ClickToCallPolicy::kNotConfigured,
+                                           ClickToCallPolicy::kFalse,
+                                           ClickToCallPolicy::kTrue));
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_utils.cc b/chrome/browser/sharing/click_to_call/click_to_call_utils.cc
index 8884e35..bcd6147 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_utils.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_utils.cc
@@ -10,9 +10,12 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/sharing/sharing_service.h"
 #include "chrome/browser/sharing/sharing_service_factory.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "url/url_constants.h"
@@ -34,6 +37,11 @@
     R"((?:^|\p{Z})((?:\(?\+[0-9]+\)?)?(?:[.\p{Z}\-(]?[0-9][\p{Z}\-)]?){8,}))";
 
 bool IsClickToCallEnabled(content::BrowserContext* browser_context) {
+  // Check Chrome enterprise policy for Click to Call.
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  if (profile && !profile->GetPrefs()->GetBoolean(prefs::kClickToCallEnabled))
+    return false;
+
   SharingService* sharing_service =
       SharingServiceFactory::GetForBrowserContext(browser_context);
   return sharing_service && base::FeatureList::IsEnabled(kClickToCallUI);
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc b/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc
index 7216d5bf..d01bce0 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc
@@ -16,7 +16,9 @@
 #include "chrome/browser/sharing/sharing_service_factory.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
 #include "chrome/browser/sharing/vapid_key_manager.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -126,6 +128,14 @@
   ExpectClickToCallDisabledForSelectionText(kSelectionTextWithNumber);
 }
 
+TEST_F(ClickToCallUtilsTest, PolicyDisabled_DoNotOfferAnyMenu) {
+  scoped_feature_list_.InitWithFeatures(
+      {kClickToCallContextMenuForSelectedText, kClickToCallUI}, {});
+  profile_.GetPrefs()->SetBoolean(prefs::kClickToCallEnabled, false);
+  EXPECT_FALSE(ShouldOfferClickToCallForURL(&profile_, GURL(kTelUrl)));
+  ExpectClickToCallDisabledForSelectionText(kSelectionTextWithNumber);
+}
+
 TEST_F(ClickToCallUtilsTest, IncognitoProfile_DoNotOfferAnyMenu) {
   scoped_feature_list_.InitWithFeatures(
       {kClickToCallUI, kClickToCallContextMenuForSelectedText}, {});
diff --git a/chrome/browser/sharing/click_to_call/feature.cc b/chrome/browser/sharing/click_to_call/feature.cc
index 7dddca3..68733f0 100644
--- a/chrome/browser/sharing/click_to_call/feature.cc
+++ b/chrome/browser/sharing/click_to_call/feature.cc
@@ -9,12 +9,10 @@
                                          base::FEATURE_ENABLED_BY_DEFAULT};
 #endif  // defined(OS_ANDROID)
 
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
-    defined(OS_CHROMEOS)
+#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
 const base::Feature kClickToCallContextMenuForSelectedText{
     "ClickToCallContextMenuForSelectedText", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kClickToCallUI{"ClickToCallUI",
                                    base::FEATURE_ENABLED_BY_DEFAULT};
-#endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) ||
-        // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
diff --git a/chrome/browser/sharing/click_to_call/feature.h b/chrome/browser/sharing/click_to_call/feature.h
index 8368a54..9a0eed16 100644
--- a/chrome/browser/sharing/click_to_call/feature.h
+++ b/chrome/browser/sharing/click_to_call/feature.h
@@ -7,21 +7,20 @@
 
 #include "base/feature_list.h"
 #include "build/build_config.h"
+#include "chrome/common/buildflags.h"
 
 #if defined(OS_ANDROID)
 // Feature to allow devices to receive the click to call message.
 extern const base::Feature kClickToCallReceiver;
 #endif  // defined(OS_ANDROID)
 
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
-    defined(OS_CHROMEOS)
+#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
 // Feature to allow click to call gets processed on desktop.
 extern const base::Feature kClickToCallUI;
 
 // Feature to show click to call in context menu when selected text is a phone
 // number.
 extern const base::Feature kClickToCallContextMenuForSelectedText;
-#endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) ||
-        // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
 
 #endif  // CHROME_BROWSER_SHARING_CLICK_TO_CALL_FEATURE_H_
diff --git a/chrome/browser/sharing/shared_clipboard/remote_copy_browsertest.cc b/chrome/browser/sharing/shared_clipboard/remote_copy_browsertest.cc
index d846c76..3e263645 100644
--- a/chrome/browser/sharing/shared_clipboard/remote_copy_browsertest.cc
+++ b/chrome/browser/sharing/shared_clipboard/remote_copy_browsertest.cc
@@ -4,9 +4,11 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/guid.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
@@ -20,13 +22,29 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/clipboard/clipboard_monitor.h"
+#include "ui/base/clipboard/clipboard_observer.h"
 #include "ui/base/clipboard/test/test_clipboard.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/message_center/public/cpp/notification.h"
 
 namespace {
-const char kText[] = "clipboard text";
-const char kDeviceName[] = "DeviceName";
+const char kDeviceName[] = "test device name";
+const char kText[] = "test text";
+
+class ClipboardObserver : public ui::ClipboardObserver {
+ public:
+  explicit ClipboardObserver(base::RepeatingClosure callback)
+      : callback_(callback) {}
+
+  void OnClipboardDataChanged() override { callback_.Run(); }
+
+ private:
+  base::RepeatingClosure callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClipboardObserver);
+};
 }  // namespace
 
 // Browser tests for the Remote Copy feature.
@@ -48,13 +66,26 @@
     ui::Clipboard::DestroyClipboardForCurrentThread();
   }
 
-  gcm::IncomingMessage CreateMessage(std::string guid,
-                                     std::string device_name,
-                                     std::string text) {
+  GURL StartServerAndGetURL(const std::string& relative_url) {
+    server_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTPS);
+    server_->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
+    EXPECT_TRUE(server_->Start());
+    return server_->GetURL(relative_url);
+  }
+
+  gcm::IncomingMessage CreateMessage(const std::string& device_name,
+                                     base::Optional<std::string> text,
+                                     base::Optional<GURL> image_url) {
     chrome_browser_sharing::SharingMessage sharing_message;
-    sharing_message.set_sender_guid(guid);
+    sharing_message.set_sender_guid(base::GenerateGUID());
     sharing_message.set_sender_device_name(device_name);
-    sharing_message.mutable_remote_copy_message()->set_text(text);
+    if (text) {
+      sharing_message.mutable_remote_copy_message()->set_text(text.value());
+    } else if (image_url) {
+      sharing_message.mutable_remote_copy_message()->set_image_url(
+          image_url.value().possibly_invalid_spec());
+    }
 
     gcm::IncomingMessage incoming_message;
     std::string serialized_sharing_message;
@@ -63,13 +94,44 @@
     return incoming_message;
   }
 
-  std::string GetClipboardText() {
+  void SendTextMessage(const std::string& device_name,
+                       const std::string& text) {
+    sharing_service_->GetFCMHandlerForTesting()->OnMessage(
+        kSharingFCMAppID,
+        CreateMessage(device_name, text, /*image_url=*/base::nullopt));
+  }
+
+  void SendImageMessage(const std::string& device_name, const GURL& image_url) {
+    base::RunLoop run_loop;
+    ClipboardObserver observer(run_loop.QuitClosure());
+    ui::ClipboardMonitor::GetInstance()->AddObserver(&observer);
+    sharing_service_->GetFCMHandlerForTesting()->OnMessage(
+        kSharingFCMAppID,
+        CreateMessage(device_name, /*text*/ base::nullopt, image_url));
+    run_loop.Run();
+    ui::ClipboardMonitor::GetInstance()->RemoveObserver(&observer);
+  }
+
+  std::vector<base::string16> GetAvailableClipboardTypes() {
+    std::vector<base::string16> types;
+    bool contains_filenames;
+    ui::Clipboard::GetForCurrentThread()->ReadAvailableTypes(
+        ui::ClipboardBuffer::kCopyPaste, &types, &contains_filenames);
+    return types;
+  }
+
+  std::string ReadClipboardText() {
     base::string16 text;
     ui::Clipboard::GetForCurrentThread()->ReadText(
         ui::ClipboardBuffer::kCopyPaste, &text);
     return base::UTF16ToUTF8(text);
   }
 
+  SkBitmap ReadClipboardImage() {
+    return ui::Clipboard::GetForCurrentThread()->ReadImage(
+        ui::ClipboardBuffer::kCopyPaste);
+  }
+
   message_center::Notification GetNotification() {
     auto notifications = notification_tester_->GetDisplayedNotificationsForType(
         NotificationHandler::Type::SHARING);
@@ -85,25 +147,97 @@
   base::test::ScopedFeatureList feature_list_;
   std::unique_ptr<NotificationDisplayServiceTester> notification_tester_;
   SharingService* sharing_service_;
+  std::unique_ptr<net::EmbeddedTestServer> server_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(RemoteCopyBrowserTestBase);
 };
 
-class RemoteCopyBrowserTest : public RemoteCopyBrowserTestBase {
+class RemoteCopyDisabledBrowserTest : public RemoteCopyBrowserTestBase {
  public:
-  RemoteCopyBrowserTest() {
-    feature_list_.InitWithFeatures({kRemoteCopyReceiver}, {});
+  RemoteCopyDisabledBrowserTest() {
+    feature_list_.InitAndDisableFeature(kRemoteCopyReceiver);
   }
 };
 
-IN_PROC_BROWSER_TEST_F(RemoteCopyBrowserTest, HandleMessage) {
-  sharing_service_->GetFCMHandlerForTesting()->OnMessage(
-      kSharingFCMAppID,
-      CreateMessage(base::GenerateGUID(), kDeviceName, kText));
-  EXPECT_EQ(GetClipboardText(), kText);
-  EXPECT_EQ(l10n_util::GetStringFUTF16(
+IN_PROC_BROWSER_TEST_F(RemoteCopyDisabledBrowserTest, FeatureDisabled) {
+  // The clipboard is empty.
+  ASSERT_TRUE(GetAvailableClipboardTypes().empty());
+
+  // Send a message with text.
+  SendTextMessage(kDeviceName, kText);
+
+  // The clipboard is still empty because the feature is disabled.
+  ASSERT_TRUE(GetAvailableClipboardTypes().empty());
+}
+
+class RemoteCopyBrowserTest : public RemoteCopyBrowserTestBase {
+ public:
+  RemoteCopyBrowserTest() {
+    feature_list_.InitAndEnableFeature(kRemoteCopyReceiver);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(RemoteCopyBrowserTest, Text) {
+  // The clipboard is empty.
+  ASSERT_TRUE(GetAvailableClipboardTypes().empty());
+
+  // Send a message with text.
+  SendTextMessage(kDeviceName, kText);
+
+  // The text is in the clipboard and a notification is shown.
+  std::vector<base::string16> types = GetAvailableClipboardTypes();
+  ASSERT_EQ(1u, types.size());
+  ASSERT_EQ(ui::kMimeTypeText, base::UTF16ToASCII(types[0]));
+  ASSERT_EQ(kText, ReadClipboardText());
+  ASSERT_EQ(l10n_util::GetStringFUTF16(
                 IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_NOTIFICATION_TITLE,
                 base::ASCIIToUTF16(kDeviceName)),
             GetNotification().title());
 }
+
+IN_PROC_BROWSER_TEST_F(RemoteCopyBrowserTest, ImageUrl) {
+  // The clipboard is empty.
+  ASSERT_TRUE(GetAvailableClipboardTypes().empty());
+
+  // Send a message with an image url.
+  SendImageMessage(kDeviceName,
+                   StartServerAndGetURL("/image_decoding/droids.jpg"));
+
+  // The image is in the clipboard and a notification is shown.
+  std::vector<base::string16> types = GetAvailableClipboardTypes();
+  ASSERT_EQ(1u, types.size());
+  ASSERT_EQ(ui::kMimeTypePNG, base::UTF16ToASCII(types[0]));
+  SkBitmap bitmap = ReadClipboardImage();
+  ASSERT_FALSE(bitmap.drawsNothing());
+  ASSERT_EQ(2560, bitmap.width());
+  ASSERT_EQ(1920, bitmap.height());
+  ASSERT_EQ(l10n_util::GetStringFUTF16(
+                IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_NOTIFICATION_TITLE,
+                base::ASCIIToUTF16(kDeviceName)),
+            GetNotification().title());
+}
+
+IN_PROC_BROWSER_TEST_F(RemoteCopyBrowserTest, TextThenImageUrl) {
+  // The clipboard is empty.
+  ASSERT_TRUE(GetAvailableClipboardTypes().empty());
+
+  // Send a message with text.
+  SendTextMessage(kDeviceName, kText);
+
+  // The text is in the clipboard.
+  std::vector<base::string16> types = GetAvailableClipboardTypes();
+  ASSERT_EQ(1u, types.size());
+  ASSERT_EQ(ui::kMimeTypeText, base::UTF16ToASCII(types[0]));
+  ASSERT_EQ(kText, ReadClipboardText());
+
+  // Send a message with an image url.
+  SendImageMessage(kDeviceName,
+                   StartServerAndGetURL("/image_decoding/droids.jpg"));
+
+  // The image is in the clipboard and the text has been cleared.
+  types = GetAvailableClipboardTypes();
+  ASSERT_EQ(1u, types.size());
+  ASSERT_EQ(ui::kMimeTypePNG, base::UTF16ToASCII(types[0]));
+  ASSERT_EQ(std::string(), ReadClipboardText());
+}
diff --git a/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.cc b/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.cc
index d998f10..4549637 100644
--- a/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.cc
+++ b/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.cc
@@ -6,14 +6,22 @@
 
 #include <memory>
 
+#include "base/bind.h"
 #include "base/guid.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/notifications/notification_display_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/sync/protocol/sharing_message.pb.h"
 #include "components/sync/protocol/sharing_remote_copy_message.pb.h"
+#include "net/base/load_flags.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/is_potentially_trustworthy.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 #include "ui/base/clipboard/clipboard_buffer.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -23,6 +31,30 @@
 #include "ui/message_center/public/cpp/notifier_id.h"
 #include "url/gurl.h"
 
+namespace {
+const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+    net::DefineNetworkTrafficAnnotation("remote_copy_message_handler",
+                                        R"(
+          semantics {
+            sender: "RemoteCopyMessageHandler"
+            description:
+              "Fetches an image from a URL specified in an FCM message."
+            trigger:
+              "The user sent an image to this device from another device that "
+              "they control."
+            data:
+              "An image URL, from a Google storage service like blobstore."
+            destination: GOOGLE_OWNED_SERVICE
+          }
+          policy {
+            cookies_allowed: NO
+            setting:
+              "Users can disable this behavior by signing out of Chrome."
+            policy_exception_justification:
+              "Can be controlled via Chrome sign-in."
+          })");
+}  // namespace
+
 RemoteCopyMessageHandler::RemoteCopyMessageHandler(Profile* profile)
     : profile_(profile) {}
 
@@ -33,26 +65,101 @@
     DoneCallback done_callback) {
   DCHECK(message.has_remote_copy_message());
 
-  ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste)
-      .WriteText(base::UTF8ToUTF16(message.remote_copy_message().text()));
+  // First cancel any pending async tasks that might otherwise overwrite the
+  // results of the more recent message.
+  url_loader_.reset();
+  ImageDecoder::Cancel(this);
 
-  ShowNotification(message.sender_device_name());
+  device_name_ = message.sender_device_name();
+
+  switch (message.remote_copy_message().content_case()) {
+    case chrome_browser_sharing::RemoteCopyMessage::kText:
+      HandleText(message.remote_copy_message().text());
+      break;
+    case chrome_browser_sharing::RemoteCopyMessage::kImageUrl:
+      HandleImage(message.remote_copy_message().image_url());
+      break;
+    case chrome_browser_sharing::RemoteCopyMessage::CONTENT_NOT_SET:
+      NOTREACHED();
+      break;
+  }
 
   std::move(done_callback).Run(/*response=*/nullptr);
 }
 
-void RemoteCopyMessageHandler::ShowNotification(
-    const std::string& device_name) {
+void RemoteCopyMessageHandler::HandleText(const std::string& text) {
+  if (!text.empty()) {
+    {
+      ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste)
+          .WriteText(base::UTF8ToUTF16(text));
+    }
+    ShowNotification();
+  }
+  Finish();
+}
+
+void RemoteCopyMessageHandler::HandleImage(const std::string& image_url) {
+  GURL url(image_url);
+  // TODO(mvanouwerkerk): Whitelist check.
+  if (!network::IsUrlPotentiallyTrustworthy(url)) {
+    Finish();
+    return;
+  }
+
+  auto request = std::make_unique<network::ResourceRequest>();
+  request->url = url;
+  // This request should be unauthenticated (no cookies), and shouldn't be
+  // stored in the cache (this URL is only fetched once, ever.)
+  request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
+  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+
+  url_loader_ =
+      network::SimpleURLLoader::Create(std::move(request), kTrafficAnnotation);
+  // TODO(mvanouwerkerk): Downloads > 1MB (kMaxBoundedStringDownloadSize).
+  url_loader_->DownloadToString(
+      profile_->GetURLLoaderFactory().get(),
+      base::BindOnce(&RemoteCopyMessageHandler::OnURLLoadComplete,
+                     base::Unretained(this)),
+      network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
+}
+
+void RemoteCopyMessageHandler::OnURLLoadComplete(
+    std::unique_ptr<std::string> content) {
+  url_loader_.reset();
+  if (!content || content->empty()) {
+    Finish();
+    return;
+  }
+
+  ImageDecoder::Start(this, *content);
+}
+
+void RemoteCopyMessageHandler::OnImageDecoded(const SkBitmap& decoded_image) {
+  if (!decoded_image.drawsNothing()) {
+    {
+      ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste)
+          .WriteImage(decoded_image);
+    }
+    ShowNotification();
+  }
+  Finish();
+}
+
+void RemoteCopyMessageHandler::OnDecodeImageFailed() {
+  Finish();
+}
+
+void RemoteCopyMessageHandler::ShowNotification() {
   std::string notification_id = base::GenerateGUID();
 
   // TODO(mvanouwerkerk): Adjust notification text and icon once we have mocks.
   base::string16 notification_title =
-      device_name.empty()
+      device_name_.empty()
           ? l10n_util::GetStringUTF16(
                 IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_NOTIFICATION_TITLE_UNKNOWN_DEVICE)
           : l10n_util::GetStringFUTF16(
                 IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_NOTIFICATION_TITLE,
-                base::UTF8ToUTF16(device_name));
+                base::UTF8ToUTF16(device_name_));
 
   message_center::Notification notification(
       message_center::NOTIFICATION_TYPE_SIMPLE, notification_id,
@@ -68,3 +175,8 @@
   NotificationDisplayServiceFactory::GetForProfile(profile_)->Display(
       NotificationHandler::Type::SHARING, notification, /*metadata=*/nullptr);
 }
+
+void RemoteCopyMessageHandler::Finish() {
+  // TODO(mvanouwerkerk): UMA logging.
+  device_name_.clear();
+}
diff --git a/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.h b/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.h
index 5514d79e..b3b17e9 100644
--- a/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.h
+++ b/chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.h
@@ -5,15 +5,22 @@
 #ifndef CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_REMOTE_COPY_MESSAGE_HANDLER_H_
 #define CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_REMOTE_COPY_MESSAGE_HANDLER_H_
 
+#include <memory>
 #include <string>
 
 #include "base/macros.h"
+#include "chrome/browser/image_decoder.h"
 #include "chrome/browser/sharing/sharing_message_handler.h"
 
 class Profile;
 
+namespace network {
+class SimpleURLLoader;
+}  // namespace network
+
 // Handles incoming messages for the remote copy feature.
-class RemoteCopyMessageHandler : public SharingMessageHandler {
+class RemoteCopyMessageHandler : public SharingMessageHandler,
+                                 public ImageDecoder::ImageRequest {
  public:
   explicit RemoteCopyMessageHandler(Profile* profile);
   ~RemoteCopyMessageHandler() override;
@@ -22,10 +29,20 @@
   void OnMessage(chrome_browser_sharing::SharingMessage message,
                  DoneCallback done_callback) override;
 
+  // ImageDecoder::ImageRequest implementation:
+  void OnImageDecoded(const SkBitmap& decoded_image) override;
+  void OnDecodeImageFailed() override;
+
  private:
-  void ShowNotification(const std::string& device_name);
+  void HandleText(const std::string& text);
+  void HandleImage(const std::string& image_url);
+  void OnURLLoadComplete(std::unique_ptr<std::string> content);
+  void ShowNotification();
+  void Finish();
 
   Profile* profile_ = nullptr;
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
+  std::string device_name_;
 
   DISALLOW_COPY_AND_ASSIGN(RemoteCopyMessageHandler);
 };
diff --git a/chrome/browser/sync/test/integration/os_sync_test.cc b/chrome/browser/sync/test/integration/os_sync_test.cc
new file mode 100644
index 0000000..321f5e5
--- /dev/null
+++ b/chrome/browser/sync/test/integration/os_sync_test.cc
@@ -0,0 +1,30 @@
+// 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/sync/test/integration/os_sync_test.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/constants/chromeos_features.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync/base/pref_names.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+OsSyncTest::OsSyncTest(TestType type) : SyncTest(type) {
+  settings_feature_list_.InitAndEnableFeature(
+      chromeos::features::kSplitSettingsSync);
+}
+
+OsSyncTest::~OsSyncTest() = default;
+
+bool OsSyncTest::SetupClients() {
+  if (!SyncTest::SetupClients())
+    return false;
+  // Enable the OS sync feature for all profiles.
+  for (Profile* profile : GetAllProfiles()) {
+    PrefService* prefs = profile->GetPrefs();
+    EXPECT_FALSE(prefs->GetBoolean(syncer::prefs::kOsSyncFeatureEnabled));
+    prefs->SetBoolean(syncer::prefs::kOsSyncFeatureEnabled, true);
+  }
+  return true;
+}
diff --git a/chrome/browser/sync/test/integration/os_sync_test.h b/chrome/browser/sync/test/integration/os_sync_test.h
new file mode 100644
index 0000000..c588751
--- /dev/null
+++ b/chrome/browser/sync/test/integration/os_sync_test.h
@@ -0,0 +1,30 @@
+// 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_SYNC_TEST_INTEGRATION_OS_SYNC_TEST_H_
+#define CHROME_BROWSER_SYNC_TEST_INTEGRATION_OS_SYNC_TEST_H_
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/sync/test/integration/sync_test.h"
+
+// Test suite for Chrome OS sync. Enables the SplitSettingsSync feature and the
+// OS sync enabled preference before running the test.
+class OsSyncTest : public SyncTest {
+ public:
+  explicit OsSyncTest(TestType type);
+  ~OsSyncTest() override;
+
+  OsSyncTest(const OsSyncTest&) = delete;
+  OsSyncTest& operator=(const OsSyncTest&) = delete;
+
+  // SyncTest:
+  bool SetupClients() override;
+
+ private:
+  // The names |scoped_feature_list_| and |feature_list_| are both used in
+  // superclasses.
+  base::test::ScopedFeatureList settings_feature_list_;
+};
+
+#endif  // CHROME_BROWSER_SYNC_TEST_INTEGRATION_OS_SYNC_TEST_H_
diff --git a/chrome/browser/sync/test/integration/single_client_os_preferences_sync_test.cc b/chrome/browser/sync/test/integration/single_client_os_preferences_sync_test.cc
index 3fc4b70..7c5c7cd 100644
--- a/chrome/browser/sync/test/integration/single_client_os_preferences_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_os_preferences_sync_test.cc
@@ -4,12 +4,12 @@
 
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/shelf_prefs.h"
-#include "base/macros.h"
-#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/sync/test/integration/os_sync_test.h"
 #include "chrome/browser/sync/test/integration/preferences_helper.h"
-#include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
-#include "chromeos/constants/chromeos_features.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/sync/driver/sync_user_settings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using preferences_helper::ChangeStringPref;
@@ -17,21 +17,10 @@
 
 namespace {
 
-class SingleClientOsPreferencesSyncTest : public SyncTest {
+class SingleClientOsPreferencesSyncTest : public OsSyncTest {
  public:
-  SingleClientOsPreferencesSyncTest() : SyncTest(SINGLE_CLIENT) {
-    settings_feature_list_.InitAndEnableFeature(
-        chromeos::features::kSplitSettingsSync);
-  }
-
+  SingleClientOsPreferencesSyncTest() : OsSyncTest(SINGLE_CLIENT) {}
   ~SingleClientOsPreferencesSyncTest() override = default;
-
- private:
-  // The names |scoped_feature_list_| and |feature_list_| are both used in
-  // superclasses.
-  base::test::ScopedFeatureList settings_feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(SingleClientOsPreferencesSyncTest);
 };
 
 IN_PROC_BROWSER_TEST_F(SingleClientOsPreferencesSyncTest, Sanity) {
@@ -45,4 +34,18 @@
   EXPECT_TRUE(StringPrefMatches(ash::prefs::kShelfAlignment));
 }
 
+IN_PROC_BROWSER_TEST_F(SingleClientOsPreferencesSyncTest,
+                       DisablingOsSyncFeatureDisablesDataType) {
+  ASSERT_TRUE(SetupSync());
+  syncer::SyncService* service = GetSyncService(0);
+  syncer::SyncUserSettings* settings = service->GetUserSettings();
+
+  EXPECT_TRUE(settings->GetOsSyncFeatureEnabled());
+  EXPECT_TRUE(service->GetActiveDataTypes().Has(syncer::OS_PREFERENCES));
+
+  settings->SetOsSyncFeatureEnabled(false);
+  EXPECT_FALSE(settings->GetOsSyncFeatureEnabled());
+  EXPECT_FALSE(service->GetActiveDataTypes().Has(syncer::OS_PREFERENCES));
+}
+
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_printers_sync_test.cc b/chrome/browser/sync/test/integration/single_client_printers_sync_test.cc
index 4b41c7e..3c0cab8c 100644
--- a/chrome/browser/sync/test/integration/single_client_printers_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_printers_sync_test.cc
@@ -5,6 +5,7 @@
 #include <stddef.h>
 
 #include "base/macros.h"
+#include "chrome/browser/sync/test/integration/os_sync_test.h"
 #include "chrome/browser/sync/test/integration/printers_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
@@ -20,6 +21,8 @@
 using printers_helper::ProfileContainsSamePrintersAsVerifier;
 using printers_helper::RemovePrinter;
 
+namespace {
+
 class SingleClientPrintersSyncTest : public SyncTest {
  public:
   SingleClientPrintersSyncTest() : SyncTest(SINGLE_CLIENT) {}
@@ -87,3 +90,26 @@
 
   EXPECT_TRUE(SetupSync()) << "SetupSync() failed.";
 }
+
+// Tests for SplitSettingsSync.
+class SingleClientPrintersOsSyncTest : public OsSyncTest {
+ public:
+  SingleClientPrintersOsSyncTest() : OsSyncTest(SINGLE_CLIENT) {}
+  ~SingleClientPrintersOsSyncTest() override = default;
+};
+
+IN_PROC_BROWSER_TEST_F(SingleClientPrintersOsSyncTest,
+                       DisablingOsSyncFeatureDisablesDataType) {
+  ASSERT_TRUE(SetupSync());
+  syncer::SyncService* service = GetSyncService(0);
+  syncer::SyncUserSettings* settings = service->GetUserSettings();
+
+  EXPECT_TRUE(settings->GetOsSyncFeatureEnabled());
+  EXPECT_TRUE(service->GetActiveDataTypes().Has(syncer::PRINTERS));
+
+  settings->SetOsSyncFeatureEnabled(false);
+  EXPECT_FALSE(settings->GetOsSyncFeatureEnabled());
+  EXPECT_FALSE(service->GetActiveDataTypes().Has(syncer::PRINTERS));
+}
+
+}  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_os_preferences_sync_test.cc b/chrome/browser/sync/test/integration/two_client_os_preferences_sync_test.cc
index 38727a6a..439c9fd 100644
--- a/chrome/browser/sync/test/integration/two_client_os_preferences_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_os_preferences_sync_test.cc
@@ -4,17 +4,13 @@
 
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/shelf_prefs.h"
-#include "base/macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/sync/test/integration/os_sync_test.h"
 #include "chrome/browser/sync/test/integration/preferences_helper.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
-#include "chrome/browser/sync/test/integration/sync_test.h"
-#include "chromeos/constants/chromeos_features.h"
 #include "components/prefs/pref_service.h"
-#include "components/sync/base/pref_names.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using preferences_helper::ChangeStringPref;
@@ -24,33 +20,13 @@
 
 namespace {
 
-class TwoClientOsPreferencesSyncTest : public SyncTest {
+class TwoClientOsPreferencesSyncTest : public OsSyncTest {
  public:
-  TwoClientOsPreferencesSyncTest() : SyncTest(TWO_CLIENT) {
-    settings_feature_list_.InitAndEnableFeature(
-        chromeos::features::kSplitSettingsSync);
-  }
-
+  TwoClientOsPreferencesSyncTest() : OsSyncTest(TWO_CLIENT) {}
   ~TwoClientOsPreferencesSyncTest() override = default;
 
   // Needed for AwaitQuiescence().
   bool TestUsesSelfNotifications() override { return true; }
-
-  bool SetupClients() override {
-    bool result = SyncTest::SetupClients();
-    for (Profile* profile : GetAllProfiles()) {
-      profile->GetPrefs()->SetBoolean(syncer::prefs::kOsSyncFeatureEnabled,
-                                      true);
-    }
-    return result;
-  }
-
- private:
-  // The names |scoped_feature_list_| and |feature_list_| are both used in
-  // superclasses.
-  base::test::ScopedFeatureList settings_feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(TwoClientOsPreferencesSyncTest);
 };
 
 IN_PROC_BROWSER_TEST_F(TwoClientOsPreferencesSyncTest, E2E_ENABLED(Sanity)) {
diff --git a/chrome/browser/ui/android/page_info/page_info_controller_android.cc b/chrome/browser/ui/android/page_info/page_info_controller_android.cc
index 12232e0..5d0f4fc 100644
--- a/chrome/browser/ui/android/page_info/page_info_controller_android.cc
+++ b/chrome/browser/ui/android/page_info/page_info_controller_android.cc
@@ -23,6 +23,7 @@
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "url/origin.h"
 
@@ -126,6 +127,8 @@
       ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER);
   permissions_to_display.push_back(ContentSettingsType::AUTOPLAY);
   permissions_to_display.push_back(ContentSettingsType::SOUND);
+  if (base::FeatureList::IsEnabled(features::kWebNfc))
+    permissions_to_display.push_back(ContentSettingsType::NFC);
   base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
   if (cmd->HasSwitch(switches::kEnableExperimentalWebPlatformFeatures))
     permissions_to_display.push_back(ContentSettingsType::BLUETOOTH_SCANNING);
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index e652c8c6..d4dd0c9 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -916,6 +916,9 @@
       <message name="IDS_POPUP_PERMISSION_TITLE" desc="Title of the permission to display pop-up windows and redirects [CHAR-LIMIT=32]">
         Pop-ups and redirects
       </message>
+      <message name="IDS_NFC_PERMISSION_TITLE" desc="Title of the permission to use NFC [CHAR-LIMIT=32]">
+        NFC devices
+      </message>
       <message name="IDS_PUSH_NOTIFICATIONS_PERMISSION_TITLE" desc="Title for the permission for showing push notifications [CHAR-LIMIT=32]">
         Notifications
       </message>
@@ -1069,6 +1072,12 @@
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_PROTECTED_CONTENT_BLOCKED" desc="Summary text explaining that sites are blocked from playing protected content.">
         Block sites from playing protected content
       </message>
+      <message name="IDS_WEBSITE_SETTINGS_CATEGORY_NFC_ASK" desc="Summary text explaining that sites need to ask for permission before using NFC and that it is the recommended setting.">
+        Ask before allowing sites to send and receive info when you tap NFC devices (recommended)
+      </message>
+      <message name="IDS_WEBSITE_SETTINGS_CATEGORY_NFC_BLOCKED" desc="Summary text explaining that sites are blocked from using NFC.">
+        Block sites from sending and receiving info when you tap NFC devices
+      </message>
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_SENSORS_ALLOWED" desc="Summary text explaining that sites are allowed to use device's sensors and that it is the recommended setting.">
         Allow sites to access sensors (recommended)
       </message>
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
index a55b0d2..9e773ad 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
@@ -585,12 +585,26 @@
     if (candidate_override_index == -1)
       continue;
 
+    // TODO(crbug.com/1011221): Remove once the bug re. zero-state drive files
+    // not being shown is resolved.
+    VLOG(1) << "Zero state files override: newtype=" << static_cast<int>(group)
+            << " newpos=" << next_modifiable_index;
     // Override the result at |next_modifiable_index| with
     // |candidate_override_index| by swapping their scores.
     std::swap(result_ptrs[candidate_override_index]->score,
               result_ptrs[next_modifiable_index]->score);
     --next_modifiable_index;
   }
+
+  // TODO(crbug.com/1011221): Remove once the bug re. zero-state drive files not
+  // being shown is resolved.
+  VLOG(1) << "Zero state files setting result scores";
+  for (const auto* result : result_ptrs) {
+    VLOG(1) << "Zero state files result score: type="
+            << static_cast<int>(
+                   RankingItemTypeFromSearchResult(*(result->result)))
+            << " score=" << result->score;
+  }
 }
 
 void SearchResultRanker::OnFilesOpened(
diff --git a/chrome/browser/ui/ash/test_login_screen_model.cc b/chrome/browser/ui/ash/test_login_screen_model.cc
index 204852fc..e6e168f 100644
--- a/chrome/browser/ui/ash/test_login_screen_model.cc
+++ b/chrome/browser/ui/ash/test_login_screen_model.cc
@@ -40,7 +40,8 @@
     bool enforced,
     const std::string& os_version_label_text,
     const std::string& enterprise_info_text,
-    const std::string& bluetooth_name) {}
+    const std::string& bluetooth_name,
+    bool adb_sideloading_enabled) {}
 void TestLoginScreenModel::SetPublicSessionDisplayName(
     const AccountId& account_id,
     const std::string& display_name) {}
diff --git a/chrome/browser/ui/ash/test_login_screen_model.h b/chrome/browser/ui/ash/test_login_screen_model.h
index 95739dc3..5991cc1e 100644
--- a/chrome/browser/ui/ash/test_login_screen_model.h
+++ b/chrome/browser/ui/ash/test_login_screen_model.h
@@ -40,7 +40,8 @@
                      bool enforced,
                      const std::string& os_version_label_text,
                      const std::string& enterprise_info_text,
-                     const std::string& bluetooth_name) override;
+                     const std::string& bluetooth_name,
+                     bool adb_sideloading_enabled) override;
   void SetPublicSessionLocales(const AccountId& account_id,
                                const std::vector<ash::LocaleItem>& locales,
                                const std::string& default_locale,
diff --git a/chrome/browser/ui/browser_ui_prefs.cc b/chrome/browser/ui/browser_ui_prefs.cc
index 30cf3e7..fa9ec80a 100644
--- a/chrome/browser/ui/browser_ui_prefs.cc
+++ b/chrome/browser/ui/browser_ui_prefs.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/first_run/first_run.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/upgrade_detector/upgrade_detector.h"
+#include "chrome/common/buildflags.h"
 #include "chrome/common/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -103,6 +104,11 @@
   registry->RegisterBooleanPref(prefs::kClearPluginLSODataEnabled, true);
   registry->RegisterBooleanPref(prefs::kHideWebStoreIcon, false);
   registry->RegisterBooleanPref(prefs::kSharedClipboardEnabled, true);
+
+#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
+  registry->RegisterBooleanPref(prefs::kClickToCallEnabled, true);
+#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
+
 #if defined(OS_MACOSX)
   // This really belongs in platform code, but there's no good place to
   // initialize it between the time when the AppController is created
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index aa5aed56..33d2e3e8 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -134,6 +134,9 @@
     ContentSettingsType::AUTOPLAY,
     ContentSettingsType::MIDI_SYSEX,
     ContentSettingsType::CLIPBOARD_READ,
+#if defined(OS_ANDROID)
+    ContentSettingsType::NFC,
+#endif
     ContentSettingsType::USB_GUARD,
 #if !defined(OS_ANDROID)
     ContentSettingsType::SERIAL_GUARD,
@@ -212,6 +215,10 @@
   if (info.type == ContentSettingsType::AUTOPLAY)
     return false;
 
+  // NFC is Android-only at the moment.
+  if (info.type == ContentSettingsType::NFC)
+    return false;
+
   // Display the Native File System write permission if the Native File System
   // API is currently being used.
   if (info.type == ContentSettingsType::NATIVE_FILE_SYSTEM_WRITE_GUARD &&
diff --git a/chrome/browser/ui/page_info/page_info_ui.cc b/chrome/browser/ui/page_info/page_info_ui.cc
index 4542595..9638216 100644
--- a/chrome/browser/ui/page_info/page_info_ui.cc
+++ b/chrome/browser/ui/page_info/page_info_ui.cc
@@ -184,6 +184,7 @@
      IDS_PAGE_INFO_TYPE_NATIVE_FILE_SYSTEM_WRITE},
     {ContentSettingsType::BLUETOOTH_SCANNING,
      IDS_PAGE_INFO_TYPE_BLUETOOTH_SCANNING},
+    {ContentSettingsType::NFC, IDS_PAGE_INFO_TYPE_NFC},
   };
   return kPermissionsUIInfo;
 }
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc
index e4f526e..3039dec9 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc
@@ -110,6 +110,10 @@
   // Cause the zoom page action icon to be visible.
   chrome::Zoom(app_browser_, content::PAGE_ZOOM_IN);
 
+  // The layout should be invalidated, but since we don't have the benefit of
+  // the compositor to immediately kick a layout off, we have to do it manually.
+  web_app_frame_toolbar_->Layout();
+
   // The page action icons should now take up width.
   EXPECT_GT(page_action_icon_container->width(), 0);
   EXPECT_EQ(menu_button->width(), original_menu_button_width);
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_container_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_container_view.cc
index ff0ba29..a8de08e 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_container_view.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_container_view.cc
@@ -263,10 +263,6 @@
   PreferredSizeChanged();
 }
 
-void PageActionIconContainerView::ChildVisibilityChanged(views::View* child) {
-  PreferredSizeChanged();
-}
-
 void PageActionIconContainerView::OnDefaultZoomLevelChanged() {
   ZoomChangedForActiveTab(false);
 }
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_container_view.h b/chrome/browser/ui/views/page_action/page_action_icon_container_view.h
index a41f10d..bd3a7d4 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_container_view.h
+++ b/chrome/browser/ui/views/page_action/page_action_icon_container_view.h
@@ -99,7 +99,6 @@
  private:
   // views::View:
   void ChildPreferredSizeChanged(views::View* child) override;
-  void ChildVisibilityChanged(views::View* child) override;
 
   // ZoomEventManagerObserver:
   // Updates the view for the zoom icon when default zoom levels change.
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
index 7cbf3975..deeb3d0 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
@@ -230,7 +230,7 @@
         selected_network.empty() || selected_network == supported_network
             ? 1.0f
             : kDimmedCardIconOpacity;
-    std::unique_ptr<views::ImageView> card_icon_view = CreateInstrumentIconView(
+    std::unique_ptr<views::ImageView> card_icon_view = CreateAppIconView(
         autofill::data_util::GetPaymentRequestData(autofill_card_type)
             .icon_resource_id,
         gfx::ImageSkia(), base::UTF8ToUTF16(supported_network), opacity);
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
index 0864a37..6117d481 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
@@ -23,7 +23,7 @@
 #include "components/autofill/core/browser/ui/address_combobox_model.h"
 #include "components/payments/content/payment_request.h"
 #include "components/payments/content/payment_request_spec.h"
-#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
 #include "components/payments/core/features.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/test/browser_test_utils.h"
@@ -59,10 +59,10 @@
 
   InvokePaymentRequestUI();
 
-  // No instruments are available.
+  // No apps are available.
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(0U, request->state()->available_instruments().size());
-  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+  EXPECT_EQ(0U, request->state()->available_apps().size());
+  EXPECT_EQ(nullptr, request->state()->selected_app());
 
   // But there must be at least one address available for billing.
   autofill::AutofillProfile billing_profile(autofill::test::GetFullProfile());
@@ -101,10 +101,10 @@
   EXPECT_EQ(base::ASCIIToUTF16("Bob Jones"),
             credit_card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
 
-  // One instrument is available and selected.
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(request->state()->available_instruments().back().get(),
-            request->state()->selected_instrument());
+  // One app is available and selected.
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(request->state()->available_apps().back().get(),
+            request->state()->selected_app());
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCreditCardEditorTest,
@@ -119,10 +119,10 @@
 
   InvokePaymentRequestUI();
 
-  // No instruments are available.
+  // No apps are available.
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(0U, request->state()->available_instruments().size());
-  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+  EXPECT_EQ(0U, request->state()->available_apps().size());
+  EXPECT_EQ(nullptr, request->state()->selected_app());
 
   OpenCreditCardEditorScreen();
 
@@ -160,10 +160,10 @@
   EXPECT_EQ(base::ASCIIToUTF16("Bob Jones"),
             credit_card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
 
-  // One instrument is available and selected.
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(request->state()->available_instruments().back().get(),
-            request->state()->selected_instrument());
+  // One app is available and selected.
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(request->state()->available_apps().back().get(),
+            request->state()->selected_app());
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCreditCardEditorTest, CancelFromEditor) {
@@ -312,9 +312,9 @@
   data_loop.Run();
 
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  autofill::CreditCard* selected = static_cast<AutofillPaymentInstrument*>(
-                                       request->state()->selected_instrument())
-                                       ->credit_card();
+  autofill::CreditCard* selected =
+      static_cast<AutofillPaymentApp*>(request->state()->selected_app())
+          ->credit_card();
   EXPECT_EQ(additional_profile.guid(), selected->billing_address_id());
 }
 
@@ -543,11 +543,11 @@
   // Focus expectations are different in Keyboard Accessible mode.
   dialog_view()->GetFocusManager()->SetKeyboardAccessible(false);
 
-  // One instrument is available, and it's selected because being expired can
-  // still select the instrument.
+  // One app is available, and it's selected because that's allowed for expired
+  // credit cards.
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_NE(nullptr, request->state()->selected_instrument());
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_NE(nullptr, request->state()->selected_app());
 
   OpenPaymentMethodScreen();
 
@@ -610,10 +610,10 @@
   EXPECT_EQ(base::ASCIIToUTF16("Test User"),
             credit_card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
 
-  // Still have one instrument, and it's still selected.
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(request->state()->available_instruments().back().get(),
-            request->state()->selected_instrument());
+  // Still have one app, and it's still selected.
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(request->state()->available_apps().back().get(),
+            request->state()->selected_app());
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCreditCardEditorTest,
@@ -631,10 +631,10 @@
 
   InvokePaymentRequestUI();
 
-  // One instrument is available, but it's not selected.
+  // One app is available, but it's not selected.
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(nullptr, request->state()->selected_app());
 
   OpenPaymentMethodScreen();
 
@@ -672,10 +672,10 @@
   EXPECT_EQ(base::ASCIIToUTF16("Test User"),
             credit_card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
 
-  // Still have one instrument, but now it's selected.
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(request->state()->available_instruments().back().get(),
-            request->state()->selected_instrument());
+  // Still have one app, but now it's selected.
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(request->state()->available_apps().back().get(),
+            request->state()->selected_app());
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCreditCardEditorTest,
@@ -692,10 +692,10 @@
 
   InvokePaymentRequestUI();
 
-  // One instrument is available, but it's not selected.
+  // One app is available, but it's not selected.
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(nullptr, request->state()->selected_app());
 
   OpenPaymentMethodScreen();
 
@@ -735,10 +735,10 @@
   EXPECT_EQ(base::ASCIIToUTF16("4111111111111111"), credit_card->number());
   EXPECT_EQ(billing_profile.guid(), credit_card->billing_address_id());
 
-  // Still have one instrument, but now it's selected.
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(request->state()->available_instruments().back().get(),
-            request->state()->selected_instrument());
+  // Still have one app, but now it's selected.
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(request->state()->available_apps().back().get(),
+            request->state()->selected_app());
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCreditCardEditorTest,
@@ -753,14 +753,14 @@
 
   InvokePaymentRequestUI();
 
-  // One instrument is available, it is not selected, but is properly named.
+  // One app is available, it is not selected, but is properly named.
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(nullptr, request->state()->selected_app());
   EXPECT_EQ(
       card.GetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
                    request->state()->GetApplicationLocale()),
-      request->state()->available_instruments()[0]->GetSublabel());
+      request->state()->available_apps()[0]->GetSublabel());
 
   OpenPaymentMethodScreen();
 
@@ -786,11 +786,11 @@
   ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
   data_loop.Run();
 
-  // One instrument is available, is selected, and is properly named.
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_NE(nullptr, request->state()->selected_instrument());
+  // One app is available, is selected, and is properly named.
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_NE(nullptr, request->state()->selected_app());
   EXPECT_EQ(base::ASCIIToUTF16("Bob the second"),
-            request->state()->selected_instrument()->GetSublabel());
+            request->state()->selected_app()->GetSublabel());
 }
 
 // FLAKY on Windows: crbug.com/1001365
@@ -812,10 +812,10 @@
 
   InvokePaymentRequestUI();
 
-  // One instrument is available, but it's not selected.
+  // One app is available, but it's not selected.
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(nullptr, request->state()->selected_app());
 
   OpenPaymentMethodScreen();
 
@@ -881,11 +881,11 @@
   ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
   data_loop.Run();
 
-  // Still have one instrument, but now it's selected.
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(request->state()->available_instruments().back().get(),
-            request->state()->selected_instrument());
-  EXPECT_TRUE(request->state()->selected_instrument()->IsCompleteForPayment());
+  // Still have one app, but now it's selected.
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(request->state()->available_apps().back().get(),
+            request->state()->selected_app());
+  EXPECT_TRUE(request->state()->selected_app()->IsCompleteForPayment());
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCreditCardEditorTest,
@@ -902,10 +902,10 @@
 
   InvokePaymentRequestUI();
 
-  // One instrument is available, but it's not selected.
+  // One app is available, but it's not selected.
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(nullptr, request->state()->selected_app());
 
   // Now add the billing address to the personal data.
   AddAutofillProfile(billing_profile);
@@ -916,11 +916,11 @@
                            /*wait_for_animation=*/false);
   InvokePaymentRequestUI();
 
-  // Still have one instrument, but now it's selected.
+  // Still have one app, but now it's selected.
   request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(request->state()->available_instruments().back().get(),
-            request->state()->selected_instrument());
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(request->state()->available_apps().back().get(),
+            request->state()->selected_app());
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCreditCardEditorTest, EnteringEmptyData) {
@@ -992,10 +992,10 @@
 
   InvokePaymentRequestUI();
 
-  // No instruments are available.
+  // No apps are available.
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(0U, request->state()->available_instruments().size());
-  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+  EXPECT_EQ(0U, request->state()->available_apps().size());
+  EXPECT_EQ(nullptr, request->state()->selected_app());
 
   // But there must be at least one address available for billing.
   autofill::AutofillProfile billing_profile(autofill::test::GetFullProfile());
@@ -1021,13 +1021,13 @@
   ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
 
   // Since this is incognito, the credit card shouldn't have been added to the
-  // PersonalDataManager but it should be available in available_instruments.
+  // PersonalDataManager but it should be available in available_apps.
   EXPECT_EQ(0U, personal_data_manager->GetCreditCards().size());
 
-  // One instrument is available and selected.
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(request->state()->available_instruments().back().get(),
-            request->state()->selected_instrument());
+  // One app is available and selected.
+  EXPECT_EQ(1U, request->state()->available_apps().size());
+  EXPECT_EQ(request->state()->available_apps().back().get(),
+            request->state()->selected_app());
 }
 
 }  // namespace payments
diff --git a/chrome/browser/ui/views/payments/order_summary_view_controller.cc b/chrome/browser/ui/views/payments/order_summary_view_controller.cc
index b2caeeaf..7d817fc4eb 100644
--- a/chrome/browser/ui/views/payments/order_summary_view_controller.cc
+++ b/chrome/browser/ui/views/payments/order_summary_view_controller.cc
@@ -173,8 +173,7 @@
       DialogViewID::ORDER_SUMMARY_LINE_ITEM_1,
       DialogViewID::ORDER_SUMMARY_LINE_ITEM_2,
       DialogViewID::ORDER_SUMMARY_LINE_ITEM_3};
-  const auto& display_items =
-      spec()->GetDisplayItems(state()->selected_instrument());
+  const auto& display_items = spec()->GetDisplayItems(state()->selected_app());
   for (size_t i = 0; i < display_items.size(); i++) {
     DialogViewID view_id =
         i < line_items.size() ? line_items[i] : DialogViewID::VIEW_ID_NONE;
@@ -194,19 +193,17 @@
   base::string16 total_label_value = l10n_util::GetStringFUTF16(
       IDS_PAYMENT_REQUEST_ORDER_SUMMARY_SHEET_TOTAL_FORMAT,
       base::UTF8ToUTF16(
-          spec()->GetTotal(state()->selected_instrument())->amount->currency),
+          spec()->GetTotal(state()->selected_app())->amount->currency),
       spec()->GetFormattedCurrencyAmount(
-          spec()->GetTotal(state()->selected_instrument())->amount));
+          spec()->GetTotal(state()->selected_app())->amount));
 
   content_view->AddChildView(
       CreateLineItemView(
+          base::UTF8ToUTF16(spec()->GetTotal(state()->selected_app())->label),
           base::UTF8ToUTF16(
-              spec()->GetTotal(state()->selected_instrument())->label),
-          base::UTF8ToUTF16(spec()
-                                ->GetTotal(state()->selected_instrument())
-                                ->amount->currency),
+              spec()->GetTotal(state()->selected_app())->amount->currency),
           spec()->GetFormattedCurrencyAmount(
-              spec()->GetTotal(state()->selected_instrument())->amount),
+              spec()->GetTotal(state()->selected_app())->amount),
           true, DialogViewID::ORDER_SUMMARY_TOTAL_CURRENCY_LABEL,
           DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL)
           .release());
diff --git a/chrome/browser/ui/views/payments/order_summary_view_controller.h b/chrome/browser/ui/views/payments/order_summary_view_controller.h
index e085fa3..7f2b3b2a 100644
--- a/chrome/browser/ui/views/payments/order_summary_view_controller.h
+++ b/chrome/browser/ui/views/payments/order_summary_view_controller.h
@@ -34,7 +34,7 @@
   void OnSpecUpdated() override;
 
   // PaymentRequestState::Observer:
-  void OnGetAllPaymentInstrumentsFinished() override {}
+  void OnGetAllPaymentAppsFinished() override {}
   void OnSelectedInformationChanged() override;
 
  private:
diff --git a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
index 9ce8186..d540735 100644
--- a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
@@ -120,7 +120,7 @@
           adjusted_width *
           IconSizeCalculator::kPaymentAppDeviceIndependentIdealIconHeight /
           icon_image_skia.height();
-      // A column for the instrument icon.
+      // A column for the app icon.
       top_level_columns->AddColumn(
           views::GridLayout::LEADING, views::GridLayout::FILL,
           views::GridLayout::kFixedSize, views::GridLayout::FIXED,
@@ -132,15 +132,15 @@
     top_level_layout->StartRow(views::GridLayout::kFixedSize, 0);
     top_level_layout->AddView(std::move(title_origin_container));
     if (has_icon) {
-      std::unique_ptr<views::ImageView> instrument_icon_view =
-          CreateInstrumentIconView(/*icon_id=*/0, icon_image_skia,
-                                   /*label=*/page_title);
+      std::unique_ptr<views::ImageView> app_icon_view =
+          CreateAppIconView(/*icon_id=*/0, icon_image_skia,
+                            /*label=*/page_title);
       // We should set image size in density independent pixels here, since
       // views::ImageView objects are rastered at the device scale factor.
-      instrument_icon_view->SetImageSize(gfx::Size(
+      app_icon_view->SetImageSize(gfx::Size(
           adjusted_width,
           IconSizeCalculator::kPaymentAppDeviceIndependentIdealIconHeight));
-      top_level_layout->AddView(std::move(instrument_icon_view));
+      top_level_layout->AddView(std::move(app_icon_view));
     }
   }
   ~ReadOnlyOriginView() override {}
@@ -233,8 +233,8 @@
       GetHeaderBackground(header_view);
   return std::make_unique<ReadOnlyOriginView>(
       GetPaymentHandlerDialogTitle(web_contents(), https_prefix_), origin,
-      state()->selected_instrument()->icon_image_skia(),
-      background->get_color(), this);
+      state()->selected_app()->icon_image_skia(), background->get_color(),
+      this);
 }
 
 views::View*
diff --git a/chrome/browser/ui/views/payments/payment_method_view_controller.cc b/chrome/browser/ui/views/payments/payment_method_view_controller.cc
index d79e62cb..094a760 100644
--- a/chrome/browser/ui/views/payments/payment_method_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_method_view_controller.cc
@@ -18,8 +18,8 @@
 #include "chrome/browser/ui/views/payments/payment_request_row_view.h"
 #include "chrome/browser/ui/views/payments/payment_request_views_util.h"
 #include "components/payments/content/payment_request_state.h"
-#include "components/payments/core/autofill_payment_instrument.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/strings_util.h"
 #include "components/strings/grit/components_strings.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -51,23 +51,23 @@
 
 class PaymentMethodListItem : public PaymentRequestItemList::Item {
  public:
-  // Does not take ownership of |instrument|, which should not be null and
-  // should outlive this object. |list| is the PaymentRequestItemList object
-  // that will own this.
-  PaymentMethodListItem(PaymentInstrument* instrument,
+  // Does not take ownership of |app|, which should not be null and should
+  // outlive this object. |list| is the PaymentRequestItemList object that will
+  // own this.
+  PaymentMethodListItem(PaymentApp* app,
                         PaymentRequestSpec* spec,
                         PaymentRequestState* state,
                         PaymentRequestItemList* list,
                         PaymentRequestDialogView* dialog,
                         bool selected)
-      : PaymentRequestItemList::Item(spec,
-                                     state,
-                                     list,
-                                     selected,
-                                     /*clickable=*/true,
-                                     /*show_edit_button=*/instrument->type() ==
-                                         PaymentInstrument::Type::AUTOFILL),
-        instrument_(instrument),
+      : PaymentRequestItemList::Item(
+            spec,
+            state,
+            list,
+            selected,
+            /*clickable=*/true,
+            /*show_edit_button=*/app->type() == PaymentApp::Type::AUTOFILL),
+        app_(app),
         dialog_(dialog) {
     Init();
   }
@@ -75,26 +75,24 @@
 
  private:
   void ShowEditor() {
-    switch (instrument_->type()) {
-      case PaymentInstrument::Type::AUTOFILL:
+    switch (app_->type()) {
+      case PaymentApp::Type::AUTOFILL:
         // Since we are a list item, we only care about the on_edited callback.
         dialog_->ShowCreditCardEditor(
             BackNavigationType::kPaymentSheet,
             static_cast<int>(PaymentMethodViewControllerTags::MAX_TAG),
             /*on_edited=*/
             base::BindOnce(
-                &PaymentRequestState::SetSelectedInstrument,
-                state()->AsWeakPtr(), instrument_,
+                &PaymentRequestState::SetSelectedApp, state()->AsWeakPtr(),
+                app_,
                 PaymentRequestState::SectionSelectionStatus::kEditedSelected),
             /*on_added=*/
             base::OnceCallback<void(const autofill::CreditCard&)>(),
-            static_cast<AutofillPaymentInstrument*>(instrument_)
-                ->credit_card());
+            static_cast<AutofillPaymentApp*>(app_)->credit_card());
         return;
-      case PaymentInstrument::Type::NATIVE_MOBILE_APP:
-      case PaymentInstrument::Type::SERVICE_WORKER_APP:
-        // We cannot edit a native mobile app instrument and service worker
-        // based payment instrument.
+      case PaymentApp::Type::NATIVE_MOBILE_APP:
+      case PaymentApp::Type::SERVICE_WORKER_APP:
+        // We cannot edit a native mobile app and service worker app.
         return;
     }
     NOTREACHED();
@@ -102,9 +100,8 @@
 
   // PaymentRequestItemList::Item:
   std::unique_ptr<views::View> CreateExtraView() override {
-    std::unique_ptr<views::ImageView> icon_view = CreateInstrumentIconView(
-        instrument_->icon_resource_id(), instrument_->icon_image_skia(),
-        instrument_->GetLabel());
+    std::unique_ptr<views::ImageView> icon_view = CreateAppIconView(
+        app_->icon_resource_id(), app_->icon_image_skia(), app_->GetLabel());
     return icon_view;
   }
 
@@ -121,15 +118,15 @@
         views::BoxLayout::CrossAxisAlignment::kStart);
     card_info_container->SetLayoutManager(std::move(box_layout));
 
-    base::string16 label = instrument_->GetLabel();
+    base::string16 label = app_->GetLabel();
     if (!label.empty())
       card_info_container->AddChildView(new views::Label(label));
-    base::string16 sublabel = instrument_->GetSublabel();
+    base::string16 sublabel = app_->GetSublabel();
     if (!sublabel.empty())
       card_info_container->AddChildView(new views::Label(sublabel));
     base::string16 missing_info;
-    if (!instrument_->IsCompleteForPayment()) {
-      missing_info = instrument_->GetMissingInfoLabel();
+    if (!app_->IsCompleteForPayment()) {
+      missing_info = app_->GetMissingInfoLabel();
       auto missing_info_label =
           std::make_unique<views::Label>(missing_info, CONTEXT_BODY_TEXT_SMALL);
       missing_info_label->SetEnabledColor(
@@ -147,8 +144,8 @@
 
   void SelectedStateChanged() override {
     if (selected()) {
-      state()->SetSelectedInstrument(
-          instrument_, PaymentRequestState::SectionSelectionStatus::kSelected);
+      state()->SetSelectedApp(
+          app_, PaymentRequestState::SectionSelectionStatus::kSelected);
       dialog_->GoBack();
     }
   }
@@ -158,16 +155,18 @@
   }
 
   bool CanBeSelected() override {
-    // If an instrument can't be selected, PerformSelectionFallback is called,
-    // where the instrument can be made complete.
-    return instrument_->IsCompleteForPayment();
+    // If an app can't be selected because it's not complete,
+    // PerformSelectionFallback is called, where the app can be made complete.
+    // This applies only to AutofillPaymentApp, each one of which is a credit
+    // card, so PerformSelectionFallback will open the card editor.
+    return app_->IsCompleteForPayment();
   }
 
   void PerformSelectionFallback() override { ShowEditor(); }
 
   void EditButtonPressed() override { ShowEditor(); }
 
-  PaymentInstrument* instrument_;
+  PaymentApp* app_;
   PaymentRequestDialogView* dialog_;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentMethodListItem);
@@ -191,12 +190,12 @@
     PaymentRequestDialogView* dialog)
     : PaymentRequestSheetController(spec, state, dialog),
       payment_method_list_(dialog) {
-  const std::vector<std::unique_ptr<PaymentInstrument>>& available_instruments =
-      state->available_instruments();
-  for (const auto& instrument : available_instruments) {
+  const std::vector<std::unique_ptr<PaymentApp>>& available_apps =
+      state->available_apps();
+  for (const auto& app : available_apps) {
     auto item = std::make_unique<PaymentMethodListItem>(
-        instrument.get(), spec, state, &payment_method_list_, dialog,
-        instrument.get() == state->selected_instrument());
+        app.get(), spec, state, &payment_method_list_, dialog,
+        app.get() == state->selected_app());
     payment_method_list_.AddItem(std::move(item));
   }
 }
@@ -237,7 +236,7 @@
         static_cast<int>(PaymentMethodViewControllerTags::MAX_TAG),
         /*on_edited=*/base::OnceClosure(),
         /*on_added=*/
-        base::BindOnce(&PaymentRequestState::AddAutofillPaymentInstrument,
+        base::BindOnce(&PaymentRequestState::AddAutofillPaymentApp,
                        state()->AsWeakPtr(), /*selected=*/true),
         /*credit_card=*/nullptr);
   } else {
diff --git a/chrome/browser/ui/views/payments/payment_method_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/payment_method_view_controller_browsertest.cc
index b943687..b9b751b 100644
--- a/chrome/browser/ui/views/payments/payment_method_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_method_view_controller_browsertest.cc
@@ -33,15 +33,15 @@
   OpenPaymentMethodScreen();
 
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(1U, request->state()->available_instruments().size());
+  EXPECT_EQ(1U, request->state()->available_apps().size());
 
   views::View* list_view = dialog_view()->GetViewByID(
       static_cast<int>(DialogViewID::PAYMENT_METHOD_SHEET_LIST_VIEW));
   EXPECT_TRUE(list_view);
   EXPECT_EQ(1u, list_view->children().size());
 
-  EXPECT_EQ(request->state()->available_instruments().front().get(),
-            request->state()->selected_instrument());
+  EXPECT_EQ(request->state()->available_apps().front().get(),
+            request->state()->selected_app());
   views::View* checkmark_view = list_view->children().front()->GetViewByID(
       static_cast<int>(DialogViewID::CHECKMARK_VIEW));
   EXPECT_TRUE(checkmark_view->GetVisible());
@@ -71,17 +71,17 @@
   OpenPaymentMethodScreen();
 
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
-  EXPECT_EQ(2U, request->state()->available_instruments().size());
-  EXPECT_EQ(request->state()->available_instruments().front().get(),
-            request->state()->selected_instrument());
+  EXPECT_EQ(2U, request->state()->available_apps().size());
+  EXPECT_EQ(request->state()->available_apps().front().get(),
+            request->state()->selected_app());
 
   views::View* list_view = dialog_view()->GetViewByID(
       static_cast<int>(DialogViewID::PAYMENT_METHOD_SHEET_LIST_VIEW));
   EXPECT_TRUE(list_view);
   EXPECT_EQ(2u, list_view->children().size());
 
-  EXPECT_EQ(request->state()->available_instruments().front().get(),
-            request->state()->selected_instrument());
+  EXPECT_EQ(request->state()->available_apps().front().get(),
+            request->state()->selected_app());
   views::View* checkmark_view = list_view->children()[0]->GetViewByID(
       static_cast<int>(DialogViewID::CHECKMARK_VIEW));
   EXPECT_TRUE(checkmark_view->GetVisible());
@@ -94,8 +94,8 @@
   // Simulate selecting the second card.
   ClickOnDialogViewAndWait(list_view->children()[1]);
 
-  EXPECT_EQ(request->state()->available_instruments().back().get(),
-            request->state()->selected_instrument());
+  EXPECT_EQ(request->state()->available_apps().back().get(),
+            request->state()->selected_app());
 
   OpenPaymentMethodScreen();
   list_view = dialog_view()->GetViewByID(
@@ -106,8 +106,8 @@
   // return to the main payment sheet.
   ClickOnDialogViewAndWait(list_view->children()[1]);
 
-  EXPECT_EQ(request->state()->available_instruments().back().get(),
-            request->state()->selected_instrument());
+  EXPECT_EQ(request->state()->available_apps().back().get(),
+            request->state()->selected_app());
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentMethodViewControllerTest, EditButtonOpensEditor) {
diff --git a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
index 8321f03f..84dd5012 100644
--- a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
@@ -25,12 +25,12 @@
 
 using PaymentRequestJourneyLoggerTestBase = PaymentRequestBrowserTestBase;
 
-using PaymentRequestJourneyLoggerSelectedPaymentInstrumentTest =
+using PaymentRequestJourneyLoggerSelectedPaymentAppTest =
     PaymentRequestJourneyLoggerTestBase;
 
-// Tests that the selected instrument metric is correctly logged when the
+// Tests that the selected app metric is correctly logged when the
 // Payment Request is completed with a credit card.
-IN_PROC_BROWSER_TEST_F(PaymentRequestJourneyLoggerSelectedPaymentInstrumentTest,
+IN_PROC_BROWSER_TEST_F(PaymentRequestJourneyLoggerSelectedPaymentAppTest,
                        TestSelectedPaymentMethod) {
   NavigateTo("/payment_request_no_shipping_test.html");
   base::HistogramTester histogram_tester;
diff --git a/chrome/browser/ui/views/payments/payment_request_payment_response_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_payment_response_browsertest.cc
index 619b7e2..fe378e8 100644
--- a/chrome/browser/ui/views/payments/payment_request_payment_response_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_payment_response_browsertest.cc
@@ -18,21 +18,19 @@
 
 namespace payments {
 
-class PaymentRequestPaymentResponseAutofillPaymentInstrumentTest
+class PaymentRequestPaymentResponseAutofillPaymentAppTest
     : public PaymentRequestBrowserTestBase {
  protected:
-  PaymentRequestPaymentResponseAutofillPaymentInstrumentTest() {}
+  PaymentRequestPaymentResponseAutofillPaymentAppTest() {}
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(
-      PaymentRequestPaymentResponseAutofillPaymentInstrumentTest);
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestPaymentResponseAutofillPaymentAppTest);
 };
 
 // Tests that the PaymentResponse contains all the required fields for an
-// Autofill payment instrument.
-IN_PROC_BROWSER_TEST_F(
-    PaymentRequestPaymentResponseAutofillPaymentInstrumentTest,
-    TestPaymentResponse) {
+// Autofill payment app.
+IN_PROC_BROWSER_TEST_F(PaymentRequestPaymentResponseAutofillPaymentAppTest,
+                       TestPaymentResponse) {
   NavigateTo("/payment_request_no_shipping_test.html");
   // Setup a credit card with an associated billing address.
   autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
diff --git a/chrome/browser/ui/views/payments/payment_request_views_util.cc b/chrome/browser/ui/views/payments/payment_request_views_util.cc
index 557d3f2a..d8bf4ae 100644
--- a/chrome/browser/ui/views/payments/payment_request_views_util.cc
+++ b/chrome/browser/ui/views/payments/payment_request_views_util.cc
@@ -235,7 +235,7 @@
   layout->AddView(std::move(header_content_view));
 }
 
-std::unique_ptr<views::ImageView> CreateInstrumentIconView(
+std::unique_ptr<views::ImageView> CreateAppIconView(
     int icon_resource_id,
     gfx::ImageSkia img,
     const base::string16& tooltip_text,
diff --git a/chrome/browser/ui/views/payments/payment_request_views_util.h b/chrome/browser/ui/views/payments/payment_request_views_util.h
index ee5dd59..6a0ba66b 100644
--- a/chrome/browser/ui/views/payments/payment_request_views_util.h
+++ b/chrome/browser/ui/views/payments/payment_request_views_util.h
@@ -79,7 +79,7 @@
 // Returns an instrument image view for the given |img| or |icon_resource_id|
 // and wanted |opacity|. Includes a rounded rect border. Callers need to set the
 // size of the resulting ImageView. Callers should set a |tooltip_text|.
-std::unique_ptr<views::ImageView> CreateInstrumentIconView(
+std::unique_ptr<views::ImageView> CreateAppIconView(
     int icon_resource_id,
     gfx::ImageSkia img,
     const base::string16& tooltip_text,
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
index 98baf3f2..7f99dc51 100644
--- a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
@@ -32,7 +32,7 @@
 #include "components/payments/content/payment_request_spec.h"
 #include "components/payments/content/payment_request_state.h"
 #include "components/payments/core/currency_formatter.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_prefs.h"
 #include "components/payments/core/strings_util.h"
 #include "components/prefs/pref_service.h"
@@ -521,7 +521,7 @@
           static_cast<int>(PaymentSheetViewControllerTags::MAX_TAG),
           /*on_edited=*/base::OnceClosure(),  // This is always an add.
           /*on_added=*/
-          base::BindOnce(&PaymentRequestState::AddAutofillPaymentInstrument,
+          base::BindOnce(&PaymentRequestState::AddAutofillPaymentApp,
                          state()->AsWeakPtr(), /*selected=*/true),
           /*credit_card=*/nullptr);
 
@@ -599,7 +599,7 @@
                      kItemSummaryPriceFixedWidth, kItemSummaryPriceFixedWidth);
 
   const std::vector<const mojom::PaymentItemPtr*>& items =
-      spec()->GetDisplayItems(state()->selected_instrument());
+      spec()->GetDisplayItems(state()->selected_app());
 
   bool is_mixed_currency = spec()->IsMixedCurrency();
   // The inline items section contains the first 2 display items of the
@@ -642,17 +642,17 @@
   }
 
   layout->StartRow(views::GridLayout::kFixedSize, 0);
-  PaymentInstrument* selected_instrument = state()->selected_instrument();
-  const mojom::PaymentItemPtr& total = spec()->GetTotal(selected_instrument);
+  PaymentApp* selected_app = state()->selected_app();
+  const mojom::PaymentItemPtr& total = spec()->GetTotal(selected_app);
   base::string16 total_label_text = base::UTF8ToUTF16(total->label);
   std::unique_ptr<views::Label> total_label = CreateBoldLabel(total_label_text);
   layout->AddView(std::move(total_label));
 
   base::string16 total_currency_code =
       base::UTF8ToUTF16(spec()->GetFormattedCurrencyCode(
-          spec()->GetTotal(state()->selected_instrument())->amount));
+          spec()->GetTotal(state()->selected_app())->amount));
   base::string16 total_amount = spec()->GetFormattedCurrencyAmount(
-      spec()->GetTotal(state()->selected_instrument())->amount);
+      spec()->GetTotal(state()->selected_app())->amount);
   layout->AddView(CreateInlineCurrencyAmountItem(total_currency_code,
                                                  total_amount, false, true));
 
@@ -744,14 +744,14 @@
 // +----------------------------------------------+
 std::unique_ptr<PaymentRequestRowView>
 PaymentSheetViewController::CreatePaymentMethodRow() {
-  PaymentInstrument* selected_instrument = state()->selected_instrument();
+  PaymentApp* selected_app = state()->selected_app();
 
   PaymentSheetRowBuilder builder(
       this, l10n_util::GetStringUTF16(
                 IDS_PAYMENT_REQUEST_PAYMENT_METHOD_SECTION_NAME));
   builder.Tag(PaymentSheetViewControllerTags::SHOW_PAYMENT_METHOD_BUTTON);
 
-  if (selected_instrument) {
+  if (selected_app) {
     std::unique_ptr<views::View> content_view = std::make_unique<views::View>();
 
     views::GridLayout* layout =
@@ -761,47 +761,45 @@
                        1.0, views::GridLayout::USE_PREF, 0, 0);
 
     layout->StartRow(views::GridLayout::kFixedSize, 0);
-    std::unique_ptr<views::Label> selected_instrument_label =
-        std::make_unique<views::Label>(selected_instrument->GetLabel());
-    selected_instrument_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    layout->AddView(std::move(selected_instrument_label));
+    std::unique_ptr<views::Label> selected_app_label =
+        std::make_unique<views::Label>(selected_app->GetLabel());
+    selected_app_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    layout->AddView(std::move(selected_app_label));
 
     layout->StartRow(views::GridLayout::kFixedSize, 0);
-    std::unique_ptr<views::Label> selected_instrument_sublabel =
-        std::make_unique<views::Label>(selected_instrument->GetSublabel());
-    selected_instrument_sublabel->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    layout->AddView(std::move(selected_instrument_sublabel));
+    std::unique_ptr<views::Label> selected_app_sublabel =
+        std::make_unique<views::Label>(selected_app->GetSublabel());
+    selected_app_sublabel->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    layout->AddView(std::move(selected_app_sublabel));
 
-    std::unique_ptr<views::ImageView> icon_view =
-        CreateInstrumentIconView(selected_instrument->icon_resource_id(),
-                                 selected_instrument->icon_image_skia(),
-                                 selected_instrument->GetLabel());
+    std::unique_ptr<views::ImageView> icon_view = CreateAppIconView(
+        selected_app->icon_resource_id(), selected_app->icon_image_skia(),
+        selected_app->GetLabel());
 
-    return builder.AccessibleContent(selected_instrument->GetLabel())
+    return builder.AccessibleContent(selected_app->GetLabel())
         .Id(DialogViewID::PAYMENT_SHEET_PAYMENT_METHOD_SECTION)
         .CreateWithChevron(std::move(content_view), std::move(icon_view));
   } else {
     builder.Id(DialogViewID::PAYMENT_SHEET_PAYMENT_METHOD_SECTION_BUTTON);
-    if (state()->available_instruments().empty()) {
+    if (state()->available_apps().empty()) {
       // If the button is "Add", navigate to the editor directly.
       builder.Tag(PaymentSheetViewControllerTags::ADD_PAYMENT_METHOD_BUTTON);
       return builder.CreateWithButton(base::string16(),
                                       l10n_util::GetStringUTF16(IDS_ADD),
                                       /*button_enabled=*/true);
-    } else if (state()->available_instruments().size() == 1) {
-      return builder.CreateWithButton(
-          state()->available_instruments()[0]->GetLabel(),
-          l10n_util::GetStringUTF16(IDS_CHOOSE),
-          /*button_enabled=*/true);
+    } else if (state()->available_apps().size() == 1) {
+      return builder.CreateWithButton(state()->available_apps()[0]->GetLabel(),
+                                      l10n_util::GetStringUTF16(IDS_CHOOSE),
+                                      /*button_enabled=*/true);
     } else {
       base::string16 format = l10n_util::GetPluralStringFUTF16(
           IDS_PAYMENT_REQUEST_PAYMENT_METHODS_PREVIEW,
-          state()->available_instruments().size() - 1);
-      return builder.CreateWithButton(
-          state()->available_instruments()[0]->GetLabel(), format,
-          state()->available_instruments().size() - 1,
-          l10n_util::GetStringUTF16(IDS_CHOOSE),
-          /*button_enabled=*/true);
+          state()->available_apps().size() - 1);
+      return builder.CreateWithButton(state()->available_apps()[0]->GetLabel(),
+                                      format,
+                                      state()->available_apps().size() - 1,
+                                      l10n_util::GetStringUTF16(IDS_CHOOSE),
+                                      /*button_enabled=*/true);
     }
   }
 }
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller.h b/chrome/browser/ui/views/payments/payment_sheet_view_controller.h
index fc5195f..b51467e08 100644
--- a/chrome/browser/ui/views/payments/payment_sheet_view_controller.h
+++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller.h
@@ -39,7 +39,7 @@
   void OnSpecUpdated() override;
 
   // PaymentRequestState::Observer:
-  void OnGetAllPaymentInstrumentsFinished() override {}
+  void OnGetAllPaymentAppsFinished() override {}
   void OnSelectedInformationChanged() override;
 
  private:
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h
index ee052f5..55bb54d 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h
@@ -57,7 +57,7 @@
   // summarily relaunched on Chrome desktop, or the device is rebooted on
   // Chrome OS.
   static constexpr base::TimeDelta kRelaunchGracePeriod =
-      base::TimeDelta::FromMinutes(15);
+      base::TimeDelta::FromMinutes(60);
 
   RelaunchNotificationController(UpgradeDetector* upgrade_detector,
                                  const base::Clock* clock,
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
index dc26d5b9..d885b691 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
@@ -100,10 +100,6 @@
   UpdateHighlight();
 }
 
-void ToolbarIconContainerView::ChildPreferredSizeChanged(views::View* child) {
-  PreferredSizeChanged();
-}
-
 gfx::Insets ToolbarIconContainerView::GetInsets() const {
   // Use empty insets to have the border paint into the view instead of around
   // it. This prevents inadvertently increasing its size while the stroke is
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
index 655909f..45167d2 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
@@ -68,7 +68,6 @@
   // views::View:
   void OnMouseEntered(const ui::MouseEvent& event) override;
   void OnMouseExited(const ui::MouseEvent& event) override;
-  void ChildPreferredSizeChanged(views::View* child) override;
   gfx::Insets GetInsets() const override;
   const char* GetClassName() const override;
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_frame_toolbar_view.cc b/chrome/browser/ui/views/web_apps/web_app_frame_toolbar_view.cc
index 0bde8357..d7b6ca5 100644
--- a/chrome/browser/ui/views/web_apps/web_app_frame_toolbar_view.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_frame_toolbar_view.cc
@@ -158,11 +158,6 @@
   }
 
  private:
-  // views::View:
-  void ChildVisibilityChanged(views::View* child) override {
-    PreferredSizeChanged();
-  }
-
   // Owned by the views hierarchy.
   std::vector<ContentSettingImageView*> content_setting_views_;
 
@@ -568,11 +563,6 @@
   PreferredSizeChanged();
 }
 
-void WebAppFrameToolbarView::ChildVisibilityChanged(views::View* child) {
-  // Changes to layout need to be taken into account by the frame view.
-  PreferredSizeChanged();
-}
-
 bool WebAppFrameToolbarView::ShouldAnimate() const {
   return !g_animation_disabled_for_testing &&
          !browser_view_->immersive_mode_controller()->IsEnabled();
diff --git a/chrome/browser/ui/views/web_apps/web_app_frame_toolbar_view.h b/chrome/browser/ui/views/web_apps/web_app_frame_toolbar_view.h
index 7a0c004..adf4cab 100644
--- a/chrome/browser/ui/views/web_apps/web_app_frame_toolbar_view.h
+++ b/chrome/browser/ui/views/web_apps/web_app_frame_toolbar_view.h
@@ -151,7 +151,6 @@
   // views::AccessiblePaneView:
   gfx::Size CalculatePreferredSize() const override;
   void ChildPreferredSizeChanged(views::View* child) override;
-  void ChildVisibilityChanged(views::View* child) override;
 
  private:
   friend class WebAppNonClientFrameViewAshTest;
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
index b6721f48..4971a157 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
@@ -110,6 +110,7 @@
   void OnEnterpriseInfoUpdated(const std::string& message_text,
                                const std::string& asset_id) override;
   void OnDeviceInfoUpdated(const std::string& bluetooth_name) override;
+  void OnAdbSideloadStatusUpdated(bool enabled) override {}
 
   // ui::EventSource implementation:
   ui::EventSink* GetEventSink() override;
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 74ac029..1850f50 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2489,6 +2489,7 @@
                        Profile* profile) {
   static constexpr LocalizedString kLocalizedStrings[] = {
       {"privacyPageTitle", IDS_SETTINGS_PRIVACY},
+      {"privacyPageMore", IDS_SETTINGS_PRIVACY_MORE},
       {"signinAllowedTitle", IDS_SETTINGS_SIGNIN_ALLOWED},
       {"signinAllowedDescription", IDS_SETTINGS_SIGNIN_ALLOWED_DESC},
       {"doNotTrack", IDS_SETTINGS_ENABLE_DO_NOT_TRACK},
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc
index f1538ed..04cb219 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc
+++ b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc
@@ -42,14 +42,12 @@
 // The default amount of time it takes for the detector's annoyance level
 // (upgrade_notification_stage()) to reach UPGRADE_ANNOYANCE_HIGH once an
 // upgrade is detected.
-constexpr base::TimeDelta kDefaultHighThreshold = base::TimeDelta::FromDays(4);
+constexpr base::TimeDelta kDefaultHighThreshold = base::TimeDelta::FromDays(7);
 
 // The default amount of time between the detector's annoyance level change
 // from UPGRADE_ANNOYANCE_ELEVATED to UPGRADE_ANNOYANCE_HIGH in ms.
-constexpr int kDefaultHeadsUpPeriodMs = 24 * 60 * 60 * 1000;  // 1 day.
-
 constexpr base::TimeDelta kDefaultHeadsUpPeriod =
-    base::TimeDelta::FromMilliseconds(kDefaultHeadsUpPeriodMs);
+    base::TimeDelta::FromDays(3);  // 3 days.
 
 // The reason of the rollback used in the UpgradeDetector.RollbackReason
 // histogram.
@@ -140,7 +138,7 @@
 // static
 void UpgradeDetectorChromeos::RegisterPrefs(PrefRegistrySimple* registry) {
   registry->RegisterIntegerPref(prefs::kRelaunchHeadsUpPeriod,
-                                kDefaultHeadsUpPeriodMs);
+                                kDefaultHeadsUpPeriod.InMilliseconds());
 }
 
 void UpgradeDetectorChromeos::Init() {
diff --git a/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface.cc b/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface.cc
index a9f3b65..01af689 100644
--- a/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface.cc
+++ b/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface.cc
@@ -259,7 +259,18 @@
 }
 
 bool SandboxDeleteService(const base::string16& name) {
-  // TODO(joenotcharles): Add some sanity checks.
+  if (name.empty()) {
+    LOG(ERROR) << "Sandbox called DeleteService with empty name.";
+    return false;
+  }
+
+  // https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-createservicea?redirectedfrom=MSDN
+  // says that the maximum service name length is 256 characters.
+  if (name.size() > 256) {
+    LOG(ERROR) << "Sandbox called DeleteService with a long string (length "
+               << name.size() << ")";
+    return false;
+  }
 
   // Attempt to stop the service, but don't let failure to stop the service
   // prevent deletion.
diff --git a/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc b/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc
index e6c44a66..8e2c1ede 100644
--- a/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc
+++ b/chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface_unittest.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/chrome_cleaner/engines/broker/cleaner_sandbox_interface.h"
 
-#include <aclapi.h>
-
 #include <limits>
 #include <memory>
 #include <utility>
@@ -44,7 +42,6 @@
 #include "chrome/chrome_cleaner/test/test_task_scheduler.h"
 #include "chrome/chrome_cleaner/test/test_util.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
-#include "sandbox/win/src/nt_internals.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using chrome_cleaner::CreateEmptyFile;
@@ -61,73 +58,6 @@
 constexpr wchar_t kTrickyNonRegistryPath[] =
     L"\\Registry\\Machine\\..\\..\\DosDevice\\C:";
 
-// Similar in intent to the ScopedProcessProtector, this does not take ownership
-// of a handle but twiddles the ACL on it on initialization and restores the
-// ACL on de-initialization. Useful for tests that require denying access to
-// something. |handle| should probably be opened with ALL_ACCESS or equivalent
-// and it would be a Bad Idea to CloseHandle or similar on the handle before
-// this goes out of scope.
-class ScopedHandleProtector {
- public:
-  explicit ScopedHandleProtector(HANDLE handle) : handle_(handle) { Protect(); }
-  ~ScopedHandleProtector() { Release(); }
-
- private:
-  void Protect() {
-    // Store its existing DACL for cleanup purposes. This API function is weird:
-    // the pointer placed into |original_dacl_| is actually a pointer into a
-    // the structure pointed to by |original_descriptor_|. To use this, one
-    // stores both, but frees ONLY the structure stuffed into
-    // |original_descriptor_|.
-    if (GetSecurityInfo(handle_, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
-                        /*ppsidOwner=*/NULL, /*ppsidOwner=*/NULL,
-                        &original_dacl_, /*ppsidOwner=*/NULL,
-                        &original_descriptor_) != ERROR_SUCCESS) {
-      PLOG(ERROR) << "Failed to retreieve original DACL.";
-      return;
-    }
-
-    // Set a new empty DACL, effectively denying all things on the process
-    // object.
-    ACL dacl;
-    if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) {
-      PLOG(ERROR) << "Failed to initialize DACL";
-      return;
-    }
-    if (SetSecurityInfo(handle_, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
-                        /*psidOwner=*/NULL, /*psidGroup=*/NULL, &dacl,
-                        /*pSacl=*/NULL) != ERROR_SUCCESS) {
-      PLOG(ERROR) << "Failed to set new DACL.";
-      return;
-    }
-
-    initialized_ = true;
-  }
-
-  void Release() {
-    if (initialized_) {
-      if (SetSecurityInfo(handle_, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
-                          /*psidOwner=*/NULL, /*psidGroup=*/NULL,
-                          original_dacl_, /*pSacl=*/NULL) != ERROR_SUCCESS) {
-        PLOG(ERROR) << "Failed to restore original DACL.";
-      }
-    }
-
-    if (original_descriptor_) {
-      ::LocalFree(original_descriptor_);
-      original_dacl_ = nullptr;
-      original_descriptor_ = nullptr;
-    }
-
-    initialized_ = false;
-  }
-
-  bool initialized_ = false;
-  HANDLE handle_;
-  PACL original_dacl_ = nullptr;
-  PSECURITY_DESCRIPTOR original_descriptor_ = nullptr;
-};
-
 String16EmbeddedNulls FullyQualifiedKeyPathWithTrailingNull(
     const ScopedTempRegistryKey& temp_key,
     const std::vector<wchar_t>& key_name) {
@@ -558,22 +488,6 @@
   EXPECT_FALSE(SandboxNtDeleteRegistryKey(no_terminating_null_key));
 }
 
-// TODO(veranika): This test is failing on win10 bots. Fix and re-enable it.
-TEST_F(CleanerInterfaceRegistryTest,
-       DISABLED_NtDeleteRegistryKey_AccessDenied) {
-  {
-    // Protect the key, expect deletion to fail.
-    ScopedHandleProtector protector(subkey_handle_);
-    EXPECT_FALSE(SandboxNtDeleteRegistryKey(full_key_path_));
-  }
-
-  // Key should now be unprotected and deletion should succeed.
-  EXPECT_TRUE(SandboxNtDeleteRegistryKey(full_key_path_));
-
-  // Shouldn't be able to do that twice.
-  EXPECT_FALSE(SandboxNtDeleteRegistryKey(full_key_path_));
-}
-
 TEST_F(CleanerInterfaceRegistryTest, NtDeleteRegistryKey_NonRegistryPath) {
   EXPECT_FALSE(SandboxNtDeleteRegistryKey(
       StringWithTrailingNull(kDirectNonRegistryPath)));
@@ -775,22 +689,6 @@
   EXPECT_EQ(valid_changed_value_, actual_value);
 }
 
-// TODO(veranika): This test is failing on win10 bots. Fix and re-enable it.
-TEST_F(CleanerInterfaceRegistryTest,
-       DISABLED_NtChangeRegistryValue_AccessDenied) {
-  {
-    // Protect the key, expect modification to fail.
-    ScopedHandleProtector protector(subkey_handle_);
-    EXPECT_FALSE(SandboxNtChangeRegistryValue(
-        full_key_path_, value_, valid_changed_value_,
-        default_value_should_be_normalized_));
-  }
-
-  EXPECT_TRUE(
-      SandboxNtChangeRegistryValue(full_key_path_, value_, valid_changed_value_,
-                                   default_value_should_be_normalized_));
-}
-
 TEST_F(CleanerInterfaceRegistryTest, NtChangeRegistryValue_NonRegistryPath) {
   EXPECT_FALSE(SandboxNtChangeRegistryValue(
       StringWithTrailingNull(kDirectNonRegistryPath), value_name_,
diff --git a/chrome/chrome_cleaner/engines/broker/engine_requests_no_blocking_unittest.cc b/chrome/chrome_cleaner/engines/broker/engine_requests_no_blocking_unittest.cc
index 528bb703..1b7510f3 100644
--- a/chrome/chrome_cleaner/engines/broker/engine_requests_no_blocking_unittest.cc
+++ b/chrome/chrome_cleaner/engines/broker/engine_requests_no_blocking_unittest.cc
@@ -273,10 +273,12 @@
                    BindOnce(&NtChangeRegistryValueCallback,
                             std::move(result_closure))));
     } else if (request_name == "DeleteService") {
+      // The broker should reject the empty string so we won't risk deleting a
+      // real service.
+      const base::string16 empty_service_name;
       cleaner_requests_proxy_->task_runner()->PostTask(
           FROM_HERE, BindOnce(IgnoreResult(&CleanerProxy::SandboxDeleteService),
-                              cleaner_requests_proxy_,
-                              RandomUnusedServiceNameForTesting().c_str(),
+                              cleaner_requests_proxy_, empty_service_name,
                               BindOnce(&DeleteServiceCallback,
                                        std::move(result_closure))));
     } else if (request_name == "DeleteTask") {
@@ -597,10 +599,7 @@
                                         "NtDeleteRegistryKey",
                                         "NtDeleteRegistryValue",
                                         "NtChangeRegistryValue",
-#if 0
-                                        // TODO(https://crbug.com/945432): Disabled due to flake.
                                         "DeleteService",
-#endif
                                         "DeleteTask",
                                         "TerminateProcess"),
                         GetParamNameForTest());
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index ed39f54..c933425 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -58,10 +58,12 @@
     }
     if (is_chromeos) {
       sources += [
+        "$root_gen_dir/ash/app_list/resources/app_list_resources_${percent}_percent.pak",
         "$root_gen_dir/ash/login/resources/login_resources_${percent}_percent.pak",
         "$root_gen_dir/ui/chromeos/resources/ui_chromeos_resources_${percent}_percent.pak",
       ]
       deps += [
+        "//ash/app_list/resources",
         "//ash/login/resources",
         "//ui/chromeos/resources",
       ]
@@ -70,10 +72,6 @@
       sources += [ "$root_gen_dir/extensions/extensions_browser_resources_${percent}_percent.pak" ]
       deps += [ "//extensions:extensions_browser_resources" ]
     }
-    if (enable_app_list) {
-      sources += [ "$root_gen_dir/ash/app_list/resources/app_list_resources_${percent}_percent.pak" ]
-      deps += [ "//ash/app_list/resources" ]
-    }
 
     output = "${invoker.output_dir}/chrome_${percent}_percent.pak"
   }
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index b10ae04..53d715d 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -40,6 +40,7 @@
     "ENABLE_BACKGROUND_CONTENTS=$enable_background_contents",
     "ENABLE_BASIC_PRINT_DIALOG=$enable_basic_print_dialog",
     "ENABLE_CAPTIVE_PORTAL_DETECTION=$enable_captive_portal_detection",
+    "ENABLE_CLICK_TO_CALL=$enable_click_to_call",
     "ENABLE_HANGOUT_SERVICES_EXTENSION=$enable_hangout_services_extension",
     "ENABLE_ONE_CLICK_SIGNIN=$enable_one_click_signin",
     "ENABLE_NATIVE_NOTIFICATIONS=$enable_native_notifications",
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index 9c311ea..896298d 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -336,20 +336,43 @@
   };
 
   dictionary AppWindowInfo {
+    // The identifier of the window. This shouldn't change across the time.
     long id;
+
+    // The name of the window object -- typically internal class name of the
+    // window (like 'BrowserFrame').
     DOMString name;
+
     AppWindowType windowType;
     WindowStateType stateType;
+
+    // The bounds of the window, in the coordinate of the root window (i.e.
+    // relative to the display where this window resides).
     Bounds boundsInRoot;
-    Bounds targetBounds;
+
+    // The identifier of the display where this window resides.
     DOMString displayId;
 
-    DOMString title;
-    boolean isAnimating;
     boolean isVisible;
-    boolean targetVisibility;
     boolean canFocus;
 
+    // The title of the window; this can be seen in the window caption, or in
+    // the overview mode. Typically, this provides the title of the webpage or
+    // the title supplied by the application.
+    DOMString title;
+
+    // Whether some animation is ongoing on the window or not.
+    boolean isAnimating;
+
+    // The final bounds of the window when the animation completes. This should
+    // be same as |boundsInRoot| when |isAnimating| is false.
+    Bounds targetBounds;
+
+    // Whether or not the window is going to be visible after the animation
+    // completes. This should be same as |isVisible| when |isAnimating| is
+    // false.
+    boolean targetVisibility;
+
     // WM Releated stats
     boolean isActive;
     boolean hasFocus;
@@ -358,7 +381,10 @@
 
     // Window frame info
     long captionHeight;
+    // The bitset of the enabled caption buttons. See
+    // ui/views/window/caption_button_types.h.
     long captionButtonEnabledStatus;
+    // The bitset of the caption buttons which are visible on the frame.
     long captionButtonVisibleStatus;
 
     DOMString? arcPackageName;
diff --git a/chrome/common/features.gni b/chrome/common/features.gni
index 648d427..d3939aa 100644
--- a/chrome/common/features.gni
+++ b/chrome/common/features.gni
@@ -29,8 +29,6 @@
   # unconditionally enabled on all platforms.
   builtin_cert_verifier_policy_supported = is_chromeos || is_desktop_linux
 
-  enable_app_list = is_chromeos
-
   # Enables support for background apps.
   enable_background_contents = !is_android && !is_chromecast
   enable_background_mode = !is_android && !is_chromecast && !is_chromeos
@@ -41,6 +39,9 @@
 
   enable_captive_portal_detection = !is_android && !is_chromecast
 
+  # Enables the Click to Call feature on desktop platforms.
+  enable_click_to_call = is_mac || is_win || is_desktop_linux || is_chromeos
+
   # Hangout services is an extension that adds extra features to Hangouts.
   # It is enableable separately to facilitate testing.
   enable_hangout_services_extension = is_chrome_branded
@@ -75,7 +76,6 @@
 # Every grit target in //chrome should apply these defines so that the
 # proper build flags can be set.
 chrome_grit_defines = [
-  "enable_app_list=$enable_app_list",
   "enable_arcore=$enable_arcore",
   "enable_background_mode=$enable_background_mode",
   "enable_background_contents=$enable_background_contents",
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 060a6e1..1670ff6 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1622,6 +1622,12 @@
 // send text across devices.
 const char kSharedClipboardEnabled[] = "browser.shared_clipboard_enabled";
 
+#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
+// A flag to enable/disable the Click to Call feature which enables users to
+// send phone numbers from desktop to Android phones.
+const char kClickToCallEnabled[] = "browser.click_to_call_enabled";
+#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
+
 // Extensions which should be opened upon completion.
 const char kDownloadExtensionsToOpen[] = "download.extensions_to_open";
 
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index fec4266..a09089d 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -542,6 +542,10 @@
 
 extern const char kSharedClipboardEnabled[];
 
+#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
+extern const char kClickToCallEnabled[];
+#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
+
 extern const char kSelectFileLastDirectory[];
 
 extern const char kExcludedSchemes[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 6e3e76c..b89022f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1497,6 +1497,7 @@
     if (!is_android && !is_chromeos) {
       sources += [
         "../browser/external_protocol/external_protocol_handler_browsertest.cc",
+        "../browser/lifetime/application_lifetime_browsertest.cc",
       ]
     }
 
@@ -2015,7 +2016,6 @@
     }
 
     if (is_chromeos) {
-      assert(enable_app_list)
       sources += [
         "../browser/apps/platform_apps/app_window_interactive_uitest_base.cc",
         "../browser/apps/platform_apps/app_window_interactive_uitest_base.h",
@@ -2163,6 +2163,7 @@
         "../browser/chromeos/login/proxy_auth_dialog_browsertest.cc",
         "../browser/chromeos/login/quick_unlock/pin_migration_browsertest.cc",
         "../browser/chromeos/login/reset_browsertest.cc",
+        "../browser/chromeos/login/saml/password_change_success_detection_browsertest.cc",
         "../browser/chromeos/login/saml/saml_browsertest.cc",
         "../browser/chromeos/login/screens/app_downloading_screen_browsertest.cc",
         "../browser/chromeos/login/screens/assistant_optin_flow_screen_browsertest.cc",
@@ -2312,7 +2313,9 @@
         "../browser/notifications/notification_platform_bridge_chromeos_browsertest.cc",
         "../browser/resources/chromeos/zip_archiver/test/zip_archiver_jstest.cc",
         "../browser/signin/chromeos_mirror_account_consistency_browsertest.cc",
+        "../browser/ui/app_list/app_list_client_impl_browsertest.cc",
         "../browser/ui/app_list/arc/arc_usb_host_permission_browsertest.cc",
+        "../browser/ui/app_list/chrome_app_list_model_updater_browsertest.cc",
         "../browser/ui/ash/accelerator_commands_browsertest.cc",
         "../browser/ui/ash/assistant/assistant_context_browsertest.cc",
         "../browser/ui/ash/chrome_new_window_client_browsertest.cc",
@@ -2565,12 +2568,6 @@
       sources +=
           [ "../browser/first_run/first_run_internal_posix_browsertest.cc" ]
     }
-    if (enable_app_list) {
-      sources += [
-        "../browser/ui/app_list/app_list_client_impl_browsertest.cc",
-        "../browser/ui/app_list/chrome_app_list_model_updater_browsertest.cc",
-      ]
-    }
     if (enable_service_discovery) {
       sources += [ "../browser/extensions/api/mdns/mdns_apitest.cc" ]
     }
@@ -4130,6 +4127,58 @@
       "../browser/signin/signin_error_notifier_ash_unittest.cc",
       "../browser/speech/tts_chromeos_unittest.cc",
       "../browser/sync/sync_error_notifier_ash_unittest.cc",
+      "../browser/ui/app_list/app_context_menu_unittest.cc",
+      "../browser/ui/app_list/app_list_syncable_service_unittest.cc",
+      "../browser/ui/app_list/app_list_test_util.cc",
+      "../browser/ui/app_list/app_list_test_util.h",
+      "../browser/ui/app_list/arc/arc_app_test.cc",
+      "../browser/ui/app_list/arc/arc_app_test.h",
+      "../browser/ui/app_list/arc/arc_app_unittest.cc",
+      "../browser/ui/app_list/arc/arc_app_utils_unittest.cc",
+      "../browser/ui/app_list/arc/arc_vpn_provider_unittest.cc",
+      "../browser/ui/app_list/arc/mock_arc_app_list_prefs_observer.cc",
+      "../browser/ui/app_list/arc/mock_arc_app_list_prefs_observer.h",
+      "../browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc",
+      "../browser/ui/app_list/extension_app_model_builder_unittest.cc",
+      "../browser/ui/app_list/internal_app/internal_app_model_builder_unittest.cc",
+      "../browser/ui/app_list/md_icon_normalizer_unittest.cc",
+      "../browser/ui/app_list/search/answer_card/answer_card_result_unittest.cc",
+      "../browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc",
+      "../browser/ui/app_list/search/arc/arc_app_data_search_provider_unittest.cc",
+      "../browser/ui/app_list/search/arc/arc_app_reinstall_search_provider_unittest.cc",
+      "../browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider_unittest.cc",
+      "../browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc",
+      "../browser/ui/app_list/search/common/file_icon_util_unittest.cc",
+      "../browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc",
+      "../browser/ui/app_list/search/launcher_search/launcher_search_icon_image_loader_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/app_launch_event_logger_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/app_launch_predictor_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/frecency_store_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/histogram_util_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/ranking_item_util_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/recurrence_predictor_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/recurrence_ranker_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/search_ranking_event_logger_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc",
+      "../browser/ui/app_list/search/search_utils/fuzzy_tokenized_string_match_unittest.cc",
+      "../browser/ui/app_list/search/search_utils/sequence_matcher_unittest.cc",
+      "../browser/ui/app_list/search/settings_shortcut/settings_shortcut_provider_unittest.cc",
+      "../browser/ui/app_list/search/settings_shortcut/settings_shortcut_result_unittest.cc",
+      "../browser/ui/app_list/search/tests/app_search_provider_unittest.cc",
+      "../browser/ui/app_list/search/tests/mixer_unittest.cc",
+      "../browser/ui/app_list/search/tests/omnibox_result_unittest.cc",
+      "../browser/ui/app_list/search/tests/term_break_iterator_unittest.cc",
+      "../browser/ui/app_list/search/tests/tokenized_string_char_iterator_unittest.cc",
+      "../browser/ui/app_list/search/tests/tokenized_string_match_unittest.cc",
+      "../browser/ui/app_list/search/tests/tokenized_string_unittest.cc",
+      "../browser/ui/app_list/search/tests/zero_state_file_provider_unittest.cc",
+      "../browser/ui/app_list/search/tests/zero_state_file_result_unittest.cc",
+      "../browser/ui/app_list/test/fake_app_list_model_updater.cc",
+      "../browser/ui/app_list/test/fake_app_list_model_updater.h",
       "../browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc",
       "../browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc",
       "../browser/ui/ash/assistant/assistant_state_client_unittest.cc",
@@ -4157,10 +4206,13 @@
     ]
     deps += [
       "//ash:test_support",
+      "//ash/app_list:test_support",
       "//ash/public/cpp/resources:ash_public_unscaled_resources",
+      "//ash/resources/vector_icons",
       "//ash/strings",
       "//chrome/browser/chromeos:test_support",
       "//chrome/browser/resources/chromeos/zip_archiver/cpp:ziparchiver_unittests",
+      "//chromeos/services/machine_learning/public/cpp:test_support",
       "//components/arc:arc_test_support",
     ]
   }
@@ -5118,70 +5170,6 @@
   if (safe_browsing_mode == 1 && enable_extensions) {
     sources += [ "../browser/extensions/blacklist_unittest.cc" ]
   }
-  if (enable_app_list) {
-    sources += [
-      "../browser/ui/app_list/app_context_menu_unittest.cc",
-      "../browser/ui/app_list/app_list_syncable_service_unittest.cc",
-      "../browser/ui/app_list/app_list_test_util.cc",
-      "../browser/ui/app_list/app_list_test_util.h",
-      "../browser/ui/app_list/arc/arc_app_test.cc",
-      "../browser/ui/app_list/arc/arc_app_test.h",
-      "../browser/ui/app_list/arc/arc_app_unittest.cc",
-      "../browser/ui/app_list/arc/arc_app_utils_unittest.cc",
-      "../browser/ui/app_list/arc/arc_vpn_provider_unittest.cc",
-      "../browser/ui/app_list/arc/mock_arc_app_list_prefs_observer.cc",
-      "../browser/ui/app_list/arc/mock_arc_app_list_prefs_observer.h",
-      "../browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc",
-      "../browser/ui/app_list/extension_app_model_builder_unittest.cc",
-      "../browser/ui/app_list/internal_app/internal_app_model_builder_unittest.cc",
-      "../browser/ui/app_list/md_icon_normalizer_unittest.cc",
-      "../browser/ui/app_list/search/answer_card/answer_card_result_unittest.cc",
-      "../browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc",
-      "../browser/ui/app_list/search/arc/arc_app_data_search_provider_unittest.cc",
-      "../browser/ui/app_list/search/arc/arc_app_reinstall_search_provider_unittest.cc",
-      "../browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider_unittest.cc",
-      "../browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc",
-      "../browser/ui/app_list/search/common/file_icon_util_unittest.cc",
-      "../browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc",
-      "../browser/ui/app_list/search/launcher_search/launcher_search_icon_image_loader_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/app_launch_event_logger_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/app_launch_predictor_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/frecency_store_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/histogram_util_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/ranking_item_util_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/recurrence_predictor_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/recurrence_ranker_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/search_ranking_event_logger_unittest.cc",
-      "../browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc",
-      "../browser/ui/app_list/search/search_utils/fuzzy_tokenized_string_match_unittest.cc",
-      "../browser/ui/app_list/search/search_utils/sequence_matcher_unittest.cc",
-      "../browser/ui/app_list/search/settings_shortcut/settings_shortcut_provider_unittest.cc",
-      "../browser/ui/app_list/search/settings_shortcut/settings_shortcut_result_unittest.cc",
-      "../browser/ui/app_list/search/tests/app_search_provider_unittest.cc",
-      "../browser/ui/app_list/search/tests/mixer_unittest.cc",
-      "../browser/ui/app_list/search/tests/omnibox_result_unittest.cc",
-      "../browser/ui/app_list/search/tests/term_break_iterator_unittest.cc",
-      "../browser/ui/app_list/search/tests/tokenized_string_char_iterator_unittest.cc",
-      "../browser/ui/app_list/search/tests/tokenized_string_match_unittest.cc",
-      "../browser/ui/app_list/search/tests/tokenized_string_unittest.cc",
-      "../browser/ui/app_list/search/tests/zero_state_file_provider_unittest.cc",
-      "../browser/ui/app_list/search/tests/zero_state_file_result_unittest.cc",
-      "../browser/ui/app_list/test/fake_app_list_model_updater.cc",
-      "../browser/ui/app_list/test/fake_app_list_model_updater.h",
-    ]
-    deps += [
-      # TODO(wutao): Put new icons resources to ash/public/cpp/vector_icons/
-      # when UX provides them.
-      "//ash/app_list:test_support",
-      "//ash/resources/vector_icons",
-      "//chromeos/services/machine_learning/public/cpp:test_support",
-      "//components/arc:arc_test_support",
-    ]
-  }
 
   if (is_win || is_mac || (is_linux && !is_chromeos)) {
     sources += [
@@ -5333,7 +5321,6 @@
   }
 
   if (is_chromeos) {
-    assert(enable_app_list)
     assert(enable_extensions)
 
     # These tests are only meant to run on an FYI bot because they
@@ -5692,6 +5679,7 @@
         "../browser/ui/views/test/view_event_test_platform_part_default.cc",
       ]
       sources += [
+        "../browser/ui/app_list/app_list_client_interactive_uitest.cc",
         "../browser/ui/ash/drag_to_overview_interactive_uitest.cc",
         "../browser/ui/ash/launcher_animations_interactive_uitest.cc",
         "../browser/ui/ash/launcher_drag_interactive_uitest.cc",
@@ -5773,11 +5761,6 @@
       configs += [ "//build/config/linux:xtst" ]
     }
 
-    if (enable_app_list) {
-      sources +=
-          [ "../browser/ui/app_list/app_list_client_interactive_uitest.cc" ]
-    }
-
     if (enable_extensions) {
       # TODO(rockot) bug 505926: The chrome_extensions_interactive_uitests
       # target should be deleted and this line removed. See the
@@ -5910,8 +5893,6 @@
       "../browser/sync/test/integration/passwords_helper.h",
       "../browser/sync/test/integration/preferences_helper.cc",
       "../browser/sync/test/integration/preferences_helper.h",
-      "../browser/sync/test/integration/printers_helper.cc",
-      "../browser/sync/test/integration/printers_helper.h",
       "../browser/sync/test/integration/profile_sync_service_harness.cc",
       "../browser/sync/test/integration/profile_sync_service_harness.h",
       "../browser/sync/test/integration/quiesce_status_change_checker.cc",
@@ -5934,10 +5915,6 @@
       "../browser/sync/test/integration/status_change_checker.h",
       "../browser/sync/test/integration/sync_app_helper.cc",
       "../browser/sync/test/integration/sync_app_helper.h",
-      "../browser/sync/test/integration/sync_app_list_helper.cc",
-      "../browser/sync/test/integration/sync_app_list_helper.h",
-      "../browser/sync/test/integration/sync_arc_package_helper.cc",
-      "../browser/sync/test/integration/sync_arc_package_helper.h",
       "../browser/sync/test/integration/sync_datatype_helper.cc",
       "../browser/sync/test/integration/sync_datatype_helper.h",
       "../browser/sync/test/integration/sync_extension_helper.cc",
@@ -5984,30 +5961,24 @@
         "../browser/sync/test/integration/dictionary_load_observer.h",
       ]
     }
-    if (enable_app_list) {
+    if (is_chromeos) {
       sources += [
+        "../browser/sync/test/integration/os_sync_test.cc",
+        "../browser/sync/test/integration/os_sync_test.h",
+        "../browser/sync/test/integration/printers_helper.cc",
+        "../browser/sync/test/integration/printers_helper.h",
+        "../browser/sync/test/integration/sync_app_list_helper.cc",
+        "../browser/sync/test/integration/sync_app_list_helper.h",
+        "../browser/sync/test/integration/sync_arc_package_helper.cc",
+        "../browser/sync/test/integration/sync_arc_package_helper.h",
         "../browser/ui/app_list/test/fake_app_list_model_updater.cc",
         "../browser/ui/app_list/test/fake_app_list_model_updater.h",
       ]
-      deps += [ "//ash/app_list:test_support" ]
-    } else {
-      sources -= [
-        "../browser/sync/test/integration/sync_app_list_helper.cc",
-        "../browser/sync/test/integration/sync_app_list_helper.h",
-      ]
-    }
-    if (is_chromeos) {
       deps += [
+        "//ash/app_list:test_support",
         "//components/arc:arc_test_support",
         "//components/user_manager:test_support",
       ]
-    } else {
-      sources -= [
-        "../browser/sync/test/integration/printers_helper.cc",
-        "../browser/sync/test/integration/printers_helper.h",
-        "../browser/sync/test/integration/sync_arc_package_helper.cc",
-        "../browser/sync/test/integration/sync_arc_package_helper.h",
-      ]
     }
   }
 
@@ -6016,7 +5987,6 @@
       "../browser/sync/test/integration/enable_disable_test.cc",
       "../browser/sync/test/integration/local_sync_test.cc",
       "../browser/sync/test/integration/migration_test.cc",
-      "../browser/sync/test/integration/single_client_app_list_sync_test.cc",
       "../browser/sync/test/integration/single_client_apps_sync_test.cc",
       "../browser/sync/test/integration/single_client_autofill_profile_sync_test.cc",
       "../browser/sync/test/integration/single_client_bookmarks_sync_test.cc",
@@ -6043,7 +6013,6 @@
       "../browser/sync/test/integration/sync_auth_test.cc",
       "../browser/sync/test/integration/sync_errors_test.cc",
       "../browser/sync/test/integration/sync_exponential_backoff_test.cc",
-      "../browser/sync/test/integration/two_client_app_list_sync_test.cc",
       "../browser/sync/test/integration/two_client_autocomplete_sync_test.cc",
       "../browser/sync/test/integration/two_client_autofill_sync_test.cc",
       "../browser/sync/test/integration/two_client_bookmarks_sync_test.cc",
@@ -6136,9 +6105,11 @@
     }
     if (is_chromeos) {
       sources += [
+        "../browser/sync/test/integration/single_client_app_list_sync_test.cc",
         "../browser/sync/test/integration/single_client_arc_package_sync_test.cc",
         "../browser/sync/test/integration/single_client_os_preferences_sync_test.cc",
         "../browser/sync/test/integration/single_client_printers_sync_test.cc",
+        "../browser/sync/test/integration/two_client_app_list_sync_test.cc",
         "../browser/sync/test/integration/two_client_arc_package_sync_test.cc",
         "../browser/sync/test/integration/two_client_os_preferences_sync_test.cc",
         "../browser/sync/test/integration/two_client_printers_sync_test.cc",
@@ -6151,12 +6122,6 @@
     if (enable_basic_printing) {
       deps += [ "//printing" ]
     }
-    if (!enable_app_list) {
-      sources -= [
-        "../browser/sync/test/integration/single_client_app_list_sync_test.cc",
-        "../browser/sync/test/integration/two_client_app_list_sync_test.cc",
-      ]
-    }
   }
 
   test("sync_performance_tests") {
diff --git a/chrome/test/android/test_support/src/org/chromium/chrome/test_support/PaymentRequestTestBridge.java b/chrome/test/android/test_support/src/org/chromium/chrome/test_support/PaymentRequestTestBridge.java
index 8ad00e38..5348f48a 100644
--- a/chrome/test/android/test_support/src/org/chromium/chrome/test_support/PaymentRequestTestBridge.java
+++ b/chrome/test/android/test_support/src/org/chromium/chrome/test_support/PaymentRequestTestBridge.java
@@ -79,21 +79,21 @@
         private final long mOnCanMakePaymentReturnedPtr;
         private final long mOnHasEnrolledInstrumentCalledPtr;
         private final long mOnHasEnrolledInstrumentReturnedPtr;
-        private final long mOnShowInstrumentsReadyPtr;
+        private final long mOnShowAppsReadyPtr;
         private final long mOnNotSupportedErrorPtr;
         private final long mOnConnectionTerminatedPtr;
         private final long mOnAbortCalledPtr;
 
         PaymentRequestNativeObserverBridgeToNativeForTest(long onCanMakePaymentCalledPtr,
                 long onCanMakePaymentReturnedPtr, long onHasEnrolledInstrumentCalledPtr,
-                long onHasEnrolledInstrumentReturnedPtr, long onShowInstrumentsReadyPtr,
+                long onHasEnrolledInstrumentReturnedPtr, long onShowAppsReadyPtr,
                 long onNotSupportedErrorPtr, long onConnectionTerminatedPtr,
                 long onAbortCalledPtr) {
             mOnCanMakePaymentCalledPtr = onCanMakePaymentCalledPtr;
             mOnCanMakePaymentReturnedPtr = onCanMakePaymentReturnedPtr;
             mOnHasEnrolledInstrumentCalledPtr = onHasEnrolledInstrumentCalledPtr;
             mOnHasEnrolledInstrumentReturnedPtr = onHasEnrolledInstrumentReturnedPtr;
-            mOnShowInstrumentsReadyPtr = onShowInstrumentsReadyPtr;
+            mOnShowAppsReadyPtr = onShowAppsReadyPtr;
             mOnNotSupportedErrorPtr = onNotSupportedErrorPtr;
             mOnConnectionTerminatedPtr = onConnectionTerminatedPtr;
             mOnAbortCalledPtr = onAbortCalledPtr;
@@ -116,8 +116,8 @@
             nativeResolvePaymentRequestObserverCallback(mOnHasEnrolledInstrumentReturnedPtr);
         }
         @Override
-        public void onShowInstrumentsReady() {
-            nativeResolvePaymentRequestObserverCallback(mOnShowInstrumentsReadyPtr);
+        public void onShowAppsReady() {
+            nativeResolvePaymentRequestObserverCallback(mOnShowAppsReadyPtr);
         }
         @Override
         public void onNotSupportedError() {
@@ -150,12 +150,12 @@
     @CalledByNative
     public static void setUseNativeObserverForTest(long onCanMakePaymentCalledPtr,
             long onCanMakePaymentReturnedPtr, long onHasEnrolledInstrumentCalledPtr,
-            long onHasEnrolledInstrumentReturnedPtr, long onShowInstrumentsReadyPtr,
+            long onHasEnrolledInstrumentReturnedPtr, long onShowAppsReadyPtr,
             long onNotSupportedErrorPtr, long onConnectionTerminatedPtr, long onAbortCalledPtr) {
         PaymentRequestFactory.sNativeObserverForTest =
                 new PaymentRequestNativeObserverBridgeToNativeForTest(onCanMakePaymentCalledPtr,
                         onCanMakePaymentReturnedPtr, onHasEnrolledInstrumentCalledPtr,
-                        onHasEnrolledInstrumentReturnedPtr, onShowInstrumentsReadyPtr,
+                        onHasEnrolledInstrumentReturnedPtr, onShowAppsReadyPtr,
                         onNotSupportedErrorPtr, onConnectionTerminatedPtr, onAbortCalledPtr);
     }
 
diff --git a/chrome/test/chromedriver/chrome_launcher.cc b/chrome/test/chromedriver/chrome_launcher.cc
index a4fd71f..916ee37 100644
--- a/chrome/test/chromedriver/chrome_launcher.cc
+++ b/chrome/test/chromedriver/chrome_launcher.cc
@@ -1104,6 +1104,8 @@
 
 std::string GetTerminationReason(base::TerminationStatus status) {
   switch (status) {
+    case base::TERMINATION_STATUS_STILL_RUNNING:
+      return "still running";
     case base::TERMINATION_STATUS_NORMAL_TERMINATION:
       return "exited normally";
     case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
@@ -1112,14 +1114,26 @@
 #if defined(OS_CHROMEOS)
     case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
 #endif
+    case base::TERMINATION_STATUS_OOM:
       return "was killed";
+#if defined(OS_ANDROID)
+    case base::TERMINATION_STATUS_OOM_PROTECTED:
+      return "protected from oom";
+#endif
     case base::TERMINATION_STATUS_PROCESS_CRASHED:
       return "crashed";
     case base::TERMINATION_STATUS_LAUNCH_FAILED:
       return "failed to launch";
-    default:
-      return "unknown";
+#if defined(OS_WIN)
+    case base::TERMINATION_STATUS_INTEGRITY_FAILURE:
+      return "integrity failure";
+#endif
+    case base::TERMINATION_STATUS_MAX_ENUM:
+      NOTREACHED();
+      return "max enum";
   }
+  NOTREACHED() << "Unknown Termination Status.";
+  return "unknown";
 }
 
 }  // namespace internal
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 8d7cab83af..5789731 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -3976,6 +3976,12 @@
     "pref_mappings": [{ "pref": "browser.shared_clipboard_enabled" }]
   },
 
+  "ClickToCallEnabled" : {
+    "os": ["win", "linux", "mac", "chromeos"],
+    "test_policy": { "ClickToCallEnabled": false },
+    "pref_mappings": [{ "pref": "browser.click_to_call_enabled" }]
+  },
+
   "AudioSandboxEnabled": {
     "note": "This policy is used directly through the policy service instead of through a pref."
   },
diff --git a/chrome/test/data/webui/settings/settings_main_test.js b/chrome/test/data/webui/settings/settings_main_test.js
index 1fea8243..0b31a1d4 100644
--- a/chrome/test/data/webui/settings/settings_main_test.js
+++ b/chrome/test/data/webui/settings/settings_main_test.js
@@ -279,7 +279,7 @@
       Polymer.dom.flush();
 
       // Navigate to an "advanced" subpage.
-      settings.navigateTo(settings.routes.SITE_SETTINGS);
+      settings.navigateTo(settings.routes.LANGUAGES);
       Polymer.dom.flush();
       return assertAdvancedVisibilityAfterSearch('block');
     });
@@ -306,7 +306,7 @@
 
     // TODO(michaelpg): Move these to a new test for settings-basic-page.
     test('can collapse advanced on advanced section route', function() {
-      settings.navigateTo(settings.routes.PRIVACY);
+      settings.navigateTo(settings.routes.LANGUAGES);
       Polymer.dom.flush();
 
       const basicPage = settingsMain.$$('settings-basic-page');
@@ -338,7 +338,7 @@
     });
 
     test('navigating to a basic page does not collapse advanced', function() {
-      settings.navigateTo(settings.routes.PRIVACY);
+      settings.navigateTo(settings.routes.LANGUAGES);
       Polymer.dom.flush();
 
       assertToggleContainerVisible(true);
diff --git a/chrome/test/payments/payment_request_test_controller.h b/chrome/test/payments/payment_request_test_controller.h
index 2dd3fcb..5c4fe62 100644
--- a/chrome/test/payments/payment_request_test_controller.h
+++ b/chrome/test/payments/payment_request_test_controller.h
@@ -25,7 +25,7 @@
   virtual void OnCanMakePaymentReturned() {}
   virtual void OnHasEnrolledInstrumentCalled() {}
   virtual void OnHasEnrolledInstrumentReturned() {}
-  virtual void OnShowInstrumentsReady() {}
+  virtual void OnShowAppsReady() {}
   virtual void OnNotSupportedError() {}
   virtual void OnConnectionTerminated() {}
   virtual void OnAbortCalled() {}
@@ -58,7 +58,7 @@
   void OnCanMakePaymentReturned();
   void OnHasEnrolledInstrumentCalled();
   void OnHasEnrolledInstrumentReturned();
-  void OnShowInstrumentsReady();
+  void OnShowAppsReady();
   void OnNotSupportedError();
   void OnConnectionTerminated();
   void OnAbortCalled();
diff --git a/chrome/test/payments/payment_request_test_controller_android.cc b/chrome/test/payments/payment_request_test_controller_android.cc
index ea00a73..ef607503 100644
--- a/chrome/test/payments/payment_request_test_controller_android.cc
+++ b/chrome/test/payments/payment_request_test_controller_android.cc
@@ -31,7 +31,7 @@
       base::BindRepeating(
           &PaymentRequestTestController::OnHasEnrolledInstrumentReturned,
           base::Unretained(this)),
-      base::BindRepeating(&PaymentRequestTestController::OnShowInstrumentsReady,
+      base::BindRepeating(&PaymentRequestTestController::OnShowAppsReady,
                           base::Unretained(this)),
       base::BindRepeating(&PaymentRequestTestController::OnNotSupportedError,
                           base::Unretained(this)),
@@ -96,9 +96,9 @@
     observer_->OnHasEnrolledInstrumentReturned();
 }
 
-void PaymentRequestTestController::OnShowInstrumentsReady() {
+void PaymentRequestTestController::OnShowAppsReady() {
   if (observer_)
-    observer_->OnShowInstrumentsReady();
+    observer_->OnShowAppsReady();
 }
 void PaymentRequestTestController::OnNotSupportedError() {
   if (observer_)
diff --git a/chrome/test/payments/payment_request_test_controller_desktop.cc b/chrome/test/payments/payment_request_test_controller_desktop.cc
index b3415f9b..d95180e 100644
--- a/chrome/test/payments/payment_request_test_controller_desktop.cc
+++ b/chrome/test/payments/payment_request_test_controller_desktop.cc
@@ -58,9 +58,7 @@
   void OnHasEnrolledInstrumentReturned() override {
     controller_->OnHasEnrolledInstrumentReturned();
   }
-  void OnShowInstrumentsReady() override {
-    controller_->OnShowInstrumentsReady();
-  }
+  void OnShowAppsReady() override { controller_->OnShowAppsReady(); }
   void OnNotSupportedError() override { controller_->OnNotSupportedError(); }
   void OnConnectionTerminated() override {
     controller_->OnConnectionTerminated();
@@ -149,9 +147,9 @@
     observer_->OnHasEnrolledInstrumentReturned();
 }
 
-void PaymentRequestTestController::OnShowInstrumentsReady() {
+void PaymentRequestTestController::OnShowAppsReady() {
   if (observer_)
-    observer_->OnShowInstrumentsReady();
+    observer_->OnShowAppsReady();
 }
 
 void PaymentRequestTestController::OnNotSupportedError() {
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index c5d135e..2a4fa6b 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -101,6 +101,8 @@
     }
 
     if (is_mac) {
+      deps += [ "//chrome/updater/mac:updater_tests" ]
+
       data_deps = [
         "//chrome/updater/mac:updater",
       ]
diff --git a/chrome/updater/mac/BUILD.gn b/chrome/updater/mac/BUILD.gn
index cb2dcb59..487a19ef 100644
--- a/chrome/updater/mac/BUILD.gn
+++ b/chrome/updater/mac/BUILD.gn
@@ -14,6 +14,37 @@
   ]
 
   deps = [
+    ":network_fetcher_sources",
     "//chrome/updater:common",
   ]
 }
+
+source_set("network_fetcher_sources") {
+  sources = [
+    "net/network.h",
+    "net/network_fetcher.h",
+    "net/network_fetcher.mm",
+  ]
+
+  deps = [
+    "//base",
+    "//components/update_client",
+    "//net",
+  ]
+}
+
+source_set("updater_tests") {
+  testonly = true
+
+  sources = [
+    "net/network_unittest.mm",
+  ]
+
+  deps = [
+    ":network_fetcher_sources",
+    "//base/test:test_support",
+    "//net:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/updater/mac/net/network.h b/chrome/updater/mac/net/network.h
new file mode 100644
index 0000000..ef0b58e
--- /dev/null
+++ b/chrome/updater/mac/net/network.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_UPDATER_MAC_NET_NETWORK_H_
+#define CHROME_UPDATER_MAC_NET_NETWORK_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/network.h"
+
+namespace updater {
+
+class NetworkFetcherFactory : public update_client::NetworkFetcherFactory {
+ public:
+  NetworkFetcherFactory();
+
+  std::unique_ptr<update_client::NetworkFetcher> Create() const override;
+
+ protected:
+  ~NetworkFetcherFactory() override;
+
+ private:
+  THREAD_CHECKER(thread_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkFetcherFactory);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_MAC_NET_NETWORK_H_
diff --git a/chrome/updater/mac/net/network_fetcher.h b/chrome/updater/mac/net/network_fetcher.h
new file mode 100644
index 0000000..37d88ca9
--- /dev/null
+++ b/chrome/updater/mac/net/network_fetcher.h
@@ -0,0 +1,60 @@
+// 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_UPDATER_MAC_NET_NETWORK_FETCHER_H_
+#define CHROME_UPDATER_MAC_NET_NETWORK_FETCHER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/network.h"
+
+class GURL;
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace updater {
+
+class NetworkFetcher : public update_client::NetworkFetcher {
+ public:
+  NetworkFetcher();
+  NetworkFetcher& operator=(const NetworkFetcher&) = delete;
+  NetworkFetcher(const NetworkFetcher&) = delete;
+  ~NetworkFetcher() override;
+
+  // NetworkFetcher overrides.
+  void PostRequest(
+      const GURL& url,
+      const std::string& post_data,
+      const base::flat_map<std::string, std::string>& post_additional_headers,
+      update_client::NetworkFetcher::ResponseStartedCallback
+          response_started_callback,
+      update_client::NetworkFetcher::ProgressCallback progress_callback,
+      update_client::NetworkFetcher::PostRequestCompleteCallback
+          post_request_complete_callback) override;
+
+  void DownloadToFile(
+      const GURL& url,
+      const base::FilePath& file_path,
+      update_client::NetworkFetcher::ResponseStartedCallback
+          response_started_callback,
+      update_client::NetworkFetcher::ProgressCallback progress_callback,
+      update_client::NetworkFetcher::DownloadToFileCompleteCallback
+          download_to_file_complete_callback) override;
+
+ private:
+  THREAD_CHECKER(thread_checker_);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_MAC_NET_NETWORK_FETCHER_H_
diff --git a/chrome/updater/mac/net/network_fetcher.mm b/chrome/updater/mac/net/network_fetcher.mm
new file mode 100644
index 0000000..9e9656d
--- /dev/null
+++ b/chrome/updater/mac/net/network_fetcher.mm
@@ -0,0 +1,330 @@
+// 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/updater/mac/net/network_fetcher.h"
+
+#import <Foundation/Foundation.h>
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#import "base/mac/foundation_util.h"
+#import "base/mac/scoped_nsobject.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/updater/mac/net/network.h"
+#import "net/base/mac/url_conversions.h"
+#include "url/gurl.h"
+
+const NSString* kHeaderEtag = @"ETag";
+const NSString* kHeaderXRetryAfter = @"X-Retry-After";
+
+using ResponseStartedCallback =
+    update_client::NetworkFetcher::ResponseStartedCallback;
+using ProgressCallback = update_client::NetworkFetcher::ProgressCallback;
+using PostRequestCompleteCallback =
+    update_client::NetworkFetcher::PostRequestCompleteCallback;
+using DownloadToFileCompleteCallback =
+    update_client::NetworkFetcher::DownloadToFileCompleteCallback;
+
+@interface CHUpdaterNetworkController : NSObject <NSURLSessionDelegate>
+- (instancetype)initWithResponseStartedCallback:
+                    (ResponseStartedCallback)responseStartedCallback
+                               progressCallback:
+                                   (ProgressCallback)progressCallback;
+@end
+
+@implementation CHUpdaterNetworkController {
+ @protected
+  ResponseStartedCallback responseStartedCallback_;
+  ProgressCallback progressCallback_;
+}
+
+- (instancetype)initWithResponseStartedCallback:
+                    (ResponseStartedCallback)responseStartedCallback
+                               progressCallback:
+                                   (ProgressCallback)progressCallback {
+  if (self == [super init]) {
+    responseStartedCallback_ = std::move(responseStartedCallback);
+    progressCallback_ = progressCallback;
+  }
+  return self;
+}
+
+#pragma mark - NSURLSessionDelegate
+
+- (void)URLSession:(NSURLSession*)session
+                    task:(NSURLSessionTask*)task
+    didCompleteWithError:(NSError*)error {
+  if (error) {
+    DLOG(ERROR) << "NSURLSession error: " << error
+                << ". NSURLSession: " << session
+                << ". NSURLSessionTask: " << task;
+  }
+}
+@end
+
+@interface CHUpdaterNetworkDataDelegate
+    : CHUpdaterNetworkController <NSURLSessionDataDelegate>
+- (instancetype)
+    initWithResponseStartedCallback:
+        (ResponseStartedCallback)responseStartedCallback
+                   progressCallback:(ProgressCallback)progressCallback
+        postRequestCompleteCallback:
+            (PostRequestCompleteCallback)postRequestCompleteCallback;
+@end
+
+@implementation CHUpdaterNetworkDataDelegate {
+  PostRequestCompleteCallback postRequestCompleteCallback_;
+  base::scoped_nsobject<NSMutableData> downloadedData_;
+}
+
+- (instancetype)
+    initWithResponseStartedCallback:
+        (ResponseStartedCallback)responseStartedCallback
+                   progressCallback:(ProgressCallback)progressCallback
+        postRequestCompleteCallback:
+            (PostRequestCompleteCallback)postRequestCompleteCallback {
+  if (self ==
+      [super initWithResponseStartedCallback:std::move(responseStartedCallback)
+                            progressCallback:progressCallback]) {
+    postRequestCompleteCallback_ = std::move(postRequestCompleteCallback);
+  }
+  return self;
+}
+
+#pragma mark - NSURLSessionDataDelegate
+
+- (void)URLSession:(NSURLSession*)session
+          dataTask:(NSURLSessionDataTask*)dataTask
+    didReceiveData:(NSData*)data {
+  if (downloadedData_ == nil) {
+    downloadedData_.reset([[NSMutableData alloc] init]);
+  }
+  [downloadedData_ appendData:data];
+
+  int64_t current = 0;
+
+  if (dataTask.countOfBytesExpectedToReceive > 0) {
+    current = (dataTask.countOfBytesReceived * 100) /
+              dataTask.countOfBytesExpectedToReceive;
+  } else {
+    current = 100;
+  }
+  progressCallback_.Run(current);
+  [dataTask resume];
+}
+
+// Tells the delegate that the data task received the initial reply (headers)
+// from the server.
+- (void)URLSession:(NSURLSession*)session
+              dataTask:(NSURLSessionDataTask*)dataTask
+    didReceiveResponse:(NSURLResponse*)response
+     completionHandler:
+         (void (^)(NSURLSessionResponseDisposition))completionHandler {
+  std::move(responseStartedCallback_)
+      .Run([(NSHTTPURLResponse*)response statusCode],
+           dataTask.countOfBytesExpectedToReceive);
+  if (completionHandler) {
+    completionHandler(NSURLSessionResponseAllow);
+  }
+  [dataTask resume];
+}
+
+#pragma mark - NSURLSessionDelegate
+
+- (void)URLSession:(NSURLSession*)session
+                    task:(NSURLSessionTask*)task
+    didCompleteWithError:(NSError*)error {
+  [super URLSession:session task:task didCompleteWithError:error];
+
+  NSHTTPURLResponse* response = (NSHTTPURLResponse*)task.response;
+  NSDictionary* headers = response.allHeaderFields;
+  NSString* etag = @"";
+  if ([headers objectForKey:kHeaderEtag]) {
+    etag = [headers objectForKey:kHeaderEtag];
+  }
+  int64_t retryAfterResult = -1;
+  NSString* xRetryAfter = [headers objectForKey:kHeaderXRetryAfter];
+  if (xRetryAfter) {
+    retryAfterResult = [xRetryAfter intValue];
+  }
+
+  std::move(postRequestCompleteCallback_)
+      .Run(std::make_unique<std::string>(
+               base::SysNSStringToUTF8(response.description)),
+           response.statusCode, std::string(base::SysNSStringToUTF8(etag)),
+           retryAfterResult);
+}
+
+@end
+
+@interface CHUpdaterNetworkDownloadDelegate
+    : CHUpdaterNetworkController <NSURLSessionDownloadDelegate>
+- (instancetype)
+    initWithResponseStartedCallback:
+        (ResponseStartedCallback)responseStartedCallback
+                   progressCallback:(ProgressCallback)progressCallback
+                           filePath:(const base::FilePath&)filePath
+     downloadToFileCompleteCallback:
+         (DownloadToFileCompleteCallback)downloadToFileCompleteCallback;
+@end
+
+@implementation CHUpdaterNetworkDownloadDelegate {
+  base::FilePath filePath_;
+  DownloadToFileCompleteCallback downloadToFileCompleteCallback_;
+}
+
+- (instancetype)
+    initWithResponseStartedCallback:
+        (ResponseStartedCallback)responseStartedCallback
+                   progressCallback:(ProgressCallback)progressCallback
+                           filePath:(const base::FilePath&)filePath
+     downloadToFileCompleteCallback:
+         (DownloadToFileCompleteCallback)downloadToFileCompleteCallback {
+  if (self ==
+      [super initWithResponseStartedCallback:std::move(responseStartedCallback)
+                            progressCallback:progressCallback]) {
+    filePath_ = filePath;
+    downloadToFileCompleteCallback_ = std::move(downloadToFileCompleteCallback);
+  }
+  return self;
+}
+
+#pragma mark - NSURLSessionDownloadDelegate
+
+- (void)URLSession:(NSURLSession*)session
+             dataTask:(NSURLSessionDataTask*)dataTask
+    willCacheResponse:(NSCachedURLResponse*)proposedResponse
+    completionHandler:
+        (void (^)(NSCachedURLResponse* _Nullable))completionHandler {
+  completionHandler(NULL);
+}
+
+- (void)URLSession:(NSURLSession*)session
+                 downloadTask:(NSURLSessionDownloadTask*)downloadTask
+    didFinishDownloadingToURL:(NSURL*)location {
+  if (!location)
+    return;
+
+  const base::FilePath tempPath =
+      base::mac::NSStringToFilePath([location path]);
+  base::File::Error fileError;
+  if (!base::ReplaceFile(tempPath, filePath_, &fileError)) {
+    DLOG(ERROR)
+        << "Failed to move the downloaded file from the temporary location: "
+        << tempPath << "to: " << filePath_
+        << " Error: " << base::File::ErrorToString(fileError);
+  }
+}
+
+#pragma mark - NSURLSessionDelegate
+
+- (void)URLSession:(NSURLSession*)session
+                    task:(NSURLSessionTask*)task
+    didCompleteWithError:(NSError*)error {
+  [super URLSession:session task:task didCompleteWithError:error];
+
+  NSHTTPURLResponse* response = (NSHTTPURLResponse*)task.response;
+  NSURL* destination = base::mac::FilePathToNSURL(filePath_);
+  NSString* filePath = [destination path];
+  NSDictionary<NSFileAttributeKey, id>* attributes =
+      [[NSFileManager defaultManager] attributesOfItemAtPath:filePath
+                                                       error:nil];
+  NSNumber* fileSizeAttribute = attributes[NSFileSize];
+  int64_t fileSize = [fileSizeAttribute integerValue];
+  std::move(downloadToFileCompleteCallback_).Run(response.statusCode, fileSize);
+}
+
+@end
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace updater {
+
+NetworkFetcher::NetworkFetcher() {}
+
+NetworkFetcher::~NetworkFetcher() {}
+
+void NetworkFetcher::PostRequest(
+    const GURL& url,
+    const std::string& post_data,
+    const base::flat_map<std::string, std::string>& post_additional_headers,
+    ResponseStartedCallback response_started_callback,
+    ProgressCallback progress_callback,
+    PostRequestCompleteCallback post_request_complete_callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  base::scoped_nsobject<CHUpdaterNetworkDataDelegate> delegate(
+      [[CHUpdaterNetworkDataDelegate alloc]
+          initWithResponseStartedCallback:std::move(response_started_callback)
+                         progressCallback:progress_callback
+              postRequestCompleteCallback:std::move(
+                                              post_request_complete_callback)]);
+
+  NSURLSession* session =
+      [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration
+                                                 defaultSessionConfiguration]
+                                    delegate:delegate
+                               delegateQueue:nil];
+
+  base::scoped_nsobject<NSMutableURLRequest> urlRequest(
+      [[NSMutableURLRequest alloc] initWithURL:net::NSURLWithGURL(url)]);
+  [urlRequest setHTTPMethod:@"POST"];
+  [urlRequest setHTTPBody:[base::SysUTF8ToNSString(post_data)
+                              dataUsingEncoding:NSUTF8StringEncoding]];
+  VLOG(1) << "Posting data: " << post_data.c_str();
+
+  NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:urlRequest];
+  [dataTask resume];
+}
+
+void NetworkFetcher::DownloadToFile(
+    const GURL& url,
+    const base::FilePath& file_path,
+    ResponseStartedCallback response_started_callback,
+    ProgressCallback progress_callback,
+    DownloadToFileCompleteCallback download_to_file_complete_callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  base::scoped_nsobject<CHUpdaterNetworkDownloadDelegate> delegate(
+      [[CHUpdaterNetworkDownloadDelegate alloc]
+          initWithResponseStartedCallback:std::move(response_started_callback)
+                         progressCallback:progress_callback
+                                 filePath:file_path
+           downloadToFileCompleteCallback:
+               std::move(download_to_file_complete_callback)]);
+
+  NSURLSession* session =
+      [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration
+                                                 defaultSessionConfiguration]
+                                    delegate:delegate
+                               delegateQueue:nil];
+
+  base::scoped_nsobject<NSMutableURLRequest> urlRequest(
+      [[NSMutableURLRequest alloc] initWithURL:net::NSURLWithGURL(url)]);
+
+  NSURLSessionDownloadTask* downloadTask =
+      [session downloadTaskWithRequest:urlRequest];
+  [downloadTask resume];
+}
+
+NetworkFetcherFactory::NetworkFetcherFactory() = default;
+NetworkFetcherFactory::~NetworkFetcherFactory() = default;
+
+std::unique_ptr<update_client::NetworkFetcher> NetworkFetcherFactory::Create()
+    const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return std::make_unique<NetworkFetcher>();
+}
+
+}  // namespace updater
diff --git a/chrome/updater/mac/net/network_unittest.mm b/chrome/updater/mac/net/network_unittest.mm
new file mode 100644
index 0000000..d70b9b60b
--- /dev/null
+++ b/chrome/updater/mac/net/network_unittest.mm
@@ -0,0 +1,154 @@
+// 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/updater/mac/net/network.h"
+#include "chrome/updater/mac/net/network_fetcher.h"
+
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using ResponseStartedCallback =
+    update_client::NetworkFetcher::ResponseStartedCallback;
+using ProgressCallback = update_client::NetworkFetcher::ProgressCallback;
+using PostRequestCompleteCallback =
+    update_client::NetworkFetcher::PostRequestCompleteCallback;
+using DownloadToFileCompleteCallback =
+    update_client::NetworkFetcher::DownloadToFileCompleteCallback;
+
+namespace updater {
+ACTION_P(RunClosure, closure) {
+  closure.Run();
+}
+
+static base::FilePath testFilePath;
+
+class ChromeUpdaterNetworkMacTest : public ::testing::Test {
+ public:
+  ~ChromeUpdaterNetworkMacTest() override = default;
+
+#pragma mark - Callback Methods
+  void StartedCallback(int response_code, int64_t content_length) {
+    EXPECT_EQ(response_code, 200);
+  }
+
+  void ProgressCallback(int64_t current) {
+    EXPECT_GE(current, 0);
+    EXPECT_LE(current, 100);
+  }
+
+  void PostRequestCompleteCallback(std::unique_ptr<std::string> response_body,
+                                   int net_error,
+                                   const std::string& header_etag,
+                                   int64_t xheader_retry_after_sec) {
+    EXPECT_EQ(net_error, 200);
+    EXPECT_GT(header_etag.length(), 0u);
+    EXPECT_EQ(xheader_retry_after_sec, 67);
+    PostRequestCompleted();
+  }
+
+  void DownloadCallback(int net_error, int64_t content_size) {
+    EXPECT_EQ(net_error, 200);
+    EXPECT_GT(content_size, 0);
+    EXPECT_FALSE(testFilePath.empty());
+    EXPECT_TRUE(base::PathExists(testFilePath));
+    DownloadToFileCompleted();
+  }
+
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) {
+    auto http_response =
+        std::make_unique<net::test_server::BasicHttpResponse>();
+    http_response->set_code(net::HTTP_OK);
+    http_response->set_content("hello");
+    http_response->set_content_type("text/plain");
+    http_response->AddCustomHeader("X-Retry-After", "67");
+    http_response->AddCustomHeader("ETag", "Wfhw789h");
+    return http_response;
+  }
+
+  MOCK_METHOD0(DownloadToFileCompleted, void(void));
+  MOCK_METHOD0(PostRequestCompleted, void(void));
+
+  base::test::SingleThreadTaskEnvironment task_environment_;
+};
+
+#pragma mark - Test Methods
+TEST_F(ChromeUpdaterNetworkMacTest, NetworkFetcherMacHTTPFactory) {
+  base::RunLoop run_loop;
+  base::RepeatingClosure quit_closure = run_loop.QuitClosure();
+  auto fetcher = base::MakeRefCounted<NetworkFetcherFactory>()->Create();
+  quit_closure.Run();
+  run_loop.Run();
+  EXPECT_NE(nullptr, fetcher.get());
+}
+
+TEST_F(ChromeUpdaterNetworkMacTest, NetworkFetcherMacPostRequest) {
+  base::RunLoop run_loop;
+  base::RepeatingClosure quit_closure = run_loop.QuitClosure();
+  EXPECT_CALL(*this, PostRequestCompleted()).WillOnce(RunClosure(quit_closure));
+
+  auto fetcher = base::MakeRefCounted<NetworkFetcherFactory>()->Create();
+
+  net::EmbeddedTestServer test_server;
+  test_server.RegisterRequestHandler(base::Bind(
+      &ChromeUpdaterNetworkMacTest::HandleRequest, base::Unretained(this)));
+  ASSERT_TRUE(test_server.Start());
+  const GURL url = test_server.GetURL("/echo");
+
+  fetcher->PostRequest(
+      url, "", {},
+      base::BindOnce(&ChromeUpdaterNetworkMacTest::StartedCallback,
+                     base::Unretained(this)),
+      base::BindRepeating(&ChromeUpdaterNetworkMacTest::ProgressCallback,
+                          base::Unretained(this)),
+      base::BindOnce(&ChromeUpdaterNetworkMacTest::PostRequestCompleteCallback,
+                     base::Unretained(this)));
+
+  run_loop.Run();
+}
+
+TEST_F(ChromeUpdaterNetworkMacTest, NetworkFetcherMacDownloadToFile) {
+  base::RunLoop run_loop;
+  base::RepeatingClosure quit_closure = run_loop.QuitClosure();
+  EXPECT_CALL(*this, DownloadToFileCompleted())
+      .WillOnce(RunClosure(quit_closure));
+  auto fetcher = base::MakeRefCounted<NetworkFetcherFactory>()->Create();
+
+  net::EmbeddedTestServer test_server;
+  test_server.RegisterRequestHandler(base::Bind(
+      &ChromeUpdaterNetworkMacTest::HandleRequest, base::Unretained(this)));
+  ASSERT_TRUE(test_server.Start());
+  const GURL url = test_server.GetURL("/echo");
+
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  testFilePath =
+      temp_dir.GetPath().Append(FILE_PATH_LITERAL("downloaded_file"));
+
+  fetcher->DownloadToFile(
+      url, testFilePath,
+      base::BindOnce(&ChromeUpdaterNetworkMacTest::StartedCallback,
+                     base::Unretained(this)),
+      base::BindRepeating(&ChromeUpdaterNetworkMacTest::ProgressCallback,
+                          base::Unretained(this)),
+      base::BindOnce(&ChromeUpdaterNetworkMacTest::DownloadCallback,
+                     base::Unretained(this)));
+
+  run_loop.Run();
+}
+}  // namespace updater
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 3371115..3f49323 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -389,8 +389,14 @@
       "exo/cast_wm_helper.h",
       "exo/wayland_server_controller.cc",
       "exo/wayland_server_controller.h",
+      "webview/cast_app_controller.cc",
+      "webview/cast_app_controller.h",
+      "webview/cast_app_rpc_instance.cc",
+      "webview/cast_app_rpc_instance.h",
       "webview/js_channel_service.cc",
       "webview/js_channel_service.h",
+      "webview/platform_views_grpc_service.cc",
+      "webview/platform_views_grpc_service.h",
       "webview/platform_views_rpc_instance.cc",
       "webview/platform_views_rpc_instance.h",
       "webview/web_content_controller.cc",
@@ -410,6 +416,7 @@
     ]
     configs += [ "//third_party/grpc:grpc_config" ]
     deps += [
+      ":web_contents_provider",
       "//chromecast/browser/webview/proto",
       "//components/exo",
       "//components/exo/wayland",
@@ -577,3 +584,13 @@
     ]
   }
 }
+
+cast_source_set("web_contents_provider") {
+  sources = [
+    "webview/web_contents_provider.h",
+  ]
+
+  deps = [
+    "//content/public/browser",
+  ]
+}
diff --git a/chromecast/browser/webview/cast_app_controller.cc b/chromecast/browser/webview/cast_app_controller.cc
new file mode 100644
index 0000000..260c1d67
--- /dev/null
+++ b/chromecast/browser/webview/cast_app_controller.cc
@@ -0,0 +1,28 @@
+// 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 "chromecast/browser/webview/cast_app_controller.h"
+
+namespace chromecast {
+
+CastAppController::CastAppController(Client* client,
+                                     content::WebContents* contents)
+    : WebContentController(client), contents_(contents) {
+  std::unique_ptr<webview::WebviewResponse> response =
+      std::make_unique<webview::WebviewResponse>();
+  client->EnqueueSend(std::move(response));
+}
+
+CastAppController::~CastAppController() {}
+
+void CastAppController::Destroy() {
+  client_ = nullptr;
+  delete this;
+}
+
+content::WebContents* CastAppController::GetWebContents() {
+  return contents_;
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/webview/cast_app_controller.h b/chromecast/browser/webview/cast_app_controller.h
new file mode 100644
index 0000000..5883d9f0
--- /dev/null
+++ b/chromecast/browser/webview/cast_app_controller.h
@@ -0,0 +1,32 @@
+// 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 CHROMECAST_BROWSER_WEBVIEW_CAST_APP_CONTROLLER_H_
+#define CHROMECAST_BROWSER_WEBVIEW_CAST_APP_CONTROLLER_H_
+
+#include "chromecast/browser/webview/web_content_controller.h"
+
+#include "base/macros.h"
+
+namespace chromecast {
+
+class CastAppController : public WebContentController {
+ public:
+  CastAppController(Client* client, content::WebContents* contents);
+  ~CastAppController() override;
+
+  void Destroy() override;
+
+ protected:
+  content::WebContents* GetWebContents() override;
+
+ private:
+  content::WebContents* contents_;
+
+  DISALLOW_COPY_AND_ASSIGN(CastAppController);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_WEBVIEW_CAST_APP_CONTROLLER_H_
diff --git a/chromecast/browser/webview/cast_app_rpc_instance.cc b/chromecast/browser/webview/cast_app_rpc_instance.cc
new file mode 100644
index 0000000..7f61cd2
--- /dev/null
+++ b/chromecast/browser/webview/cast_app_rpc_instance.cc
@@ -0,0 +1,70 @@
+// 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 "chromecast/browser/webview/cast_app_rpc_instance.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "chromecast/browser/webview/cast_app_controller.h"
+#include "chromecast/browser/webview/platform_views_rpc_instance.h"
+#include "chromecast/browser/webview/web_contents_provider.h"
+#include "third_party/grpc/src/include/grpcpp/grpcpp.h"
+#include "third_party/grpc/src/include/grpcpp/security/server_credentials.h"
+#include "third_party/grpc/src/include/grpcpp/server.h"
+#include "third_party/grpc/src/include/grpcpp/server_builder.h"
+
+namespace chromecast {
+
+CastAppRpcInstance::CastAppRpcInstance(
+    webview::PlatformViewsService::AsyncService* service,
+    grpc::ServerCompletionQueue* cq,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    WebviewWindowManager* window_manager,
+    WebContentsProvider* web_contents_provider)
+    : PlatformViewsRpcInstance(cq, task_runner, window_manager),
+      service_(service),
+      web_contents_provider_(web_contents_provider) {
+  service_->RequestCreateCastAppWindowLink(&ctx_, &io_, cq_, cq_,
+                                           &init_callback_);
+}
+
+CastAppRpcInstance::~CastAppRpcInstance() {}
+
+void CastAppRpcInstance::CreateNewInstance() {
+  new CastAppRpcInstance(service_, cq_, task_runner_, window_manager_,
+                         web_contents_provider_);
+}
+
+bool CastAppRpcInstance::Initialize() {
+  if (request_->type_case() != webview::WebviewRequest::kAssociate)
+    return false;
+
+  // This needs to be done on a valid thread.
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&CastAppRpcInstance::CreateCastAppWindowLink,
+                                base::Unretained(this),
+                                request_->associate().platform_view_id(),
+                                request_->associate().app_window_id()));
+  return true;
+}
+
+void CastAppRpcInstance::CreateCastAppWindowLink(int platform_view_id,
+                                                 int app_window_id) {
+  app_id_ = platform_view_id;
+
+  content::WebContents* web_contents =
+      web_contents_provider_->GetWebContents(app_window_id);
+  Observe(web_contents);
+  controller_ = std::make_unique<CastAppController>(this, web_contents);
+  window_manager_->AddObserver(this);
+
+  // Begin reading again.
+  io_.Read(request_.get(), &read_callback_);
+}
+
+void CastAppRpcInstance::WebContentsDestroyed() {
+  controller_.reset();
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/webview/cast_app_rpc_instance.h b/chromecast/browser/webview/cast_app_rpc_instance.h
new file mode 100644
index 0000000..0530111
--- /dev/null
+++ b/chromecast/browser/webview/cast_app_rpc_instance.h
@@ -0,0 +1,40 @@
+// 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 CHROMECAST_BROWSER_WEBVIEW_CAST_APP_RPC_INSTANCE_H_
+#define CHROMECAST_BROWSER_WEBVIEW_CAST_APP_RPC_INSTANCE_H_
+
+#include "chromecast/browser/webview/platform_views_rpc_instance.h"
+
+#include "content/public/browser/web_contents_observer.h"
+
+namespace chromecast {
+class WebContentsProvider;
+
+class CastAppRpcInstance : public PlatformViewsRpcInstance,
+                           public content::WebContentsObserver {
+ public:
+  CastAppRpcInstance(webview::PlatformViewsService::AsyncService* service,
+                     grpc::ServerCompletionQueue* cq,
+                     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+                     WebviewWindowManager* window_manager,
+                     WebContentsProvider* web_contents_provider);
+  ~CastAppRpcInstance() override;
+
+ protected:
+  void CreateNewInstance() override;
+  bool Initialize() override;
+
+ private:
+  void CreateCastAppWindowLink(int platform_view_id, int app_window_id);
+  void WebContentsDestroyed() override;
+  webview::PlatformViewsService::AsyncService* service_;
+  WebContentsProvider* web_contents_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(CastAppRpcInstance);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_WEBVIEW_CAST_APP_RPC_INSTANCE_H_
diff --git a/chromecast/browser/webview/platform_views_grpc_service.cc b/chromecast/browser/webview/platform_views_grpc_service.cc
new file mode 100644
index 0000000..41b763d
--- /dev/null
+++ b/chromecast/browser/webview/platform_views_grpc_service.cc
@@ -0,0 +1,50 @@
+// 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 "chromecast/browser/webview/platform_views_grpc_service.h"
+
+#include "base/callback.h"
+#include "chromecast/browser/webview/cast_app_rpc_instance.h"
+#include "chromecast/browser/webview/web_contents_provider.h"
+#include "chromecast/browser/webview/webview_rpc_instance.h"
+#include "third_party/grpc/src/include/grpcpp/grpcpp.h"
+#include "third_party/grpc/src/include/grpcpp/security/server_credentials.h"
+#include "third_party/grpc/src/include/grpcpp/server_builder.h"
+
+namespace chromecast {
+
+PlatformViewsAsyncService::PlatformViewsAsyncService(
+    std::unique_ptr<webview::PlatformViewsService::AsyncService> service,
+    std::unique_ptr<grpc::ServerCompletionQueue> cq,
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+    WebContentsProvider* web_contents_provider)
+    : ui_task_runner_(std::move(ui_task_runner)),
+      cq_(std::move(cq)),
+      service_(std::move(service)),
+      window_manager_(nullptr),
+      web_contents_provider_(web_contents_provider) {
+  base::PlatformThread::Create(0, this, &rpc_thread_);
+}
+
+PlatformViewsAsyncService::~PlatformViewsAsyncService() {
+  base::PlatformThread::Join(rpc_thread_);
+}
+
+void PlatformViewsAsyncService::ThreadMain() {
+  base::PlatformThread::SetName("CastPlatformViewsGrpcMessagePump");
+
+  void* tag;
+  bool ok;
+  // These self-delete.
+  new CastAppRpcInstance(service_.get(), cq_.get(), ui_task_runner_,
+                         &window_manager_, web_contents_provider_);
+  new WebviewRpcInstance(service_.get(), cq_.get(), ui_task_runner_,
+                         &window_manager_);
+  // This thread is joined when this service is destroyed.
+  while (cq_->Next(&tag, &ok)) {
+    reinterpret_cast<GrpcCallback*>(tag)->Run(ok);
+  }
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/webview/platform_views_grpc_service.h b/chromecast/browser/webview/platform_views_grpc_service.h
new file mode 100644
index 0000000..65904266
--- /dev/null
+++ b/chromecast/browser/webview/platform_views_grpc_service.h
@@ -0,0 +1,51 @@
+// 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 CHROMECAST_BROWSER_WEBVIEW_PLATFORM_VIEWS_GRPC_SERVICE_H_
+#define CHROMECAST_BROWSER_WEBVIEW_PLATFORM_VIEWS_GRPC_SERVICE_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chromecast/browser/webview/proto/webview.grpc.pb.h"
+#include "chromecast/browser/webview/webview_window_manager.h"
+#include "third_party/grpc/src/include/grpcpp/server.h"
+
+namespace chromecast {
+class WebContentsProvider;
+// This is a service that provides a GRPC interface to create and control
+// webviews, as well as control cast apps. See the proto file for commands.
+class PlatformViewsAsyncService : public base::PlatformThread::Delegate {
+ public:
+  PlatformViewsAsyncService(
+      std::unique_ptr<webview::PlatformViewsService::AsyncService> service,
+      std::unique_ptr<grpc::ServerCompletionQueue> cq,
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+      WebContentsProvider* web_contents_provider);
+  ~PlatformViewsAsyncService() override;
+
+ private:
+  void ThreadMain() override;
+
+  // Separate thread to run the gRPC completion queue on.
+  base::PlatformThreadHandle rpc_thread_;
+
+  // Requests need to be posted back to the browser main UI thread to manage
+  // Webview state.
+  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+
+  std::unique_ptr<grpc::ServerCompletionQueue> cq_;
+  std::unique_ptr<webview::PlatformViewsService::AsyncService> service_;
+
+  WebviewWindowManager window_manager_;
+  WebContentsProvider* web_contents_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformViewsAsyncService);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_WEBVIEW_PLATFORM_VIEWS_GRPC_SERVICE_H_
diff --git a/chromecast/browser/webview/web_contents_provider.h b/chromecast/browser/webview/web_contents_provider.h
new file mode 100644
index 0000000..55bc7fd
--- /dev/null
+++ b/chromecast/browser/webview/web_contents_provider.h
@@ -0,0 +1,20 @@
+// 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 CHROMECAST_BROWSER_WEBVIEW_WEB_CONTENTS_PROVIDER_H_
+#define CHROMECAST_BROWSER_WEBVIEW_WEB_CONTENTS_PROVIDER_H_
+
+#include "content/public/browser/web_contents.h"
+
+namespace chromecast {
+
+// Class that provides WebContents when given a window ID.
+class WebContentsProvider {
+ public:
+  virtual content::WebContents* GetWebContents(int window_id) = 0;
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_WEBVIEW_WEB_CONTENTS_PROVIDER_H_
diff --git a/chromecast/browser/webview/webview_rpc_instance.cc b/chromecast/browser/webview/webview_rpc_instance.cc
index cabd3c7..4e0cfaf2 100644
--- a/chromecast/browser/webview/webview_rpc_instance.cc
+++ b/chromecast/browser/webview/webview_rpc_instance.cc
@@ -17,20 +17,40 @@
 
 namespace chromecast {
 
+// TODO(sagallea): Remove this when ready to deprecate WebviewService.
 WebviewRpcInstance::WebviewRpcInstance(
     webview::WebviewService::AsyncService* service,
     grpc::ServerCompletionQueue* cq,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     WebviewWindowManager* window_manager)
     : PlatformViewsRpcInstance(cq, task_runner, window_manager),
-      service_(service) {
-  service_->RequestCreateWebview(&ctx_, &io_, cq_, cq_, &init_callback_);
+      webview_service_(service),
+      platform_views_service_(nullptr) {
+  webview_service_->RequestCreateWebview(&ctx_, &io_, cq_, cq_,
+                                         &init_callback_);
+}
+
+WebviewRpcInstance::WebviewRpcInstance(
+    webview::PlatformViewsService::AsyncService* service,
+    grpc::ServerCompletionQueue* cq,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    WebviewWindowManager* window_manager)
+    : PlatformViewsRpcInstance(cq, task_runner, window_manager),
+      webview_service_(nullptr),
+      platform_views_service_(service) {
+  platform_views_service_->RequestCreateWebview(&ctx_, &io_, cq_, cq_,
+                                                &init_callback_);
 }
 
 WebviewRpcInstance::~WebviewRpcInstance() {}
 
 void WebviewRpcInstance::CreateNewInstance() {
-  new WebviewRpcInstance(service_, cq_, task_runner_, window_manager_);
+  if (webview_service_)
+    new WebviewRpcInstance(webview_service_, cq_, task_runner_,
+                           window_manager_);
+  else
+    new WebviewRpcInstance(platform_views_service_, cq_, task_runner_,
+                           window_manager_);
 }
 
 bool WebviewRpcInstance::Initialize() {
diff --git a/chromecast/browser/webview/webview_rpc_instance.h b/chromecast/browser/webview/webview_rpc_instance.h
index b60c3bd7..7d30ecb2 100644
--- a/chromecast/browser/webview/webview_rpc_instance.h
+++ b/chromecast/browser/webview/webview_rpc_instance.h
@@ -15,6 +15,10 @@
                      grpc::ServerCompletionQueue* cq,
                      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
                      WebviewWindowManager* window_manager);
+  WebviewRpcInstance(webview::PlatformViewsService::AsyncService* service,
+                     grpc::ServerCompletionQueue* cq,
+                     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+                     WebviewWindowManager* window_manager);
   ~WebviewRpcInstance() override;
 
  protected:
@@ -23,7 +27,8 @@
 
  private:
   void CreateWebview(int app_id, int window_id);
-  webview::WebviewService::AsyncService* service_;
+  webview::WebviewService::AsyncService* webview_service_;
+  webview::PlatformViewsService::AsyncService* platform_views_service_;
 
   DISALLOW_COPY_AND_ASSIGN(WebviewRpcInstance);
 };
diff --git a/chromeos/components/BUILD.gn b/chromeos/components/BUILD.gn
index 992e381..4cd12e62 100644
--- a/chromeos/components/BUILD.gn
+++ b/chromeos/components/BUILD.gn
@@ -31,7 +31,9 @@
 }
 
 group("closure_compile") {
+  testonly = true
   deps = [
+    "//chromeos/components/media_app_ui:closure_compile",
     "//chromeos/components/multidevice/debug_webui/resources:closure_compile",
   ]
 }
diff --git a/chromeos/components/media_app_ui/BUILD.gn b/chromeos/components/media_app_ui/BUILD.gn
index 65a30ed..a2d240f 100644
--- a/chromeos/components/media_app_ui/BUILD.gn
+++ b/chromeos/components/media_app_ui/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//chrome/test/base/js2gtest.gni")
+import("//third_party/closure_compiler/compile_js.gni")
 
 assert(is_chromeos, "Media App is Chrome OS only")
 
@@ -34,6 +35,67 @@
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
   deps = [
+    ":browser_test_support",
     "//chromeos/constants",
   ]
 }
+
+source_set("browser_test_support") {
+  testonly = true
+  sources = [
+    "test/media_app_ui_browsertest.cc",
+    "test/media_app_ui_browsertest.h",
+  ]
+
+  deps = [
+    ":media_app_ui",
+    "//base/test:test_support",
+    "//chrome/test:test_support_ui",
+    "//chromeos/constants:constants",
+    "//content/test:test_support",
+  ]
+
+  data = [
+    "test/driver.js",
+    "test/driver_api.js",
+    "test/guest_query_receiver.js",
+  ]
+}
+
+js_type_check("closure_compile") {
+  testonly = true
+  closure_flags = default_closure_args + [ "jscomp_error=strictCheckTypes" ]
+  deps = [
+    ":test_driver_api_js",
+    ":test_driver_js",
+    ":test_guest_query_receiver_js",
+  ]
+}
+
+js_library("test_driver_api_js") {
+  testonly = true
+  sources = [
+    "test/driver_api.js",
+  ]
+}
+
+js_library("test_guest_query_receiver_js") {
+  testonly = true
+  sources = [
+    "test/guest_query_receiver.js",
+  ]
+  deps = [
+    ":test_driver_api_js",
+  ]
+}
+
+js_library("test_driver_js") {
+  testonly = true
+  sources = [
+    "test/driver.js",
+  ]
+  deps = [
+    ":test_driver_api_js",
+    "//ui/webui/resources/js:assert",
+  ]
+}
diff --git a/chromeos/components/media_app_ui/resources/mock/js/app_main.js b/chromeos/components/media_app_ui/resources/mock/js/app_main.js
index 53aaef0..e2fab98 100644
--- a/chromeos/components/media_app_ui/resources/mock/js/app_main.js
+++ b/chromeos/components/media_app_ui/resources/mock/js/app_main.js
@@ -7,3 +7,14 @@
  * Provides a mock of http://go/media-app/index.ts which is pre-built and
  * brought in via DEPS to ../../app/js/app_main.js. Runs in an isolated guest.
  */
+
+/** A mock app used for testing when src-internal is not available. */
+class BacklightApp extends HTMLElement {}
+
+window.customElements.define('backlight-app', BacklightApp);
+
+document.addEventListener('DOMContentLoaded', () => {
+  // The "real" app first loads translations for populating strings in the app
+  // for the initial load, then does this.
+  document.body.appendChild(new BacklightApp());
+});
diff --git a/chromeos/components/media_app_ui/test/DEPS b/chromeos/components/media_app_ui/test/DEPS
index ac5677f9..d7ebe649 100644
--- a/chromeos/components/media_app_ui/test/DEPS
+++ b/chromeos/components/media_app_ui/test/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   # Tests run in browser_tests, so can access things under chrome/test/base*.
   "+chrome/test/base",
+  "+content/public/test",
 ]
diff --git a/chromeos/components/media_app_ui/test/driver.js b/chromeos/components/media_app_ui/test/driver.js
new file mode 100644
index 0000000..c66597f
--- /dev/null
+++ b/chromeos/components/media_app_ui/test/driver.js
@@ -0,0 +1,71 @@
+// 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.
+
+/** Host-side of web-driver like controller for sandboxed guest frames. */
+class GuestDriver {
+  /** @param {string} origin */
+  constructor(origin) {
+    this.origin = origin;
+
+    /** @type {Array<MessageEvent<TestMessageResponseData>>} */
+    this.testMessageQueue = [];
+
+    /** @type {?function(MessageEvent<TestMessageResponseData>)} */
+    this.testMessageWaiter = null;
+
+    this.messageListener = (/** Event */ event) => {
+      const testEvent =
+          /** @type{MessageEvent<TestMessageResponseData>} */ (event);
+
+      console.log('Event from guest: ' + JSON.stringify(testEvent.data));
+      if (this.testMessageWaiter) {
+        this.testMessageWaiter(testEvent);
+        this.testMessageWaiter = null;
+      } else {
+        this.testMessageQueue.push(testEvent);
+      }
+    };
+
+    window.addEventListener('message', this.messageListener);
+  }
+
+  tearDown() {
+    window.removeEventListener('message', this.messageListener);
+  }
+
+  /**
+   * Returns the next message from the guest.
+   * @return {Promise<MessageEvent<TestMessageResponseData>>}
+   */
+  popTestMessage() {
+    if (this.testMessageQueue.length > 0) {
+      const front = this.testMessageQueue[0];
+      this.testMessageQueue.shift();
+      return Promise.resolve(front);
+    }
+    return new Promise(resolve => {
+      this.testMessageWaiter = resolve;
+    });
+  }
+
+  /**
+   * Sends a query to the guest that repeatedly runs a query selector until
+   * it returns an element.
+   *
+   * @param {string} query the querySelector to run in the guest.
+   * @param {?string} opt_property a property to request on the found element.
+   * @return Promise<string> JSON.stringify()'d value of the property, or
+   *   tagName if unspecified.
+   */
+  async waitForElementInGuest(query, opt_property) {
+    const frame = assertInstanceof(
+        document.querySelector(`iframe[src^="${this.origin}"]`),
+        HTMLIFrameElement);
+    /** @type{TestMessageQueryData} */
+    const message = {testQuery: query, property: opt_property};
+    frame.contentWindow.postMessage(message, this.origin);
+    const event = await this.popTestMessage();
+    return event.data.testQueryResult;
+  }
+}
diff --git a/chromeos/components/media_app_ui/test/driver_api.js b/chromeos/components/media_app_ui/test/driver_api.js
new file mode 100644
index 0000000..03254301
--- /dev/null
+++ b/chromeos/components/media_app_ui/test/driver_api.js
@@ -0,0 +1,9 @@
+// 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.
+
+/** @typedef {{testQueryResult: string}} */
+var TestMessageResponseData;
+
+/** @typedef {{testQuery: string, property: ?string}} */
+var TestMessageQueryData;
diff --git a/chromeos/components/media_app_ui/test/guest_query_receiver.js b/chromeos/components/media_app_ui/test/guest_query_receiver.js
new file mode 100644
index 0000000..12f22fd4
--- /dev/null
+++ b/chromeos/components/media_app_ui/test/guest_query_receiver.js
@@ -0,0 +1,53 @@
+// 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.
+
+/**
+ * Repeatedly runs a query selector until it finds an element.
+ *
+ * @param {string} query
+ * @return {Promise<Element>}
+ */
+function waitForNode(query) {
+  const node = document.body;
+  const existingElement = node.querySelector(query);
+  if (existingElement) {
+    return Promise.resolve(existingElement);
+  }
+  console.log('Waiting for ' + query);
+  return new Promise(resolve => {
+    const observer = new MutationObserver((mutationList, observer) => {
+      const element = node.querySelector(query);
+      if (element) {
+        resolve(element);
+        observer.disconnect();
+      }
+    });
+    observer.observe(node, {childList: true, subtree: true});
+  });
+}
+
+/**
+ * Acts on received TestMessageQueryData.
+ *
+ * @param {MessageEvent<TestMessageQueryData>} event
+ */
+async function runTestQuery(event) {
+  const data = event.data;
+  const element = await waitForNode(data.testQuery);
+  const result =
+      data.property ? JSON.stringify(element[data.property]) : element.tagName;
+  const response = {testQueryResult: result};
+  event.source.postMessage(response, event.origin);
+}
+
+function receiveTestMessage(/** Event */ e) {
+  const event = /** @type{MessageEvent<TestMessageQueryData>} */ (e);
+  if (event.data.testQuery) {
+    runTestQuery(event);
+  }
+}
+
+window.addEventListener('message', receiveTestMessage, false);
+
+//# sourceURL=guest_query_reciever.js
diff --git a/chromeos/components/media_app_ui/test/media_app_ui_browsertest.cc b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.cc
new file mode 100644
index 0000000..3829bae7e
--- /dev/null
+++ b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.cc
@@ -0,0 +1,65 @@
+// 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 "chromeos/components/media_app_ui/test/media_app_ui_browsertest.h"
+
+#include "base/base_paths.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/threading/thread_restrictions.h"
+#include "chromeos/components/media_app_ui/url_constants.h"
+#include "chromeos/constants/chromeos_features.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Path to the JS that is injected into the guest frame when it navigates.
+constexpr char kTestScriptPath[] =
+    "chromeos/components/media_app_ui/test/guest_query_receiver.js";
+
+}  // namespace
+
+class MediaAppUiBrowserTest::TestCodeInjector
+    : public content::TestNavigationObserver {
+ public:
+  TestCodeInjector()
+      : TestNavigationObserver(GURL(chromeos::kChromeUIMediaAppURL)) {
+    WatchExistingWebContents();
+    StartWatchingNewWebContents();
+  }
+
+  // TestNavigationObserver:
+  void OnDidFinishNavigation(
+      content::NavigationHandle* navigation_handle) override {
+    if (navigation_handle->GetURL().GetOrigin() !=
+        GURL(chromeos::kChromeUIMediaAppGuestURL))
+      return;
+
+    base::FilePath source_root;
+    ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root));
+    const auto script_path = source_root.AppendASCII(kTestScriptPath);
+
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    std::string injected_content;
+    ASSERT_TRUE(base::ReadFileToString(script_path, &injected_content));
+
+    auto* render_frame_host = navigation_handle->GetRenderFrameHost();
+
+    // Use ExecuteScript(), not ExecJs(), because of Content Security Policy
+    // directive: "script-src chrome://resources 'self'"
+    ASSERT_TRUE(content::ExecuteScript(render_frame_host, injected_content));
+    TestNavigationObserver::OnDidFinishNavigation(navigation_handle);
+  }
+};
+
+MediaAppUiBrowserTest::MediaAppUiBrowserTest() = default;
+
+MediaAppUiBrowserTest::~MediaAppUiBrowserTest() = default;
+
+void MediaAppUiBrowserTest::SetUpOnMainThread() {
+  injector_ = std::make_unique<TestCodeInjector>();
+  MojoWebUIBrowserTest::SetUpOnMainThread();
+}
diff --git a/chromeos/components/media_app_ui/test/media_app_ui_browsertest.h b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.h
new file mode 100644
index 0000000..b194831
--- /dev/null
+++ b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.h
@@ -0,0 +1,28 @@
+// 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 CHROMEOS_COMPONENTS_MEDIA_APP_UI_TEST_MEDIA_APP_UI_BROWSERTEST_H_
+#define CHROMEOS_COMPONENTS_MEDIA_APP_UI_TEST_MEDIA_APP_UI_BROWSERTEST_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/test/base/mojo_web_ui_browser_test.h"
+
+class MediaAppUiBrowserTest : public MojoWebUIBrowserTest {
+ public:
+  MediaAppUiBrowserTest();
+  ~MediaAppUiBrowserTest() override;
+
+  // MojoWebUIBrowserTest:
+  void SetUpOnMainThread() override;
+
+ private:
+  class TestCodeInjector;
+  std::unique_ptr<TestCodeInjector> injector_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaAppUiBrowserTest);
+};
+
+#endif  // CHROMEOS_COMPONENTS_MEDIA_APP_UI_TEST_MEDIA_APP_UI_BROWSERTEST_H_
diff --git a/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js
index b4f93bd..418a7a1 100644
--- a/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js
+++ b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js
@@ -7,9 +7,12 @@
  */
 
 GEN('#include "chromeos/constants/chromeos_features.h"');
+GEN('#include "chromeos/components/media_app_ui/test/media_app_ui_browsertest.h"');
 
 const HOST_ORIGIN = 'chrome://media-app';
-const GUEST_SRC = 'chrome://media-app-guest/app.html';
+const GUEST_ORIGIN = 'chrome://media-app-guest';
+
+let driver = null;
 
 var MediaAppUIBrowserTest = class extends testing.Test {
   /** @override */
@@ -18,6 +21,20 @@
   }
 
   /** @override */
+  get extraLibraries() {
+    return [
+      ...super.extraLibraries,
+      '//ui/webui/resources/js/assert.js',
+      '//chromeos/components/media_app_ui/test/driver.js',
+    ];
+  }
+
+  /** @override */
+  get isAsync() {
+    return true;
+  }
+
+  /** @override */
   get featureList() {
     return {enabled: ['chromeos::features::kMediaApp']};
   }
@@ -26,6 +43,22 @@
   get runAccessibilityChecks() {
     return false;
   }
+
+  /** @override */
+  get typedefCppFixture() {
+    return 'MediaAppUiBrowserTest';
+  }
+
+  /** @override */
+  setUp() {
+    super.setUp();
+    driver = new GuestDriver(GUEST_ORIGIN);
+  }
+
+  /** @override */
+  tearDown() {
+    driver.tearDown();
+  }
 };
 
 // Tests that chrome://media-app is allowed to frame chrome://media-app-guest.
@@ -35,14 +68,13 @@
 // This test also fails if the guest renderer is terminated, e.g., due to webui
 // performing bad IPC such as network requests (failure detected in
 // content/public/test/no_renderer_crashes_assertion.cc).
-TEST_F('MediaAppUIBrowserTest', 'GuestCanLoad', () => {
+TEST_F('MediaAppUIBrowserTest', 'GuestCanLoad', async () => {
   const guest = document.querySelector('iframe');
+  const app = await driver.waitForElementInGuest('backlight-app', 'tagName');
 
   assertEquals(document.location.origin, HOST_ORIGIN);
-  assertEquals(guest.src, GUEST_SRC);
-});
+  assertEquals(guest.src, GUEST_ORIGIN + '/app.html');
+  assertEquals(app, '"BACKLIGHT-APP"');
 
-// TODO(crbug/996088): Add Tests that inspect the guest itself. Until the guest
-// listens for postMessage calls, we can't test for anything interesting due to
-// CORS (i.e. "Blocked a frame with origin chrome://media-app from accessing a
-// cross-origin frame.").
+  testDone();
+});
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index 42cb04da..7e7c898 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -72,6 +72,10 @@
 // Signals the availability of the ARC instance on this device.
 const char kArcAvailable[] = "arc-available";
 
+// A JSON dictionary whose content is the same as cros config's
+// /arc/build-properties.
+const char kArcBuildProperties[] = "arc-build-properties";
+
 // Flag that forces ARC data be cleaned on each start.
 const char kArcDataCleanupOnStart[] = "arc-data-cleanup-on-start";
 
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index 8bb5af0..a04643c 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -35,6 +35,7 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAppOemManifestFile[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcAvailability[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcAvailable[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcBuildProperties[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcDataCleanupOnStart[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcDisableAppSync[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 3bc5a30..83e75f4 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -219,6 +219,8 @@
     "session/arc_upgrade_params.h",
     "session/arc_vm_client_adapter.cc",
     "session/arc_vm_client_adapter.h",
+    "session/arc_vm_client_adapter_util.cc",
+    "session/arc_vm_client_adapter_util.h",
   ]
 
   deps = [
@@ -366,6 +368,7 @@
     "session/arc_session_impl_unittest.cc",
     "session/arc_session_runner_unittest.cc",
     "session/arc_vm_client_adapter_unittest.cc",
+    "session/arc_vm_client_adapter_util_unittest.cc",
     "timer/arc_timer_bridge_unittest.cc",
     "wake_lock/arc_wake_lock_bridge_unittest.cc",
   ]
diff --git a/components/arc/session/arc_vm_client_adapter_util.cc b/components/arc/session/arc_vm_client_adapter_util.cc
new file mode 100644
index 0000000..262c5f6
--- /dev/null
+++ b/components/arc/session/arc_vm_client_adapter_util.cc
@@ -0,0 +1,276 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/arc/session/arc_vm_client_adapter_util.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/process/launch.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "chromeos/constants/chromeos_switches.h"
+
+namespace brillo {
+// This is to use the platform2 code without any modifications.
+using CrosConfigInterface = arc::CrosConfig;
+}  // namespace brillo
+
+namespace arc {
+namespace {
+
+// The path in the chromeos-config database where Android properties will be
+// looked up.
+constexpr char kCrosConfigPropertiesPath[] = "/arc/build-properties";
+
+// Android property name used to store the board name.
+constexpr char kBoardPropertyPrefix[] = "ro.product.board=";
+
+// Android property name for custom key used for Play Auto Install selection.
+constexpr char kOEMKey1PropertyPrefix[] = "ro.oem.key1=";
+
+// Configuration property name of an optional string that contains a comma-
+// separated list of regions to include in the OEM key property.
+constexpr char kPAIRegionsPropertyName[] = "pai-regions";
+
+// Maximum length of an Android property value.
+constexpr int kAndroidMaxPropertyLength = 91;
+
+// The following 4 functions as well as the constants above are the _exact_ copy
+// of the ones in platform2/arc/setup/arc_setup_util.cc. Do not modify the
+// implementation directly here. Instead, modify it in platform2 with proper
+// unit tests, then roll the change into Chromium.
+// TODO(yusukes): Once we stop supporting the container, move the unit tests
+// to Chromium and delete platform2/arc/setup/.
+
+bool FindProperty(const std::string& line_prefix_to_find,
+                  std::string* out_prop,
+                  const std::string& line) {
+  if (base::StartsWith(line, line_prefix_to_find,
+                       base::CompareCase::SENSITIVE)) {
+    *out_prop = line.substr(line_prefix_to_find.length());
+    return true;
+  }
+  return false;
+}
+
+bool TruncateAndroidProperty(const std::string& line, std::string* truncated) {
+  // If line looks like key=value, cut value down to the max length of an
+  // Android property.  Build fingerprint needs special handling to preserve the
+  // trailing dev-keys indicator, but other properties can just be truncated.
+  size_t eq_pos = line.find('=');
+  if (eq_pos == std::string::npos) {
+    *truncated = line;
+    return true;
+  }
+
+  std::string val = line.substr(eq_pos + 1);
+  base::TrimWhitespaceASCII(val, base::TRIM_ALL, &val);
+  if (val.length() <= kAndroidMaxPropertyLength) {
+    *truncated = line;
+    return true;
+  }
+
+  const std::string key = line.substr(0, eq_pos);
+  LOG(WARNING) << "Truncating property " << key << " value: " << val;
+  if (key == "ro.bootimage.build.fingerprint" &&
+      base::EndsWith(val, "/dev-keys", base::CompareCase::SENSITIVE)) {
+    // Typical format is brand/product/device/.../dev-keys.  We want to remove
+    // characters from product and device to get below the length limit.
+    // Assume device has the format {product}_cheets.
+    std::vector<std::string> fields =
+        base::SplitString(val, "/", base::WhitespaceHandling::KEEP_WHITESPACE,
+                          base::SplitResult::SPLIT_WANT_ALL);
+    if (fields.size() < 5) {
+      LOG(ERROR) << "Invalid build fingerprint: " << val;
+      return false;
+    }
+
+    size_t remove_chars = (val.length() - kAndroidMaxPropertyLength + 1) / 2;
+    if (fields[1].length() <= remove_chars) {
+      LOG(ERROR) << "Unable to remove " << remove_chars << " characters from "
+                 << fields[1];
+      return false;
+    }
+    fields[1] = fields[1].substr(0, fields[1].length() - remove_chars);
+    fields[2] = fields[1] + "_cheets";
+    val = base::JoinString(fields, "/");
+  } else {
+    val = val.substr(0, kAndroidMaxPropertyLength);
+  }
+
+  *truncated = key + "=" + val;
+  return true;
+}
+
+std::string ComputeOEMKey(brillo::CrosConfigInterface* config,
+                          const std::string& board) {
+  std::string regions;
+  if (!config->GetString(kCrosConfigPropertiesPath, kPAIRegionsPropertyName,
+                         &regions)) {
+    // No region list found, just use the board name as before.
+    return board;
+  }
+
+  std::string region_code;
+  if (!base::GetAppOutput({"cros_region_data", "region_code"}, &region_code)) {
+    LOG(WARNING) << "Failed to get region code";
+    return board;
+  }
+
+  // Remove trailing newline.
+  region_code.erase(std::remove(region_code.begin(), region_code.end(), '\n'),
+                    region_code.end());
+
+  // Allow wildcard configuration to indicate that all regions should be
+  // included.
+  if (regions.compare("*") == 0 && region_code.length() >= 2)
+    return board + "_" + region_code;
+
+  // Check to see if region code is in the list of regions that should be
+  // included in the property.
+  std::vector<std::string> region_vector =
+      base::SplitString(regions, ",", base::WhitespaceHandling::TRIM_WHITESPACE,
+                        base::SplitResult::SPLIT_WANT_NONEMPTY);
+  for (const auto& region : region_vector) {
+    if (region_code.compare(region) == 0)
+      return board + "_" + region_code;
+  }
+
+  return board;
+}
+
+bool ExpandPropertyContents(const std::string& content,
+                            brillo::CrosConfigInterface* config,
+                            std::string* expanded_content) {
+  const std::vector<std::string> lines = base::SplitString(
+      content, "\n", base::WhitespaceHandling::KEEP_WHITESPACE,
+      base::SplitResult::SPLIT_WANT_ALL);
+
+  std::string new_properties;
+  for (std::string line : lines) {
+    // First expand {property} substitutions in the string.  The insertions
+    // may contain substitutions of their own, so we need to repeat until
+    // nothing more is found.
+    bool inserted;
+    do {
+      inserted = false;
+      size_t match_start = line.find('{');
+      size_t prev_match = 0;  // 1 char past the end of the previous {} match.
+      std::string expanded;
+      // Find all of the {} matches on the line.
+      while (match_start != std::string::npos) {
+        expanded += line.substr(prev_match, match_start - prev_match);
+
+        size_t match_end = line.find('}', match_start);
+        if (match_end == std::string::npos) {
+          LOG(ERROR) << "Unmatched { found in line: " << line;
+          return false;
+        }
+
+        const std::string keyword =
+            line.substr(match_start + 1, match_end - match_start - 1);
+        std::string replacement;
+        if (config->GetString(kCrosConfigPropertiesPath, keyword,
+                              &replacement)) {
+          expanded += replacement;
+          inserted = true;
+        } else {
+          LOG(ERROR) << "Did not find a value for " << keyword
+                     << " while expanding " << line;
+          return false;
+        }
+
+        prev_match = match_end + 1;
+        match_start = line.find('{', match_end);
+      }
+      if (prev_match != std::string::npos)
+        expanded += line.substr(prev_match);
+      line = expanded;
+    } while (inserted);
+
+    std::string truncated;
+    if (!TruncateAndroidProperty(line, &truncated)) {
+      LOG(ERROR) << "Unable to truncate property: " << line;
+      return false;
+    }
+    new_properties += truncated + "\n";
+
+    // Special-case ro.product.board to compute ro.oem.key1 at runtime, as it
+    // can depend upon the device region.
+    std::string property;
+    if (FindProperty(kBoardPropertyPrefix, &property, line)) {
+      std::string oem_key_property = ComputeOEMKey(config, property);
+      new_properties +=
+          std::string(kOEMKey1PropertyPrefix) + oem_key_property + "\n";
+    }
+  }
+
+  *expanded_content = new_properties;
+  return true;
+}
+
+}  // namespace
+
+CrosConfig::CrosConfig() {
+  const base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
+  if (!cl->HasSwitch(chromeos::switches::kArcBuildProperties)) {
+    LOG(ERROR) << chromeos::switches::kArcBuildProperties << " is not found";
+    return;
+  }
+  std::string command_line_value =
+      cl->GetSwitchValueASCII(chromeos::switches::kArcBuildProperties);
+  info_ = base::JSONReader::Read(command_line_value);
+  if (!info_) {
+    LOG(ERROR) << "JSONReader failed reading ARC build properties: "
+               << command_line_value;
+    return;
+  }
+  if (!info_->is_dict()) {
+    LOG(ERROR) << chromeos::switches::kArcBuildProperties
+               << " is not a dictionary";
+    info_.reset();
+    return;
+  }
+}
+
+CrosConfig::~CrosConfig() = default;
+
+bool CrosConfig::GetString(const std::string& path,
+                           const std::string& property,
+                           std::string* val_out) {
+  if (path != kCrosConfigPropertiesPath)
+    return false;
+  if (!info_)
+    return false;
+  const std::string* value = info_->FindStringKey(property);
+  if (!value)
+    return false;
+  *val_out = *value;
+  return true;
+}
+
+bool ExpandPropertyFile(const base::FilePath& input,
+                        const base::FilePath& output,
+                        CrosConfig* config) {
+  std::string content;
+  std::string expanded;
+  if (!base::ReadFileToString(input, &content)) {
+    PLOG(ERROR) << "Failed to read " << input;
+    return false;
+  }
+  if (!ExpandPropertyContents(content, config, &expanded))
+    return false;
+  if (base::WriteFile(output, expanded.data(), expanded.size()) !=
+      static_cast<int>(expanded.size())) {
+    PLOG(ERROR) << "Failed to write to " << output;
+    return false;
+  }
+  return true;
+}
+
+}  // namespace arc
diff --git a/components/arc/session/arc_vm_client_adapter_util.h b/components/arc/session/arc_vm_client_adapter_util.h
new file mode 100644
index 0000000..4757ce13
--- /dev/null
+++ b/components/arc/session/arc_vm_client_adapter_util.h
@@ -0,0 +1,49 @@
+// 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 COMPONENTS_ARC_SESSION_ARC_VM_CLIENT_ADAPTER_UTIL_H_
+#define COMPONENTS_ARC_SESSION_ARC_VM_CLIENT_ADAPTER_UTIL_H_
+
+#include <string>
+
+#include "base/optional.h"
+#include "base/values.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace arc {
+
+// A class that reads and parses |kArcBuildProperties| command line flags and
+// holds the result as a dictionary. This is a drop-in replacement of the
+// brillo::CrosConfigInterface classes (which are not available in Chromium).
+class CrosConfig {
+ public:
+  CrosConfig();
+  ~CrosConfig();
+  CrosConfig(const CrosConfig&) = delete;
+  CrosConfig& operator=(const CrosConfig&) = delete;
+
+  // Find the |property| in the dictionary and assigns the result to |val_out|.
+  // Returns true when the property is found. The function always returns false
+  // when |path| is not |kCrosConfigPropertiesPath|.
+  bool GetString(const std::string& path,
+                 const std::string& property,
+                 std::string* val_out);
+
+ private:
+  base::Optional<base::Value> info_;
+};
+
+// Expands properties (i.e. {property-name}) in |input| with the dictionary
+// |config| provides, and writes the results to |output|. Returns true if the
+// output file is successfully written.
+bool ExpandPropertyFile(const base::FilePath& input,
+                        const base::FilePath& output,
+                        CrosConfig* config);
+
+}  // namespace arc
+
+#endif  // COMPONENTS_ARC_SESSION_ARC_VM_CLIENT_ADAPTER_UTIL_H_
diff --git a/components/arc/session/arc_vm_client_adapter_util_unittest.cc b/components/arc/session/arc_vm_client_adapter_util_unittest.cc
new file mode 100644
index 0000000..b9874558
--- /dev/null
+++ b/components/arc/session/arc_vm_client_adapter_util_unittest.cc
@@ -0,0 +1,178 @@
+// 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 "components/arc/session/arc_vm_client_adapter_util.h"
+
+#include <memory>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "chromeos/constants/chromeos_switches.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace arc {
+namespace {
+
+constexpr char kCrosConfigPropertiesPath[] = "/arc/build-properties";
+
+class ArcVmClientAdapterUtilTest : public testing::Test {
+ public:
+  ArcVmClientAdapterUtilTest() = default;
+  ~ArcVmClientAdapterUtilTest() override = default;
+  ArcVmClientAdapterUtilTest(const ArcVmClientAdapterUtilTest&) = delete;
+  ArcVmClientAdapterUtilTest& operator=(const ArcVmClientAdapterUtilTest&) =
+      delete;
+
+  void SetUp() override { ASSERT_TRUE(dir_.CreateUniqueTempDir()); }
+
+ protected:
+  const base::FilePath& GetTempDir() const { return dir_.GetPath(); }
+
+  CrosConfig* config() {
+    if (!config_)
+      config_ = std::make_unique<CrosConfig>();
+    return config_.get();
+  }
+
+ private:
+  std::unique_ptr<CrosConfig> config_;
+  base::ScopedTempDir dir_;
+};
+
+// Tests that the GetString method works as intended.
+TEST_F(ArcVmClientAdapterUtilTest, CrosConfig_GetString) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitchASCII(chromeos::switches::kArcBuildProperties,
+                                  "{\"k1\":\"v1\",\"k2\":\"v2\",\"k3\":3}");
+  std::string str;
+  EXPECT_TRUE(config()->GetString(kCrosConfigPropertiesPath, "k1", &str));
+  EXPECT_EQ("v1", str);
+  EXPECT_TRUE(config()->GetString(kCrosConfigPropertiesPath, "k2", &str));
+  EXPECT_EQ("v2", str);
+  // The value is not a string.
+  EXPECT_FALSE(config()->GetString(kCrosConfigPropertiesPath, "k3", &str));
+  // The property path is invalid.
+  EXPECT_FALSE(config()->GetString("/unknown/path", "k1", &str));
+}
+
+// Tests that CrosConfig can handle the case where the command line is not
+// passed.
+TEST_F(ArcVmClientAdapterUtilTest, CrosConfig_NoCommandline) {
+  std::string str;
+  EXPECT_FALSE(config()->GetString(kCrosConfigPropertiesPath, "k1", &str));
+}
+
+// Tests that CrosConfig can handle an empty command line.
+TEST_F(ArcVmClientAdapterUtilTest, CrosConfig_EmptyCommandline) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitchASCII(chromeos::switches::kArcBuildProperties, "");
+  std::string str;
+  EXPECT_FALSE(config()->GetString(kCrosConfigPropertiesPath, "k1", &str));
+}
+
+// Tests that CrosConfig can handle JSON whose top-level is not a dict.
+TEST_F(ArcVmClientAdapterUtilTest, CrosConfig_NoDict) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitchASCII(chromeos::switches::kArcBuildProperties,
+                                  "[\"k1\"]");
+  std::string str;
+  EXPECT_FALSE(config()->GetString(kCrosConfigPropertiesPath, "k1", &str));
+}
+
+// Tests that CrosConfig can handle an invalid JSON.
+TEST_F(ArcVmClientAdapterUtilTest, CrosConfig_InvalidJson) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitchASCII(chromeos::switches::kArcBuildProperties,
+                                  "{\"k1\":}");  // parse error: no value
+  std::string str;
+  EXPECT_FALSE(config()->GetString(kCrosConfigPropertiesPath, "k1", &str));
+}
+
+// Tests that ExpandPropertyFile works as intended when no property expantion
+// is needed.
+TEST_F(ArcVmClientAdapterUtilTest, ExpandPropertyFile_NoExpansion) {
+  constexpr const char kValidProp[] = "ro.foo=bar\nro.baz=boo";
+  base::FilePath path;
+  ASSERT_TRUE(CreateTemporaryFileInDir(GetTempDir(), &path));
+  base::WriteFile(path, kValidProp, strlen(kValidProp));
+
+  const base::FilePath dest = GetTempDir().Append("new.prop");
+  EXPECT_TRUE(ExpandPropertyFile(path, dest, config()));
+  std::string content;
+  EXPECT_TRUE(base::ReadFileToString(dest, &content));
+  // Note: ExpandPropertyFile() adds a trailing LF.
+  EXPECT_EQ(std::string(kValidProp) + "\n", content);
+}
+
+// Tests that ExpandPropertyFile works as intended when property expantion
+// is needed.
+TEST_F(ArcVmClientAdapterUtilTest, ExpandPropertyFile_Expansion) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitchASCII(chromeos::switches::kArcBuildProperties,
+                                  "{\"k1\":\"v1\",\"k2\":\"v2\"}");
+  constexpr const char kValidProp[] = "ro.foo={k1}\nro.baz={k2}";
+  base::FilePath path;
+  ASSERT_TRUE(CreateTemporaryFileInDir(GetTempDir(), &path));
+  base::WriteFile(path, kValidProp, strlen(kValidProp));
+
+  const base::FilePath dest = GetTempDir().Append("new.prop");
+  EXPECT_TRUE(ExpandPropertyFile(path, dest, config()));
+  std::string content;
+  EXPECT_TRUE(base::ReadFileToString(dest, &content));
+  // Note: ExpandPropertyFile() adds a trailing LF.
+  EXPECT_EQ("ro.foo=v1\nro.baz=v2\n", content);
+}
+
+// Tests that ExpandPropertyFile works as intended when nested property
+// expantion is needed.
+TEST_F(ArcVmClientAdapterUtilTest, ExpandPropertyFile_NestedExpansion) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitchASCII(chromeos::switches::kArcBuildProperties,
+                                  "{\"k1\":\"{k2}\",\"k2\":\"v2\"}");
+  constexpr const char kValidProp[] = "ro.foo={k1}\nro.baz={k2}";
+  base::FilePath path;
+  ASSERT_TRUE(CreateTemporaryFileInDir(GetTempDir(), &path));
+  base::WriteFile(path, kValidProp, strlen(kValidProp));
+
+  const base::FilePath dest = GetTempDir().Append("new.prop");
+  EXPECT_TRUE(ExpandPropertyFile(path, dest, config()));
+  std::string content;
+  EXPECT_TRUE(base::ReadFileToString(dest, &content));
+  // Note: ExpandPropertyFile() adds a trailing LF.
+  EXPECT_EQ("ro.foo=v2\nro.baz=v2\n", content);
+}
+
+// Test that ExpandPropertyFile handles the case where a property is not found.
+TEST_F(ArcVmClientAdapterUtilTest, ExpandPropertyFile_CannotExpand) {
+  constexpr const char kValidProp[] =
+      "ro.foo={nonexistent-property}\nro.baz=boo\n";
+  base::FilePath path;
+  ASSERT_TRUE(CreateTemporaryFileInDir(GetTempDir(), &path));
+  base::WriteFile(path, kValidProp, strlen(kValidProp));
+  const base::FilePath dest = GetTempDir().Append("new.prop");
+  EXPECT_FALSE(ExpandPropertyFile(path, dest, config()));
+}
+
+// Test that ExpandPropertyFile handles the case where the input file is not
+// found.
+TEST_F(ArcVmClientAdapterUtilTest, ExpandPropertyFile_NoSourceFile) {
+  EXPECT_FALSE(ExpandPropertyFile(base::FilePath("/nonexistent"),
+                                  base::FilePath("/nonexistent2"), config()));
+}
+
+// Test that ExpandPropertyFile handles the case where the output file cannot
+// be written.
+TEST_F(ArcVmClientAdapterUtilTest, ExpandPropertyFile_CannotWrite) {
+  constexpr const char kValidProp[] = "ro.foo=bar\nro.baz=boo\n";
+  base::FilePath path;
+  ASSERT_TRUE(CreateTemporaryFileInDir(GetTempDir(), &path));
+  base::WriteFile(path, kValidProp, strlen(kValidProp));
+  EXPECT_FALSE(
+      ExpandPropertyFile(path, base::FilePath("/nonexistent2"), config()));
+}
+
+}  // namespace
+}  // namespace arc
diff --git a/components/autofill/content/renderer/form_cache.cc b/components/autofill/content/renderer/form_cache.cc
index bb42998..3a54514 100644
--- a/components/autofill/content/renderer/form_cache.cc
+++ b/components/autofill/content/renderer/form_cache.cc
@@ -559,23 +559,11 @@
 
 void FormCache::PruneInitialValueCaches(
     const std::set<uint32_t>& ids_to_retain) {
-  // Prune initial_select_values_.
-  for (auto iter = initial_select_values_.begin();
-       iter != initial_select_values_.end();) {
-    if (!base::Contains(ids_to_retain, iter->first))
-      iter = initial_select_values_.erase(iter);
-    else
-      ++iter;
-  }
-
-  // Prune initial_checked_state_.
-  for (auto iter = initial_checked_state_.begin();
-       iter != initial_checked_state_.end();) {
-    if (!base::Contains(ids_to_retain, iter->first))
-      iter = initial_checked_state_.erase(iter);
-    else
-      ++iter;
-  }
+  auto should_not_retain = [&ids_to_retain](const auto& p) {
+    return !base::Contains(ids_to_retain, p.first);
+  };
+  base::EraseIf(initial_select_values_, should_not_retain);
+  base::EraseIf(initial_checked_state_, should_not_retain);
 }
 
 }  // namespace autofill
diff --git a/components/autofill/content/renderer/form_cache.h b/components/autofill/content/renderer/form_cache.h
index c70e654..5aae18ad 100644
--- a/components/autofill/content/renderer/form_cache.h
+++ b/components/autofill/content/renderer/form_cache.h
@@ -86,7 +86,8 @@
   blink::WebLocalFrame* frame_;
 
   // The cached forms. Used to prevent re-extraction of forms.
-  std::set<FormData> parsed_forms_;
+  // TODO(crbug/896689) Move to std::map<unique_rederer_id, FormData>.
+  std::set<FormData, FormData::IdentityComparator> parsed_forms_;
 
   // The synthetic FormData is for all the fieldsets in the document without a
   // form owner.
diff --git a/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc b/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc
index 5ca5f4e..08547fa 100644
--- a/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc
+++ b/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc
@@ -51,6 +51,9 @@
       card_label_(card.NetworkAndLastFourDigits()),
       card_sub_label_(card.AbbreviatedExpirationDateForDisplay(false)),
       card_last_four_digits_(card.LastFourDigits()),
+      cardholder_name_(card.GetRawInfo(CREDIT_CARD_NAME_FULL)),
+      expiration_date_month_(card.ExpirationMonthAsString()),
+      expiration_date_year_(card.Expiration4DigitYearAsString()),
       legal_message_lines_(legal_message_lines),
       is_off_the_record_(is_off_the_record) {
   DCHECK_EQ(upload, !upload_save_card_prompt_callback_.is_null());
diff --git a/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h b/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h
index f0dcb5f..0dbeaaa5 100644
--- a/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h
+++ b/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h
@@ -46,6 +46,16 @@
   const LegalMessageLines& legal_message_lines() const {
     return legal_message_lines_;
   }
+  const base::string16& card_last_four_digits() const {
+    return card_last_four_digits_;
+  }
+  const base::string16& cardholder_name() const { return cardholder_name_; }
+  const base::string16& expiration_date_month() const {
+    return expiration_date_month_;
+  }
+  const base::string16& expiration_date_year() const {
+    return expiration_date_year_;
+  }
 
   // Called when a link in the legal message text was clicked.
   void OnLegalMessageLinkClicked(GURL url);
@@ -111,6 +121,15 @@
   // The last four digits of the card for which save is being offered.
   base::string16 card_last_four_digits_;
 
+  // The card holder name of the card for which save is being offered.
+  base::string16 cardholder_name_;
+
+  // The expiration month of the card for which save is being offered.
+  base::string16 expiration_date_month_;
+
+  // The expiration year of the card for which save is being offered.
+  base::string16 expiration_date_year_;
+
   // The legal message lines to show in the content of the infobar.
   const LegalMessageLines& legal_message_lines_;
 
diff --git a/components/autofill/core/common/form_data.cc b/components/autofill/core/common/form_data.cc
index e69ae3a..f4f3552 100644
--- a/components/autofill/core/common/form_data.cc
+++ b/components/autofill/core/common/form_data.cc
@@ -123,28 +123,23 @@
   return true;
 }
 
-bool FormData::operator==(const FormData& form) const {
-  return name == form.name && id_attribute == form.id_attribute &&
-         name_attribute == form.name_attribute && url == form.url &&
-         action == form.action && is_action_empty == form.is_action_empty &&
-         unique_renderer_id == form.unique_renderer_id &&
-         submission_event == form.submission_event &&
-         is_form_tag == form.is_form_tag &&
-         is_formless_checkout == form.is_formless_checkout &&
-         fields == form.fields &&
-         username_predictions == form.username_predictions;
-}
-
-bool FormData::operator!=(const FormData& form) const {
-  return !(*this == form);
-}
-
-bool FormData::operator<(const FormData& form) const {
-  return std::tie(name, id_attribute, name_attribute, url, action, is_form_tag,
-                  is_formless_checkout, fields) <
-         std::tie(form.name, form.id_attribute, form.name_attribute, form.url,
-                  form.action, form.is_form_tag, form.is_formless_checkout,
-                  form.fields);
+bool FormData::IdentityComparator::operator()(const FormData& a,
+                                              const FormData& b) const {
+  // |unique_renderer_id| uniquely identifies the form, if and only if it is
+  // set; the other members compared below together uniquely identify the form
+  // as well.
+  auto tie = [](const FormData& f) {
+    return std::tie(f.unique_renderer_id, f.name, f.id_attribute,
+                    f.name_attribute, f.url, f.action, f.is_form_tag,
+                    f.is_formless_checkout);
+  };
+  if (tie(a) < tie(b))
+    return true;
+  if (tie(b) < tie(a))
+    return false;
+  return std::lexicographical_compare(a.fields.begin(), a.fields.end(),
+                                      b.fields.begin(), b.fields.end(),
+                                      FormFieldData::IdentityComparator());
 }
 
 std::ostream& operator<<(std::ostream& os, const FormData& form) {
diff --git a/components/autofill/core/common/form_data.h b/components/autofill/core/common/form_data.h
index 932394b..92c35c6c 100644
--- a/components/autofill/core/common/form_data.h
+++ b/components/autofill/core/common/form_data.h
@@ -29,6 +29,12 @@
 
 // Holds information about a form to be filled and/or submitted.
 struct FormData {
+  // Less-than relation for STL containers. Compares only members needed to
+  // uniquely identify a form.
+  struct IdentityComparator {
+    bool operator()(const FormData& a, const FormData& b) const;
+  };
+
   // TODO(https://crbug.com/875768): Rename this const to kNotSetRendererId, and
   // use it also for not set renderer ids in FormFieldData.
   static constexpr uint32_t kNotSetFormRendererId =
@@ -52,11 +58,6 @@
   // If |form| is the same as this from the POV of dynamic refills.
   bool DynamicallySameFormAs(const FormData& form) const;
 
-  // Note: operator==() performs a full-field-comparison(byte by byte), this is
-  // different from SameFormAs(), which ignores comparison for those "values" of
-  // all form fields, just like what FormFieldData::SameFieldAs() ignores.
-  bool operator==(const FormData& form) const;
-  bool operator!=(const FormData& form) const;
   // Allow FormData to be a key in STL containers.
   bool operator<(const FormData& form) const;
 
@@ -66,6 +67,11 @@
   // The name attribute of the form.
   base::string16 name_attribute;
 
+  // NOTE: update IdentityComparator                when adding new a member.
+  // NOTE: update SameFormAs()            if needed when adding new a member.
+  // NOTE: update SimilarFormAs()         if needed when adding new a member.
+  // NOTE: update DynamicallySameFormAs() if needed when adding new a member.
+
   // The name by which autofill knows this form. This is generally either the
   // name attribute or the id_attribute value, which-ever is non-empty with
   // priority given to the name_attribute. This value is used when computing
@@ -91,9 +97,9 @@
   // and used if features::kAutofillRestrictUnownedFieldsToFormlessCheckout is
   // enabled, to prevent heuristics from running on formless non-checkout.
   bool is_formless_checkout = false;
-  //  Unique renderer id which is returned by function
-  //  WebFormElement::UniqueRendererFormId(). It is not persistant between page
-  //  loads, so it is not saved and not used in comparison in SameFormAs().
+  // Unique renderer id returned by WebFormElement::UniqueRendererFormId(). It
+  // is not persistent between page loads, so it is not saved and not used in
+  // comparison in SameFormAs().
   uint32_t unique_renderer_id = kNotSetFormRendererId;
   // The type of the event that was taken as an indication that this form is
   // being or has already been submitted. This field is filled only in Password
diff --git a/components/autofill/core/common/form_field_data.cc b/components/autofill/core/common/form_field_data.cc
index 1a804330..2f233ce7 100644
--- a/components/autofill/core/common/form_field_data.cc
+++ b/components/autofill/core/common/form_field_data.cc
@@ -4,7 +4,11 @@
 
 #include "components/autofill/core/common/form_field_data.h"
 
+#include <algorithm>
+#include <tuple>
+
 #include "base/pickle.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_util.h"
@@ -126,18 +130,71 @@
   return iter->ReadString16(&field_data->name_attribute);
 }
 
-bool HaveSameLabel(const FormFieldData& field1, const FormFieldData& field2) {
-  if (field1.label == field2.label)
-    return true;
+// LabelInfo is used to implement that "a.label == b.label" can be weakened to
+// "a.label == b.label OR a certain feature is enabled and {a,b}.label_source !=
+// kLabelTag and a.label_source == b.label_source".
+// Beware of the StringPiece member and resulting lifetime issues. Deleted copy
+// and move ctors/operators to reduce risk potential.
+struct LabelInfo {
+  explicit LabelInfo(const FormFieldData& f)
+      : label(f.label), source(f.label_source) {}
+  LabelInfo(const LabelInfo&) = delete;
+  LabelInfo& operator=(const LabelInfo&) = delete;
+  LabelInfo(LabelInfo&&) = default;
+  LabelInfo& operator=(LabelInfo&&) = default;
 
-  // Assume the labels same if they come from same source but not LABEL tag
-  // when kAutofillSkipComparingInferredLabels is enabled.
-  if (base::FeatureList::IsEnabled(
-          features::kAutofillSkipComparingInferredLabels)) {
-    return field1.label_source == field2.label_source &&
-           field1.label_source != FormFieldData::LabelSource::kLabelTag;
+  bool operator==(const LabelInfo& that) const {
+    if (label == that.label)
+      return true;
+
+    // Feature |kAutofillSkipComparingInferredLabels| weakens equivalence of
+    // labels: two labels are equivalent if they were inferred from the same
+    // type of tag other than a LABEL tag.
+    return base::FeatureList::IsEnabled(
+               features::kAutofillSkipComparingInferredLabels) &&
+           source != FormFieldData::LabelSource::kLabelTag &&
+           source == that.source;
   }
-  return false;
+
+  bool operator<(const LabelInfo& that) const { return label < that.label; }
+
+  base::StringPiece16 label;
+  FormFieldData::LabelSource source = FormFieldData::LabelSource::kLabelTag;
+};
+
+// CommonTuple(), SimilarityTuple(), DynamicIdentityTuple(), IdentityTuple()
+// return values should be used as temporaries only because they include a
+// StringPiece.
+
+auto CommonTuple(const FormFieldData& f) {
+  return std::tuple_cat(
+      std::make_tuple(LabelInfo(f)),
+      std::tie(f.name, f.name_attribute, f.id_attribute, f.form_control_type));
+}
+
+auto SimilarityTuple(const FormFieldData& f) {
+  return std::tuple_cat(CommonTuple(f),
+                        std::make_tuple(IsCheckable(f.check_status)));
+}
+
+auto DynamicIdentityTuple(const FormFieldData& f) {
+  return std::tuple_cat(CommonTuple(f), std::make_tuple(f.IsVisible()));
+}
+
+auto IdentityTuple(const FormFieldData& f) {
+  // |unique_renderer_id| uniquely identifies the field, if and only if it is
+  // set; the other members compared below (excluding label_source) together
+  // uniquely identify the field as well.
+  return std::tuple_cat(
+      SimilarityTuple(f),
+      std::tie(
+// TODO(crbug.com/896689): On iOS the unique_id member uniquely addresses
+// this field in the DOM.
+#if defined(OS_IOS)
+          f.unique_id,
+#endif
+          f.autocomplete_attribute, f.placeholder, f.max_length, f.css_classes,
+          f.is_focusable, f.should_autocomplete, f.role, f.text_direction));
 }
 
 }  // namespace
@@ -155,55 +212,21 @@
 FormFieldData::~FormFieldData() = default;
 
 bool FormFieldData::SameFieldAs(const FormFieldData& field) const {
-// TODO(crbug.com/896689): On iOS the unique_id member uniquely addresses
-// this field in the DOM.
-#if defined(OS_IOS)
-  if (unique_id != field.unique_id)
-    return false;
-#endif
-
-  // A FormFieldData stores a value, but the value is not part of the identity
-  // of the field, so we don't want to compare the values.
-  // Similarly, flags like is_enabled, which are only used for parsing but are
-  // not stored persistently, are not used for comparison.
-  // is_autofilled and section are also secondary properties of a field. Two
-  // fields could be the same, and have different sections, because the section
-  // is updated for one, but not for the other.
-  return name == field.name && id_attribute == field.id_attribute &&
-         name_attribute == field.name_attribute &&
-         form_control_type == field.form_control_type &&
-         autocomplete_attribute == field.autocomplete_attribute &&
-         placeholder == field.placeholder && max_length == field.max_length &&
-         css_classes == field.css_classes &&
-         // is_checked and is_autofilled counts as "value" since these change
-         // when we fill things in.
-         IsCheckable(check_status) == IsCheckable(field.check_status) &&
-         is_focusable == field.is_focusable &&
-         should_autocomplete == field.should_autocomplete &&
-         role == field.role && text_direction == field.text_direction &&
-         HaveSameLabel(*this, field);
-  // The option values/contents which are the list of items in the list
-  // of a drop-down are currently not considered part of the identity of
-  // a form element. This is debatable, since one might base heuristics
-  // on the types of elements that are available. Alternatively, one
-  // could imagine some forms that dynamically change the element
-  // contents (say, insert years starting from the current year) that
-  // should not be considered changes in the structure of the form.
+  return IdentityTuple(*this) == IdentityTuple(field);
 }
 
 bool FormFieldData::SimilarFieldAs(const FormFieldData& field) const {
-  return HaveSameLabel(*this, field) && name == field.name &&
-         id_attribute == field.id_attribute &&
-         name_attribute == field.name_attribute &&
-         form_control_type == field.form_control_type &&
-         IsCheckable(check_status) == IsCheckable(field.check_status);
+  return SimilarityTuple(*this) == SimilarityTuple(field);
 }
 
 bool FormFieldData::DynamicallySameFieldAs(const FormFieldData& field) const {
-  return name == field.name && id_attribute == field.id_attribute &&
-         name_attribute == field.name_attribute &&
-         HaveSameLabel(*this, field) && IsVisible() == field.IsVisible() &&
-         form_control_type == field.form_control_type;
+  return DynamicIdentityTuple(*this) == DynamicIdentityTuple(field);
+}
+
+bool FormFieldData::IdentityComparator::operator()(
+    const FormFieldData& a,
+    const FormFieldData& b) const {
+  return IdentityTuple(a) < IdentityTuple(b);
 }
 
 bool FormFieldData::IsTextInputElement() const {
@@ -228,99 +251,6 @@
   return properties_mask & AUTOFILLED;
 }
 
-bool FormFieldData::operator==(const FormFieldData& field) const {
-  return SameFieldAs(field) && unique_renderer_id == field.unique_renderer_id &&
-         form_control_ax_id == field.form_control_ax_id &&
-         is_autofilled == field.is_autofilled &&
-         check_status == field.check_status &&
-         option_values == field.option_values &&
-         option_contents == field.option_contents &&
-         properties_mask == field.properties_mask;
-}
-
-bool FormFieldData::operator!=(const FormFieldData& field) const {
-  return !(*this == field);
-}
-
-bool FormFieldData::operator<(const FormFieldData& field) const {
-  // This does not use std::tie() as that generates more implicit variables
-  // than the max-vartrack-size for var-tracking-assignments when compiling
-  // for Android, producing build warnings. (See https://crbug.com/555171 for
-  // context.)
-
-  // Like SameFieldAs this ignores the value.
-  if (label < field.label)
-    return true;
-  if (label > field.label)
-    return false;
-  if (name < field.name)
-    return true;
-  if (name > field.name)
-    return false;
-
-// TODO(crbug.com/896689): On iOS the unique_id member uniquely addresses
-// this field in the DOM.
-#if defined(OS_IOS)
-  if (unique_id < field.unique_id)
-    return true;
-  if (unique_id < field.unique_id)
-    return false;
-#endif
-
-  if (id_attribute < field.id_attribute)
-    return true;
-  if (id_attribute > field.id_attribute)
-    return false;
-  if (name_attribute < field.name_attribute)
-    return true;
-  if (name_attribute > field.name_attribute)
-    return false;
-  if (form_control_type < field.form_control_type)
-    return true;
-  if (form_control_type > field.form_control_type)
-    return false;
-  if (autocomplete_attribute < field.autocomplete_attribute)
-    return true;
-  if (autocomplete_attribute > field.autocomplete_attribute)
-    return false;
-  if (placeholder < field.placeholder)
-    return true;
-  if (placeholder > field.placeholder)
-    return false;
-  if (max_length < field.max_length)
-    return true;
-  if (max_length > field.max_length)
-    return false;
-  if (css_classes < field.css_classes)
-    return true;
-  if (css_classes > field.css_classes)
-    return false;
-  // Skip |is_checked| and |is_autofilled| as in SameFieldAs.
-  if (IsCheckable(check_status) < IsCheckable(field.check_status))
-    return true;
-  if (IsCheckable(check_status) > IsCheckable(field.check_status))
-    return false;
-  if (is_focusable < field.is_focusable)
-    return true;
-  if (is_focusable > field.is_focusable)
-    return false;
-  if (should_autocomplete < field.should_autocomplete)
-    return true;
-  if (should_autocomplete > field.should_autocomplete)
-    return false;
-  if (role < field.role)
-    return true;
-  if (role > field.role)
-    return false;
-  if (text_direction < field.text_direction)
-    return true;
-  if (text_direction > field.text_direction)
-    return false;
-  // See SameFieldAs above for why we don't check option_values/contents and
-  // flags like is_enabled.
-  return false;
-}
-
 void SerializeFormFieldData(const FormFieldData& field_data,
                             base::Pickle* pickle) {
   pickle->WriteInt(kFormFieldDataPickleVersion);
diff --git a/components/autofill/core/common/form_field_data.h b/components/autofill/core/common/form_field_data.h
index 605f9e0..e1c8198c 100644
--- a/components/autofill/core/common/form_field_data.h
+++ b/components/autofill/core/common/form_field_data.h
@@ -57,6 +57,12 @@
   using RoleAttribute = mojom::FormFieldData_RoleAttribute;
   using LabelSource = mojom::FormFieldData_LabelSource;
 
+  // Less-than relation for STL containers. Compares only members needed to
+  // uniquely identify a field.
+  struct IdentityComparator {
+    bool operator()(const FormFieldData& a, const FormFieldData& b) const;
+  };
+
   static constexpr uint32_t kNotSetFormControlRendererId =
       std::numeric_limits<uint32_t>::max();
 
@@ -67,20 +73,22 @@
   FormFieldData& operator=(FormFieldData&&);
   ~FormFieldData();
 
-  // Returns true if two form fields are the same, not counting the value.
+  // Returns true if both fields are identical, ignoring value- and
+  // parsing related members.
+  // See also SimilarFieldAs(), DynamicallySameFieldAs().
   bool SameFieldAs(const FormFieldData& field) const;
 
-  // SameFieldAs() is a little restricted when field's style changed
-  // dynamically, like css.
-  // This method only compares critical attributes of field to check whether
-  // they are similar enough to be considered as same field if form's
-  // other information isn't changed.
+  // Returns true if both fields are identical, ignoring members that
+  // are typically changed dynamically.
+  // Strictly weaker than SameFieldAs().
   bool SimilarFieldAs(const FormFieldData& field) const;
 
-  // If |field| is the same as this from the POV of dynamic refills.
+  // Returns true if both forms are equivalent from the POV of dynamic refills.
+  // Strictly weaker than SameFieldAs(): replaces equality of |is_focusable| and
+  // |role| with equality of IsVisible().
   bool DynamicallySameFieldAs(const FormFieldData& field) const;
 
-  // Returns true for all of textfield-looking types such as text, password,
+  // Returns true for all of textfield-looking types: text, password,
   // search, email, url, and number. It must work the same way as Blink function
   // WebInputElement::IsTextField(), and it returns false if |*this| represents
   // a textarea.
@@ -99,15 +107,6 @@
   bool HadFocus() const;
   bool WasAutofilled() const;
 
-  // Note: operator==() performs a full-field-comparison(byte by byte), this is
-  // different from SameFieldAs(), which ignores comparison for those "values"
-  // not regarded as part of identity of the field, such as is_autofilled and
-  // the option_values/contents etc.
-  bool operator==(const FormFieldData& field) const;
-  bool operator!=(const FormFieldData& field) const;
-  // Comparison operator exposed for STL map. Uses label, then name to sort.
-  bool operator<(const FormFieldData& field) const;
-
 #if defined(OS_IOS)
   // The identifier which uniquely addresses this field in the DOM. This is an
   // ephemeral value which is not guaranteed to be stable across page loads. It
@@ -122,6 +121,11 @@
 #define EXPECT_EQ_UNIQUE_ID(expected, actual)
 #endif
 
+  // NOTE: update IdentityComparator                 when adding new a member.
+  // NOTE: update SameFieldAs()            if needed when adding new a member.
+  // NOTE: update SimilarFieldAs()         if needed when adding new a member.
+  // NOTE: update DynamicallySameFieldAs() if needed when adding new a member.
+
   // The name by which autofill knows this field. This is generally either the
   // name attribute or the id_attribute value, which-ever is non-empty with
   // priority given to the name_attribute. This value is used when computing
@@ -129,8 +133,6 @@
   // TODO(crbug/896689): remove this and use attributes/unique_id instead.
   base::string16 name;
 
-  // If you add more, be sure to update the comparison operators, SameFieldAs,
-  // serializing functions (in the .cc file) and the constructor.
   base::string16 id_attribute;
   base::string16 name_attribute;
   base::string16 label;
@@ -142,10 +144,9 @@
   base::string16 aria_label;
   base::string16 aria_description;
 
-  // Unique renderer id which is returned by function
-  // WebFormControlElement::UniqueRendererFormControlId(). It is not persistant
-  // between page loads, so it is not saved and not used in comparison in
-  // SameFieldAs().
+  // Unique renderer id returned by WebFormElement::UniqueRendererFormId(). It
+  // is not persistent between page loads, so it is not saved and not used in
+  // comparison in SameFieldAs().
   uint32_t unique_renderer_id = kNotSetFormControlRendererId;
 
   // The ax node id of the form control in the accessibility tree.
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
index 5146310..8b545885 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
@@ -28,6 +28,12 @@
                                            "Option4"};
 namespace {
 
+template <typename T>
+bool EquivalentData(const T& a, const T& b) {
+  typename T::IdentityComparator less;
+  return !less(a, b) && !less(b, a);
+}
+
 void CreateTestFieldDataPredictions(const std::string& signature,
                                     FormFieldDataPredictions* field_predict) {
   test::CreateTestSelectField("TestLabel", "TestName", "TestValue", kOptions,
@@ -129,8 +135,8 @@
   EXPECT_EQ(expected.form_renderer_id, actual.form_renderer_id);
   EXPECT_EQ(expected.origin, actual.origin);
   EXPECT_EQ(expected.action, actual.action);
-  EXPECT_EQ(expected.username_field, actual.username_field);
-  EXPECT_EQ(expected.password_field, actual.password_field);
+  EXPECT_TRUE(EquivalentData(expected.username_field, actual.username_field));
+  EXPECT_TRUE(EquivalentData(expected.password_field, actual.password_field));
   EXPECT_EQ(expected.preferred_realm, actual.preferred_realm);
   EXPECT_EQ(expected.uses_account_store, actual.uses_account_store);
 
@@ -234,7 +240,7 @@
 void ExpectFormFieldData(const FormFieldData& expected,
                          base::OnceClosure closure,
                          const FormFieldData& passed) {
-  EXPECT_EQ(expected, passed);
+  EXPECT_TRUE(EquivalentData(expected, passed));
   EXPECT_EQ(expected.value, passed.value);
   EXPECT_EQ(expected.typed_value, passed.typed_value);
   std::move(closure).Run();
@@ -243,7 +249,7 @@
 void ExpectFormData(const FormData& expected,
                     base::OnceClosure closure,
                     const FormData& passed) {
-  EXPECT_EQ(expected, passed);
+  EXPECT_TRUE(EquivalentData(expected, passed));
   std::move(closure).Run();
 }
 
diff --git a/components/autofill_assistant/browser/web/element_finder.cc b/components/autofill_assistant/browser/web/element_finder.cc
index 158253d4..cf55da6 100644
--- a/components/autofill_assistant/browser/web/element_finder.cc
+++ b/components/autofill_assistant/browser/web/element_finder.cc
@@ -347,51 +347,9 @@
   auto* node = result->GetNode();
   std::vector<int> backend_ids;
 
-  if (node->HasContentDocument()) {
-    // If the frame has a ContentDocument, it's considered a local frame.
-    // We need to resolve the RenderFrameHost for autofill.
+  if (node->GetNodeName() == "IFRAME") {
+    DCHECK(node->HasFrameId());  // Ensure all frames have an id.
 
-    backend_ids.emplace_back(node->GetContentDocument()->GetBackendNodeId());
-
-    element_result_->container_frame_selector_index = index;
-
-    if (node->HasFrameId()) {
-      element_result_->container_frame_host =
-          FindCorrespondingRenderFrameHost(node->GetFrameId());
-    } else {
-      // TODO(b/143318024): Remove the fallback.
-      std::string frame_name;
-      if (node->HasAttributes()) {
-        const std::vector<std::string>* attributes = node->GetAttributes();
-        for (size_t i = 0; i < attributes->size();) {
-          if ((*attributes)[i] == "name") {
-            frame_name = (*attributes)[i + 1];
-            break;
-          }
-          // Jump two positions since attribute name and value are always
-          // paired.
-          i = i + 2;
-        }
-      }
-      element_result_->container_frame_host = FindCorrespondingRenderFrameHost(
-          frame_name, node->GetContentDocument()->GetDocumentURL());
-    }
-
-    if (!element_result_->container_frame_host) {
-      DVLOG(1) << __func__ << " Failed to find corresponding owner frame.";
-      SendResult(ClientStatus(FRAME_HOST_NOT_FOUND));
-      return;
-    }
-  } else if (node->HasFrameId()) {
-    // If the frame has no ContentDocument, it's considered an
-    // OutOfProcessIFrame.
-    // See https://www.chromium.org/developers/design-documents/oop-iframes for
-    // full documentation.
-    // We need to assign the frame id, such that devtools can resolve the
-    // session calls should be executed on. We also need to resolve the
-    // RenderFrameHost for autofill.
-
-    element_result_->node_frame_id = node->GetFrameId();
     element_result_->container_frame_selector_index = index;
     element_result_->container_frame_host =
         FindCorrespondingRenderFrameHost(node->GetFrameId());
@@ -402,12 +360,29 @@
       return;
     }
 
-    // Kick off another find element chain to walk down the OOP iFrame.
-    devtools_client_->GetRuntime()->Evaluate(
-        std::string(kGetDocumentElement), element_result_->node_frame_id,
-        base::BindOnce(&ElementFinder::OnGetDocumentElement,
-                       weak_ptr_factory_.GetWeakPtr(), index + 1));
-    return;
+    if (node->HasContentDocument()) {
+      // If the frame has a ContentDocument it's considered a local frame. We
+      // don't need to assign the frame id, since devtools can just send the
+      // commands to the main session.
+
+      backend_ids.emplace_back(node->GetContentDocument()->GetBackendNodeId());
+    } else {
+      // If the frame has no ContentDocument, it's considered an
+      // OutOfProcessIFrame.
+      // See https://www.chromium.org/developers/design-documents/oop-iframes
+      // for full documentation.
+      // With the iFrame running in a different process it is necessary to pass
+      // the correct session id from devtools. We need to set the frame id,
+      // such that devtools can resolve the corresponding session id.
+      element_result_->node_frame_id = node->GetFrameId();
+
+      // Kick off another find element chain to walk down the OOP iFrame.
+      devtools_client_->GetRuntime()->Evaluate(
+          std::string(kGetDocumentElement), element_result_->node_frame_id,
+          base::BindOnce(&ElementFinder::OnGetDocumentElement,
+                         weak_ptr_factory_.GetWeakPtr(), index + 1));
+      return;
+    }
   }
 
   if (node->HasShadowRoots()) {
@@ -454,19 +429,4 @@
   return nullptr;
 }
 
-content::RenderFrameHost* ElementFinder::FindCorrespondingRenderFrameHost(
-    std::string name,
-    std::string document_url) {
-  content::RenderFrameHost* ret_frame = nullptr;
-  for (auto* frame : web_contents_->GetAllFrames()) {
-    if (frame->GetFrameName() == name &&
-        frame->GetLastCommittedURL().spec() == document_url) {
-      DCHECK(!ret_frame);
-      ret_frame = frame;
-    }
-  }
-
-  return ret_frame;
-}
-
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/web/element_finder.h b/components/autofill_assistant/browser/web/element_finder.h
index 880bc86c..aa72d4e 100644
--- a/components/autofill_assistant/browser/web/element_finder.h
+++ b/components/autofill_assistant/browser/web/element_finder.h
@@ -90,9 +90,6 @@
 
   content::RenderFrameHost* FindCorrespondingRenderFrameHost(
       std::string frame_id);
-  content::RenderFrameHost* FindCorrespondingRenderFrameHost(
-      std::string name,
-      std::string document_url);
 
   content::WebContents* const web_contents_;
   DevtoolsClient* const devtools_client_;
diff --git a/components/autofill_assistant/browser/web/web_controller_browsertest.cc b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
index 7aff9b5..b74eb749 100644
--- a/components/autofill_assistant/browser/web/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
@@ -908,6 +908,15 @@
   FindElementAndCheck(selector, 1, false);
   selector.must_be_visible = true;
   FindElementAndCheck(selector, 1, false);
+
+  // OutOfProcessIFrame.
+  selector.selectors.clear();
+  selector.selectors.emplace_back("#iframeExternal");
+  selector.selectors.emplace_back("#button");
+  selector.must_be_visible = false;
+  FindElementAndCheck(selector, 1, false);
+  selector.must_be_visible = true;
+  FindElementAndCheck(selector, 1, false);
 }
 
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FindElementNotFound) {
diff --git a/components/invalidation/impl/invalidator_registrar_with_memory.cc b/components/invalidation/impl/invalidator_registrar_with_memory.cc
index 0aea14a..fa57d2a6 100644
--- a/components/invalidation/impl/invalidator_registrar_with_memory.cc
+++ b/components/invalidation/impl/invalidator_registrar_with_memory.cc
@@ -52,24 +52,24 @@
 // static
 void InvalidatorRegistrarWithMemory::RegisterPrefs(
     PrefRegistrySimple* registry) {
-  registry->RegisterDictionaryPref(kTopicsToHandlerDeprecated);
-  registry->RegisterDictionaryPref(kTopicsToHandler);
+  // For local state, we want to register exactly the same prefs as for profile
+  // prefs; see comment in the header.
+  RegisterProfilePrefs(registry);
 }
 
 InvalidatorRegistrarWithMemory::InvalidatorRegistrarWithMemory(
-    PrefService* local_state,
+    PrefService* prefs,
     const std::string& sender_id,
     bool migrate_old_prefs)
-    : state_(DEFAULT_INVALIDATION_ERROR),
-      local_state_(local_state),
-      sender_id_(sender_id) {
+    : state_(DEFAULT_INVALIDATION_ERROR), prefs_(prefs), sender_id_(sender_id) {
+  DCHECK(!sender_id_.empty());
   if (migrate_old_prefs) {
-    MigratePrefs(local_state_, sender_id_);
+    MigratePrefs(prefs_, sender_id_);
   }
   const base::Value* pref_data =
-      local_state_->Get(kTopicsToHandler)->FindDictKey(sender_id_);
+      prefs_->Get(kTopicsToHandler)->FindDictKey(sender_id_);
   if (!pref_data) {
-    DictionaryPrefUpdate update(local_state_, kTopicsToHandler);
+    DictionaryPrefUpdate update(prefs_, kTopicsToHandler);
     update->SetKey(sender_id_, base::DictionaryValue());
     return;
   }
@@ -114,14 +114,9 @@
   handlers_.RemoveObserver(handler);
   registered_handler_to_topics_map_.erase(handler);
   // Note: Do *not* remove the entry from
-  // |handler_name_to_subscribed_topics_map_| so that GetAllSubscribedTopics()
-  // still returns the registered topics.
-}
-
-bool InvalidatorRegistrarWithMemory::IsHandlerRegistered(
-    const InvalidationHandler* handler) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  return handlers_.HasObserver(handler);
+  // |handler_name_to_subscribed_topics_map_| - we haven't actually unsubscribed
+  // from any of the topics on the server, so GetAllSubscribedTopics() should
+  // still return the topics.
 }
 
 bool InvalidatorRegistrarWithMemory::UpdateRegisteredTopics(
@@ -143,19 +138,14 @@
     }
   }
 
-  // TODO(treib): This is unreachable, |handler| couldn't have been removed
-  // from |handlers_|. What was the intention behind this check?
-  if (!IsHandlerRegistered(handler)) {
-    NOTREACHED();
-    return success;
-  }
-
   // TODO(treib): This seems inconsistent - if there's a duplicate, we don't
   // update |registered_handler_to_topics_map_| but we *do* still update
   // |handler_name_to_subscribed_topics_map_| and the prefs?!
 
-  DictionaryPrefUpdate update(local_state_, kTopicsToHandler);
+  DictionaryPrefUpdate update(prefs_, kTopicsToHandler);
   base::Value* pref_data = update->FindDictKey(sender_id_);
+  // TODO(treib): This does *not* remove subscribed topics which were not
+  // registered. Bug or feature?
   auto to_unregister = FindRemovedTopics(old_topics, topics);
   for (const auto& topic : to_unregister) {
     pref_data->RemoveKey(topic);
diff --git a/components/invalidation/impl/invalidator_registrar_with_memory.h b/components/invalidation/impl/invalidator_registrar_with_memory.h
index a7e7cd7..dd456e5 100644
--- a/components/invalidation/impl/invalidator_registrar_with_memory.h
+++ b/components/invalidation/impl/invalidator_registrar_with_memory.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_INVALIDATION_IMPL_INVALIDATOR_REGISTRAR_WITH_MEMORY_H_
 
 #include <map>
+#include <string>
 
 #include "base/macros.h"
 #include "base/observer_list.h"
@@ -22,14 +23,11 @@
 
 namespace syncer {
 
-
-// A helper class for implementations of the Invalidator interface.  It helps
-// keep track of registered handlers and which object ID registrations are
-// associated with which handlers, so implementors can just reuse the logic
-// here to dispatch invalidations and other interesting notifications.
+// A helper class for FCMInvalidationService.  It helps keep track of registered
+// handlers and which topic registrations are associated with each handler.
 class INVALIDATION_EXPORT InvalidatorRegistrarWithMemory {
  public:
-  InvalidatorRegistrarWithMemory(PrefService* local_state,
+  InvalidatorRegistrarWithMemory(PrefService* prefs,
                                  const std::string& sender_id,
                                  bool migrate_old_prefs);
 
@@ -54,15 +52,16 @@
   // topics associated with |handler| from the server.
   void UnregisterHandler(InvalidationHandler* handler);
 
-  bool IsHandlerRegistered(const InvalidationHandler* handler) const;
-
   // Updates the set of topics associated with |handler|.  |handler| must
   // not be nullptr, and must already be registered.  A topic must be registered
   // for at most one handler. If any of the |topics| is already registered
   // to a different handler, returns false.
+  // Note that this also updates the *subscribed* topics - assuming that whoever
+  // called this will also send (un)subscription requests to the server.
   bool UpdateRegisteredTopics(InvalidationHandler* handler,
                               const Topics& topics) WARN_UNUSED_RESULT;
 
+  // Returns all topics currently registered to |handler|.
   Topics GetRegisteredTopics(InvalidationHandler* handler) const;
 
   // Returns the set of all topics that (we think) we are subscribed to on the
@@ -125,9 +124,9 @@
 
   // This can be either a regular (Profile-attached) PrefService or the local
   // state PrefService.
-  PrefService* const local_state_;
+  PrefService* const prefs_;
 
-  // The FCM sender ID. May be empty.
+  // The FCM sender ID.
   const std::string sender_id_;
 
   DISALLOW_COPY_AND_ASSIGN(InvalidatorRegistrarWithMemory);
diff --git a/components/offline_pages/core/model/model_task_test_base.cc b/components/offline_pages/core/model/model_task_test_base.cc
index 5a96e54..ed599a2 100644
--- a/components/offline_pages/core/model/model_task_test_base.cc
+++ b/components/offline_pages/core/model/model_task_test_base.cc
@@ -6,9 +6,10 @@
 
 #include "base/files/file_util.h"
 #include "base/logging.h"
+#include "base/threading/thread_task_runner_handle.h"
 
 namespace offline_pages {
-ModelTaskTestBase::ModelTaskTestBase() : store_test_util_(task_runner()) {}
+ModelTaskTestBase::ModelTaskTestBase() {}
 ModelTaskTestBase::~ModelTaskTestBase() {}
 
 void ModelTaskTestBase::SetUp() {
@@ -17,7 +18,8 @@
   ASSERT_TRUE(private_dir_.CreateUniqueTempDir());
   ASSERT_TRUE(public_dir_.CreateUniqueTempDir());
   archive_manager_ = std::make_unique<ArchiveManager>(
-      TemporaryDir(), PrivateDir(), PublicDir(), task_runner());
+      TemporaryDir(), PrivateDir(), PublicDir(),
+      base::ThreadTaskRunnerHandle::Get());
   generator()->SetArchiveDirectory(TemporaryDir());
   store_test_util_.BuildStoreInMemory();
 }
diff --git a/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc b/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
index 7989210..9ee01b29 100644
--- a/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
+++ b/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/time/clock.h"
 #include "build/build_config.h"
@@ -87,7 +88,10 @@
 
   // Runs until all of the tasks that are not delayed are gone from the task
   // queue.
-  void PumpLoop() { task_runner_->RunUntilIdle(); }
+  void PumpLoop() { task_environment_.RunUntilIdle(); }
+  void FastForwardBy(const base::TimeDelta delta) {
+    task_environment_.FastForwardBy(delta);
+  }
   void BuildStore();
   void BuildModel();
   void ResetModel();
@@ -147,8 +151,7 @@
   }
 
   // Getters for private fields.
-  base::TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); }
-  base::Clock* clock() { return task_runner_->GetMockClock(); }
+  const base::Clock* clock() { return task_environment_.GetMockClock(); }
   OfflinePageModelTaskified* model() { return model_.get(); }
   OfflinePageMetadataStore* store() { return store_test_util_.store(); }
   OfflinePageMetadataStoreTestUtil* store_test_util() {
@@ -182,8 +185,8 @@
   OfflinePageTestArchivePublisher* publisher() { return publisher_; }
 
  private:
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   std::unique_ptr<OfflinePageModelTaskified> model_;
   OfflinePageMetadataStoreTestUtil store_test_util_;
   ArchiveManager* archive_manager_;
@@ -204,11 +207,7 @@
 };
 
 OfflinePageModelTaskifiedTest::OfflinePageModelTaskifiedTest()
-    : task_runner_(new base::TestMockTimeTaskRunner(base::Time::Now(),
-                                                    base::TimeTicks::Now())),
-      task_runner_handle_(task_runner_),
-      store_test_util_(task_runner_),
-      clock_(task_runner_->GetMockClock()) {}
+    : clock_(task_environment_.GetMockClock()) {}
 
 OfflinePageModelTaskifiedTest::~OfflinePageModelTaskifiedTest() {}
 
@@ -1307,10 +1306,8 @@
   // to trigger StartupMaintenanceTask execution.
   base::MockCallback<MultipleOfflinePageItemCallback> callback;
   model()->GetAllPages(callback.Get());
-  task_runner()->FastForwardBy(
-      OfflinePageModelTaskified::kMaintenanceTasksDelay +
-      base::TimeDelta::FromMilliseconds(1));
-  PumpLoop();
+  FastForwardBy(OfflinePageModelTaskified::kMaintenanceTasksDelay +
+                base::TimeDelta::FromMilliseconds(1));
 
   EXPECT_EQ(2LL, store_test_util()->GetPageCount());
   EXPECT_EQ(0UL, test_utils::GetFileCountInDirectory(temporary_dir_path()));
@@ -1321,8 +1318,7 @@
 TEST_F(OfflinePageModelTaskifiedTest, ClearStorage) {
   // The ClearStorage task should not be executed based on time delays after
   // launch (aka the model being built).
-  task_runner()->FastForwardBy(base::TimeDelta::FromDays(1));
-  PumpLoop();
+  FastForwardBy(base::TimeDelta::FromDays(1));
   EXPECT_EQ(base::Time(), last_maintenance_tasks_schedule_time());
 
   // GetAllPages should schedule a delayed task that will eventually run
@@ -1341,8 +1337,7 @@
   const base::TimeDelta run_delay =
       OfflinePageModelTaskified::kMaintenanceTasksDelay +
       base::TimeDelta::FromMilliseconds(1);
-  task_runner()->FastForwardBy(run_delay);
-  PumpLoop();
+  FastForwardBy(run_delay);
   EXPECT_EQ(last_scheduling_time, last_maintenance_tasks_schedule_time());
   // Check that CleanupVisualsTask ran.
   histogram_tester()->ExpectUniqueSample("OfflinePages.CleanupThumbnails.Count",
@@ -1351,13 +1346,12 @@
   // Calling GetAllPages after only half of the enforced interval between
   // ClearStorage runs should not schedule ClearStorage.
   // Note: The previous elapsed delay is discounted from the clock advance here.
-  task_runner()->FastForwardBy(
-      OfflinePageModelTaskified::kClearStorageInterval / 2 - run_delay);
+  FastForwardBy(OfflinePageModelTaskified::kClearStorageInterval / 2 -
+                run_delay);
   ASSERT_GT(clock()->Now(), last_scheduling_time);
   model()->GetAllPages(callback.Get());
   // And advance the delay too.
-  task_runner()->FastForwardBy(run_delay);
-  PumpLoop();
+  FastForwardBy(run_delay);
   EXPECT_EQ(last_scheduling_time, last_maintenance_tasks_schedule_time());
   // Confirm a single run happened so far.
   histogram_tester()->ExpectUniqueSample(
@@ -1366,9 +1360,8 @@
 
   // Forwarding by the full interval (plus 1 second just in case) should allow
   // the task to be enqueued again.
-  task_runner()->FastForwardBy(
-      OfflinePageModelTaskified::kClearStorageInterval / 2 +
-      base::TimeDelta::FromSeconds(1));
+  FastForwardBy(OfflinePageModelTaskified::kClearStorageInterval / 2 +
+                base::TimeDelta::FromSeconds(1));
   // Saving a page should also immediately enqueue the ClearStorage task.
   auto archiver = BuildArchiver(kTestUrl, ArchiverResult::SUCCESSFULLY_CREATED);
   SavePageWithExpectedResult(kTestUrl, kTestClientId1, kTestUrl2,
@@ -1376,8 +1369,7 @@
                              SavePageResult::SUCCESS);
   last_scheduling_time = clock()->Now();
   // Advance the delay again.
-  task_runner()->FastForwardBy(run_delay);
-  PumpLoop();
+  FastForwardBy(run_delay);
   EXPECT_EQ(last_scheduling_time, last_maintenance_tasks_schedule_time());
 
   // Confirm that two runs happened.
@@ -1403,8 +1395,7 @@
 TEST_F(OfflinePageModelTaskifiedTest, PersistentPageConsistencyCheckExecuted) {
   // The PersistentPageConsistencyCheckTask should not be executed based on time
   // delays after launch (aka the model being built).
-  task_runner()->FastForwardBy(base::TimeDelta::FromDays(1));
-  PumpLoop();
+  FastForwardBy(base::TimeDelta::FromDays(1));
   histogram_tester()->ExpectTotalCount(
       "OfflinePages.ConsistencyCheck.Persistent.Result", 0);
 
@@ -1431,8 +1422,7 @@
   const base::TimeDelta run_delay =
       OfflinePageModelTaskified::kMaintenanceTasksDelay +
       base::TimeDelta::FromMilliseconds(1);
-  task_runner()->FastForwardBy(run_delay);
-  PumpLoop();
+  FastForwardBy(run_delay);
   // But nothing should change.
   EXPECT_EQ(1UL,
             test_utils::GetFileCountInDirectory(public_archive_dir_path()));
@@ -1447,12 +1437,11 @@
   // Calling GetAllPages after only half of the enforced interval between
   // consistency check runs should not schedule the task.
   // Note: The previous elapsed delay is discounted from the clock advance here.
-  task_runner()->FastForwardBy(
-      OfflinePageModelTaskified::kClearStorageInterval / 2 - run_delay);
+  FastForwardBy(OfflinePageModelTaskified::kClearStorageInterval / 2 -
+                run_delay);
   model()->GetAllPages(callback.Get());
   // And advance the delay too.
-  task_runner()->FastForwardBy(run_delay);
-  PumpLoop();
+  FastForwardBy(run_delay);
   // Confirm no persistent page consistency check is executed.
   histogram_tester()->ExpectTotalCount(
       "OfflinePages.ConsistencyCheck.Persistent.Result", 1);
@@ -1460,13 +1449,11 @@
   // Forwarding by the full interval (plus 1 second just in case) should allow
   // the task to be enqueued again and call GetAllPages again to enqueue the
   // task.
-  task_runner()->FastForwardBy(
-      OfflinePageModelTaskified::kClearStorageInterval / 2 +
-      base::TimeDelta::FromSeconds(1));
+  FastForwardBy(OfflinePageModelTaskified::kClearStorageInterval / 2 +
+                base::TimeDelta::FromSeconds(1));
   model()->GetAllPages(callback.Get());
   // And advance the delay too.
-  task_runner()->FastForwardBy(run_delay);
-  PumpLoop();
+  FastForwardBy(run_delay);
   // Confirm persistent page consistency check is executed, and the page is
   // marked as missing file.
   EXPECT_EQ(0UL,
@@ -1480,15 +1467,14 @@
 
   // Forwarding by a long time that is enough for the page with missing file to
   // get expired.
-  task_runner()->FastForwardBy(base::TimeDelta::FromDays(400));
+  FastForwardBy(base::TimeDelta::FromDays(400));
   // Saving a page should also immediately enqueue the consistency check task.
   auto archiver = BuildArchiver(kTestUrl, ArchiverResult::SUCCESSFULLY_CREATED);
   SavePageWithExpectedResult(kTestUrl, kTestClientId1, kTestUrl2,
                              kEmptyRequestOrigin, std::move(archiver),
                              SavePageResult::SUCCESS);
   // Advance the delay to activate task execution.
-  task_runner()->FastForwardBy(run_delay);
-  PumpLoop();
+  FastForwardBy(run_delay);
   // Confirm persistent page consistency check is executed, and the page is
   // deleted from database, also notified system download manager.
   EXPECT_EQ(0UL,
@@ -1516,8 +1502,7 @@
   EXPECT_EQ(base::Time(), last_maintenance_tasks_schedule_time());
 
   // Advance the clock considerably and confirm no runs happened.
-  task_runner()->FastForwardBy(base::TimeDelta::FromDays(1));
-  PumpLoop();
+  FastForwardBy(base::TimeDelta::FromDays(1));
   EXPECT_EQ(base::Time(), last_maintenance_tasks_schedule_time());
   histogram_tester()->ExpectTotalCount(
       "OfflinePages.ClearTemporaryPages.Result", 0);
diff --git a/components/offline_pages/core/offline_page_metadata_store_test_util.cc b/components/offline_pages/core/offline_page_metadata_store_test_util.cc
index b121417..b31d621 100644
--- a/components/offline_pages/core/offline_page_metadata_store_test_util.cc
+++ b/components/offline_pages/core/offline_page_metadata_store_test_util.cc
@@ -9,6 +9,9 @@
 #include "base/bind.h"
 #include "base/callback_forward.h"
 #include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/core/model/add_page_task.h"
 #include "components/offline_pages/core/model/get_pages_task.h"
 #include "components/offline_pages/core/offline_page_types.h"
@@ -31,9 +34,8 @@
 
 }  // namespace
 
-OfflinePageMetadataStoreTestUtil::OfflinePageMetadataStoreTestUtil(
-    scoped_refptr<base::TestMockTimeTaskRunner> task_runner)
-    : task_runner_(task_runner), store_ptr_(nullptr) {}
+OfflinePageMetadataStoreTestUtil::OfflinePageMetadataStoreTestUtil()
+    : store_ptr_(nullptr) {}
 
 OfflinePageMetadataStoreTestUtil::~OfflinePageMetadataStoreTestUtil() {}
 
@@ -43,20 +45,20 @@
     return;
   }
 
-  store_.reset(
-      new OfflinePageMetadataStore(task_runner_, temp_directory_.GetPath()));
+  store_.reset(new OfflinePageMetadataStore(base::ThreadTaskRunnerHandle::Get(),
+                                            temp_directory_.GetPath()));
   store_ptr_ = store_.get();
 }
 
 void OfflinePageMetadataStoreTestUtil::BuildStoreInMemory() {
-  store_.reset(new OfflinePageMetadataStore(task_runner_));
+  store_.reset(
+      new OfflinePageMetadataStore(base::ThreadTaskRunnerHandle::Get()));
   store_ptr_ = store_.get();
 }
 
 void OfflinePageMetadataStoreTestUtil::DeleteStore() {
   store_.reset();
   store_ptr_ = nullptr;
-  task_runner_->FastForwardUntilNoTasksRemain();
 }
 
 std::unique_ptr<OfflinePageMetadataStore>
@@ -65,45 +67,48 @@
 }
 
 void OfflinePageMetadataStoreTestUtil::InsertItem(const OfflinePageItem& page) {
+  base::RunLoop run_loop;
   AddPageResult result;
   auto task = std::make_unique<AddPageTask>(
-      store(), page,
-      base::BindOnce([](AddPageResult* out_result,
-                        AddPageResult cb_result) { *out_result = cb_result; },
-                     &result));
+      store(), page, base::BindLambdaForTesting([&](AddPageResult cb_result) {
+        result = cb_result;
+        run_loop.Quit();
+      }));
   task->Run();
-  task_runner_->RunUntilIdle();
+  run_loop.Run();
   EXPECT_EQ(AddPageResult::SUCCESS, result);
 }
 
 int64_t OfflinePageMetadataStoreTestUtil::GetPageCount() {
+  base::RunLoop run_loop;
   int64_t count = 0;
   store()->Execute(
       base::BindOnce(&GetPageCountSync),
-      base::BindOnce(
-          [](int64_t* out_count, int64_t cb_count) { *out_count = cb_count; },
-          &count),
+      base::BindOnce(base::BindLambdaForTesting([&](int64_t cb_count) {
+        count = cb_count;
+        run_loop.Quit();
+      })),
       int64_t());
-  task_runner_->RunUntilIdle();
+  run_loop.Run();
   return count;
 }
 
 std::unique_ptr<OfflinePageItem>
 OfflinePageMetadataStoreTestUtil::GetPageByOfflineId(int64_t offline_id) {
+  base::RunLoop run_loop;
   PageCriteria criteria;
   criteria.offline_ids = std::vector<int64_t>{offline_id};
   OfflinePageItem* page = nullptr;
   auto task = std::make_unique<GetPagesTask>(
       store(), criteria,
-      base::BindOnce(
-          [](OfflinePageItem** out_page,
-             const std::vector<OfflinePageItem>& cb_pages) {
+      base::BindOnce(base::BindLambdaForTesting(
+          [&](const std::vector<OfflinePageItem>& cb_pages) {
             if (!cb_pages.empty())
-              *out_page = new OfflinePageItem(cb_pages[0]);
-          },
-          &page));
+              page = new OfflinePageItem(cb_pages[0]);
+            run_loop.Quit();
+          })));
   task->Run();
-  task_runner_->RunUntilIdle();
+  run_loop.Run();
   return base::WrapUnique<OfflinePageItem>(page);
 }
 
diff --git a/components/offline_pages/core/offline_page_metadata_store_test_util.h b/components/offline_pages/core/offline_page_metadata_store_test_util.h
index 4bffad5..977f34f 100644
--- a/components/offline_pages/core/offline_page_metadata_store_test_util.h
+++ b/components/offline_pages/core/offline_page_metadata_store_test_util.h
@@ -25,8 +25,7 @@
 // operations on the store, for test writing convenience.
 class OfflinePageMetadataStoreTestUtil {
  public:
-  explicit OfflinePageMetadataStoreTestUtil(
-      scoped_refptr<base::TestMockTimeTaskRunner> task_runner);
+  OfflinePageMetadataStoreTestUtil();
   ~OfflinePageMetadataStoreTestUtil();
 
   // Builds a new store in a temporary directory.
@@ -54,9 +53,6 @@
   base::SimpleTestClock* clock() { return &clock_; }
 
  private:
-  void RunUntilIdle();
-
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
   base::ScopedTempDir temp_directory_;
   // TODO(romax): Refactor the test util along with the similar one used in
   // Prefetching, to remove the ownership to the store. And clean up related
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
index c805b14b..cc7c349 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
@@ -12,8 +12,13 @@
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop_current.h"
+#include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "components/image_fetcher/core/mock_image_fetcher.h"
 #include "components/image_fetcher/core/request_metadata.h"
 #include "components/offline_pages/core/client_namespace_constants.h"
@@ -330,7 +335,7 @@
     offline_model_ = model.get();
     taco_->SetOfflinePageModel(std::move(model));
     taco_->SetPrefetchImporter(std::make_unique<PrefetchImporterImpl>(
-        dispatcher_, offline_model_, task_runner()));
+        dispatcher_, offline_model_, base::ThreadTaskRunnerHandle::Get()));
 
     taco_->CreatePrefetchService();
 
@@ -389,7 +394,7 @@
         .WillOnce([&, thumbnail_data](
                       const ClientId& client_id,
                       ThumbnailFetcher::ImageDataFetchedCallback callback) {
-          task_runner()->PostTask(
+          base::ThreadTaskRunnerHandle::Get()->PostTask(
               FROM_HERE, base::BindOnce(std::move(callback), thumbnail_data));
         });
   }
@@ -445,7 +450,7 @@
   MockThumbnailFetcher* thumbnail_fetcher_;
   image_fetcher::MockImageFetcher* thumbnail_image_fetcher_;
 
-  PrefetchStoreTestUtil store_util_{task_runner()};
+  PrefetchStoreTestUtil store_util_;
   MockPrefetchItemGenerator item_generator_;
   base::ScopedTempDir archive_directory_;
   std::unique_ptr<FakeSuggestionsProvider> suggestions_provider_;
@@ -652,10 +657,26 @@
   EXPECT_THAT(*network_request_factory()->GetAllUrlsRequested(),
               Contains(prefetch_url.url.spec()));
 
-  // When the network request finishes, the dispatcher should still hold the
-  // ScopedBackgroundTask because it needs to process the results of the
-  // request.
-  RespondWithHttpError(net::HTTP_INTERNAL_SERVER_ERROR);
+  // We want to make sure the response is received before the dispatcher goes
+  // for the next task. For that we need to make sure that only file handle
+  // events (and no regular tasks) get processed by the RunLoop().RunUntilIdle()
+  // call done inside of RespondWithNetError. This can be acomplished by turning
+  // that RunLoop into a nested one (which would only run system tasks). By
+  // posting a task that makes the RespondWithNetError call we will already be
+  // running a RunLoop when the call happens thus turning the
+  // RespondWithNetError RunLoop into a nested one.
+  base::RunLoop run_loop;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindLambdaForTesting([&]() {
+        // When the network request finishes, the dispatcher should still hold
+        // the ScopedBackgroundTask because it needs to process the results of
+        // the request
+        RespondWithHttpError(net::HTTP_INTERNAL_SERVER_ERROR);
+        // Stop right after the error is processed, so that we can check
+        // GetBackgroundTask()
+        run_loop.Quit();
+      }));
+  run_loop.Run();
   EXPECT_NE(nullptr, GetBackgroundTask());
   RunUntilIdle();
 
@@ -747,8 +768,19 @@
 
   EXPECT_FALSE(dispatcher_suspended());
 
-  // This should trigger suspend.
-  RespondWithNetError(net::ERR_BLOCKED_BY_ADMINISTRATOR);
+  // We want to make sure the response is received before the dispatcher goes
+  // for the next task. For that we need to make sure that only file handle
+  // events (and no regular tasks) get processed by the RunLoop().RunUntilIdle()
+  // call done inside of RespondWithNetError. This can be acomplished by turning
+  // that RunLoop into a nested one (which would only run system tasks). By
+  // posting a task that makes the RespondWithNetError call we will already be
+  // running a RunLoop when the call happens thus turning the
+  // RespondWithNetError RunLoop into a nested one.
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindLambdaForTesting([this]() {
+        // This should trigger suspend.
+        RespondWithNetError(net::ERR_BLOCKED_BY_ADMINISTRATOR);
+      }));
   RunUntilIdle();
 
   EXPECT_TRUE(reschedule_called());
diff --git a/components/offline_pages/core/prefetch/prefetch_request_test_base.cc b/components/offline_pages/core/prefetch/prefetch_request_test_base.cc
index d784d18..9064831 100644
--- a/components/offline_pages/core/prefetch/prefetch_request_test_base.cc
+++ b/components/offline_pages/core/prefetch/prefetch_request_test_base.cc
@@ -10,6 +10,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/mock_entropy_provider.h"
+#include "base/test/task_environment.h"
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/offline_pages/core/prefetch/prefetch_server_urls.h"
 #include "net/url_request/url_fetcher_delegate.h"
@@ -21,12 +22,9 @@
     "Test Experiment";
 
 PrefetchRequestTestBase::PrefetchRequestTestBase()
-    : task_runner_(new base::TestMockTimeTaskRunner),
-      test_shared_url_loader_factory_(
+    : test_shared_url_loader_factory_(
           base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-              &test_url_loader_factory_)) {
-  message_loop_.SetTaskRunner(task_runner_);
-}
+              &test_url_loader_factory_)) {}
 
 PrefetchRequestTestBase::~PrefetchRequestTestBase() {}
 
@@ -100,11 +98,15 @@
 }
 
 void PrefetchRequestTestBase::RunUntilIdle() {
-  task_runner_->RunUntilIdle();
+  task_environment_.RunUntilIdle();
+}
+
+void PrefetchRequestTestBase::FastForwardBy(base::TimeDelta delta) {
+  task_environment_.FastForwardBy(delta);
 }
 
 void PrefetchRequestTestBase::FastForwardUntilNoTasksRemain() {
-  task_runner_->FastForwardUntilNoTasksRemain();
+  task_environment_.FastForwardUntilNoTasksRemain();
 }
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_request_test_base.h b/components/offline_pages/core/prefetch/prefetch_request_test_base.h
index 63b2fe5f..8da7c32e 100644
--- a/components/offline_pages/core/prefetch/prefetch_request_test_base.h
+++ b/components/offline_pages/core/prefetch/prefetch_request_test_base.h
@@ -9,6 +9,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/field_trial.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -45,17 +46,13 @@
   }
 
   void RunUntilIdle();
+  void FastForwardBy(base::TimeDelta delta);
   void FastForwardUntilNoTasksRemain();
 
- protected:
-  // Derived classes may need these to construct other members.
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner() {
-    return task_runner_;
-  }
-
  private:
-  base::MessageLoopForIO message_loop_;
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::IO,
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 
   network::TestURLLoaderFactory test_url_loader_factory_;
   scoped_refptr<network::SharedURLLoaderFactory>
diff --git a/components/offline_pages/core/prefetch/store/prefetch_downloader_quota_unittest.cc b/components/offline_pages/core/prefetch/store/prefetch_downloader_quota_unittest.cc
index f3922335..9488e17 100644
--- a/components/offline_pages/core/prefetch/store/prefetch_downloader_quota_unittest.cc
+++ b/components/offline_pages/core/prefetch/store/prefetch_downloader_quota_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/offline_pages/core/prefetch/store/prefetch_downloader_quota.h"
 
 #include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/core/offline_page_feature.h"
@@ -23,12 +24,8 @@
 
 class PrefetchDownloaderQuotaTest : public testing::Test {
  public:
-  PrefetchDownloaderQuotaTest();
-  ~PrefetchDownloaderQuotaTest() override = default;
-
-  void SetUp() override { store_test_util_.BuildStoreInMemory(); }
-
-  void TearDown() override { store_test_util_.DeleteStore(); }
+  PrefetchDownloaderQuotaTest() { store_test_util_.BuildStoreInMemory(); }
+  ~PrefetchDownloaderQuotaTest() override { store_test_util_.DeleteStore(); }
 
   PrefetchStore* store() { return store_test_util_.store(); }
 
@@ -37,17 +34,11 @@
   void SetTestingMaxDailyQuotaBytes(const std::string& max_config);
 
  private:
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
+  base::test::SingleThreadTaskEnvironment task_environment_;
   PrefetchStoreTestUtil store_test_util_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-PrefetchDownloaderQuotaTest::PrefetchDownloaderQuotaTest()
-    : task_runner_(new base::TestMockTimeTaskRunner),
-      task_runner_handle_(task_runner_),
-      store_test_util_(task_runner_) {}
-
 void PrefetchDownloaderQuotaTest::SetTestingMaxDailyQuotaBytes(
     const std::string& max_config) {
   scoped_feature_list_.Reset();
diff --git a/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc b/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc
index 45fd12f..22e4f3fb 100644
--- a/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc
+++ b/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc
@@ -6,7 +6,10 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/simple_test_clock.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/clock.h"
@@ -199,9 +202,7 @@
 
 }  // namespace
 
-PrefetchStoreTestUtil::PrefetchStoreTestUtil(
-    scoped_refptr<base::TestMockTimeTaskRunner> task_runner)
-    : task_runner_(task_runner) {}
+PrefetchStoreTestUtil::PrefetchStoreTestUtil() = default;
 
 PrefetchStoreTestUtil::~PrefetchStoreTestUtil() = default;
 
@@ -209,13 +210,13 @@
   if (!temp_directory_.CreateUniqueTempDir())
     DVLOG(1) << "temp_directory_ not created";
 
-  owned_store_.reset(
-      new PrefetchStore(task_runner_, temp_directory_.GetPath()));
+  owned_store_.reset(new PrefetchStore(base::ThreadTaskRunnerHandle::Get(),
+                                       temp_directory_.GetPath()));
   store_ = owned_store_.get();
 }
 
 void PrefetchStoreTestUtil::BuildStoreInMemory() {
-  owned_store_.reset(new PrefetchStore(task_runner_));
+  owned_store_.reset(new PrefetchStore(base::ThreadTaskRunnerHandle::Get()));
   store_ = owned_store_.get();
 }
 
@@ -229,42 +230,52 @@
     if (!temp_directory_.Delete())
       DVLOG(1) << "temp_directory_ not created";
   }
-  task_runner_->FastForwardUntilNoTasksRemain();
+  // The actual deletion happens in a task. So wait until all have been
+  // processed.
+  base::RunLoop().RunUntilIdle();
 }
 
 bool PrefetchStoreTestUtil::InsertPrefetchItem(const PrefetchItem& item) {
+  base::RunLoop run_loop;
   bool success = false;
-  store_->Execute(
-      base::BindOnce(&InsertPrefetchItemSync, item),
-      base::BindOnce([](bool* alias, bool s) { *alias = s; }, &success), false);
-  RunUntilIdle();
+  store_->Execute(base::BindOnce(&InsertPrefetchItemSync, item),
+                  base::BindOnce(base::BindLambdaForTesting([&](bool s) {
+                    success = s;
+                    run_loop.Quit();
+                  })),
+                  false);
+  run_loop.Run();
   return success;
 }
 
 int PrefetchStoreTestUtil::CountPrefetchItems() {
+  base::RunLoop run_loop;
   int count = 0;
-  store_->Execute(
-      base::BindOnce(&CountPrefetchItemsSync),
-      base::BindOnce([](int* alias, int result) { *alias = result; }, &count),
-      kPrefetchStoreCommandFailed);
-  RunUntilIdle();
+  store_->Execute(base::BindOnce(&CountPrefetchItemsSync),
+                  base::BindOnce(base::BindLambdaForTesting([&](int result) {
+                    count = result;
+                    run_loop.Quit();
+                  })),
+                  kPrefetchStoreCommandFailed);
+  run_loop.Run();
   return count;
 }
 
 std::unique_ptr<PrefetchItem> PrefetchStoreTestUtil::GetPrefetchItem(
     int64_t offline_id) {
+  base::RunLoop run_loop;
   std::unique_ptr<PrefetchItem> item;
-  store_->Execute(base::BindOnce(&GetPrefetchItemSync, offline_id),
-                  base::BindOnce(
-                      [](std::unique_ptr<PrefetchItem>* alias,
-                         base::Optional<PrefetchItem> result) {
-                        if (result)
-                          *alias = std::make_unique<PrefetchItem>(
-                              std::move(result).value());
-                      },
-                      &item),
-                  base::Optional<PrefetchItem>());
-  RunUntilIdle();
+  store_->Execute(
+      base::BindOnce(&GetPrefetchItemSync, offline_id),
+      base::BindOnce(
+          base::BindLambdaForTesting([&](base::Optional<PrefetchItem> result) {
+            if (result) {
+              item = std::make_unique<PrefetchItem>(std::move(result).value());
+            }
+            run_loop.Quit();
+          })),
+      base::Optional<PrefetchItem>());
+  run_loop.Run();
   return item;
 }
 
@@ -276,16 +287,16 @@
 }
 
 std::set<PrefetchItem> PrefetchStoreTestUtil::GetAllItems() {
+  base::RunLoop run_loop;
   std::set<PrefetchItem> items;
-  store_->Execute(
-      base::BindOnce(&GetAllItemsSync),
-      base::BindOnce(
-          [](std::set<PrefetchItem>* alias, std::set<PrefetchItem> result) {
-            *alias = std::move(result);
-          },
-          &items),
-      std::set<PrefetchItem>());
-  RunUntilIdle();
+  store_->Execute(base::BindOnce(&GetAllItemsSync),
+                  base::BindOnce(base::BindLambdaForTesting(
+                      [&](std::set<PrefetchItem> result) {
+                        items = std::move(result);
+                        run_loop.Quit();
+                      })),
+                  std::set<PrefetchItem>());
+  run_loop.Run();
   return items;
 }
 
@@ -303,51 +314,58 @@
 
 int PrefetchStoreTestUtil::ZombifyPrefetchItems(const std::string& name_space,
                                                 const GURL& url) {
+  base::RunLoop run_loop;
   int count = -1;
-  store_->Execute(
-      base::BindOnce(&UpdateItemsStateSync, name_space, url.spec(),
-                     PrefetchItemState::ZOMBIE),
-      base::BindOnce([](int* alias, int result) { *alias = result; }, &count),
-      kPrefetchStoreCommandFailed);
-  RunUntilIdle();
+  store_->Execute(base::BindOnce(&UpdateItemsStateSync, name_space, url.spec(),
+                                 PrefetchItemState::ZOMBIE),
+                  base::BindOnce(base::BindLambdaForTesting([&](int result) {
+                    count = result;
+                    run_loop.Quit();
+                  })),
+                  kPrefetchStoreCommandFailed);
+  run_loop.Run();
   return count;
 }
 
-void PrefetchStoreTestUtil::RunUntilIdle() {
-  task_runner_->RunUntilIdle();
-}
-
 int PrefetchStoreTestUtil::LastCommandChangeCount() {
+  base::RunLoop run_loop;
   int count = 0;
-  store_->Execute(
-      base::BindOnce([](sql::Database* connection) {
-        return connection->GetLastChangeCount();
-      }),
-      base::BindOnce([](int* result, int count) { *result = count; }, &count),
-      0);
-  RunUntilIdle();
+  store_->Execute(base::BindOnce([](sql::Database* connection) {
+                    return connection->GetLastChangeCount();
+                  }),
+                  base::BindOnce(base::BindLambdaForTesting([&](int result) {
+                    count = result;
+                    run_loop.Quit();
+                  })),
+                  0);
+  run_loop.Run();
   return count;
 }
 
 int64_t PrefetchStoreTestUtil::GetPrefetchQuota() {
+  base::RunLoop run_loop;
   int64_t result;
-  store_->Execute(
-      base::BindOnce(&GetPrefetchQuotaSync, clock()),
-      base::BindOnce([](int64_t* result, int64_t quota) { *result = quota; },
-                     &result),
-      int64_t());
-  RunUntilIdle();
+  store_->Execute(base::BindOnce(&GetPrefetchQuotaSync, clock()),
+                  base::BindOnce(base::BindLambdaForTesting([&](int64_t quota) {
+                    result = quota;
+                    run_loop.Quit();
+                  })),
+                  int64_t());
+  run_loop.Run();
   return result;
 }
 
 bool PrefetchStoreTestUtil::SetPrefetchQuota(int64_t available_quota) {
+  base::RunLoop run_loop;
   bool result;
   store_->Execute(
       base::BindOnce(&SetPrefetchQuotaSync, available_quota, clock()),
-      base::BindOnce([](bool* result, bool success) { *result = success; },
-                     &result),
+      base::BindOnce(base::BindLambdaForTesting([&](bool success) {
+        result = success;
+        run_loop.Quit();
+      })),
       false);
-  RunUntilIdle();
+  run_loop.Run();
   return result;
 }
 
diff --git a/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h b/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h
index 2fb2ef2..7f592602 100644
--- a/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h
+++ b/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h
@@ -34,8 +34,7 @@
 // store, for test writing convenience.
 class PrefetchStoreTestUtil {
  public:
-  explicit PrefetchStoreTestUtil(
-      scoped_refptr<base::TestMockTimeTaskRunner> task_runner);
+  PrefetchStoreTestUtil();
   ~PrefetchStoreTestUtil();
 
   // Builds a new store in a temporary directory.
@@ -88,9 +87,6 @@
   base::SimpleTestClock* clock() { return &clock_; }
 
  private:
-  void RunUntilIdle();
-
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
   base::ScopedTempDir temp_directory_;
   // TODO(jianli): Refactor this class to avoid owning the store.
   std::unique_ptr<PrefetchStore> owned_store_;
diff --git a/components/offline_pages/core/prefetch/store/prefetch_store_unittest.cc b/components/offline_pages/core/prefetch/store/prefetch_store_unittest.cc
index fd44031..56783c7 100644
--- a/components/offline_pages/core/prefetch/store/prefetch_store_unittest.cc
+++ b/components/offline_pages/core/prefetch/store/prefetch_store_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/offline_pages/core/prefetch/store/prefetch_store.h"
 
+#include "base/test/task_environment.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/offline_pages/core/offline_store_utils.h"
@@ -22,33 +23,24 @@
 
 class PrefetchStoreTest : public testing::Test {
  public:
-  PrefetchStoreTest();
-  ~PrefetchStoreTest() override = default;
+  PrefetchStoreTest() { store_test_util_.BuildStoreInMemory(); }
 
-  void SetUp() override { store_test_util_.BuildStoreInMemory(); }
-
-  void TearDown() override {
-    store_test_util_.DeleteStore();
-  }
+  ~PrefetchStoreTest() override { store_test_util_.DeleteStore(); }
 
   PrefetchStore* store() { return store_test_util_.store(); }
 
   PrefetchStoreTestUtil* store_util() { return &store_test_util_; }
   MockPrefetchItemGenerator* item_generator() { return &item_generator_; }
-  base::TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); }
+
+ protected:
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 
  private:
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
   PrefetchStoreTestUtil store_test_util_;
   MockPrefetchItemGenerator item_generator_;
 };
 
-PrefetchStoreTest::PrefetchStoreTest()
-    : task_runner_(new base::TestMockTimeTaskRunner),
-      task_runner_handle_(task_runner_),
-      store_test_util_(task_runner_) {}
-
 TEST_F(PrefetchStoreTest, InitializeStore) {
   EXPECT_EQ(0, store_util()->CountPrefetchItems());
 }
@@ -93,7 +85,7 @@
   EXPECT_EQ(InitializationStatus::kSuccess,
             store()->initialization_status_for_testing());
 
-  task_runner()->FastForwardBy(PrefetchStore::kClosingDelay);
+  task_environment_.FastForwardBy(PrefetchStore::kClosingDelay);
   EXPECT_EQ(InitializationStatus::kNotInitialized,
             store()->initialization_status_for_testing());
 
@@ -113,7 +105,7 @@
   EXPECT_EQ(InitializationStatus::kSuccess,
             store()->initialization_status_for_testing());
 
-  task_runner()->FastForwardBy(PrefetchStore::kClosingDelay / 2);
+  task_environment_.FastForwardBy(PrefetchStore::kClosingDelay / 2);
   EXPECT_EQ(InitializationStatus::kSuccess,
             store()->initialization_status_for_testing());
 
@@ -127,15 +119,15 @@
   // This adds up to more than kClosingDelay after the first call, which means
   // the closing would trigger, it does not however, since second call caused it
   // to be postponed.
-  task_runner()->FastForwardBy(2 * PrefetchStore::kClosingDelay / 3);
+  task_environment_.FastForwardBy(2 * PrefetchStore::kClosingDelay / 3);
   // Store should still be initialized.
   EXPECT_EQ(InitializationStatus::kSuccess,
             store()->initialization_status_for_testing());
   // There is still a pending task to close the store.
-  EXPECT_TRUE(task_runner()->HasPendingTask());
+  EXPECT_NE(0u, task_environment_.GetPendingMainThreadTaskCount());
 
   // After this step the store should be closed.
-  task_runner()->FastForwardBy(PrefetchStore::kClosingDelay);
+  task_environment_.FastForwardBy(PrefetchStore::kClosingDelay);
   EXPECT_EQ(InitializationStatus::kNotInitialized,
             store()->initialization_status_for_testing());
 }
diff --git a/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc b/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc
index 0c4a0711..001cd47 100644
--- a/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc
+++ b/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/offline_pages/core/prefetch/prefetch_prefs.h"
 #include "components/offline_pages/core/prefetch/store/prefetch_downloader_quota.h"
@@ -103,8 +104,11 @@
 }
 
 TEST_F(DownloadArchivesTaskTest, SingleArchiveToDownload) {
+  constexpr auto kFreshnessDelta = base::TimeDelta::FromMilliseconds(123);
+
   int64_t dummy_item_id = InsertDummyItem();
   int64_t download_item_id = InsertItemToDownload(kLargeArchiveSize);
+  FastForwardBy(kFreshnessDelta);
 
   std::set<PrefetchItem> items_before_run;
   EXPECT_EQ(2U, store_util()->GetAllItems(&items_before_run));
@@ -133,10 +137,8 @@
   ASSERT_TRUE(download_item);
   EXPECT_EQ(PrefetchItemState::DOWNLOADING, download_item->state);
   EXPECT_EQ(1, download_item->download_initiation_attempts);
-  // These times are created using base::Time::Now() in short distance from each
-  // other, therefore putting *_LE was considered too.
-  EXPECT_LT(download_item_before->freshness_time,
-            download_item->freshness_time);
+  EXPECT_EQ(kFreshnessDelta, download_item->freshness_time -
+                                 download_item_before->freshness_time);
 
   const TestPrefetchDownloader::RequestMap& requested_downloads =
       prefetch_downloader()->requested_downloads();
@@ -203,6 +205,7 @@
   int64_t dummy_item_id = InsertDummyItem();
   // download_item_1 is expected to be fresher, therefore we create it second.
   int64_t download_item_id_2 = InsertItemToDownload(kLargeArchiveSize);
+  FastForwardBy(base::TimeDelta::FromMilliseconds(1));
   int64_t download_item_id_1 = InsertItemToDownload(kLargeArchiveSize);
 
   std::set<PrefetchItem> items_before_run;
@@ -252,8 +255,12 @@
   const int total_items = DownloadArchivesTask::kMaxConcurrentDownloads + 2;
   // Create more than we allow to download in parallel and put then in the
   // |item_ids| in front.
-  for (int i = 0; i < total_items; ++i)
+  for (int i = 0; i < total_items; ++i) {
     item_ids.insert(item_ids.begin(), InsertItemToDownload(kSmallArchiveSize));
+    // Add some time in between them so that the download order is deterministic
+    // and the checks further down work.
+    FastForwardBy(base::TimeDelta::FromMilliseconds(1));
+  }
 
   std::set<PrefetchItem> items_before_run;
   EXPECT_EQ(static_cast<size_t>(total_items),
@@ -317,8 +324,12 @@
   // and put them in the fresher |item_ids| in front.
   const size_t total_items = limitless_max_concurrent_downloads + 1;
   std::vector<int64_t> item_ids;
-  for (size_t i = 0; i < total_items; ++i)
+  for (size_t i = 0; i < total_items; ++i) {
     item_ids.insert(item_ids.begin(), InsertItemToDownload(kLargeArchiveSize));
+    // Add some time in between them so that the download order is deterministic
+    // and the checks further down work.
+    FastForwardBy(base::TimeDelta::FromMilliseconds(1));
+  }
 
   std::set<PrefetchItem> items_before_run;
   EXPECT_EQ(total_items, store_util()->GetAllItems(&items_before_run));
diff --git a/components/offline_pages/core/prefetch/tasks/prefetch_task_test_base.cc b/components/offline_pages/core/prefetch/tasks/prefetch_task_test_base.cc
index 87ec865..166fc096 100644
--- a/components/offline_pages/core/prefetch/tasks/prefetch_task_test_base.cc
+++ b/components/offline_pages/core/prefetch/tasks/prefetch_task_test_base.cc
@@ -21,8 +21,7 @@
           base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
               &test_url_loader_factory_)),
       prefs_(std::make_unique<TestingPrefServiceSimple>()),
-      prefetch_request_factory_(test_shared_url_loader_factory_, prefs()),
-      store_test_util_(task_runner()) {}
+      prefetch_request_factory_(test_shared_url_loader_factory_, prefs()) {}
 
 PrefetchTaskTestBase::~PrefetchTaskTestBase() = default;
 
diff --git a/components/offline_pages/task/task_test_base.cc b/components/offline_pages/task/task_test_base.cc
index 5561179..987add2 100644
--- a/components/offline_pages/task/task_test_base.cc
+++ b/components/offline_pages/task/task_test_base.cc
@@ -5,17 +5,14 @@
 #include "components/offline_pages/task/task_test_base.h"
 
 #include "base/test/mock_callback.h"
+#include "components/offline_pages/task/test_task_runner.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using testing::_;
 
 namespace offline_pages {
 
-TaskTestBase::TaskTestBase()
-    : task_runner_(new base::TestMockTimeTaskRunner),
-      test_task_runner_(task_runner_) {
-  message_loop_.SetTaskRunner(task_runner_);
-}
+TaskTestBase::TaskTestBase() = default;
 
 TaskTestBase::~TaskTestBase() = default;
 
@@ -24,20 +21,24 @@
 }
 
 void TaskTestBase::TearDown() {
-  task_runner_->FastForwardUntilNoTasksRemain();
+  task_environment_.FastForwardUntilNoTasksRemain();
   testing::Test::TearDown();
 }
 
+void TaskTestBase::FastForwardBy(base::TimeDelta delta) {
+  task_environment_.FastForwardBy(delta);
+}
+
 void TaskTestBase::RunUntilIdle() {
-  task_runner_->RunUntilIdle();
+  task_environment_.RunUntilIdle();
 }
 
 void TaskTestBase::RunTask(std::unique_ptr<Task> task) {
-  test_task_runner_.RunTask(std::move(task));
+  TestTaskRunner::RunTask(std::move(task));
 }
 
 void TaskTestBase::RunTask(Task* task) {
-  test_task_runner_.RunTask(task);
+  TestTaskRunner::RunTask(task);
 }
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/task/task_test_base.h b/components/offline_pages/task/task_test_base.h
index 14c5d33..51cd51f 100644
--- a/components/offline_pages/task/task_test_base.h
+++ b/components/offline_pages/task/task_test_base.h
@@ -5,12 +5,9 @@
 #ifndef COMPONENTS_OFFLINE_PAGES_TASK_TASK_TEST_BASE_H_
 #define COMPONENTS_OFFLINE_PAGES_TASK_TASK_TEST_BASE_H_
 
-#include "testing/gtest/include/gtest/gtest.h"
-
-#include "base/message_loop/message_loop.h"
-#include "base/test/test_mock_time_task_runner.h"
+#include "base/test/task_environment.h"
 #include "components/offline_pages/task/task.h"
-#include "components/offline_pages/task/test_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
 
@@ -30,15 +27,16 @@
   // Task is not cleaned up after completing.
   void RunTask(Task* task);
   void RunUntilIdle();
+  void FastForwardBy(base::TimeDelta delta);
 
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner() {
-    return task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner() {
+    return task_environment_.GetMainThreadTaskRunner();
   }
 
  private:
-  base::MessageLoopForIO message_loop_;
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-  TestTaskRunner test_task_runner_;
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::IO,
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 };
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/task/test_task_runner.cc b/components/offline_pages/task/test_task_runner.cc
index caeccae..5ebdc31 100644
--- a/components/offline_pages/task/test_task_runner.cc
+++ b/components/offline_pages/task/test_task_runner.cc
@@ -5,35 +5,23 @@
 #include "components/offline_pages/task/test_task_runner.h"
 
 #include "base/bind.h"
+#include "base/run_loop.h"
 #include "components/offline_pages/task/task.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
 
-TestTaskRunner::TestTaskRunner(
-    scoped_refptr<base::TestMockTimeTaskRunner> task_runner)
-    : task_runner_(task_runner) {}
-
-TestTaskRunner::~TestTaskRunner() {}
-
 void TestTaskRunner::RunTask(std::unique_ptr<Task> task) {
-  RunTask(task.get());
+  TestTaskRunner::RunTask(task.get());
 }
 
 void TestTaskRunner::RunTask(Task* task) {
   DCHECK(task);
-  Task* completed_task = nullptr;
+  base::RunLoop run_loop;
   task->SetTaskCompletionCallbackForTesting(base::BindOnce(
-      &TestTaskRunner::TaskComplete, base::Unretained(this), &completed_task));
+      [](base::RunLoop* run_loop, Task*) { run_loop->Quit(); }, &run_loop));
   task->Run();
-  task_runner_->RunUntilIdle();
-  EXPECT_EQ(task, completed_task) << "Task did not complete";
-}
-
-void TestTaskRunner::TaskComplete(Task** completed_task_ptr, Task* task) {
-  auto set_task_callback = [](Task** t_ptr, Task* t) { *t_ptr = t; };
-  task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(set_task_callback, completed_task_ptr, task));
+  run_loop.Run();
 }
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/task/test_task_runner.h b/components/offline_pages/task/test_task_runner.h
index 5e0ccde..f91bef6 100644
--- a/components/offline_pages/task/test_task_runner.h
+++ b/components/offline_pages/task/test_task_runner.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/test/test_mock_time_task_runner.h"
 
 namespace offline_pages {
 class Task;
@@ -16,27 +15,12 @@
 // Tool for running (task queue related) tasks in test.
 class TestTaskRunner {
  public:
-  explicit TestTaskRunner(
-      scoped_refptr<base::TestMockTimeTaskRunner> task_runner);
-  ~TestTaskRunner();
-
   // Runs task with expectation that it correctly completes.
   // Task is also cleaned up after completing.
-  void RunTask(std::unique_ptr<Task> task);
+  static void RunTask(std::unique_ptr<Task> task);
   // Runs task with expectation that it correctly completes.
   // Task is not cleaned up after completing.
-  void RunTask(Task* task);
-
- private:
-  void TaskComplete(Task** completed_task_ptr, Task* task);
-
-  // Certainly confusing, but internal task runner, is simply a test version of
-  // a single thread task runner. It is used for running closures.
-  // The difference between that and the encapsulating task runner, is that a
-  // task in a Task Queue sense may be build of multiple closures.
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestTaskRunner);
+  static void RunTask(Task* task);
 };
 
 }  // namespace offline_pages
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp
index 4da37838..9fe6220 100644
--- a/components/page_info_strings.grdp
+++ b/components/page_info_strings.grdp
@@ -285,6 +285,9 @@
     <message name="IDS_PAGE_INFO_TYPE_BLUETOOTH_SCANNING" desc="The label used for the Bluetooth scanning controls in the Page Info popup.">
       Bluetooth scanning
     </message>
+    <message name="IDS_PAGE_INFO_TYPE_NFC" desc="The label used for the NFC permission controls in the Page Info popup.">
+      NFC devices
+    </message>
     <!-- TODO(crbug.com/716303): A few permissions are missing here. -->
 
     <!-- Permission values -->
diff --git a/components/paint_preview/browser/BUILD.gn b/components/paint_preview/browser/BUILD.gn
index 19b85e2..a976b57 100644
--- a/components/paint_preview/browser/BUILD.gn
+++ b/components/paint_preview/browser/BUILD.gn
@@ -11,17 +11,25 @@
       "file_manager.h",
       "hit_tester.cc",
       "hit_tester.h",
+      "paint_preview_client.cc",
+      "paint_preview_client.h",
     ]
 
     deps = [
       "//base",
       "//cc/base",
+      "//content/public/browser",
+      "//mojo/public/cpp/base",
+      "//third_party/blink/public:blink_headers",
+      "//third_party/blink/public/common",
       "//third_party/zlib/google:zip",
       "//ui/gfx/geometry",
       "//url",
     ]
 
     public_deps = [
+      "//components/paint_preview/common",
+      "//components/paint_preview/common/mojom",
       "//components/paint_preview/common/proto",
     ]
   }
@@ -32,12 +40,16 @@
     sources = [
       "file_manager_unittest.cc",
       "hit_tester_unittest.cc",
+      "paint_preview_client_unittest.cc",
     ]
 
     deps = [
       ":browser",
       "//base",
       "//base/test:test_support",
+      "//components/paint_preview/common:test_utils",
+      "//content/public/browser",
+      "//content/test:test_support",
       "//testing/gmock",
       "//testing/gtest",
       "//ui/gfx/geometry",
diff --git a/components/paint_preview/browser/DEPS b/components/paint_preview/browser/DEPS
index eca036b..b5ac44e 100644
--- a/components/paint_preview/browser/DEPS
+++ b/components/paint_preview/browser/DEPS
@@ -1,4 +1,7 @@
 include_rules = [
   "+cc/base",
+  "+content/public/browser",
+  "+content/public/test",
+  "+third_party/blink/public/common",
   "+third_party/zlib/google",
 ]
diff --git a/components/paint_preview/browser/paint_preview_client.cc b/components/paint_preview/browser/paint_preview_client.cc
new file mode 100644
index 0000000..7a704a9f
--- /dev/null
+++ b/components/paint_preview/browser/paint_preview_client.cc
@@ -0,0 +1,320 @@
+// 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 "components/paint_preview/browser/paint_preview_client.h"
+
+#include <utility>
+
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+
+namespace paint_preview {
+
+namespace {
+
+// A frame's GUID is (ProcessID || Routing ID) where || is a bitwise
+// concatenation.
+uint64_t GenerateFrameGuid(content::RenderFrameHost* render_frame_host) {
+  int process_id = render_frame_host->GetProcess()->GetID();
+  int frame_id = render_frame_host->GetRoutingID();
+  return static_cast<uint64_t>(process_id) << 32 | frame_id;
+}
+
+// Converts gfx::Rect to its RectProto form.
+void RectToRectProto(const gfx::Rect& rect, RectProto* proto) {
+  proto->set_x(rect.x());
+  proto->set_y(rect.y());
+  proto->set_width(rect.width());
+  proto->set_height(rect.height());
+}
+
+// Converts |response| into |proto|. Returns a list of the frame GUIDs
+// referenced by the response.
+std::vector<uint64_t> PaintPreviewCaptureResponseToPaintPreviewFrameProto(
+    mojom::PaintPreviewCaptureResponsePtr response,
+    content::RenderFrameHost* render_frame_host,
+    PaintPreviewFrameProto* proto) {
+  int process_id = render_frame_host->GetProcess()->GetID();
+  proto->set_id(static_cast<uint64_t>(process_id) << 32 | response->id);
+
+  std::vector<uint64_t> frame_guids;
+  auto* proto_content_id_proxy_map = proto->mutable_content_id_proxy_id_map();
+  for (const auto& id_pair : response->content_id_proxy_id_map) {
+    content::RenderFrameHost* rfh =
+        content::RenderFrameHost::FromPlaceholderId(process_id, id_pair.second);
+    if (!rfh) {
+      // The render frame host doesn't exist. We won't be able to infill this
+      // content. 0 is an invalid content_id so an empty picture will be used
+      // instead.
+      proto_content_id_proxy_map->insert({id_pair.first, 0});
+      continue;
+    }
+    auto guid = GenerateFrameGuid(rfh);
+    frame_guids.push_back(guid);
+    proto_content_id_proxy_map->insert({id_pair.first, guid});
+  }
+
+  for (const auto& link : response->links) {
+    auto* link_proto = proto->add_links();
+    link_proto->set_url(link->url.spec());
+    RectToRectProto(link->rect, link_proto->mutable_rect());
+  }
+
+  return frame_guids;
+}
+
+}  // namespace
+
+PaintPreviewClient::PaintPreviewParams::PaintPreviewParams() = default;
+PaintPreviewClient::PaintPreviewParams::~PaintPreviewParams() = default;
+
+PaintPreviewClient::PaintPreviewData::PaintPreviewData() = default;
+PaintPreviewClient::PaintPreviewData::~PaintPreviewData() = default;
+PaintPreviewClient::PaintPreviewData& PaintPreviewClient::PaintPreviewData::
+operator=(PaintPreviewData&& rhs) noexcept = default;
+PaintPreviewClient::PaintPreviewData::PaintPreviewData(
+    PaintPreviewData&& other) noexcept = default;
+
+PaintPreviewClient::CreateResult::CreateResult(base::File file,
+                                               base::File::Error error)
+    : file(std::move(file)), error(error) {}
+PaintPreviewClient::CreateResult::~CreateResult() = default;
+PaintPreviewClient::CreateResult::CreateResult(CreateResult&& other) = default;
+PaintPreviewClient::CreateResult& PaintPreviewClient::CreateResult::operator=(
+    CreateResult&& other) = default;
+
+PaintPreviewClient::PaintPreviewClient(content::WebContents* web_contents)
+    : content::WebContentsObserver(web_contents) {}
+PaintPreviewClient::~PaintPreviewClient() = default;
+
+void PaintPreviewClient::CapturePaintPreview(
+    const PaintPreviewParams& params,
+    content::RenderFrameHost* render_frame_host,
+    PaintPreviewCallback callback) {
+  if (base::Contains(document_data_, params.document_guid)) {
+    std::move(callback).Run(params.document_guid,
+                            mojom::PaintPreviewStatus::kGuidCollision, nullptr);
+    return;
+  }
+  document_data_.insert({params.document_guid, PaintPreviewData()});
+  document_data_[params.document_guid].callback = std::move(callback);
+  CapturePaintPreviewInternal(params, render_frame_host);
+}
+
+void PaintPreviewClient::CaptureSubframePaintPreview(
+    const base::UnguessableToken& guid,
+    const gfx::Rect& rect,
+    content::RenderFrameHost* render_subframe_host) {
+  PaintPreviewParams params;
+  params.document_guid = guid;
+  params.clip_rect = rect;
+  params.is_main_frame = false;
+  CapturePaintPreviewInternal(params, render_subframe_host);
+}
+
+void PaintPreviewClient::RenderFrameDeleted(
+    content::RenderFrameHost* render_frame_host) {
+  uint64_t frame_guid = GenerateFrameGuid(render_frame_host);
+  auto it = pending_previews_on_subframe_.find(frame_guid);
+  if (it == pending_previews_on_subframe_.end())
+    return;
+  for (const auto& document_guid : it->second) {
+    auto data_it = document_data_.find(document_guid);
+    if (data_it == document_data_.end())
+      continue;
+    data_it->second.awaiting_subframes.erase(frame_guid);
+    data_it->second.finished_subframes.insert(frame_guid);
+    data_it->second.had_error = true;
+    if (data_it->second.awaiting_subframes.empty()) {
+      interface_ptrs_.erase(frame_guid);
+      OnFinished(document_guid, &data_it->second);
+    }
+  }
+  pending_previews_on_subframe_.erase(frame_guid);
+}
+
+PaintPreviewClient::CreateResult PaintPreviewClient::CreateFileHandle(
+    const base::FilePath& path) {
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  base::File file(path,
+                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  return CreateResult(std::move(file), file.error_details());
+}
+
+mojom::PaintPreviewCaptureParamsPtr PaintPreviewClient::CreateMojoParams(
+    const PaintPreviewParams& params,
+    base::File file) {
+  mojom::PaintPreviewCaptureParamsPtr mojo_params =
+      mojom::PaintPreviewCaptureParams::New();
+  mojo_params->guid = params.document_guid;
+  mojo_params->clip_rect = params.clip_rect;
+  mojo_params->is_main_frame = params.is_main_frame;
+  mojo_params->file = std::move(file);
+  return mojo_params;
+}
+
+void PaintPreviewClient::CapturePaintPreviewInternal(
+    const PaintPreviewParams& params,
+    content::RenderFrameHost* render_frame_host) {
+  uint64_t frame_guid = GenerateFrameGuid(render_frame_host);
+  auto* document_data = &document_data_[params.document_guid];
+  // Deduplicate data if a subframe is required multiple times.
+  if (base::Contains(document_data->awaiting_subframes, frame_guid) ||
+      base::Contains(document_data->finished_subframes, frame_guid))
+    return;
+  base::FilePath file_path = params.root_dir.AppendASCII(
+      base::StrCat({base::NumberToString(frame_guid), ".skp"}));
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+      base::BindOnce(&CreateFileHandle, file_path),
+      base::BindOnce(&PaintPreviewClient::RequestCaptureOnUIThread,
+                     weak_ptr_factory_.GetWeakPtr(), params, frame_guid,
+                     base::Unretained(render_frame_host), file_path));
+}
+
+void PaintPreviewClient::RequestCaptureOnUIThread(
+    const PaintPreviewParams& params,
+    uint64_t frame_guid,
+    content::RenderFrameHost* render_frame_host,
+    const base::FilePath& file_path,
+    CreateResult result) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  auto* document_data = &document_data_[params.document_guid];
+  if (result.error != base::File::FILE_OK) {
+    // Don't block up the UI thread and answer the callback on a different
+    // thread.
+    base::PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(document_data->callback), params.document_guid,
+                       mojom::PaintPreviewStatus::kFileCreationError, nullptr));
+    return;
+  }
+
+  document_data->awaiting_subframes.insert(frame_guid);
+  auto it = pending_previews_on_subframe_.find(frame_guid);
+  if (it != pending_previews_on_subframe_.end()) {
+    it->second.insert(params.document_guid);
+  } else {
+    pending_previews_on_subframe_.insert(std::make_pair(
+        frame_guid,
+        base::flat_set<base::UnguessableToken>({params.document_guid})));
+  }
+
+  if (!base::Contains(interface_ptrs_, frame_guid)) {
+    interface_ptrs_.insert(
+        {frame_guid, mojo::AssociatedRemote<mojom::PaintPreviewRecorder>()});
+    render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
+        &interface_ptrs_[frame_guid]);
+  }
+  interface_ptrs_[frame_guid]->CapturePaintPreview(
+      CreateMojoParams(params, std::move(result.file)),
+      base::BindOnce(&PaintPreviewClient::OnPaintPreviewCapturedCallback,
+                     weak_ptr_factory_.GetWeakPtr(), params.document_guid,
+                     frame_guid, params.is_main_frame, file_path,
+                     base::Unretained(render_frame_host)));
+}
+
+void PaintPreviewClient::OnPaintPreviewCapturedCallback(
+    base::UnguessableToken guid,
+    uint64_t frame_guid,
+    bool is_main_frame,
+    const base::FilePath& filename,
+    content::RenderFrameHost* render_frame_host,
+    mojom::PaintPreviewStatus status,
+    mojom::PaintPreviewCaptureResponsePtr response) {
+  // There is no retry logic so always treat a frame as processed regardless of
+  // |status|
+  MarkFrameAsProcessed(guid, frame_guid);
+
+  if (status == mojom::PaintPreviewStatus::kOk)
+    status = RecordFrame(guid, frame_guid, is_main_frame, filename,
+                         render_frame_host, std::move(response));
+  auto* document_data = &document_data_[guid];
+  if (status != mojom::PaintPreviewStatus::kOk)
+    document_data->had_error = true;
+
+  if (document_data->awaiting_subframes.empty()) {
+    OnFinished(guid, document_data);
+  }
+}
+
+void PaintPreviewClient::MarkFrameAsProcessed(base::UnguessableToken guid,
+                                              uint64_t frame_guid) {
+  pending_previews_on_subframe_[frame_guid].erase(guid);
+  if (pending_previews_on_subframe_[frame_guid].empty())
+    interface_ptrs_.erase(frame_guid);
+  document_data_[guid].finished_subframes.insert(frame_guid);
+  document_data_[guid].awaiting_subframes.erase(frame_guid);
+}
+
+mojom::PaintPreviewStatus PaintPreviewClient::RecordFrame(
+    base::UnguessableToken guid,
+    uint64_t frame_guid,
+    bool is_main_frame,
+    const base::FilePath& filename,
+    content::RenderFrameHost* render_frame_host,
+    mojom::PaintPreviewCaptureResponsePtr response) {
+  auto it = document_data_.find(guid);
+  if (!it->second.proto)
+    it->second.proto = std::make_unique<PaintPreviewProto>();
+
+  PaintPreviewProto* proto_ptr = it->second.proto.get();
+
+  PaintPreviewFrameProto* frame_proto;
+  if (is_main_frame) {
+    frame_proto = proto_ptr->mutable_root_frame();
+    frame_proto->set_is_main_frame(true);
+  } else {
+    frame_proto = proto_ptr->add_subframes();
+  }
+  // Safe since always <#>.skp.
+  frame_proto->set_file_path(filename.AsUTF8Unsafe());
+
+  std::vector<uint64_t> remote_frame_guids =
+      PaintPreviewCaptureResponseToPaintPreviewFrameProto(
+          std::move(response), render_frame_host, frame_proto);
+
+  for (const auto& remote_frame_guid : remote_frame_guids) {
+    if (!base::Contains(it->second.finished_subframes, remote_frame_guid))
+      it->second.awaiting_subframes.insert(remote_frame_guid);
+  }
+  return mojom::PaintPreviewStatus::kOk;
+}
+
+void PaintPreviewClient::OnFinished(base::UnguessableToken guid,
+                                    PaintPreviewData* document_data) {
+  if (!document_data)
+    return;
+  if (document_data->proto) {
+    // At a minimum one frame was captured successfully, it is up to the
+    // caller to decide if a partial success is acceptable based on what is
+    // contained in the proto.
+    base::PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(document_data->callback), guid,
+                       document_data->had_error
+                           ? mojom::PaintPreviewStatus::kPartialSuccess
+                           : mojom::PaintPreviewStatus::kOk,
+                       std::move(document_data->proto)));
+  } else {
+    // A proto could not be created indicating all frames failed to capture.
+    base::PostTask(FROM_HERE,
+                   base::BindOnce(std::move(document_data->callback), guid,
+                                  mojom::PaintPreviewStatus::kFailed, nullptr));
+  }
+  document_data_.erase(guid);
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(PaintPreviewClient)
+
+}  // namespace paint_preview
diff --git a/components/paint_preview/browser/paint_preview_client.h b/components/paint_preview/browser/paint_preview_client.h
new file mode 100644
index 0000000..d28b86c4
--- /dev/null
+++ b/components/paint_preview/browser/paint_preview_client.h
@@ -0,0 +1,203 @@
+// 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 COMPONENTS_PAINT_PREVIEW_BROWSER_PAINT_PREVIEW_CLIENT_H_
+#define COMPONENTS_PAINT_PREVIEW_BROWSER_PAINT_PREVIEW_CLIENT_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/unguessable_token.h"
+#include "components/paint_preview/common/mojom/paint_preview_recorder.mojom.h"
+#include "components/paint_preview/common/proto/paint_preview.pb.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace paint_preview {
+
+// Client responsible for making requests to the mojom::PaintPreviewService. A
+// client coordinates between multiple frames and handles capture and
+// aggreagation of data from both the main frame and subframes.
+class PaintPreviewClient
+    : public content::WebContentsUserData<PaintPreviewClient>,
+      public content::WebContentsObserver {
+ public:
+  using PaintPreviewCallback =
+      base::OnceCallback<void(base::UnguessableToken,
+                              mojom::PaintPreviewStatus,
+                              std::unique_ptr<PaintPreviewProto>)>;
+
+  // Augmented version of mojom::PaintPreviewServiceParams.
+  struct PaintPreviewParams {
+    PaintPreviewParams();
+    ~PaintPreviewParams();
+
+    // The document GUID for this capture.
+    base::UnguessableToken document_guid;
+
+    // The root directory in which to store paint_previews. This should be
+    // a subdirectory inside the active user profile's directory.
+    base::FilePath root_dir;
+
+    // The rect to which to clip the capture to.
+    gfx::Rect clip_rect;
+
+    // Whether the capture is for the main frame or an OOP subframe.
+    bool is_main_frame;
+  };
+
+  ~PaintPreviewClient() override;
+
+  // Captures a paint preview corresponding to the content of
+  // |render_frame_host|. This will work for capturing entire documents if
+  // passed the main frame or for just a specific subframe depending on
+  // |render_frame_host|. |callback| is invoked on completion.
+  void CapturePaintPreview(const PaintPreviewParams& params,
+                           content::RenderFrameHost* render_frame_host,
+                           PaintPreviewCallback callback);
+
+  // Captures a paint preview of the subframe corresponding to
+  // |render_subframe_host|.
+  void CaptureSubframePaintPreview(
+      const base::UnguessableToken& guid,
+      const gfx::Rect& rect,
+      content::RenderFrameHost* render_subframe_host);
+
+  // WebContentsObserver implementation ---------------------------------------
+
+  void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
+
+ private:
+  explicit PaintPreviewClient(content::WebContents* web_contents);
+  friend class content::WebContentsUserData<PaintPreviewClient>;
+
+  // Internal Storage Classes -------------------------------------------------
+
+  // Representation of data for capturing a paint preview.
+  struct PaintPreviewData {
+   public:
+    PaintPreviewData();
+    ~PaintPreviewData();
+
+    base::UnguessableToken guid;
+
+    // Callback that is invoked on completion of data.
+    PaintPreviewCallback callback;
+
+    // All the render frames that are still required.
+    base::flat_set<uint64_t> awaiting_subframes;
+
+    // All the render frames that have finished.
+    base::flat_set<uint64_t> finished_subframes;
+
+    // Data proto that is returned via callback.
+    std::unique_ptr<PaintPreviewProto> proto;
+
+    bool had_error = false;
+
+    PaintPreviewData& operator=(PaintPreviewData&& other) noexcept;
+    PaintPreviewData(PaintPreviewData&& other) noexcept;
+
+   private:
+    PaintPreviewData(const PaintPreviewData&) = delete;
+    PaintPreviewData& operator=(const PaintPreviewData&) = delete;
+  };
+
+  struct CreateResult {
+   public:
+    CreateResult(base::File file, base::File::Error error);
+    ~CreateResult();
+    CreateResult(CreateResult&& other);
+    CreateResult& operator=(CreateResult&& other);
+
+    base::File file;
+    base::File::Error error;
+
+   private:
+    CreateResult(const CreateResult&) = delete;
+    CreateResult& operator=(const CreateResult&) = delete;
+  };
+
+  // Helpers -------------------------------------------------------------------
+
+  static CreateResult CreateFileHandle(const base::FilePath& path);
+
+  mojom::PaintPreviewCaptureParamsPtr CreateMojoParams(
+      const PaintPreviewParams& params,
+      base::File file);
+
+  // Sets up for a capture of a frame on |render_frame_host| according to
+  // |params|.
+  void CapturePaintPreviewInternal(const PaintPreviewParams& params,
+                                   content::RenderFrameHost* render_frame_host);
+
+  // Initiates capture via the PaintPreviewRecorder associated with
+  // |render_frame_host| using |params| to configure the request. |frame_guid|
+  // is the GUID associated with the frame. |path| is file path associated with
+  // the File stored in |result| (base::File isn't aware of its file path).
+  void RequestCaptureOnUIThread(const PaintPreviewParams& params,
+                                uint64_t frame_guid,
+                                content::RenderFrameHost* render_frame_host,
+                                const base::FilePath& path,
+                                CreateResult result);
+
+  // Handles recording the frame and updating client state when capture is
+  // complete.
+  void OnPaintPreviewCapturedCallback(
+      base::UnguessableToken guid,
+      uint64_t frame_guid,
+      bool is_main_frame,
+      const base::FilePath& filename,
+      content::RenderFrameHost* render_frame_host,
+      mojom::PaintPreviewStatus status,
+      mojom::PaintPreviewCaptureResponsePtr response);
+
+  // Marks a frame as having been processed, this should occur regardless of
+  // whether the processed frame is valid as there is no retry.
+  void MarkFrameAsProcessed(base::UnguessableToken guid, uint64_t frame_guid);
+
+  // Records the data from a processed frame if it was captured successfully.
+  mojom::PaintPreviewStatus RecordFrame(
+      base::UnguessableToken guid,
+      uint64_t frame_guid,
+      bool is_main_frame,
+      const base::FilePath& filename,
+      content::RenderFrameHost* render_frame_host,
+      mojom::PaintPreviewCaptureResponsePtr response);
+
+  // Handles finishing the capture once all frames are received.
+  void OnFinished(base::UnguessableToken guid, PaintPreviewData* document_data);
+
+  // Storage ------------------------------------------------------------------
+
+  // Maps a RenderFrameHost and document to a remote interface.
+  base::flat_map<uint64_t, mojo::AssociatedRemote<mojom::PaintPreviewRecorder>>
+      interface_ptrs_;
+
+  // Maps render frame's GUID and document cookies that requested the frame.
+  base::flat_map<uint64_t, base::flat_set<base::UnguessableToken>>
+      pending_previews_on_subframe_;
+
+  // Maps a document GUID to its data.
+  base::flat_map<base::UnguessableToken, PaintPreviewData> document_data_;
+
+  base::WeakPtrFactory<PaintPreviewClient> weak_ptr_factory_{this};
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  PaintPreviewClient(const PaintPreviewClient&) = delete;
+  PaintPreviewClient& operator=(const PaintPreviewClient&) = delete;
+};
+
+}  // namespace paint_preview
+
+#endif  // COMPONENTS_PAINT_PREVIEW_BROWSER_PAINT_PREVIEW_CLIENT_H_
diff --git a/components/paint_preview/browser/paint_preview_client_unittest.cc b/components/paint_preview/browser/paint_preview_client_unittest.cc
new file mode 100644
index 0000000..d3be81d
--- /dev/null
+++ b/components/paint_preview/browser/paint_preview_client_unittest.cc
@@ -0,0 +1,263 @@
+// 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 "components/paint_preview/browser/paint_preview_client.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "base/strings/strcat.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/unguessable_token.h"
+#include "build/build_config.h"
+#include "components/paint_preview/common/mojom/paint_preview_recorder.mojom.h"
+#include "components/paint_preview/common/proto/paint_preview.pb.h"
+#include "components/paint_preview/common/test_utils.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_renderer_host.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+
+namespace paint_preview {
+
+namespace {
+
+class MockPaintPreviewRecorder : public mojom::PaintPreviewRecorder {
+ public:
+  MockPaintPreviewRecorder() = default;
+  ~MockPaintPreviewRecorder() override = default;
+
+  void CapturePaintPreview(
+      mojom::PaintPreviewCaptureParamsPtr params,
+      mojom::PaintPreviewRecorder::CapturePaintPreviewCallback callback)
+      override {
+    {
+      base::ScopedAllowBlockingForTesting scope;
+      if (closure_)
+        std::move(closure_).Run();
+      CheckParams(std::move(params));
+      std::move(callback).Run(status_, std::move(response_));
+    }
+  }
+
+  void SetExpectedParams(mojom::PaintPreviewCaptureParamsPtr params) {
+    expected_params_ = std::move(params);
+  }
+
+  void SetResponse(mojom::PaintPreviewStatus status,
+                   mojom::PaintPreviewCaptureResponsePtr response) {
+    status_ = status;
+    response_ = std::move(response);
+  }
+
+  void SetResponseAction(base::OnceClosure closure) {
+    closure_ = std::move(closure);
+  }
+
+  void BindRequest(mojo::ScopedInterfaceEndpointHandle handle) {
+    binding_.Bind(mojo::PendingAssociatedReceiver<mojom::PaintPreviewRecorder>(
+        std::move(handle)));
+  }
+
+ private:
+  void CheckParams(mojom::PaintPreviewCaptureParamsPtr input_params) {
+    EXPECT_EQ(input_params->guid, expected_params_->guid);
+    EXPECT_EQ(input_params->clip_rect, expected_params_->clip_rect);
+    EXPECT_EQ(input_params->is_main_frame, expected_params_->is_main_frame);
+  }
+
+  base::OnceClosure closure_;
+  mojom::PaintPreviewCaptureParamsPtr expected_params_;
+  mojom::PaintPreviewStatus status_;
+  mojom::PaintPreviewCaptureResponsePtr response_;
+  mojo::AssociatedReceiver<mojom::PaintPreviewRecorder> binding_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(MockPaintPreviewRecorder);
+};
+
+// Returns the GUID corresponding to |rfh|.
+uint64_t FrameGuid(content::RenderFrameHost* rfh) {
+  return static_cast<uint64_t>(rfh->GetProcess()->GetID()) << 32 |
+         rfh->GetRoutingID();
+}
+
+// Convert |params| to the mojo::PaintPreviewServiceParams format. NOTE: this
+// does not set the file parameter as the file is created in the client
+// internals and should be treated as an opaque file (with an unknown path) in
+// the render frame's service.
+mojom::PaintPreviewCaptureParamsPtr ToMojoParams(
+    PaintPreviewClient::PaintPreviewParams params) {
+  mojom::PaintPreviewCaptureParamsPtr params_ptr =
+      mojom::PaintPreviewCaptureParams::New();
+  params_ptr->guid = params.document_guid;
+  params_ptr->is_main_frame = params.is_main_frame;
+  params_ptr->clip_rect = params.clip_rect;
+  return params_ptr;
+}
+
+}  // namespace
+
+class PaintPreviewClientRenderViewHostTest
+    : public content::RenderViewHostTestHarness {
+ public:
+  PaintPreviewClientRenderViewHostTest() {}
+
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    RenderViewHostTestHarness::SetUp();
+    content::RenderFrameHostTester::For(main_rfh())
+        ->InitializeRenderFrameIfNeeded();
+  }
+
+  void OverrideInterface(MockPaintPreviewRecorder* service) {
+    blink::AssociatedInterfaceProvider* remote_interfaces =
+        web_contents()->GetMainFrame()->GetRemoteAssociatedInterfaces();
+    remote_interfaces->OverrideBinderForTesting(
+        mojom::PaintPreviewRecorder::Name_,
+        base::BindRepeating(&MockPaintPreviewRecorder::BindRequest,
+                            base::Unretained(service)));
+  }
+
+  base::ScopedTempDir temp_dir_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PaintPreviewClientRenderViewHostTest);
+};
+
+TEST_F(PaintPreviewClientRenderViewHostTest, CaptureMainFrameMock) {
+  PaintPreviewClient::PaintPreviewParams params;
+  params.document_guid = base::UnguessableToken::Create();
+  params.root_dir = temp_dir_.GetPath();
+  params.is_main_frame = true;
+
+  content::RenderFrameHost* rfh = main_rfh();
+  uint64_t frame_guid = FrameGuid(rfh);
+
+  auto response = mojom::PaintPreviewCaptureResponse::New();
+  response->id = rfh->GetRoutingID();
+
+  PaintPreviewProto expected_proto;
+  PaintPreviewFrameProto* main_frame = expected_proto.mutable_root_frame();
+  main_frame->set_is_main_frame(true);
+  main_frame->set_id(frame_guid);
+  main_frame->set_file_path(
+      temp_dir_.GetPath()
+          .AppendASCII(
+              base::StrCat({base::NumberToString(main_frame->id()), ".skp"}))
+          .AsUTF8Unsafe());
+
+  base::RunLoop loop;
+  auto callback = base::BindOnce(
+      [](base::RepeatingClosure quit, base::UnguessableToken expected_guid,
+         PaintPreviewProto expected_proto, base::UnguessableToken returned_guid,
+         mojom::PaintPreviewStatus status,
+         std::unique_ptr<PaintPreviewProto> proto) {
+        EXPECT_EQ(returned_guid, expected_guid);
+        EXPECT_EQ(status, mojom::PaintPreviewStatus::kOk);
+
+        EXPECT_THAT(*proto, EqualsProto(expected_proto));
+        {
+          base::ScopedAllowBlockingForTesting scope;
+#if defined(OS_WIN)
+          base::FilePath path = base::FilePath(
+              base::UTF8ToUTF16(proto->root_frame().file_path()));
+#else
+          base::FilePath path = base::FilePath(proto->root_frame().file_path());
+#endif
+          EXPECT_TRUE(base::PathExists(path));
+        }
+        quit.Run();
+      },
+      loop.QuitClosure(), params.document_guid, expected_proto);
+  MockPaintPreviewRecorder service;
+  service.SetExpectedParams(ToMojoParams(params));
+  service.SetResponse(mojom::PaintPreviewStatus::kOk, std::move(response));
+  OverrideInterface(&service);
+  PaintPreviewClient::CreateForWebContents(web_contents());
+  auto* client = PaintPreviewClient::FromWebContents(web_contents());
+  ASSERT_NE(client, nullptr);
+  client->CapturePaintPreview(params, rfh, std::move(callback));
+  loop.Run();
+}
+
+TEST_F(PaintPreviewClientRenderViewHostTest, CaptureFailureMock) {
+  PaintPreviewClient::PaintPreviewParams params;
+  params.document_guid = base::UnguessableToken::Create();
+  params.root_dir = temp_dir_.GetPath();
+  params.is_main_frame = true;
+
+  auto response = mojom::PaintPreviewCaptureResponse::New();
+
+  base::RunLoop loop;
+  auto callback = base::BindOnce(
+      [](base::RepeatingClosure quit, base::UnguessableToken expected_guid,
+         base::UnguessableToken returned_guid, mojom::PaintPreviewStatus status,
+         std::unique_ptr<PaintPreviewProto> proto) {
+        EXPECT_EQ(returned_guid, expected_guid);
+        EXPECT_EQ(status, mojom::PaintPreviewStatus::kFailed);
+        quit.Run();
+      },
+      loop.QuitClosure(), params.document_guid);
+  MockPaintPreviewRecorder recorder;
+  recorder.SetExpectedParams(ToMojoParams(params));
+  recorder.SetResponse(mojom::PaintPreviewStatus::kCaptureFailed,
+                       std::move(response));
+  OverrideInterface(&recorder);
+  PaintPreviewClient::CreateForWebContents(web_contents());
+  auto* client = PaintPreviewClient::FromWebContents(web_contents());
+  ASSERT_NE(client, nullptr);
+  client->CapturePaintPreview(params, main_rfh(), std::move(callback));
+  loop.Run();
+}
+
+TEST_F(PaintPreviewClientRenderViewHostTest, RenderFrameDeletedNotCapturing) {
+  // Test that a deleting a render frame doesn't cause any problems if not
+  // capturing.
+  PaintPreviewClient::CreateForWebContents(web_contents());
+  auto* client = PaintPreviewClient::FromWebContents(web_contents());
+  ASSERT_NE(client, nullptr);
+  client->RenderFrameDeleted(main_rfh());
+}
+
+TEST_F(PaintPreviewClientRenderViewHostTest, RenderFrameDeletedDuringCapture) {
+  PaintPreviewClient::PaintPreviewParams params;
+  params.document_guid = base::UnguessableToken::Create();
+  params.root_dir = temp_dir_.GetPath();
+  params.is_main_frame = true;
+
+  content::RenderFrameHost* rfh = main_rfh();
+
+  auto response = mojom::PaintPreviewCaptureResponse::New();
+  response->id = rfh->GetRoutingID();
+
+  base::RunLoop loop;
+  auto callback = base::BindOnce(
+      [](base::RepeatingClosure quit, base::UnguessableToken returned_guid,
+         mojom::PaintPreviewStatus status,
+         std::unique_ptr<PaintPreviewProto> proto) {
+        EXPECT_EQ(status, mojom::PaintPreviewStatus::kFailed);
+        EXPECT_EQ(proto, nullptr);
+        quit.Run();
+      },
+      loop.QuitClosure());
+  MockPaintPreviewRecorder service;
+  service.SetExpectedParams(ToMojoParams(params));
+  service.SetResponse(mojom::PaintPreviewStatus::kOk, std::move(response));
+  OverrideInterface(&service);
+  PaintPreviewClient::CreateForWebContents(web_contents());
+  auto* client = PaintPreviewClient::FromWebContents(web_contents());
+  ASSERT_NE(client, nullptr);
+  service.SetResponseAction(
+      base::BindOnce(&PaintPreviewClient::RenderFrameDeleted,
+                     base::Unretained(client), base::Unretained(rfh)));
+  client->CapturePaintPreview(params, rfh, std::move(callback));
+  loop.Run();
+}
+
+}  // namespace paint_preview
diff --git a/components/paint_preview/common/mojom/paint_preview_recorder.mojom b/components/paint_preview/common/mojom/paint_preview_recorder.mojom
index d20ad4a3..bb6ba91 100644
--- a/components/paint_preview/common/mojom/paint_preview_recorder.mojom
+++ b/components/paint_preview/common/mojom/paint_preview_recorder.mojom
@@ -20,9 +20,19 @@
   // Capturing the SkPicture for the frame failed or the file provided was bad.
   kCaptureFailed,
 
-  // Serializing the proto to shared memory failed. Either the data was invalid,
-  // or the memory could not be allocated.
-  kProtoSerializationFailed,
+  // The GUID provided for the document collides with another in progress
+  // capture.
+  kGuidCollision,
+
+  // Failed to create the file for serialization of the SkPicture.
+  kFileCreationError,
+
+  // Indicates that the capture was at least partially complete but there was an
+  // error.
+  kPartialSuccess,
+
+  // Indicates that the capture failed entirely.
+  kFailed,
 };
 
 struct PaintPreviewCaptureParams {
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index e15961e..84e53f1 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -80,6 +80,8 @@
     "export/password_csv_writer.h",
     "export/password_manager_exporter.cc",
     "export/password_manager_exporter.h",
+    "field_info_manager.cc",
+    "field_info_manager.h",
     "field_info_table.cc",
     "field_info_table.h",
     "form_fetcher.h",
@@ -510,6 +512,7 @@
     "export/csv_writer_unittest.cc",
     "export/password_csv_writer_unittest.cc",
     "export/password_manager_exporter_unittest.cc",
+    "field_info_manager_unittest.cc",
     "field_info_table_unittest.cc",
     "form_fetcher_impl_unittest.cc",
     "form_saver_impl_unittest.cc",
diff --git a/components/password_manager/core/browser/field_info_manager.cc b/components/password_manager/core/browser/field_info_manager.cc
new file mode 100644
index 0000000..2f91da2
--- /dev/null
+++ b/components/password_manager/core/browser/field_info_manager.cc
@@ -0,0 +1,47 @@
+// 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 "components/password_manager/core/browser/field_info_manager.h"
+
+#include "components/password_manager/core/browser/field_info_table.h"
+#include "components/password_manager/core/browser/password_store.h"
+
+namespace password_manager {
+
+FieldInfoManager::FieldInfoManager(
+    scoped_refptr<password_manager::PasswordStore> store)
+    : store_(store) {
+  store_->GetAllFieldInfo(this);
+}
+
+FieldInfoManager::~FieldInfoManager() = default;
+
+void FieldInfoManager::AddFieldType(uint64_t form_signature,
+                                    uint32_t field_signature,
+                                    autofill::ServerFieldType field_type) {
+  field_types_[std::make_pair(form_signature, field_signature)] = field_type;
+  store_->AddFieldInfo(
+      {form_signature, field_signature, field_type, base::Time::Now()});
+}
+
+autofill::ServerFieldType FieldInfoManager::GetFieldType(
+    uint64_t form_signature,
+    uint32_t field_signature) const {
+  auto it = field_types_.find(std::make_pair(form_signature, field_signature));
+  return it == field_types_.end() ? autofill::UNKNOWN_TYPE : it->second;
+}
+
+void FieldInfoManager::OnGetPasswordStoreResults(
+    std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
+  NOTREACHED();
+}
+
+void FieldInfoManager::OnGetAllFieldInfo(std::vector<FieldInfo> field_infos) {
+  for (const auto& field : field_infos) {
+    field_types_[std::make_pair(field.form_signature, field.field_signature)] =
+        field.field_type;
+  }
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/field_info_manager.h b/components/password_manager/core/browser/field_info_manager.h
new file mode 100644
index 0000000..1b89048
--- /dev/null
+++ b/components/password_manager/core/browser/field_info_manager.h
@@ -0,0 +1,46 @@
+// 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 COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FIELD_INFO_MANAGER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FIELD_INFO_MANAGER_H_
+
+#include <map>
+
+#include "components/autofill/core/browser/field_types.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
+
+namespace password_manager {
+
+class PasswordStore;
+
+// Keeps semantic types of web forms fields. Fields are specified with a pair
+// (FormSignature, FieldSignature), which uniquely defines fields in the web
+// (more details on the signature calculation are in signature_util.cc). Types
+// might be PASSWORD, USERNAME, NEW_PASSWORD etc.
+class FieldInfoManager : public KeyedService, public PasswordStoreConsumer {
+ public:
+  FieldInfoManager(scoped_refptr<password_manager::PasswordStore> store);
+  ~FieldInfoManager() override;
+
+  void AddFieldType(uint64_t form_signature,
+                    uint32_t field_signature,
+                    autofill::ServerFieldType field_type);
+  autofill::ServerFieldType GetFieldType(uint64_t form_signature,
+                                         uint32_t field_signature) const;
+
+ private:
+  // PasswordStoreConsumer:
+  void OnGetPasswordStoreResults(
+      std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
+  void OnGetAllFieldInfo(std::vector<FieldInfo>) override;
+
+  std::map<std::pair<uint64_t, uint32_t>, autofill::ServerFieldType>
+      field_types_;
+  scoped_refptr<password_manager::PasswordStore> store_;
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FIELD_INFO_MANAGER_H_
diff --git a/components/password_manager/core/browser/field_info_manager_unittest.cc b/components/password_manager/core/browser/field_info_manager_unittest.cc
new file mode 100644
index 0000000..37d7106
--- /dev/null
+++ b/components/password_manager/core/browser/field_info_manager_unittest.cc
@@ -0,0 +1,78 @@
+// 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 "components/password_manager/core/browser/field_info_manager.h"
+
+#include <vector>
+
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/password_manager/core/browser/field_info_table.h"
+#include "components/password_manager/core/browser/mock_password_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using autofill::PASSWORD;
+using autofill::SINGLE_USERNAME;
+using autofill::UNKNOWN_TYPE;
+using autofill::USERNAME;
+using base::Time;
+
+namespace password_manager {
+namespace {
+
+MATCHER_P3(FieldInfoHasData, form_signature, field_signature, field_type, "") {
+  return arg.form_signature == form_signature &&
+         arg.field_signature == field_signature &&
+         arg.field_type == field_type && arg.create_time != base::Time();
+}
+
+class FieldInfoManagerTest : public testing::Test {
+ public:
+  FieldInfoManagerTest() {
+    test_data_.push_back({101u, 1u, USERNAME, Time::FromTimeT(1)});
+    test_data_.push_back({101u, 10u, PASSWORD, Time::FromTimeT(5)});
+    test_data_.push_back({102u, 1u, SINGLE_USERNAME, Time::FromTimeT(10)});
+
+    store_ = new MockPasswordStore;
+    store_->Init(syncer::SyncableService::StartSyncFlare(), /*prefs=*/nullptr);
+    EXPECT_CALL(*store_, GetAllFieldInfoImpl());
+    field_info_manager_ = std::make_unique<FieldInfoManager>(store_);
+    task_environment_.RunUntilIdle();
+  }
+
+  ~FieldInfoManagerTest() override { store_->ShutdownOnUIThread(); }
+
+ protected:
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::UI};
+  scoped_refptr<MockPasswordStore> store_;
+  std::vector<FieldInfo> test_data_;
+  std::unique_ptr<FieldInfoManager> field_info_manager_;
+};
+
+TEST_F(FieldInfoManagerTest, AddFieldType) {
+  EXPECT_EQ(UNKNOWN_TYPE, field_info_manager_->GetFieldType(101u, 1u));
+
+  EXPECT_CALL(*store_, AddFieldInfoImpl(FieldInfoHasData(101u, 1u, PASSWORD)));
+  field_info_manager_->AddFieldType(101u, 1u, PASSWORD);
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(PASSWORD, field_info_manager_->GetFieldType(101u, 1u));
+}
+
+TEST_F(FieldInfoManagerTest, OnGetAllFieldInfo) {
+  auto* field_info_manager_as_password_consumer =
+      static_cast<PasswordStoreConsumer*>(field_info_manager_.get());
+  field_info_manager_as_password_consumer->OnGetAllFieldInfo(test_data_);
+  for (const FieldInfo& field_info : test_data_) {
+    EXPECT_EQ(field_info.field_type,
+              field_info_manager_->GetFieldType(field_info.form_signature,
+                                                field_info.field_signature));
+  }
+  EXPECT_EQ(UNKNOWN_TYPE, field_info_manager_->GetFieldType(1234u, 1u));
+}
+
+}  // namespace
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index f6e53d6..bc8007b 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -136,7 +136,9 @@
   EXPECT_EQ(expected.username_element, actual.username_element);
   EXPECT_EQ(expected.password_element, actual.password_element);
   EXPECT_EQ(expected.blacklisted_by_user, actual.blacklisted_by_user);
-  EXPECT_EQ(expected.form_data, actual.form_data);
+  FormData::IdentityComparator less;
+  EXPECT_FALSE(less(expected.form_data, actual.form_data));
+  EXPECT_FALSE(less(actual.form_data, expected.form_data));
 }
 
 struct ExpectedGenerationUKM {
diff --git a/components/payments/content/BUILD.gn b/components/payments/content/BUILD.gn
index 711d338..b6711e2 100644
--- a/components/payments/content/BUILD.gn
+++ b/components/payments/content/BUILD.gn
@@ -54,8 +54,8 @@
       "payment_request_web_contents_manager.h",
       "payment_response_helper.cc",
       "payment_response_helper.h",
-      "service_worker_payment_instrument.cc",
-      "service_worker_payment_instrument.h",
+      "service_worker_payment_app.cc",
+      "service_worker_payment_app.h",
     ]
 
     deps += [ "//components/payments/core:method_strings" ]
@@ -130,11 +130,11 @@
 
   if (!is_android) {
     sources += [
-      "payment_instrument_unittest.cc",
+      "payment_app_unittest.cc",
       "payment_request_spec_unittest.cc",
       "payment_request_state_unittest.cc",
       "payment_response_helper_unittest.cc",
-      "service_worker_payment_instrument_unittest.cc",
+      "service_worker_payment_app_unittest.cc",
     ]
   }
 
diff --git a/components/payments/content/mock_identity_observer.cc b/components/payments/content/mock_identity_observer.cc
index c580003..ad1ecca 100644
--- a/components/payments/content/mock_identity_observer.cc
+++ b/components/payments/content/mock_identity_observer.cc
@@ -10,7 +10,7 @@
 
 MockIdentityObserver::~MockIdentityObserver() = default;
 
-base::WeakPtr<ServiceWorkerPaymentInstrument::IdentityObserver>
+base::WeakPtr<ServiceWorkerPaymentApp::IdentityObserver>
 MockIdentityObserver::AsWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
diff --git a/components/payments/content/mock_identity_observer.h b/components/payments/content/mock_identity_observer.h
index b688633..de20859 100644
--- a/components/payments/content/mock_identity_observer.h
+++ b/components/payments/content/mock_identity_observer.h
@@ -9,21 +9,20 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "components/payments/content/service_worker_payment_instrument.h"
+#include "components/payments/content/service_worker_payment_app.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "url/origin.h"
 
 namespace payments {
 
-class MockIdentityObserver
-    : public ServiceWorkerPaymentInstrument::IdentityObserver {
+class MockIdentityObserver : public ServiceWorkerPaymentApp::IdentityObserver {
  public:
   MockIdentityObserver();
   ~MockIdentityObserver() override;
   MOCK_METHOD2(SetInvokedServiceWorkerIdentity,
                void(const url::Origin& origin, int64_t registration_id));
 
-  base::WeakPtr<ServiceWorkerPaymentInstrument::IdentityObserver> AsWeakPtr();
+  base::WeakPtr<ServiceWorkerPaymentApp::IdentityObserver> AsWeakPtr();
 
  private:
   base::WeakPtrFactory<MockIdentityObserver> weak_ptr_factory_{this};
diff --git a/components/payments/content/payment_app_unittest.cc b/components/payments/content/payment_app_unittest.cc
new file mode 100644
index 0000000..02d80277
--- /dev/null
+++ b/components/payments/content/payment_app_unittest.cc
@@ -0,0 +1,349 @@
+// 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 "components/payments/core/payment_app.h"
+
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/payments/content/mock_identity_observer.h"
+#include "components/payments/content/service_worker_payment_app.h"
+#include "components/payments/core/autofill_payment_app.h"
+#include "components/payments/core/mock_payment_request_delegate.h"
+#include "content/public/browser/stored_payment_app.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_browser_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
+
+namespace payments {
+
+namespace {
+
+enum class RequiredPaymentOptions {
+  // None of the shipping address or contact information is required.
+  kNone,
+  // Shipping Address is required.
+  kShippingAddress,
+  // Payer's contact information(phone, name, email) is required.
+  kContactInformation,
+  // Payer's email is required.
+  kPayerEmail,
+  // Both contact information and shipping address are required.
+  kContactInformationAndShippingAddress,
+};
+
+}  // namespace
+
+class PaymentAppTest : public testing::TestWithParam<RequiredPaymentOptions>,
+                       public PaymentRequestSpec::Observer {
+ protected:
+  PaymentAppTest()
+      : address_(autofill::test::GetFullProfile()),
+        local_card_(autofill::test::GetCreditCard()),
+        billing_profiles_({&address_}),
+        required_options_(GetParam()) {
+    local_card_.set_billing_address_id(address_.guid());
+    CreateSpec();
+  }
+
+  std::unique_ptr<ServiceWorkerPaymentApp> CreateServiceWorkerPaymentApp(
+      bool can_preselect,
+      bool handles_shipping,
+      bool handles_name,
+      bool handles_phone,
+      bool handles_email) {
+    constexpr int kBitmapDimension = 16;
+
+    std::unique_ptr<content::StoredPaymentApp> stored_app =
+        std::make_unique<content::StoredPaymentApp>();
+    stored_app->registration_id = 123456;
+    stored_app->scope = GURL("https://bobpay.com");
+    stored_app->name = "bobpay";
+    stored_app->icon = std::make_unique<SkBitmap>();
+    if (can_preselect) {
+      stored_app->icon->allocN32Pixels(kBitmapDimension, kBitmapDimension);
+      stored_app->icon->eraseColor(SK_ColorRED);
+    }
+    if (handles_shipping) {
+      stored_app->supported_delegations.shipping_address = true;
+    }
+    if (handles_name) {
+      stored_app->supported_delegations.payer_name = true;
+    }
+    if (handles_phone) {
+      stored_app->supported_delegations.payer_phone = true;
+    }
+    if (handles_email) {
+      stored_app->supported_delegations.payer_email = true;
+    }
+
+    return std::make_unique<ServiceWorkerPaymentApp>(
+        &browser_context_, GURL("https://testmerchant.com"),
+        GURL("https://testmerchant.com/bobpay"), spec_.get(),
+        std::move(stored_app), &delegate_, identity_observer_.AsWeakPtr());
+  }
+
+  autofill::CreditCard& local_credit_card() { return local_card_; }
+  std::vector<autofill::AutofillProfile*>& billing_profiles() {
+    return billing_profiles_;
+  }
+
+  RequiredPaymentOptions required_options() const { return required_options_; }
+
+ private:
+  // PaymentRequestSpec::Observer
+  void OnSpecUpdated() override {}
+
+  void CreateSpec() {
+    std::vector<mojom::PaymentMethodDataPtr> method_data;
+    mojom::PaymentOptionsPtr payment_options = mojom::PaymentOptions::New();
+    switch (required_options_) {
+      case RequiredPaymentOptions::kNone:
+        break;
+      case RequiredPaymentOptions::kShippingAddress:
+        payment_options->request_shipping = true;
+        break;
+      case RequiredPaymentOptions::kContactInformation:
+        payment_options->request_payer_name = true;
+        payment_options->request_payer_email = true;
+        payment_options->request_payer_phone = true;
+        break;
+      case RequiredPaymentOptions::kPayerEmail:
+        payment_options->request_payer_email = true;
+        break;
+      case RequiredPaymentOptions::kContactInformationAndShippingAddress:
+        payment_options->request_shipping = true;
+        payment_options->request_payer_name = true;
+        payment_options->request_payer_email = true;
+        payment_options->request_payer_phone = true;
+        break;
+    }
+    spec_ = std::make_unique<PaymentRequestSpec>(
+        std::move(payment_options), mojom::PaymentDetails::New(),
+        std::move(method_data), this, "en-US");
+  }
+
+  content::BrowserTaskEnvironment task_environment_;
+  content::TestBrowserContext browser_context_;
+  autofill::AutofillProfile address_;
+  autofill::CreditCard local_card_;
+  std::vector<autofill::AutofillProfile*> billing_profiles_;
+  MockPaymentRequestDelegate delegate_;
+  MockIdentityObserver identity_observer_;
+  RequiredPaymentOptions required_options_;
+  std::unique_ptr<PaymentRequestSpec> spec_;
+
+  DISALLOW_COPY_AND_ASSIGN(PaymentAppTest);
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    PaymentAppTest,
+    ::testing::Values(
+        RequiredPaymentOptions::kNone,
+        RequiredPaymentOptions::kShippingAddress,
+        RequiredPaymentOptions::kContactInformation,
+        RequiredPaymentOptions::kPayerEmail,
+        RequiredPaymentOptions::kContactInformationAndShippingAddress));
+
+TEST_P(PaymentAppTest, SortApps) {
+  std::vector<PaymentApp*> apps;
+  // Add a complete app with mismatching type.
+  autofill::CreditCard complete_dismatching_card = local_credit_card();
+  AutofillPaymentApp complete_dismatching_cc_app(
+      "visa", complete_dismatching_card,
+      /*matches_merchant_card_type_exactly=*/false, billing_profiles(), "en-US",
+      nullptr);
+  apps.push_back(&complete_dismatching_cc_app);
+
+  // Add an app with no billing address.
+  autofill::CreditCard card_with_no_address = local_credit_card();
+  card_with_no_address.set_billing_address_id("");
+  AutofillPaymentApp cc_app_with_no_address(
+      "visa", card_with_no_address, /*matches_merchant_card_type_exactly=*/true,
+      billing_profiles(), "en-US", nullptr);
+  apps.push_back(&cc_app_with_no_address);
+
+  // Add an expired app.
+  autofill::CreditCard expired_card = local_credit_card();
+  expired_card.SetExpirationYear(2016);
+  AutofillPaymentApp expired_cc_app("visa", expired_card,
+                                    /*matches_merchant_card_type_exactly=*/true,
+                                    billing_profiles(), "en-US", nullptr);
+  apps.push_back(&expired_cc_app);
+
+  // Add a non-preselectable sw based payment app.
+  std::unique_ptr<ServiceWorkerPaymentApp> non_preselectable_sw_app =
+      CreateServiceWorkerPaymentApp(
+          false /* = can_preselect */, false /* = handles_shipping */,
+          false /* = handles_name */, false /* = handles_phone */,
+          false /* = handles_email */);
+  apps.push_back(non_preselectable_sw_app.get());
+
+  // Add a preselectable sw based payment app.
+  std::unique_ptr<ServiceWorkerPaymentApp> preselectable_sw_app =
+      CreateServiceWorkerPaymentApp(
+          true /* = can_preselect */, false /* = handles_shipping */,
+          false /* = handles_name */, false /* = handles_phone */,
+          false /* = handles_email */);
+  apps.push_back(preselectable_sw_app.get());
+
+  // Add an app with no name.
+  autofill::CreditCard card_with_no_name = local_credit_card();
+  card_with_no_name.SetInfo(
+      autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
+      base::ASCIIToUTF16(""), "en-US");
+  AutofillPaymentApp cc_app_with_no_name(
+      "visa", card_with_no_name, /*matches_merchant_card_type_exactly=*/true,
+      billing_profiles(), "en-US", nullptr);
+  apps.push_back(&cc_app_with_no_name);
+
+  // Add a complete matching app.
+  autofill::CreditCard complete_matching_card = local_credit_card();
+  AutofillPaymentApp complete_matching_cc_app(
+      "visa", complete_matching_card,
+      /*matches_merchant_card_type_exactly=*/true, billing_profiles(), "en-US",
+      nullptr);
+  apps.push_back(&complete_matching_cc_app);
+
+  // Add an app with no number.
+  autofill::CreditCard card_with_no_number = local_credit_card();
+  card_with_no_number.SetNumber(base::ASCIIToUTF16(""));
+  AutofillPaymentApp cc_app_with_no_number(
+      "visa", card_with_no_number, /*matches_merchant_card_type_exactly=*/true,
+      billing_profiles(), "en-US", nullptr);
+  apps.push_back(&cc_app_with_no_number);
+
+  // Add a complete matching app that is most frequently used.
+  autofill::CreditCard complete_frequently_used_card = local_credit_card();
+  AutofillPaymentApp complete_frequently_used_cc_app(
+      "visa", complete_frequently_used_card,
+      /*matches_merchant_card_type_exactly=*/true, billing_profiles(), "en-US",
+      nullptr);
+  apps.push_back(&complete_frequently_used_cc_app);
+  // Record use of this app.
+  complete_frequently_used_cc_app.credit_card()->RecordAndLogUse();
+
+  // Sort the apps and validate the new order.
+  PaymentApp::SortApps(&apps);
+  size_t i = 0;
+  EXPECT_EQ(apps[i++], preselectable_sw_app.get());
+  EXPECT_EQ(apps[i++], non_preselectable_sw_app.get());
+
+  // Autfill apps (credit cards) come after sw apps.
+  EXPECT_EQ(apps[i++], &complete_frequently_used_cc_app);
+  EXPECT_EQ(apps[i++], &complete_matching_cc_app);
+  EXPECT_EQ(apps[i++], &complete_dismatching_cc_app);
+  EXPECT_EQ(apps[i++], &expired_cc_app);
+  EXPECT_EQ(apps[i++], &cc_app_with_no_name);
+  EXPECT_EQ(apps[i++], &cc_app_with_no_address);
+  EXPECT_EQ(apps[i++], &cc_app_with_no_number);
+}
+
+TEST_P(PaymentAppTest, SortAppsBasedOnSupportedDelegations) {
+  std::vector<PaymentApp*> apps;
+  // Add a preselectable sw based payment app which does not support
+  // shipping or contact delegation.
+  std::unique_ptr<ServiceWorkerPaymentApp> does_not_support_delegations =
+      CreateServiceWorkerPaymentApp(
+          true /* = can_preselect */, false /* = handles_shipping */,
+          false /* = handles_name */, false /* = handles_phone */,
+          false /* = handles_email */);
+  apps.push_back(does_not_support_delegations.get());
+
+  // Add a preselectable sw based payment app which handles shipping.
+  std::unique_ptr<ServiceWorkerPaymentApp> handles_shipping_address =
+      CreateServiceWorkerPaymentApp(
+          true /* = can_preselect */, true /* = handles_shipping */,
+          false /* = handles_name */, false /* = handles_phone */,
+          false /* = handles_email */);
+  apps.push_back(handles_shipping_address.get());
+
+  // Add a preselectable sw based payment app which handles payer's
+  // email.
+  std::unique_ptr<ServiceWorkerPaymentApp> handles_payer_email =
+      CreateServiceWorkerPaymentApp(
+          true /* = can_preselect */, false /* = handles_shipping */,
+          false /* = handles_name */, false /* = handles_phone */,
+          true /* = handles_email */);
+  apps.push_back(handles_payer_email.get());
+
+  // Add a preselectable sw based payment app which handles contact
+  // information.
+  std::unique_ptr<ServiceWorkerPaymentApp> handles_contact_info =
+      CreateServiceWorkerPaymentApp(
+          true /* = can_preselect */, false /* = handles_shipping */,
+          true /* = handles_name */, true /* = handles_phone */,
+          true /* = handles_email */);
+  apps.push_back(handles_contact_info.get());
+
+  // Add a preselectable sw based payment app which handles both shipping
+  // address and contact information.
+  std::unique_ptr<ServiceWorkerPaymentApp> handles_shipping_and_contact =
+      CreateServiceWorkerPaymentApp(
+          true /* = can_preselect */, true /* = handles_shipping */,
+          true /* = handles_name */, true /* = handles_phone */,
+          true /* = handles_email */);
+  apps.push_back(handles_shipping_and_contact.get());
+
+  PaymentApp::SortApps(&apps);
+  size_t i = 0;
+
+  switch (required_options()) {
+    case RequiredPaymentOptions::kNone: {
+      // When no payemnt option is required the order of the payment apps
+      // does not change.
+      EXPECT_EQ(apps[i++], does_not_support_delegations.get());
+      EXPECT_EQ(apps[i++], handles_shipping_address.get());
+      EXPECT_EQ(apps[i++], handles_payer_email.get());
+      EXPECT_EQ(apps[i++], handles_contact_info.get());
+      EXPECT_EQ(apps[i++], handles_shipping_and_contact.get());
+      break;
+    }
+    case RequiredPaymentOptions::kShippingAddress: {
+      // apps that handle shipping address come first.
+      EXPECT_EQ(apps[i++], handles_shipping_address.get());
+      EXPECT_EQ(apps[i++], handles_shipping_and_contact.get());
+      // The order is unchanged for apps that do not handle shipping.
+      EXPECT_EQ(apps[i++], does_not_support_delegations.get());
+      EXPECT_EQ(apps[i++], handles_payer_email.get());
+      EXPECT_EQ(apps[i++], handles_contact_info.get());
+      break;
+    }
+    case RequiredPaymentOptions::kContactInformation: {
+      // apps that handle all required contact information come first.
+      EXPECT_EQ(apps[i++], handles_contact_info.get());
+      EXPECT_EQ(apps[i++], handles_shipping_and_contact.get());
+      // The app that partially handles contact information comes next.
+      EXPECT_EQ(apps[i++], handles_payer_email.get());
+      // The order for apps that do not handle contact information is not
+      // changed.
+      EXPECT_EQ(apps[i++], does_not_support_delegations.get());
+      EXPECT_EQ(apps[i++], handles_shipping_address.get());
+      break;
+    }
+    case RequiredPaymentOptions::kPayerEmail: {
+      EXPECT_EQ(apps[i++], handles_payer_email.get());
+      EXPECT_EQ(apps[i++], handles_contact_info.get());
+      EXPECT_EQ(apps[i++], handles_shipping_and_contact.get());
+      EXPECT_EQ(apps[i++], does_not_support_delegations.get());
+      EXPECT_EQ(apps[i++], handles_shipping_address.get());
+      break;
+    }
+    case RequiredPaymentOptions::kContactInformationAndShippingAddress: {
+      EXPECT_EQ(apps[i++], handles_shipping_and_contact.get());
+      EXPECT_EQ(apps[i++], handles_shipping_address.get());
+      EXPECT_EQ(apps[i++], handles_contact_info.get());
+      EXPECT_EQ(apps[i++], handles_payer_email.get());
+      EXPECT_EQ(apps[i++], does_not_support_delegations.get());
+      break;
+    }
+  }
+}
+
+}  // namespace payments
diff --git a/components/payments/content/payment_instrument_unittest.cc b/components/payments/content/payment_instrument_unittest.cc
deleted file mode 100644
index 9352064..0000000
--- a/components/payments/content/payment_instrument_unittest.cc
+++ /dev/null
@@ -1,350 +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 "components/payments/core/payment_instrument.h"
-
-#include <vector>
-
-#include "base/strings/utf_string_conversions.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/payments/content/mock_identity_observer.h"
-#include "components/payments/content/service_worker_payment_instrument.h"
-#include "components/payments/core/autofill_payment_instrument.h"
-#include "components/payments/core/mock_payment_request_delegate.h"
-#include "content/public/browser/stored_payment_app.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_browser_context.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
-
-namespace payments {
-
-namespace {
-
-enum class RequiredPaymentOptions {
-  // None of the shipping address or contact information is required.
-  kNone,
-  // Shipping Address is required.
-  kShippingAddress,
-  // Payer's contact information(phone, name, email) is required.
-  kContactInformation,
-  // Payer's email is required.
-  kPayerEmail,
-  // Both contact information and shipping address are required.
-  kContactInformationAndShippingAddress,
-};
-
-}  // namespace
-
-class PaymentInstrumentTest
-    : public testing::TestWithParam<RequiredPaymentOptions>,
-      public PaymentRequestSpec::Observer {
- protected:
-  PaymentInstrumentTest()
-      : address_(autofill::test::GetFullProfile()),
-        local_card_(autofill::test::GetCreditCard()),
-        billing_profiles_({&address_}),
-        required_options_(GetParam()) {
-    local_card_.set_billing_address_id(address_.guid());
-    CreateSpec();
-  }
-
-  std::unique_ptr<ServiceWorkerPaymentInstrument>
-  CreateServiceWorkerPaymentInstrument(bool can_preselect,
-                                       bool handles_shipping,
-                                       bool handles_name,
-                                       bool handles_phone,
-                                       bool handles_email) {
-    constexpr int kBitmapDimension = 16;
-
-    std::unique_ptr<content::StoredPaymentApp> stored_app =
-        std::make_unique<content::StoredPaymentApp>();
-    stored_app->registration_id = 123456;
-    stored_app->scope = GURL("https://bobpay.com");
-    stored_app->name = "bobpay";
-    stored_app->icon = std::make_unique<SkBitmap>();
-    if (can_preselect) {
-      stored_app->icon->allocN32Pixels(kBitmapDimension, kBitmapDimension);
-      stored_app->icon->eraseColor(SK_ColorRED);
-    }
-    if (handles_shipping) {
-      stored_app->supported_delegations.shipping_address = true;
-    }
-    if (handles_name) {
-      stored_app->supported_delegations.payer_name = true;
-    }
-    if (handles_phone) {
-      stored_app->supported_delegations.payer_phone = true;
-    }
-    if (handles_email) {
-      stored_app->supported_delegations.payer_email = true;
-    }
-
-    return std::make_unique<ServiceWorkerPaymentInstrument>(
-        &browser_context_, GURL("https://testmerchant.com"),
-        GURL("https://testmerchant.com/bobpay"), spec_.get(),
-        std::move(stored_app), &delegate_, identity_observer_.AsWeakPtr());
-  }
-
-  autofill::CreditCard& local_credit_card() { return local_card_; }
-  std::vector<autofill::AutofillProfile*>& billing_profiles() {
-    return billing_profiles_;
-  }
-
-  RequiredPaymentOptions required_options() const { return required_options_; }
-
- private:
-  // PaymentRequestSpec::Observer
-  void OnSpecUpdated() override {}
-
-  void CreateSpec() {
-    std::vector<mojom::PaymentMethodDataPtr> method_data;
-    mojom::PaymentOptionsPtr payment_options = mojom::PaymentOptions::New();
-    switch (required_options_) {
-      case RequiredPaymentOptions::kNone:
-        break;
-      case RequiredPaymentOptions::kShippingAddress:
-        payment_options->request_shipping = true;
-        break;
-      case RequiredPaymentOptions::kContactInformation:
-        payment_options->request_payer_name = true;
-        payment_options->request_payer_email = true;
-        payment_options->request_payer_phone = true;
-        break;
-      case RequiredPaymentOptions::kPayerEmail:
-        payment_options->request_payer_email = true;
-        break;
-      case RequiredPaymentOptions::kContactInformationAndShippingAddress:
-        payment_options->request_shipping = true;
-        payment_options->request_payer_name = true;
-        payment_options->request_payer_email = true;
-        payment_options->request_payer_phone = true;
-        break;
-    }
-    spec_ = std::make_unique<PaymentRequestSpec>(
-        std::move(payment_options), mojom::PaymentDetails::New(),
-        std::move(method_data), this, "en-US");
-  }
-
-  content::BrowserTaskEnvironment task_environment_;
-  content::TestBrowserContext browser_context_;
-  autofill::AutofillProfile address_;
-  autofill::CreditCard local_card_;
-  std::vector<autofill::AutofillProfile*> billing_profiles_;
-  MockPaymentRequestDelegate delegate_;
-  MockIdentityObserver identity_observer_;
-  RequiredPaymentOptions required_options_;
-  std::unique_ptr<PaymentRequestSpec> spec_;
-
-  DISALLOW_COPY_AND_ASSIGN(PaymentInstrumentTest);
-};
-
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    PaymentInstrumentTest,
-    ::testing::Values(
-        RequiredPaymentOptions::kNone,
-        RequiredPaymentOptions::kShippingAddress,
-        RequiredPaymentOptions::kContactInformation,
-        RequiredPaymentOptions::kPayerEmail,
-        RequiredPaymentOptions::kContactInformationAndShippingAddress));
-
-TEST_P(PaymentInstrumentTest, SortInstruments) {
-  std::vector<PaymentInstrument*> instruments;
-  // Add a complete instrument with mismatching type.
-  autofill::CreditCard complete_dismatching_card = local_credit_card();
-  AutofillPaymentInstrument complete_dismatching_cc_instrument(
-      "visa", complete_dismatching_card,
-      /*matches_merchant_card_type_exactly=*/false, billing_profiles(), "en-US",
-      nullptr);
-  instruments.push_back(&complete_dismatching_cc_instrument);
-
-  // Add an instrument with no billing address.
-  autofill::CreditCard card_with_no_address = local_credit_card();
-  card_with_no_address.set_billing_address_id("");
-  AutofillPaymentInstrument cc_instrument_with_no_address(
-      "visa", card_with_no_address, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  instruments.push_back(&cc_instrument_with_no_address);
-
-  // Add an expired instrument.
-  autofill::CreditCard expired_card = local_credit_card();
-  expired_card.SetExpirationYear(2016);
-  AutofillPaymentInstrument expired_cc_instrument(
-      "visa", expired_card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  instruments.push_back(&expired_cc_instrument);
-
-  // Add a non-preselectable sw based payment instrument.
-  std::unique_ptr<ServiceWorkerPaymentInstrument>
-      non_preselectable_sw_instrument = CreateServiceWorkerPaymentInstrument(
-          false /* = can_preselect */, false /* = handles_shipping */,
-          false /* = handles_name */, false /* = handles_phone */,
-          false /* = handles_email */);
-  instruments.push_back(non_preselectable_sw_instrument.get());
-
-  // Add a preselectable sw based payment instrument.
-  std::unique_ptr<ServiceWorkerPaymentInstrument> preselectable_sw_instrument =
-      CreateServiceWorkerPaymentInstrument(
-          true /* = can_preselect */, false /* = handles_shipping */,
-          false /* = handles_name */, false /* = handles_phone */,
-          false /* = handles_email */);
-  instruments.push_back(preselectable_sw_instrument.get());
-
-  // Add an instrument with no name.
-  autofill::CreditCard card_with_no_name = local_credit_card();
-  card_with_no_name.SetInfo(
-      autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
-      base::ASCIIToUTF16(""), "en-US");
-  AutofillPaymentInstrument cc_instrument_with_no_name(
-      "visa", card_with_no_name, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  instruments.push_back(&cc_instrument_with_no_name);
-
-  // Add a complete matching instrument.
-  autofill::CreditCard complete_matching_card = local_credit_card();
-  AutofillPaymentInstrument complete_matching_cc_instrument(
-      "visa", complete_matching_card,
-      /*matches_merchant_card_type_exactly=*/true, billing_profiles(), "en-US",
-      nullptr);
-  instruments.push_back(&complete_matching_cc_instrument);
-
-  // Add an instrument with no number.
-  autofill::CreditCard card_with_no_number = local_credit_card();
-  card_with_no_number.SetNumber(base::ASCIIToUTF16(""));
-  AutofillPaymentInstrument cc_instrument_with_no_number(
-      "visa", card_with_no_number, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  instruments.push_back(&cc_instrument_with_no_number);
-
-  // Add a complete matching instrument that is most frequently used.
-  autofill::CreditCard complete_frequently_used_card = local_credit_card();
-  AutofillPaymentInstrument complete_frequently_used_cc_instrument(
-      "visa", complete_frequently_used_card,
-      /*matches_merchant_card_type_exactly=*/true, billing_profiles(), "en-US",
-      nullptr);
-  instruments.push_back(&complete_frequently_used_cc_instrument);
-  // Record use of this instrument.
-  complete_frequently_used_cc_instrument.credit_card()->RecordAndLogUse();
-
-  // Sort the instruments and validate the new order.
-  PaymentInstrument::SortInstruments(&instruments);
-  size_t i = 0;
-  EXPECT_EQ(instruments[i++], preselectable_sw_instrument.get());
-  EXPECT_EQ(instruments[i++], non_preselectable_sw_instrument.get());
-
-  // Autfill instruments (credit cards) come after sw instruments.
-  EXPECT_EQ(instruments[i++], &complete_frequently_used_cc_instrument);
-  EXPECT_EQ(instruments[i++], &complete_matching_cc_instrument);
-  EXPECT_EQ(instruments[i++], &complete_dismatching_cc_instrument);
-  EXPECT_EQ(instruments[i++], &expired_cc_instrument);
-  EXPECT_EQ(instruments[i++], &cc_instrument_with_no_name);
-  EXPECT_EQ(instruments[i++], &cc_instrument_with_no_address);
-  EXPECT_EQ(instruments[i++], &cc_instrument_with_no_number);
-}
-
-TEST_P(PaymentInstrumentTest, SortInstrumentsBasedOnSupportedDelegations) {
-  std::vector<PaymentInstrument*> instruments;
-  // Add a preselectable sw based payment instrument which does not support
-  // shipping or contact delegation.
-  std::unique_ptr<ServiceWorkerPaymentInstrument> does_not_support_delegations =
-      CreateServiceWorkerPaymentInstrument(
-          true /* = can_preselect */, false /* = handles_shipping */,
-          false /* = handles_name */, false /* = handles_phone */,
-          false /* = handles_email */);
-  instruments.push_back(does_not_support_delegations.get());
-
-  // Add a preselectable sw based payment instrument which handles shipping.
-  std::unique_ptr<ServiceWorkerPaymentInstrument> handles_shipping_address =
-      CreateServiceWorkerPaymentInstrument(
-          true /* = can_preselect */, true /* = handles_shipping */,
-          false /* = handles_name */, false /* = handles_phone */,
-          false /* = handles_email */);
-  instruments.push_back(handles_shipping_address.get());
-
-  // Add a preselectable sw based payment instrument which handles payer's
-  // email.
-  std::unique_ptr<ServiceWorkerPaymentInstrument> handles_payer_email =
-      CreateServiceWorkerPaymentInstrument(
-          true /* = can_preselect */, false /* = handles_shipping */,
-          false /* = handles_name */, false /* = handles_phone */,
-          true /* = handles_email */);
-  instruments.push_back(handles_payer_email.get());
-
-  // Add a preselectable sw based payment instrument which handles contact
-  // information.
-  std::unique_ptr<ServiceWorkerPaymentInstrument> handles_contact_info =
-      CreateServiceWorkerPaymentInstrument(
-          true /* = can_preselect */, false /* = handles_shipping */,
-          true /* = handles_name */, true /* = handles_phone */,
-          true /* = handles_email */);
-  instruments.push_back(handles_contact_info.get());
-
-  // Add a preselectable sw based payment instrument which handles both shipping
-  // address and contact information.
-  std::unique_ptr<ServiceWorkerPaymentInstrument> handles_shipping_and_contact =
-      CreateServiceWorkerPaymentInstrument(
-          true /* = can_preselect */, true /* = handles_shipping */,
-          true /* = handles_name */, true /* = handles_phone */,
-          true /* = handles_email */);
-  instruments.push_back(handles_shipping_and_contact.get());
-
-  PaymentInstrument::SortInstruments(&instruments);
-  size_t i = 0;
-
-  switch (required_options()) {
-    case RequiredPaymentOptions::kNone: {
-      // When no payemnt option is required the order of the payment instruments
-      // does not change.
-      EXPECT_EQ(instruments[i++], does_not_support_delegations.get());
-      EXPECT_EQ(instruments[i++], handles_shipping_address.get());
-      EXPECT_EQ(instruments[i++], handles_payer_email.get());
-      EXPECT_EQ(instruments[i++], handles_contact_info.get());
-      EXPECT_EQ(instruments[i++], handles_shipping_and_contact.get());
-      break;
-    }
-    case RequiredPaymentOptions::kShippingAddress: {
-      // Instruments that handle shipping address come first.
-      EXPECT_EQ(instruments[i++], handles_shipping_address.get());
-      EXPECT_EQ(instruments[i++], handles_shipping_and_contact.get());
-      // The order is unchanged for instruments that do not handle shipping.
-      EXPECT_EQ(instruments[i++], does_not_support_delegations.get());
-      EXPECT_EQ(instruments[i++], handles_payer_email.get());
-      EXPECT_EQ(instruments[i++], handles_contact_info.get());
-      break;
-    }
-    case RequiredPaymentOptions::kContactInformation: {
-      // Instruments that handle all required contact information come first.
-      EXPECT_EQ(instruments[i++], handles_contact_info.get());
-      EXPECT_EQ(instruments[i++], handles_shipping_and_contact.get());
-      // The instrument that partially handles contact information comes next.
-      EXPECT_EQ(instruments[i++], handles_payer_email.get());
-      // The order for instruments that do not handle contact information is not
-      // changed.
-      EXPECT_EQ(instruments[i++], does_not_support_delegations.get());
-      EXPECT_EQ(instruments[i++], handles_shipping_address.get());
-      break;
-    }
-    case RequiredPaymentOptions::kPayerEmail: {
-      EXPECT_EQ(instruments[i++], handles_payer_email.get());
-      EXPECT_EQ(instruments[i++], handles_contact_info.get());
-      EXPECT_EQ(instruments[i++], handles_shipping_and_contact.get());
-      EXPECT_EQ(instruments[i++], does_not_support_delegations.get());
-      EXPECT_EQ(instruments[i++], handles_shipping_address.get());
-      break;
-    }
-    case RequiredPaymentOptions::kContactInformationAndShippingAddress: {
-      EXPECT_EQ(instruments[i++], handles_shipping_and_contact.get());
-      EXPECT_EQ(instruments[i++], handles_shipping_address.get());
-      EXPECT_EQ(instruments[i++], handles_contact_info.get());
-      EXPECT_EQ(instruments[i++], handles_payer_email.get());
-      EXPECT_EQ(instruments[i++], does_not_support_delegations.get());
-      break;
-    }
-  }
-}
-
-}  // namespace payments
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc
index 18ca4f9..e3e7866 100644
--- a/components/payments/content/payment_request.cc
+++ b/components/payments/content/payment_request.cc
@@ -17,15 +17,14 @@
 #include "components/payments/content/payment_details_converter.h"
 #include "components/payments/content/payment_request_converter.h"
 #include "components/payments/content/payment_request_web_contents_manager.h"
-#include "components/payments/content/service_worker_payment_instrument.h"
 #include "components/payments/core/can_make_payment_query.h"
 #include "components/payments/core/error_strings.h"
 #include "components/payments/core/features.h"
 #include "components/payments/core/method_strings.h"
 #include "components/payments/core/native_error_strings.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_details.h"
 #include "components/payments/core/payment_details_validation.h"
-#include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/payment_prefs.h"
 #include "components/payments/core/payments_experimental_features.h"
 #include "components/payments/core/payments_validators.h"
@@ -45,7 +44,7 @@
 using ::payments::mojom::CanMakePaymentQueryResult;
 using ::payments::mojom::HasEnrolledInstrumentQueryResult;
 
-bool IsGooglePaymentMethodInstrumentSelected(const std::string& method_name) {
+bool IsGooglePaymentMethod(const std::string& method_name) {
   return method_name == methods::kGooglePay ||
          method_name == methods::kAndroidPay;
 }
@@ -342,14 +341,13 @@
     return;
   }
 
-  if (state()->selected_instrument() && state()->IsPaymentAppInvoked() &&
+  if (state()->selected_app() && state()->IsPaymentAppInvoked() &&
       payment_handler_host_.is_changing()) {
     payment_handler_host_.UpdateWith(
         PaymentDetailsConverter::ConvertToPaymentRequestDetailsUpdate(
-            details, state()->selected_instrument()->HandlesShippingAddress(),
-            base::BindRepeating(
-                &PaymentInstrument::IsValidForPaymentMethodIdentifier,
-                state()->selected_instrument()->AsWeakPtr())));
+            details, state()->selected_app()->HandlesShippingAddress(),
+            base::BindRepeating(&PaymentApp::IsValidForPaymentMethodIdentifier,
+                                state()->selected_app()->AsWeakPtr())));
   }
 
   bool is_resolving_promise_passed_into_show_method = !spec_->IsInitialized();
@@ -558,7 +556,7 @@
     bool methods_supported,
     const std::string& error_message) {
   if (is_show_called_ && observer_for_testing_)
-    observer_for_testing_->OnShowInstrumentsReady();
+    observer_for_testing_->OnShowAppsReady();
 
   if (methods_supported) {
     if (SatisfiesSkipUIConstraints())
@@ -594,16 +592,16 @@
       base::FeatureList::IsEnabled(features::kWebPaymentsSingleAppUiSkip) &&
       base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps) &&
       is_show_user_gesture_ && state()->IsInitialized() &&
-      spec()->IsInitialized() && state()->available_instruments().size() == 1 &&
+      spec()->IsInitialized() && state()->available_apps().size() == 1 &&
       spec()->stringified_method_data().size() == 1 &&
       (!spec()->request_shipping() ||
-       state()->available_instruments().front()->HandlesShippingAddress()) &&
+       state()->available_apps().front()->HandlesShippingAddress()) &&
       (!spec()->request_payer_name() ||
-       state()->available_instruments().front()->HandlesPayerName()) &&
+       state()->available_apps().front()->HandlesPayerName()) &&
       (!spec()->request_payer_phone() ||
-       state()->available_instruments().front()->HandlesPayerPhone()) &&
+       state()->available_apps().front()->HandlesPayerPhone()) &&
       (!spec()->request_payer_email() ||
-       state()->available_instruments().front()->HandlesPayerEmail());
+       state()->available_apps().front()->HandlesPayerEmail());
   if (skipped_payment_request_ui_) {
     DCHECK(state()->IsInitialized() && spec()->IsInitialized());
     journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SKIPPED_SHOW);
@@ -624,30 +622,29 @@
 
   // Log the correct "selected instrument" metric according to its type and
   // the method name in response.
-  DCHECK(state_->selected_instrument());
+  DCHECK(state_->selected_app());
   JourneyLogger::Event selected_event =
       JourneyLogger::Event::EVENT_SELECTED_OTHER;
-  switch (state_->selected_instrument()->type()) {
-    case PaymentInstrument::Type::AUTOFILL:
+  switch (state_->selected_app()->type()) {
+    case PaymentApp::Type::AUTOFILL:
       selected_event = JourneyLogger::Event::EVENT_SELECTED_CREDIT_CARD;
       break;
-    case PaymentInstrument::Type::SERVICE_WORKER_APP: {
-      selected_event =
-          IsGooglePaymentMethodInstrumentSelected(response->method_name)
-              ? JourneyLogger::Event::EVENT_SELECTED_GOOGLE
-              : JourneyLogger::Event::EVENT_SELECTED_OTHER;
+    case PaymentApp::Type::SERVICE_WORKER_APP: {
+      selected_event = IsGooglePaymentMethod(response->method_name)
+                           ? JourneyLogger::Event::EVENT_SELECTED_GOOGLE
+                           : JourneyLogger::Event::EVENT_SELECTED_OTHER;
       break;
     }
-    case PaymentInstrument::Type::NATIVE_MOBILE_APP:
+    case PaymentApp::Type::NATIVE_MOBILE_APP:
       NOTREACHED();
       break;
   }
   journey_logger_.SetEventOccurred(selected_event);
 
   // If currently interactive, show the processing spinner. Autofill payment
-  // instruments request a CVC, so they are always interactive at this point. A
-  // payment handler may elect to be non-interactive by not showing a
-  // confirmation page to the user.
+  // apps request a CVC, so they are always interactive at this point. A payment
+  // handler may elect to be non-interactive by not showing a confirmation page
+  // to the user.
   if (delegate_->IsInteractive())
     delegate_->ShowProcessingSpinner();
 
@@ -734,10 +731,9 @@
 
 void PaymentRequest::Pay() {
   journey_logger_.SetEventOccurred(JourneyLogger::EVENT_PAY_CLICKED);
-  DCHECK(state_->selected_instrument());
-  if (state_->selected_instrument()->type() ==
-      PaymentInstrument::Type::SERVICE_WORKER_APP) {
-    static_cast<ServiceWorkerPaymentInstrument*>(state_->selected_instrument())
+  DCHECK(state_->selected_app());
+  if (state_->selected_app()->type() == PaymentApp::Type::SERVICE_WORKER_APP) {
+    static_cast<ServiceWorkerPaymentApp*>(state_->selected_app())
         ->set_payment_handler_host(payment_handler_host_.Bind());
   }
   state_->GeneratePaymentResponse();
diff --git a/components/payments/content/payment_request.h b/components/payments/content/payment_request.h
index 1707075..2a3976f 100644
--- a/components/payments/content/payment_request.h
+++ b/components/payments/content/payment_request.h
@@ -15,7 +15,7 @@
 #include "components/payments/content/payment_request_display_manager.h"
 #include "components/payments/content/payment_request_spec.h"
 #include "components/payments/content/payment_request_state.h"
-#include "components/payments/content/service_worker_payment_instrument.h"
+#include "components/payments/content/service_worker_payment_app.h"
 #include "components/payments/core/journey_logger.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -42,7 +42,7 @@
                        public PaymentHandlerHost::Delegate,
                        public PaymentRequestSpec::Observer,
                        public PaymentRequestState::Delegate,
-                       public ServiceWorkerPaymentInstrument::IdentityObserver {
+                       public ServiceWorkerPaymentApp::IdentityObserver {
  public:
   class ObserverForTest {
    public:
@@ -50,7 +50,7 @@
     virtual void OnCanMakePaymentReturned() = 0;
     virtual void OnHasEnrolledInstrumentCalled() = 0;
     virtual void OnHasEnrolledInstrumentReturned() = 0;
-    virtual void OnShowInstrumentsReady() {}
+    virtual void OnShowAppsReady() {}
     virtual void OnNotSupportedError() = 0;
     virtual void OnConnectionTerminated() = 0;
     virtual void OnAbortCalled() = 0;
@@ -99,7 +99,7 @@
   void OnShippingAddressSelected(mojom::PaymentAddressPtr address) override;
   void OnPayerInfoSelected(mojom::PayerDetailPtr payer_info) override;
 
-  // ServiceWorkerPaymentInstrument::IdentityObserver:
+  // ServiceWorkerPaymentApp::IdentityObserver:
   void SetInvokedServiceWorkerIdentity(const url::Origin& origin,
                                        int64_t registration_id) override;
 
diff --git a/components/payments/content/payment_request_spec.cc b/components/payments/content/payment_request_spec.cc
index 98e2306..31fe5c1 100644
--- a/components/payments/content/payment_request_spec.cc
+++ b/components/payments/content/payment_request_spec.cc
@@ -14,7 +14,7 @@
 #include "components/payments/content/payment_request_converter.h"
 #include "components/payments/core/features.h"
 #include "components/payments/core/method_strings.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_method_data.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/payments_experimental_features.h"
@@ -333,17 +333,17 @@
 }
 
 const mojom::PaymentItemPtr& PaymentRequestSpec::GetTotal(
-    PaymentInstrument* selected_instrument) const {
+    PaymentApp* selected_app) const {
   const mojom::PaymentDetailsModifierPtr* modifier =
-      GetApplicableModifier(selected_instrument);
+      GetApplicableModifier(selected_app);
   return modifier && (*modifier)->total ? (*modifier)->total : details_->total;
 }
 
 std::vector<const mojom::PaymentItemPtr*> PaymentRequestSpec::GetDisplayItems(
-    PaymentInstrument* selected_instrument) const {
+    PaymentApp* selected_app) const {
   std::vector<const mojom::PaymentItemPtr*> display_items;
   const mojom::PaymentDetailsModifierPtr* modifier =
-      GetApplicableModifier(selected_instrument);
+      GetApplicableModifier(selected_app);
   DCHECK(details_->display_items);
   for (const auto& item : *details_->display_items) {
     display_items.push_back(&item);
@@ -364,9 +364,8 @@
 }
 
 const mojom::PaymentDetailsModifierPtr*
-PaymentRequestSpec::GetApplicableModifier(
-    PaymentInstrument* selected_instrument) const {
-  if (!selected_instrument ||
+PaymentRequestSpec::GetApplicableModifier(PaymentApp* selected_app) const {
+  if (!selected_app ||
       !base::FeatureList::IsEnabled(features::kWebPaymentsModifiers))
     return nullptr;
 
@@ -386,7 +385,7 @@
         &supported_types, &url_payment_method_identifiers,
         &payment_method_identifiers_set, &stringified_method_data);
 
-    if (selected_instrument->IsValidForModifier(
+    if (selected_app->IsValidForModifier(
             modifier->method_data->supported_method,
             !modifier->method_data->supported_networks.empty(),
             supported_card_networks_set,
diff --git a/components/payments/content/payment_request_spec.h b/components/payments/content/payment_request_spec.h
index 1b93d50..ea28e95 100644
--- a/components/payments/content/payment_request_spec.h
+++ b/components/payments/content/payment_request_spec.h
@@ -23,7 +23,7 @@
 
 namespace payments {
 
-class PaymentInstrument;
+class PaymentApp;
 
 // The spec contains all the options that the merchant has specified about this
 // Payment Request. It's a (mostly) read-only view, which can be updated in
@@ -189,13 +189,12 @@
   UpdateReason current_update_reason() const { return current_update_reason_; }
 
   // Returns the total object of this payment request, taking into account the
-  // applicable modifier for |selected_instrument| if any.
-  const mojom::PaymentItemPtr& GetTotal(
-      PaymentInstrument* selected_instrument) const;
+  // applicable modifier for |selected_app| if any.
+  const mojom::PaymentItemPtr& GetTotal(PaymentApp* selected_app) const;
   // Returns the display items for this payment request, taking into account the
-  // applicable modifier for |selected_instrument| if any.
+  // applicable modifier for |selected_app| if any.
   std::vector<const mojom::PaymentItemPtr*> GetDisplayItems(
-      PaymentInstrument* selected_instrument) const;
+      PaymentApp* selected_app) const;
 
   const std::vector<mojom::PaymentShippingOptionPtr>& GetShippingOptions()
       const;
@@ -207,9 +206,9 @@
 
  private:
   // Returns the first applicable modifier in the Payment Request for the
-  // |selected_instrument|.
+  // |selected_app|.
   const mojom::PaymentDetailsModifierPtr* GetApplicableModifier(
-      PaymentInstrument* selected_instrument) const;
+      PaymentApp* selected_app) const;
 
   // Updates the |selected_shipping_option| based on the data passed to this
   // payment request by the website. This will set selected_shipping_option_ to
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index 40699a93..0511a13 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -21,13 +21,13 @@
 #include "components/payments/content/content_payment_request_delegate.h"
 #include "components/payments/content/payment_manifest_web_data_service.h"
 #include "components/payments/content/payment_response_helper.h"
-#include "components/payments/content/service_worker_payment_instrument.h"
+#include "components/payments/content/service_worker_payment_app.h"
 #include "components/payments/core/autofill_card_validation.h"
-#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
 #include "components/payments/core/error_strings.h"
 #include "components/payments/core/features.h"
 #include "components/payments/core/method_strings.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/payments_experimental_features.h"
 #include "content/public/common/content_features.h"
@@ -35,14 +35,12 @@
 namespace payments {
 namespace {
 
-// Checks whether any of the |instruments| return true in
-// IsValidForCanMakePayment().
+// Checks whether any of the |apps| return true in IsValidForCanMakePayment().
 bool GetHasEnrolledInstrument(
-    const std::vector<std::unique_ptr<PaymentInstrument>>& instruments) {
-  return std::any_of(instruments.begin(), instruments.end(),
-                     [](const auto& instrument) {
-                       return instrument->IsValidForCanMakePayment();
-                     });
+    const std::vector<std::unique_ptr<PaymentApp>>& apps) {
+  return std::any_of(apps.begin(), apps.end(), [](const auto& app) {
+    return app->IsValidForCanMakePayment();
+  });
 }
 
 // Invokes the |callback| with |status|.
@@ -70,7 +68,7 @@
     const std::string& app_locale,
     autofill::PersonalDataManager* personal_data_manager,
     ContentPaymentRequestDelegate* payment_request_delegate,
-    base::WeakPtr<ServiceWorkerPaymentInstrument::IdentityObserver>
+    base::WeakPtr<ServiceWorkerPaymentApp::IdentityObserver>
         sw_identity_observer,
     JourneyLogger* journey_logger)
     : is_ready_to_pay_(false),
@@ -87,8 +85,8 @@
       selected_contact_profile_(nullptr),
       invalid_shipping_profile_(nullptr),
       invalid_contact_profile_(nullptr),
-      selected_instrument_(nullptr),
-      number_of_pending_sw_payment_instruments_(0),
+      selected_app_(nullptr),
+      number_of_pending_sw_payment_apps_(0),
       payment_request_delegate_(payment_request_delegate),
       sw_identity_observer_(sw_identity_observer),
       profile_comparator_(app_locale, *spec) {
@@ -114,8 +112,8 @@
   } else {
     PopulateProfileCache();
     SetDefaultProfileSelections();
-    get_all_instruments_finished_ = true;
-    has_enrolled_instrument_ = GetHasEnrolledInstrument(available_instruments_);
+    get_all_apps_finished_ = true;
+    has_enrolled_instrument_ = GetHasEnrolledInstrument(available_apps_);
   }
   spec_->AddObserver(this);
 }
@@ -129,58 +127,53 @@
     content::PaymentAppProvider::PaymentApps apps,
     ServiceWorkerPaymentAppFactory::InstallablePaymentApps installable_apps,
     const std::string& error_message) {
-  number_of_pending_sw_payment_instruments_ =
-      apps.size() + installable_apps.size();
+  number_of_pending_sw_payment_apps_ = apps.size() + installable_apps.size();
   get_all_payment_apps_error_ = error_message;
-  if (number_of_pending_sw_payment_instruments_ == 0U) {
-    FinishedGetAllSWPaymentInstruments();
+  if (number_of_pending_sw_payment_apps_ == 0U) {
+    FinishedGetAllSWPaymentApps();
     return;
   }
 
-  for (auto& app : apps) {
-    std::unique_ptr<ServiceWorkerPaymentInstrument> instrument =
-        std::make_unique<ServiceWorkerPaymentInstrument>(
-            web_contents->GetBrowserContext(), top_level_origin, frame_origin,
-            spec_, std::move(app.second), payment_request_delegate_,
-            sw_identity_observer_);
-    instrument->ValidateCanMakePayment(
-        base::BindOnce(&PaymentRequestState::OnSWPaymentInstrumentValidated,
+  for (auto& installed_app : apps) {
+    auto app = std::make_unique<ServiceWorkerPaymentApp>(
+        web_contents->GetBrowserContext(), top_level_origin, frame_origin,
+        spec_, std::move(installed_app.second), payment_request_delegate_,
+        sw_identity_observer_);
+    app->ValidateCanMakePayment(
+        base::BindOnce(&PaymentRequestState::OnSWPaymentAppValidated,
                        weak_ptr_factory_.GetWeakPtr()));
-    available_instruments_.push_back(std::move(instrument));
+    available_apps_.push_back(std::move(app));
   }
 
   for (auto& installable_app : installable_apps) {
-    std::unique_ptr<ServiceWorkerPaymentInstrument> instrument =
-        std::make_unique<ServiceWorkerPaymentInstrument>(
-            web_contents, top_level_origin, frame_origin, spec_,
-            std::move(installable_app.second), installable_app.first.spec(),
-            payment_request_delegate_, sw_identity_observer_);
-    instrument->ValidateCanMakePayment(
-        base::BindOnce(&PaymentRequestState::OnSWPaymentInstrumentValidated,
+    auto app = std::make_unique<ServiceWorkerPaymentApp>(
+        web_contents, top_level_origin, frame_origin, spec_,
+        std::move(installable_app.second), installable_app.first.spec(),
+        payment_request_delegate_, sw_identity_observer_);
+    app->ValidateCanMakePayment(
+        base::BindOnce(&PaymentRequestState::OnSWPaymentAppValidated,
                        weak_ptr_factory_.GetWeakPtr()));
-    available_instruments_.push_back(std::move(instrument));
+    available_apps_.push_back(std::move(app));
   }
 }
 
-void PaymentRequestState::OnSWPaymentInstrumentValidated(
-    ServiceWorkerPaymentInstrument* instrument,
-    bool result) {
-  has_non_autofill_instrument_ |= result;
+void PaymentRequestState::OnSWPaymentAppValidated(ServiceWorkerPaymentApp* app,
+                                                  bool result) {
+  has_non_autofill_app_ |= result;
 
-  // Remove service worker payment instruments failed on validation.
+  // Remove service worker payment apps failed on validation.
   if (!result) {
-    for (size_t i = 0; i < available_instruments_.size(); i++) {
-      if (available_instruments_[i].get() == instrument) {
-        available_instruments_.erase(available_instruments_.begin() + i);
+    for (size_t i = 0; i < available_apps_.size(); i++) {
+      if (available_apps_[i].get() == app) {
+        available_apps_.erase(available_apps_.begin() + i);
         break;
       }
     }
   }
 
-  std::vector<std::string> instrument_method_names =
-      instrument->GetInstrumentMethodNames();
-  if (base::Contains(instrument_method_names, methods::kGooglePay) ||
-      base::Contains(instrument_method_names, methods::kAndroidPay)) {
+  std::vector<std::string> app_method_names = app->GetAppMethodNames();
+  if (base::Contains(app_method_names, methods::kGooglePay) ||
+      base::Contains(app_method_names, methods::kAndroidPay)) {
     journey_logger_->SetEventOccurred(
         JourneyLogger::EVENT_AVAILABLE_METHOD_GOOGLE);
   } else {
@@ -188,20 +181,20 @@
         JourneyLogger::EVENT_AVAILABLE_METHOD_OTHER);
   }
 
-  if (--number_of_pending_sw_payment_instruments_ > 0)
+  if (--number_of_pending_sw_payment_apps_ > 0)
     return;
 
-  FinishedGetAllSWPaymentInstruments();
+  FinishedGetAllSWPaymentApps();
 }
 
-void PaymentRequestState::FinishedGetAllSWPaymentInstruments() {
+void PaymentRequestState::FinishedGetAllSWPaymentApps() {
   PopulateProfileCache();
   SetDefaultProfileSelections();
 
-  get_all_instruments_finished_ = true;
-  has_enrolled_instrument_ = GetHasEnrolledInstrument(available_instruments_);
-  are_requested_methods_supported_ |= !available_instruments_.empty();
-  NotifyOnGetAllPaymentInstrumentsFinished();
+  get_all_apps_finished_ = true;
+  has_enrolled_instrument_ = GetHasEnrolledInstrument(available_apps_);
+  are_requested_methods_supported_ |= !available_apps_.empty();
+  NotifyOnGetAllPaymentAppsFinished();
   NotifyInitialized();
 
   // Fulfill the pending CanMakePayment call.
@@ -262,7 +255,7 @@
 }
 
 void PaymentRequestState::CanMakePayment(StatusCallback callback) {
-  if (!get_all_instruments_finished_) {
+  if (!get_all_apps_finished_) {
     DCHECK(!can_make_payment_callback_);
     can_make_payment_callback_ = std::move(callback);
     return;
@@ -272,7 +265,7 @@
 }
 
 void PaymentRequestState::HasEnrolledInstrument(StatusCallback callback) {
-  if (!get_all_instruments_finished_) {
+  if (!get_all_apps_finished_) {
     DCHECK(!has_enrolled_instrument_callback_);
     has_enrolled_instrument_callback_ = std::move(callback);
     return;
@@ -283,7 +276,7 @@
 
 void PaymentRequestState::AreRequestedMethodsSupported(
     MethodsSupportedCallback callback) {
-  if (!get_all_instruments_finished_) {
+  if (!get_all_apps_finished_) {
     are_requested_methods_supported_callback_ = std::move(callback);
     return;
   }
@@ -296,7 +289,7 @@
 
 void PaymentRequestState::CheckRequestedMethodsSupported(
     MethodsSupportedCallback callback) {
-  DCHECK(get_all_instruments_finished_);
+  DCHECK(get_all_apps_finished_);
 
   // Don't modify the value of |are_requested_methods_supported_|, because it's
   // used for canMakePayment().
@@ -304,7 +297,7 @@
   if (supported && is_show_user_gesture_ &&
       base::Contains(spec_->payment_method_identifiers_set(),
                      methods::kBasicCard) &&
-      !has_non_autofill_instrument_ && !has_enrolled_instrument_ &&
+      !has_non_autofill_app_ && !has_enrolled_instrument_ &&
       PaymentsExperimentalFeatures::IsEnabled(
           features::kStrictHasEnrolledAutofillInstrument)) {
     supported = false;
@@ -332,14 +325,14 @@
 
   // Once the response is ready, will call back into OnPaymentResponseReady.
   response_helper_ = std::make_unique<PaymentResponseHelper>(
-      app_locale_, spec_, selected_instrument_, payment_request_delegate_,
+      app_locale_, spec_, selected_app_, payment_request_delegate_,
       selected_shipping_profile_, selected_contact_profile_, this);
 }
 
 void PaymentRequestState::OnPaymentAppWindowClosed() {
-  DCHECK(selected_instrument_);
+  DCHECK(selected_app_);
   response_helper_.reset();
-  selected_instrument_->OnPaymentAppWindowClosed();
+  selected_app_->OnPaymentAppWindowClosed();
 }
 
 void PaymentRequestState::RecordUseStats() {
@@ -359,10 +352,10 @@
     }
   }
 
-  selected_instrument_->RecordUse();
+  selected_app_->RecordUse();
 }
 
-void PaymentRequestState::AddAutofillPaymentInstrument(
+void PaymentRequestState::AddAutofillPaymentApp(
     bool selected,
     const autofill::CreditCard& card) {
   std::string basic_card_network =
@@ -383,20 +376,20 @@
       card.card_type() != autofill::CreditCard::CARD_TYPE_UNKNOWN ||
       spec_->supported_card_types_set().size() == kTotalNumberOfCardTypes;
 
-  // AutofillPaymentInstrument makes a copy of |card| so it is effectively
-  // owned by this object.
-  auto instrument = std::make_unique<AutofillPaymentInstrument>(
+  // AutofillPaymentApp makes a copy of |card| so it is effectively owned by
+  // this object.
+  auto app = std::make_unique<AutofillPaymentApp>(
       basic_card_network, card, matches_merchant_card_type_exactly,
       shipping_profiles_, app_locale_, payment_request_delegate_);
-  instrument->set_is_requested_autofill_data_available(
+  app->set_is_requested_autofill_data_available(
       is_requested_autofill_data_available_);
-  available_instruments_.push_back(std::move(instrument));
+  available_apps_.push_back(std::move(app));
   journey_logger_->SetEventOccurred(
       JourneyLogger::EVENT_AVAILABLE_METHOD_BASIC_CARD);
 
   if (selected) {
-    SetSelectedInstrument(available_instruments_.back().get(),
-                          SectionSelectionStatus::kAddedSelected);
+    SetSelectedApp(available_apps_.back().get(),
+                   SectionSelectionStatus::kAddedSelected);
   }
 }
 
@@ -482,10 +475,10 @@
                            selection_status);
 }
 
-void PaymentRequestState::SetSelectedInstrument(
-    PaymentInstrument* instrument,
+void PaymentRequestState::SetSelectedApp(
+    PaymentApp* app,
     SectionSelectionStatus selection_status) {
-  selected_instrument_ = instrument;
+  selected_app_ = app;
   UpdateIsReadyToPayAndNotifyObservers();
   IncrementSelectionStatus(JourneyLogger::Section::SECTION_PAYMENT_METHOD,
                            selection_status);
@@ -530,7 +523,7 @@
 }
 
 bool PaymentRequestState::IsInitialized() const {
-  return get_all_instruments_finished_;
+  return get_all_apps_finished_;
 }
 
 void PaymentRequestState::SelectDefaultShippingAddressAndNotifyObservers() {
@@ -552,21 +545,20 @@
   if (!spec_->request_shipping())
     return false;
 
-  return selected_instrument_ ? !selected_instrument_->HandlesShippingAddress()
-                              : true;
+  return selected_app_ ? !selected_app_->HandlesShippingAddress() : true;
 }
 
 bool PaymentRequestState::ShouldShowContactSection() const {
   if (spec_->request_payer_name() &&
-      (!selected_instrument_ || !selected_instrument_->HandlesPayerName())) {
+      (!selected_app_ || !selected_app_->HandlesPayerName())) {
     return true;
   }
   if (spec_->request_payer_email() &&
-      (!selected_instrument_ || !selected_instrument_->HandlesPayerEmail())) {
+      (!selected_app_ || !selected_app_->HandlesPayerEmail())) {
     return true;
   }
   if (spec_->request_payer_phone() &&
-      (!selected_instrument_ || !selected_instrument_->HandlesPayerPhone())) {
+      (!selected_app_ || !selected_app_->HandlesPayerPhone())) {
     return true;
   }
 
@@ -621,14 +613,14 @@
         shipping_profiles_.size(), has_complete_shipping);
   }
 
-  // Create the list of available instruments. A copy of each card will be made
-  // by their respective AutofillPaymentInstrument.
+  // Create the list of available apps. A copy of each card will be made by
+  // their respective AutofillPaymentApps.
   const std::vector<autofill::CreditCard*>& cards =
       personal_data_manager_->GetCreditCardsToSuggest(
           /*include_server_cards=*/base::FeatureList::IsEnabled(
               payments::features::kReturnGooglePayInBasicCard));
   for (autofill::CreditCard* card : cards)
-    AddAutofillPaymentInstrument(/*selected=*/false, *card);
+    AddAutofillPaymentApp(/*selected=*/false, *card);
 }
 
 void PaymentRequestState::SetDefaultProfileSelections() {
@@ -643,19 +635,18 @@
   profile_comparator()->RecordMissingFieldsOfContactProfile(
       contact_profiles().empty() ? nullptr : contact_profiles()[0]);
 
-  // Sort instruments.
-  PaymentInstrument::SortInstruments(&available_instruments_);
+  // Sort apps.
+  PaymentApp::SortApps(&available_apps_);
 
-  selected_instrument_ = nullptr;
-  if (!available_instruments_.empty() &&
-      available_instruments_[0]->IsCompleteForPayment() &&
-      available_instruments_[0]->IsExactlyMatchingMerchantRequest()) {
-    selected_instrument_ = available_instruments_[0].get();
+  selected_app_ = nullptr;
+  if (!available_apps_.empty() && available_apps_[0]->IsCompleteForPayment() &&
+      available_apps_[0]->IsExactlyMatchingMerchantRequest()) {
+    selected_app_ = available_apps_[0].get();
   }
 
   // Record the missing required payment fields when no complete payment
   // info exists.
-  if (available_instruments_.empty()) {
+  if (available_apps_.empty()) {
     if (spec_->supports_basic_card()) {
       // All fields are missing when basic-card is requested but no card exits.
       base::UmaHistogramSparse("PaymentRequest.MissingPaymentFields",
@@ -663,20 +654,18 @@
                                    CREDIT_CARD_NO_NUMBER |
                                    CREDIT_CARD_NO_BILLING_ADDRESS);
     }
-  } else if (available_instruments_[0]->type() ==
-             PaymentInstrument::Type::AUTOFILL) {
-    // Record the missing fields (if any) of the most complete instrument when
-    // it's autofill based. SW based instruments are always complete.
-    static_cast<const AutofillPaymentInstrument*>(
-        available_instruments_[0].get())
-        ->RecordMissingFieldsForInstrument();
+  } else if (available_apps_[0]->type() == PaymentApp::Type::AUTOFILL) {
+    // Record the missing fields (if any) of the most complete app when
+    // it's autofill based. SW based apps are always complete.
+    static_cast<const AutofillPaymentApp*>(available_apps_[0].get())
+        ->RecordMissingFieldsForApp();
   }
 
   SelectDefaultShippingAddressAndNotifyObservers();
 
   journey_logger_->SetNumberOfSuggestionsShown(
-      JourneyLogger::Section::SECTION_PAYMENT_METHOD,
-      available_instruments().size(), selected_instrument_);
+      JourneyLogger::Section::SECTION_PAYMENT_METHOD, available_apps().size(),
+      selected_app_);
 }
 
 void PaymentRequestState::UpdateIsReadyToPayAndNotifyObservers() {
@@ -685,9 +674,9 @@
   NotifyOnSelectedInformationChanged();
 }
 
-void PaymentRequestState::NotifyOnGetAllPaymentInstrumentsFinished() {
+void PaymentRequestState::NotifyOnGetAllPaymentAppsFinished() {
   for (auto& observer : observers_)
-    observer.OnGetAllPaymentInstrumentsFinished();
+    observer.OnGetAllPaymentAppsFinished();
 }
 
 void PaymentRequestState::NotifyOnSelectedInformationChanged() {
@@ -697,9 +686,8 @@
 
 bool PaymentRequestState::ArePaymentDetailsSatisfied() {
   // There is no need to check for supported networks, because only supported
-  // instruments are listed/created in the flow.
-  return selected_instrument_ != nullptr &&
-         selected_instrument_->IsCompleteForPayment();
+  // apps are listed/created in the flow.
+  return selected_app_ != nullptr && selected_app_->IsCompleteForPayment();
 }
 
 bool PaymentRequestState::ArePaymentOptionsSatisfied() {
diff --git a/components/payments/content/payment_request_state.h b/components/payments/content/payment_request_state.h
index 94c80d0..5ceee5c 100644
--- a/components/payments/content/payment_request_state.h
+++ b/components/payments/content/payment_request_state.h
@@ -16,8 +16,8 @@
 #include "components/payments/content/initialization_task.h"
 #include "components/payments/content/payment_request_spec.h"
 #include "components/payments/content/payment_response_helper.h"
+#include "components/payments/content/service_worker_payment_app.h"
 #include "components/payments/content/service_worker_payment_app_factory.h"
-#include "components/payments/content/service_worker_payment_instrument.h"
 #include "components/payments/core/journey_logger.h"
 #include "components/payments/core/payments_profile_comparator.h"
 #include "content/public/browser/payment_app_provider.h"
@@ -34,7 +34,7 @@
 namespace payments {
 
 class ContentPaymentRequestDelegate;
-class PaymentInstrument;
+class PaymentApp;
 
 // Keeps track of the information currently selected by the user and whether the
 // user is ready to pay. Uses information from the PaymentRequestSpec, which is
@@ -51,8 +51,8 @@
   // notification about the state changing.
   class Observer {
    public:
-    // Called when finished getting all available payment instruments.
-    virtual void OnGetAllPaymentInstrumentsFinished() = 0;
+    // Called when finished getting all available payment apps.
+    virtual void OnGetAllPaymentAppsFinished() = 0;
 
     // Called when the information (payment method, address/contact info,
     // shipping option) changes.
@@ -107,18 +107,17 @@
       base::OnceCallback<void(bool methods_supported,
                               const std::string& error_message)>;
 
-  PaymentRequestState(
-      content::WebContents* web_contents,
-      const GURL& top_level_origin,
-      const GURL& frame_origin,
-      PaymentRequestSpec* spec,
-      Delegate* delegate,
-      const std::string& app_locale,
-      autofill::PersonalDataManager* personal_data_manager,
-      ContentPaymentRequestDelegate* payment_request_delegate,
-      base::WeakPtr<ServiceWorkerPaymentInstrument::IdentityObserver>
-          sw_identity_observer,
-      JourneyLogger* journey_logger);
+  PaymentRequestState(content::WebContents* web_contents,
+                      const GURL& top_level_origin,
+                      const GURL& frame_origin,
+                      PaymentRequestSpec* spec,
+                      Delegate* delegate,
+                      const std::string& app_locale,
+                      autofill::PersonalDataManager* personal_data_manager,
+                      ContentPaymentRequestDelegate* payment_request_delegate,
+                      base::WeakPtr<ServiceWorkerPaymentApp::IdentityObserver>
+                          sw_identity_observer,
+                      JourneyLogger* journey_logger);
   ~PaymentRequestState() override;
 
   // PaymentResponseHelper::Delegate
@@ -180,12 +179,9 @@
   autofill::AutofillProfile* invalid_contact_profile() const {
     return invalid_contact_profile_;
   }
-  // Returns the currently selected instrument for this PaymentRequest flow.
-  // It's not guaranteed to be complete. Returns nullptr if there is no selected
-  // instrument.
-  PaymentInstrument* selected_instrument() const {
-    return selected_instrument_;
-  }
+  // Returns the currently selected app for this PaymentRequest flow. It's not
+  // guaranteed to be complete. Returns nullptr if there is no selected app.
+  PaymentApp* selected_app() const { return selected_app_; }
 
   // Returns the appropriate Autofill Profiles for this user. The profiles
   // returned are owned by the PaymentRequestState.
@@ -195,16 +191,14 @@
   const std::vector<autofill::AutofillProfile*>& contact_profiles() {
     return contact_profiles_;
   }
-  const std::vector<std::unique_ptr<PaymentInstrument>>&
-  available_instruments() {
-    return available_instruments_;
+  const std::vector<std::unique_ptr<PaymentApp>>& available_apps() {
+    return available_apps_;
   }
 
-  // Creates and adds an AutofillPaymentInstrument, which makes a copy of
-  // |card|. |selected| indicates if the newly-created instrument should be
-  // selected, after which observers will be notified.
-  void AddAutofillPaymentInstrument(bool selected,
-                                    const autofill::CreditCard& card);
+  // Creates and adds an AutofillPaymentApp, which makes a copy of |card|.
+  // |selected| indicates if the newly-created app should be selected, after
+  // which observers will be notified.
+  void AddAutofillPaymentApp(bool selected, const autofill::CreditCard& card);
 
   // Creates and adds an AutofillProfile as a shipping profile, which makes a
   // copy of |profile|. |selected| indicates if the newly-created shipping
@@ -225,19 +219,16 @@
                                   SectionSelectionStatus selection_status);
   void SetSelectedContactProfile(autofill::AutofillProfile* profile,
                                  SectionSelectionStatus selection_status);
-  void SetSelectedInstrument(PaymentInstrument* instrument,
-                             SectionSelectionStatus selection_status);
+  void SetSelectedApp(PaymentApp* app, SectionSelectionStatus selection_status);
 
   bool is_ready_to_pay() { return is_ready_to_pay_; }
 
-  // Checks whether getting all available instruments is finished.
-  bool is_get_all_instruments_finished() {
-    return get_all_instruments_finished_;
-  }
+  // Checks whether getting all available apps is finished.
+  bool is_get_all_apps_finished() { return get_all_apps_finished_; }
 
-  // Returns true after is_get_all_instruments_finished() is true and supported
-  // payment method are found. Should not be called before
-  // is_get_all_instruments_finished() is true.
+  // Returns true after is_get_all_apps_finished() is true and supported payment
+  // method are found. Should not be called before is_get_all_apps_finished() is
+  // true.
   bool are_requested_methods_supported() const {
     return are_requested_methods_supported_;
   }
@@ -264,12 +255,12 @@
   // Selects the default shipping address.
   void SelectDefaultShippingAddressAndNotifyObservers();
 
-  // Returns true when shipping address is required and the selected instrument
-  // (if any) does not support shipping address delegation.
+  // Returns true when shipping address is required and the selected app (if
+  // any) does not support shipping address delegation.
   bool ShouldShowShippingSection() const;
 
-  // Returns true when payer name/phone/email is required and the selected
-  // instrument (if any) does not support required contact info delegation.
+  // Returns true when payer name/phone/email is required and the selected app
+  // (if any) does not support required contact info delegation.
   bool ShouldShowContactSection() const;
 
   base::WeakPtr<PaymentRequestState> AsWeakPtr();
@@ -284,8 +275,7 @@
   // profile_cache_.
   void PopulateProfileCache();
 
-  // Sets the initial selections for instruments and profiles, and notifies
-  // observers.
+  // Sets the initial selections for apps and profiles, and notifies observers.
   void SetDefaultProfileSelections();
 
   // Uses the user-selected information as well as the merchant spec to update
@@ -293,8 +283,8 @@
   // required information is available. Will notify observers.
   void UpdateIsReadyToPayAndNotifyObservers();
 
-  // Notifies all observers that getting all payment instruments is finished.
-  void NotifyOnGetAllPaymentInstrumentsFinished();
+  // Notifies all observers that getting all payment apps is finished.
+  void NotifyOnGetAllPaymentAppsFinished();
 
   // Notifies all observers that selected information has changed.
   void NotifyOnSelectedInformationChanged();
@@ -316,11 +306,9 @@
       ServiceWorkerPaymentAppFactory::InstallablePaymentApps installable_apps,
       const std::string& error_message);
 
-  // The ServiceWorkerPaymentInstrument::ValidateCanMakePaymentCallback.
-  void OnSWPaymentInstrumentValidated(
-      ServiceWorkerPaymentInstrument* instrument,
-      bool result);
-  void FinishedGetAllSWPaymentInstruments();
+  // The ServiceWorkerPaymentApp::ValidateCanMakePaymentCallback.
+  void OnSWPaymentAppValidated(ServiceWorkerPaymentApp* app, bool result);
+  void FinishedGetAllSWPaymentApps();
 
   // Checks if the payment methods that the merchant website have
   // requested are supported and call the |callback| to return the result.
@@ -339,19 +327,19 @@
 
   // True when the requested autofill data (shipping address and/or contact
   // information) is complete and valid, even if not selected. This variable is
-  // not affected by payment instruments.
+  // not affected by payment apps.
   bool is_requested_autofill_data_available_ = true;
 
-  // Whether getting all available instruments is finished.
-  bool get_all_instruments_finished_ = false;
+  // Whether getting all available apps is finished.
+  bool get_all_apps_finished_ = false;
 
   // The value returned by hasEnrolledInstrument(). Can be used only after
-  // |get_all_instruments_finished_| is true.
+  // |get_all_apps_finished_| is true.
   bool has_enrolled_instrument_ = false;
 
-  // Whether there's at least one instrument that is not autofill. Can be used
-  // only after |get_all_instruments_finished_| is true.
-  bool has_non_autofill_instrument_ = false;
+  // Whether there's at least one app that is not an autofill credit card. Can
+  // be used only after |get_all_apps_finished_| is true.
+  bool has_non_autofill_app_ = false;
 
   // Whether the data is currently being validated by the merchant.
   bool is_waiting_for_merchant_validation_;
@@ -375,11 +363,10 @@
   autofill::AutofillProfile* selected_contact_profile_;
   autofill::AutofillProfile* invalid_shipping_profile_;
   autofill::AutofillProfile* invalid_contact_profile_;
-  PaymentInstrument* selected_instrument_;
+  PaymentApp* selected_app_;
 
-  // Number of pending service worker payment instruments waiting for
-  // validation.
-  int number_of_pending_sw_payment_instruments_;
+  // Number of pending service worker payment apps waiting for validation.
+  int number_of_pending_sw_payment_apps_;
 
   // Profiles may change due to (e.g.) sync events, so profiles are cached after
   // loading and owned here. They are populated once only, and ordered by
@@ -388,11 +375,11 @@
   std::vector<autofill::AutofillProfile*> shipping_profiles_;
   std::vector<autofill::AutofillProfile*> contact_profiles_;
 
-  // Credit cards are directly owned by the instruments in this list.
-  std::vector<std::unique_ptr<PaymentInstrument>> available_instruments_;
+  // Credit cards are directly owned by the apps in this list.
+  std::vector<std::unique_ptr<PaymentApp>> available_apps_;
 
   ContentPaymentRequestDelegate* payment_request_delegate_;
-  base::WeakPtr<ServiceWorkerPaymentInstrument::IdentityObserver>
+  base::WeakPtr<ServiceWorkerPaymentApp::IdentityObserver>
       sw_identity_observer_;
 
   std::unique_ptr<PaymentResponseHelper> response_helper_;
diff --git a/components/payments/content/payment_request_state_unittest.cc b/components/payments/content/payment_request_state_unittest.cc
index 5671454..ee32dbb21 100644
--- a/components/payments/content/payment_request_state_unittest.cc
+++ b/components/payments/content/payment_request_state_unittest.cc
@@ -50,7 +50,7 @@
   ~PaymentRequestStateTest() override {}
 
   // PaymentRequestState::Observer:
-  void OnGetAllPaymentInstrumentsFinished() override {}
+  void OnGetAllPaymentAppsFinished() override {}
   void OnSelectedInformationChanged() override {
     num_on_selected_information_changed_called_++;
   }
@@ -177,7 +177,7 @@
       }));
 
   // CanMakePayment returns true because the requested method is supported, even
-  // though the payment instrument is not ready to pay.
+  // though the payment app is not ready to pay.
   state()->CanMakePayment(base::BindOnce(
       [](bool can_make_payment) { EXPECT_TRUE(can_make_payment); }));
 }
@@ -197,7 +197,7 @@
       }));
 
   // CanMakePayment returns true because the requested method is supported, even
-  // though the payment instrument is not ready to pay.
+  // though the payment app is not ready to pay.
   state()->CanMakePayment(base::BindOnce(
       [](bool can_make_payment) { EXPECT_FALSE(can_make_payment); }));
 }
@@ -326,12 +326,12 @@
   // Default options.
   RecreateStateWithOptions(mojom::PaymentOptions::New());
 
-  // Ready to pay because the default instrument is selected and supported.
+  // Ready to pay because the default app is selected and supported.
   EXPECT_TRUE(state()->is_ready_to_pay());
 
-  // There's only one instrument available, even though there's an Amex in
+  // There's only one app available, even though there's an Amex in
   // PersonalDataManager.
-  EXPECT_EQ(1u, state()->available_instruments().size());
+  EXPECT_EQ(1u, state()->available_apps().size());
 }
 
 // Test selecting a contact info profile will make the user ready to pay.
diff --git a/components/payments/content/payment_response_helper.cc b/components/payments/content/payment_response_helper.cc
index 8691a032..2b69fca 100644
--- a/components/payments/content/payment_response_helper.cc
+++ b/components/payments/content/payment_response_helper.cc
@@ -25,7 +25,7 @@
 PaymentResponseHelper::PaymentResponseHelper(
     const std::string& app_locale,
     PaymentRequestSpec* spec,
-    PaymentInstrument* selected_instrument,
+    PaymentApp* selected_app,
     PaymentRequestDelegate* payment_request_delegate,
     autofill::AutofillProfile* selected_shipping_profile,
     autofill::AutofillProfile* selected_contact_profile,
@@ -35,18 +35,17 @@
       is_waiting_for_instrument_details_(false),
       spec_(spec),
       delegate_(delegate),
-      selected_instrument_(selected_instrument),
+      selected_app_(selected_app),
       payment_request_delegate_(payment_request_delegate),
       selected_contact_profile_(selected_contact_profile) {
   DCHECK(spec_);
-  DCHECK(selected_instrument_);
+  DCHECK(selected_app_);
   DCHECK(delegate_);
 
   is_waiting_for_instrument_details_ = true;
 
   // Start to normalize the shipping address, if necessary.
-  if (spec_->request_shipping() &&
-      !selected_instrument_->HandlesShippingAddress()) {
+  if (spec_->request_shipping() && !selected_app_->HandlesShippingAddress()) {
     DCHECK(selected_shipping_profile);
     DCHECK(spec_->selected_shipping_option());
 
@@ -61,7 +60,7 @@
 
   // Start to get the instrument details. Will call back into
   // OnInstrumentDetailsReady.
-  selected_instrument_->InvokePaymentApp(this);
+  selected_app_->InvokePaymentApp(this);
 }
 
 PaymentResponseHelper::~PaymentResponseHelper() {}
@@ -75,12 +74,11 @@
 
   method_name_ = method_name;
   stringified_details_ = stringified_details;
-  payer_data_from_instrument_.payer_name = payer_data.payer_name;
-  payer_data_from_instrument_.payer_email = payer_data.payer_email;
-  payer_data_from_instrument_.payer_phone = payer_data.payer_phone;
-  payer_data_from_instrument_.shipping_address =
-      payer_data.shipping_address.Clone();
-  payer_data_from_instrument_.selected_shipping_option_id =
+  payer_data_from_app_.payer_name = payer_data.payer_name;
+  payer_data_from_app_.payer_email = payer_data.payer_email;
+  payer_data_from_app_.payer_phone = payer_data.payer_phone;
+  payer_data_from_app_.shipping_address = payer_data.shipping_address.Clone();
+  payer_data_from_app_.selected_shipping_option_id =
       payer_data.selected_shipping_option_id;
   is_waiting_for_instrument_details_ = false;
 
@@ -116,8 +114,8 @@
   mojom::PayerDetailPtr payer = mojom::PayerDetail::New();
 
   if (spec_->request_payer_name()) {
-    if (selected_instrument_->HandlesPayerName()) {
-      payer->name = payer_data_from_instrument_.payer_name;
+    if (selected_app_->HandlesPayerName()) {
+      payer->name = payer_data_from_app_.payer_name;
     } else {
       DCHECK(selected_contact_profile);
       payer->name = base::UTF16ToUTF8(
@@ -125,8 +123,8 @@
     }
   }
   if (spec_->request_payer_email()) {
-    if (selected_instrument_->HandlesPayerEmail()) {
-      payer->email = payer_data_from_instrument_.payer_email;
+    if (selected_app_->HandlesPayerEmail()) {
+      payer->email = payer_data_from_app_.payer_email;
     } else {
       DCHECK(selected_contact_profile);
       payer->email = base::UTF16ToUTF8(
@@ -134,8 +132,8 @@
     }
   }
   if (spec_->request_payer_phone()) {
-    if (selected_instrument_->HandlesPayerPhone()) {
-      payer->phone = payer_data_from_instrument_.payer_phone;
+    if (selected_app_->HandlesPayerPhone()) {
+      payer->phone = payer_data_from_app_.payer_phone;
     } else {
       DCHECK(selected_contact_profile);
 
@@ -164,8 +162,8 @@
   mojom::PaymentResponsePtr payment_response = mojom::PaymentResponse::New();
 
   // Make sure that we return the method name that the merchant specified for
-  // this instrument: cards can be either specified through their name (e.g.,
-  // "visa") or through basic-card's supportedNetworks.
+  // this app: cards can be either specified through their name (e.g., "visa")
+  // or through basic-card's supportedNetworks.
   payment_response->method_name =
       spec_->IsMethodSupportedThroughBasicCard(method_name_)
           ? methods::kBasicCard
@@ -174,11 +172,11 @@
 
   // Shipping Address section
   if (spec_->request_shipping()) {
-    if (selected_instrument_->HandlesShippingAddress()) {
+    if (selected_app_->HandlesShippingAddress()) {
       payment_response->shipping_address =
-          std::move(payer_data_from_instrument_.shipping_address);
+          std::move(payer_data_from_app_.shipping_address);
       payment_response->shipping_option =
-          payer_data_from_instrument_.selected_shipping_option_id;
+          payer_data_from_app_.selected_shipping_option_id;
     } else {
       payment_response->shipping_address =
           data_util::GetPaymentAddressFromAutofillProfile(shipping_address_,
diff --git a/components/payments/content/payment_response_helper.h b/components/payments/content/payment_response_helper.h
index 4b5658c..cdf23a6 100644
--- a/components/payments/content/payment_response_helper.h
+++ b/components/payments/content/payment_response_helper.h
@@ -11,7 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/address_normalizer.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
 
 namespace payments {
@@ -21,7 +21,7 @@
 
 // A helper class to facilitate the creation of the PaymentResponse.
 class PaymentResponseHelper
-    : public PaymentInstrument::Delegate,
+    : public PaymentApp::Delegate,
       public base::SupportsWeakPtr<PaymentResponseHelper> {
  public:
   class Delegate {
@@ -34,17 +34,17 @@
     virtual void OnPaymentResponseError(const std::string& error_message) = 0;
   };
 
-  // The spec, selected_instrument and delegate cannot be null.
+  // The spec, selected_app and delegate cannot be null.
   PaymentResponseHelper(const std::string& app_locale,
                         PaymentRequestSpec* spec,
-                        PaymentInstrument* selected_instrument,
+                        PaymentApp* selected_app,
                         PaymentRequestDelegate* payment_request_delegate,
                         autofill::AutofillProfile* selected_shipping_profile,
                         autofill::AutofillProfile* selected_contact_profile,
                         Delegate* delegate);
   ~PaymentResponseHelper() override;
 
-  // PaymentInstrument::Delegate
+  // PaymentApp::Delegate
   void OnInstrumentDetailsReady(const std::string& method_name,
                                 const std::string& stringified_details,
                                 const PayerData& payer_data) override;
@@ -68,7 +68,7 @@
   // Not owned, cannot be null.
   PaymentRequestSpec* spec_;
   Delegate* delegate_;
-  PaymentInstrument* selected_instrument_;
+  PaymentApp* selected_app_;
   PaymentRequestDelegate* payment_request_delegate_;
 
   // Not owned, can be null (dependent on the spec).
@@ -85,7 +85,7 @@
   // Details from payment handler response that will be included in the
   // PaymentResponse when shipping/contact handling is delegated to the payment
   // handler.
-  PayerData payer_data_from_instrument_;
+  PayerData payer_data_from_app_;
 
   base::WeakPtrFactory<PaymentResponseHelper> weak_ptr_factory_{this};
 
diff --git a/components/payments/content/payment_response_helper_unittest.cc b/components/payments/content/payment_response_helper_unittest.cc
index 04530c9..9bbecf2 100644
--- a/components/payments/content/payment_response_helper_unittest.cc
+++ b/components/payments/content/payment_response_helper_unittest.cc
@@ -16,7 +16,7 @@
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/payments/content/payment_request_spec.h"
-#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
 #include "components/payments/core/test_payment_request_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
@@ -32,11 +32,11 @@
         billing_addresses_({&address_}) {
     test_personal_data_manager_.AddProfile(address_);
 
-    // Set up the autofill payment instrument.
+    // Set up the autofill payment app.
     autofill::CreditCard visa_card = autofill::test::GetCreditCard();
     visa_card.set_billing_address_id(address_.guid());
     visa_card.set_use_count(5u);
-    autofill_instrument_ = std::make_unique<AutofillPaymentInstrument>(
+    autofill_app_ = std::make_unique<AutofillPaymentApp>(
         "visa", visa_card, /*matches_merchant_card_type_exactly=*/true,
         billing_addresses_, "en-US", &test_payment_request_delegate_);
   }
@@ -90,7 +90,7 @@
   PaymentRequestSpec* spec() { return spec_.get(); }
   const mojom::PaymentResponsePtr& response() { return payment_response_; }
   autofill::AutofillProfile* test_address() { return &address_; }
-  PaymentInstrument* test_instrument() { return autofill_instrument_.get(); }
+  PaymentApp* test_app() { return autofill_app_.get(); }
   PaymentRequestDelegate* test_payment_request_delegate() {
     return &test_payment_request_delegate_;
   }
@@ -104,7 +104,7 @@
   // Test data.
   autofill::AutofillProfile address_;
   const std::vector<autofill::AutofillProfile*> billing_addresses_;
-  std::unique_ptr<AutofillPaymentInstrument> autofill_instrument_;
+  std::unique_ptr<AutofillPaymentApp> autofill_app_;
 };
 
 // Test generating a PaymentResponse.
@@ -114,7 +114,7 @@
 
   // "visa" is specified directly in the supportedMethods so it is returned
   // as the method name.
-  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+  PaymentResponseHelper helper("en-US", spec(), test_app(),
                                test_payment_request_delegate(), test_address(),
                                test_address(), this);
   EXPECT_EQ("visa", response()->method_name);
@@ -152,7 +152,7 @@
                                     std::move(method_data));
 
   // "basic-card" is specified so it is returned as the method name.
-  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+  PaymentResponseHelper helper("en-US", spec(), test_app(),
                                test_payment_request_delegate(), test_address(),
                                test_address(), this);
   EXPECT_EQ("basic-card", response()->method_name);
@@ -192,7 +192,7 @@
   RecreateSpecWithOptionsAndDetails(std::move(options), std::move(details),
                                     GetMethodDataForVisa());
 
-  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+  PaymentResponseHelper helper("en-US", spec(), test_app(),
                                test_payment_request_delegate(), test_address(),
                                test_address(), this);
 
@@ -220,7 +220,7 @@
   options->request_payer_email = true;
   RecreateSpecWithOptions(std::move(options));
 
-  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+  PaymentResponseHelper helper("en-US", spec(), test_app(),
                                test_payment_request_delegate(), test_address(),
                                test_address(), this);
 
@@ -238,7 +238,7 @@
   options->request_payer_name = true;
   RecreateSpecWithOptions(std::move(options));
 
-  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+  PaymentResponseHelper helper("en-US", spec(), test_app(),
                                test_payment_request_delegate(), test_address(),
                                test_address(), this);
 
@@ -259,7 +259,7 @@
                              base::UTF8ToUTF16("(515) 223-1234"));
   RecreateSpecWithOptions(std::move(options));
 
-  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+  PaymentResponseHelper helper("en-US", spec(), test_app(),
                                test_payment_request_delegate(), test_address(),
                                test_address(), this);
 
@@ -278,7 +278,7 @@
                              base::UTF8ToUTF16("(515) 123-1234"));
   RecreateSpecWithOptions(std::move(options));
 
-  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+  PaymentResponseHelper helper("en-US", spec(), test_app(),
                                test_payment_request_delegate(), test_address(),
                                test_address(), this);
 
diff --git a/components/payments/content/service_worker_payment_instrument.cc b/components/payments/content/service_worker_payment_app.cc
similarity index 84%
rename from components/payments/content/service_worker_payment_instrument.cc
rename to components/payments/content/service_worker_payment_app.cc
index 0c6afae..1575d04 100644
--- a/components/payments/content/service_worker_payment_instrument.cc
+++ b/components/payments/content/service_worker_payment_app.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/payments/content/service_worker_payment_instrument.h"
+#include "components/payments/content/service_worker_payment_app.h"
 
 #include <limits>
 #include <utility>
@@ -27,7 +27,7 @@
 
 // Service worker payment app provides icon through bitmap, so set 0 as invalid
 // resource Id.
-ServiceWorkerPaymentInstrument::ServiceWorkerPaymentInstrument(
+ServiceWorkerPaymentApp::ServiceWorkerPaymentApp(
     content::BrowserContext* browser_context,
     const GURL& top_origin,
     const GURL& frame_origin,
@@ -35,7 +35,7 @@
     std::unique_ptr<content::StoredPaymentApp> stored_payment_app_info,
     PaymentRequestDelegate* payment_request_delegate,
     base::WeakPtr<IdentityObserver> identity_observer)
-    : PaymentInstrument(0, PaymentInstrument::Type::SERVICE_WORKER_APP),
+    : PaymentApp(0, PaymentApp::Type::SERVICE_WORKER_APP),
       browser_context_(browser_context),
       top_origin_(top_origin),
       frame_origin_(frame_origin),
@@ -65,7 +65,7 @@
 
 // Service worker payment app provides icon through bitmap, so set 0 as invalid
 // resource Id.
-ServiceWorkerPaymentInstrument::ServiceWorkerPaymentInstrument(
+ServiceWorkerPaymentApp::ServiceWorkerPaymentApp(
     content::WebContents* web_contents,
     const GURL& top_origin,
     const GURL& frame_origin,
@@ -74,7 +74,7 @@
     const std::string& enabled_method,
     PaymentRequestDelegate* payment_request_delegate,
     base::WeakPtr<IdentityObserver> identity_observer)
-    : PaymentInstrument(0, PaymentInstrument::Type::SERVICE_WORKER_APP),
+    : PaymentApp(0, PaymentApp::Type::SERVICE_WORKER_APP),
       top_origin_(top_origin),
       frame_origin_(frame_origin),
       spec_(spec),
@@ -103,7 +103,7 @@
   }
 }
 
-ServiceWorkerPaymentInstrument::~ServiceWorkerPaymentInstrument() {
+ServiceWorkerPaymentApp::~ServiceWorkerPaymentApp() {
   if (delegate_ && !needs_installation_) {
     // If there's a payment in progress, abort it before destroying this so that
     // it can update its internal state. Since the PaymentRequest will be
@@ -115,7 +115,7 @@
   }
 }
 
-void ServiceWorkerPaymentInstrument::ValidateCanMakePayment(
+void ServiceWorkerPaymentApp::ValidateCanMakePayment(
     ValidateCanMakePaymentCallback callback) {
   // Returns true for payment app that needs installation.
   if (needs_installation_) {
@@ -140,9 +140,9 @@
   mojom::CanMakePaymentEventDataPtr event_data =
       CreateCanMakePaymentEventData();
   if (event_data.is_null()) {
-    // This could only happen if this instrument only supports non-url based
-    // payment methods of the payment request, then return true
-    // and do not send CanMakePaymentEvent to the payment app.
+    // This could only happen if this app only supports non-url based payment
+    // methods of the payment request, then return true and do not send
+    // CanMakePaymentEvent to the payment app.
     OnCanMakePaymentEventSkipped(std::move(callback));
     return;
   }
@@ -151,13 +151,12 @@
       browser_context_, stored_payment_app_info_->registration_id,
       url::Origin::Create(stored_payment_app_info_->scope),
       *spec_->details().id, std::move(event_data),
-      base::BindOnce(
-          &ServiceWorkerPaymentInstrument::OnCanMakePaymentEventResponded,
-          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+      base::BindOnce(&ServiceWorkerPaymentApp::OnCanMakePaymentEventResponded,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 mojom::CanMakePaymentEventDataPtr
-ServiceWorkerPaymentInstrument::CreateCanMakePaymentEventData() {
+ServiceWorkerPaymentApp::CreateCanMakePaymentEventData() {
   std::set<std::string> requested_url_methods;
   for (const auto& method : spec_->payment_method_identifiers_set()) {
     GURL url_method(method);
@@ -171,8 +170,8 @@
   std::set<std::string> supported_url_methods =
       base::STLSetIntersection<std::set<std::string>>(requested_url_methods,
                                                       supported_methods);
-  // Only fire CanMakePaymentEvent if this instrument supports non-url based
-  // payment methods of the payment request.
+  // Only fire CanMakePaymentEvent if this app supports non-url based payment
+  // methods of the payment request.
   if (supported_url_methods.empty())
     return nullptr;
 
@@ -199,7 +198,7 @@
   return event_data;
 }
 
-void ServiceWorkerPaymentInstrument::OnCanMakePaymentEventSkipped(
+void ServiceWorkerPaymentApp::OnCanMakePaymentEventSkipped(
     ValidateCanMakePaymentCallback callback) {
   // |can_make_payment| is true as long as there is a matching payment handler.
   can_make_payment_result_ = true;
@@ -209,7 +208,7 @@
       base::BindOnce(std::move(callback), this, can_make_payment_result_));
 }
 
-void ServiceWorkerPaymentInstrument::OnCanMakePaymentEventResponded(
+void ServiceWorkerPaymentApp::OnCanMakePaymentEventResponded(
     ValidateCanMakePaymentCallback callback,
     bool result) {
   // |can_make_payment| is true as long as there is a matching payment handler.
@@ -220,7 +219,7 @@
       base::BindOnce(std::move(callback), this, can_make_payment_result_));
 }
 
-void ServiceWorkerPaymentInstrument::InvokePaymentApp(Delegate* delegate) {
+void ServiceWorkerPaymentApp::InvokePaymentApp(Delegate* delegate) {
   delegate_ = delegate;
 
   if (needs_installation_) {
@@ -238,7 +237,7 @@
             &IdentityObserver::SetInvokedServiceWorkerIdentity,
             identity_observer_,
             url::Origin::Create(GURL(installable_web_app_info_->sw_scope))),
-        base::BindOnce(&ServiceWorkerPaymentInstrument::OnPaymentAppInvoked,
+        base::BindOnce(&ServiceWorkerPaymentApp::OnPaymentAppInvoked,
                        weak_ptr_factory_.GetWeakPtr()));
   } else {
     url::Origin sw_origin =
@@ -248,14 +247,14 @@
     content::PaymentAppProvider::GetInstance()->InvokePaymentApp(
         browser_context_, stored_payment_app_info_->registration_id, sw_origin,
         CreatePaymentRequestEventData(),
-        base::BindOnce(&ServiceWorkerPaymentInstrument::OnPaymentAppInvoked,
+        base::BindOnce(&ServiceWorkerPaymentApp::OnPaymentAppInvoked,
                        weak_ptr_factory_.GetWeakPtr()));
   }
 
   payment_request_delegate_->ShowProcessingSpinner();
 }
 
-void ServiceWorkerPaymentInstrument::OnPaymentAppWindowClosed() {
+void ServiceWorkerPaymentApp::OnPaymentAppWindowClosed() {
   delegate_ = nullptr;
   content::PaymentAppProvider::GetInstance()->OnClosingOpenedWindow(
       browser_context_,
@@ -263,7 +262,7 @@
 }
 
 mojom::PaymentRequestEventDataPtr
-ServiceWorkerPaymentInstrument::CreatePaymentRequestEventData() {
+ServiceWorkerPaymentApp::CreatePaymentRequestEventData() {
   mojom::PaymentRequestEventDataPtr event_data =
       mojom::PaymentRequestEventData::New();
 
@@ -352,7 +351,7 @@
   return event_data;
 }
 
-void ServiceWorkerPaymentInstrument::OnPaymentAppInvoked(
+void ServiceWorkerPaymentApp::OnPaymentAppInvoked(
     mojom::PaymentHandlerResponsePtr response) {
   if (!delegate_)
     return;
@@ -383,49 +382,48 @@
   delegate_ = nullptr;
 }
 
-bool ServiceWorkerPaymentInstrument::IsCompleteForPayment() const {
+bool ServiceWorkerPaymentApp::IsCompleteForPayment() const {
   return true;
 }
 
-uint32_t ServiceWorkerPaymentInstrument::GetCompletenessScore() const {
-  // Return max value to ensure that SW instruments always score higher than
+uint32_t ServiceWorkerPaymentApp::GetCompletenessScore() const {
+  // Return max value to ensure that SW payment app always score higher than
   // autofill.
   return std::numeric_limits<uint32_t>::max();
 }
 
-bool ServiceWorkerPaymentInstrument::CanPreselect() const {
-  // Do not preselect the payment instrument when the name and/or icon is
-  // missing.
+bool ServiceWorkerPaymentApp::CanPreselect() const {
+  // Do not preselect the payment app when the name and/or icon is missing.
   return !GetLabel().empty() && !icon_image_.size().IsEmpty();
 }
 
-bool ServiceWorkerPaymentInstrument::IsExactlyMatchingMerchantRequest() const {
+bool ServiceWorkerPaymentApp::IsExactlyMatchingMerchantRequest() const {
   return true;
 }
 
-base::string16 ServiceWorkerPaymentInstrument::GetMissingInfoLabel() const {
+base::string16 ServiceWorkerPaymentApp::GetMissingInfoLabel() const {
   NOTREACHED();
   return base::string16();
 }
 
-bool ServiceWorkerPaymentInstrument::IsValidForCanMakePayment() const {
-  // This instrument should not be used when can_make_payment_result_ is false,
-  // so this interface should not be invoked.
+bool ServiceWorkerPaymentApp::IsValidForCanMakePayment() const {
+  // This app should not be used when can_make_payment_result_ is false, so this
+  // interface should not be invoked.
   DCHECK(can_make_payment_result_);
   return has_enrolled_instrument_result_;
 }
 
-void ServiceWorkerPaymentInstrument::RecordUse() {
+void ServiceWorkerPaymentApp::RecordUse() {
   NOTIMPLEMENTED();
 }
 
-base::string16 ServiceWorkerPaymentInstrument::GetLabel() const {
+base::string16 ServiceWorkerPaymentApp::GetLabel() const {
   return base::UTF8ToUTF16(needs_installation_
                                ? installable_web_app_info_->name
                                : stored_payment_app_info_->name);
 }
 
-base::string16 ServiceWorkerPaymentInstrument::GetSublabel() const {
+base::string16 ServiceWorkerPaymentApp::GetSublabel() const {
   if (needs_installation_) {
     DCHECK(GURL(installable_web_app_info_->sw_scope).is_valid());
     return base::UTF8ToUTF16(
@@ -435,7 +433,7 @@
       url::Origin::Create(stored_payment_app_info_->scope).host());
 }
 
-bool ServiceWorkerPaymentInstrument::IsValidForModifier(
+bool ServiceWorkerPaymentApp::IsValidForModifier(
     const std::string& method,
     bool supported_networks_specified,
     const std::set<std::string>& supported_networks,
@@ -456,13 +454,13 @@
   if (method != methods::kBasicCard)
     return true;
 
-  // Checking the capabilities of this instrument against the modifier.
+  // Checking the capabilities of this app against the modifier.
   // Return true if both card networks and types are not specified in the
   // modifier.
   if (!supported_networks_specified && !supported_types_specified)
     return true;
 
-  // Return false if no capabilities for this instrument.
+  // Return false if no capabilities for this app.
   if (stored_payment_app_info_->capabilities.empty())
     return false;
 
@@ -506,7 +504,7 @@
   return i < stored_payment_app_info_->capabilities.size();
 }
 
-void ServiceWorkerPaymentInstrument::IsValidForPaymentMethodIdentifier(
+void ServiceWorkerPaymentApp::IsValidForPaymentMethodIdentifier(
     const std::string& payment_method_identifier,
     bool* is_valid) const {
   DCHECK(!needs_installation_);
@@ -514,15 +512,15 @@
                              payment_method_identifier);
 }
 
-base::WeakPtr<PaymentInstrument> ServiceWorkerPaymentInstrument::AsWeakPtr() {
+base::WeakPtr<PaymentApp> ServiceWorkerPaymentApp::AsWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
 
-gfx::ImageSkia ServiceWorkerPaymentInstrument::icon_image_skia() const {
+gfx::ImageSkia ServiceWorkerPaymentApp::icon_image_skia() const {
   return icon_image_;
 }
 
-bool ServiceWorkerPaymentInstrument::HandlesShippingAddress() const {
+bool ServiceWorkerPaymentApp::HandlesShippingAddress() const {
   if (!spec_->request_shipping())
     return false;
 
@@ -531,7 +529,7 @@
              : stored_payment_app_info_->supported_delegations.shipping_address;
 }
 
-bool ServiceWorkerPaymentInstrument::HandlesPayerName() const {
+bool ServiceWorkerPaymentApp::HandlesPayerName() const {
   if (!spec_->request_payer_name())
     return false;
 
@@ -540,7 +538,7 @@
              : stored_payment_app_info_->supported_delegations.payer_name;
 }
 
-bool ServiceWorkerPaymentInstrument::HandlesPayerEmail() const {
+bool ServiceWorkerPaymentApp::HandlesPayerEmail() const {
   if (!spec_->request_payer_email())
     return false;
 
@@ -549,7 +547,7 @@
              : stored_payment_app_info_->supported_delegations.payer_email;
 }
 
-bool ServiceWorkerPaymentInstrument::HandlesPayerPhone() const {
+bool ServiceWorkerPaymentApp::HandlesPayerPhone() const {
   if (!spec_->request_payer_phone())
     return false;
 
@@ -558,8 +556,7 @@
              : stored_payment_app_info_->supported_delegations.payer_phone;
 }
 
-std::vector<std::string>
-ServiceWorkerPaymentInstrument::GetInstrumentMethodNames() const {
+std::vector<std::string> ServiceWorkerPaymentApp::GetAppMethodNames() const {
   if (stored_payment_app_info_) {
     return stored_payment_app_info_->enabled_methods;
   }
diff --git a/components/payments/content/service_worker_payment_instrument.h b/components/payments/content/service_worker_payment_app.h
similarity index 77%
rename from components/payments/content/service_worker_payment_instrument.h
rename to components/payments/content/service_worker_payment_app.h
index c2847a0c..7eee3b0 100644
--- a/components/payments/content/service_worker_payment_instrument.h
+++ b/components/payments/content/service_worker_payment_app.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_PAYMENTS_CONTENT_SERVICE_WORKER_PAYMENT_INSTRUMENT_H_
-#define COMPONENTS_PAYMENTS_CONTENT_SERVICE_WORKER_PAYMENT_INSTRUMENT_H_
+#ifndef COMPONENTS_PAYMENTS_CONTENT_SERVICE_WORKER_PAYMENT_APP_H_
+#define COMPONENTS_PAYMENTS_CONTENT_SERVICE_WORKER_PAYMENT_APP_H_
 
 #include <stdint.h>
 
 #include "base/memory/weak_ptr.h"
 #include "components/payments/content/payment_request_spec.h"
 #include "components/payments/content/web_app_manifest.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "content/public/browser/stored_payment_app.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/payments/payment_app.mojom.h"
@@ -31,7 +31,7 @@
 class PaymentRequestDelegate;
 
 // Represents a service worker based payment app.
-class ServiceWorkerPaymentInstrument : public PaymentInstrument {
+class ServiceWorkerPaymentApp : public PaymentApp {
  public:
   // Observer for identity of the service worker, which is known ahead of time
   // when already installed, but may be unknown at first when using just-in-time
@@ -50,7 +50,7 @@
 
   // This constructor is used for a payment app that has been installed in
   // Chrome.
-  ServiceWorkerPaymentInstrument(
+  ServiceWorkerPaymentApp(
       content::BrowserContext* browser_context,
       const GURL& top_origin,
       const GURL& frame_origin,
@@ -61,7 +61,7 @@
 
   // This constructor is used for a payment app that has not been installed in
   // Chrome but can be installed when paying with it.
-  ServiceWorkerPaymentInstrument(
+  ServiceWorkerPaymentApp(
       content::WebContents* web_contents,
       const GURL& top_origin,
       const GURL& frame_origin,
@@ -70,26 +70,26 @@
       const std::string& enabled_methods,
       PaymentRequestDelegate* payment_request_delegate,
       base::WeakPtr<IdentityObserver> identity_observer);
-  ~ServiceWorkerPaymentInstrument() override;
+  ~ServiceWorkerPaymentApp() override;
 
   // The callback for ValidateCanMakePayment.
   // The first return value is a pointer point to the corresponding
-  // ServiceWorkerPaymentInstrument of the result. The second return value is
+  // ServiceWorkerPaymentApp of the result. The second return value is
   // the result.
   using ValidateCanMakePaymentCallback =
-      base::OnceCallback<void(ServiceWorkerPaymentInstrument*, bool)>;
+      base::OnceCallback<void(ServiceWorkerPaymentApp*, bool)>;
 
-  // Validates whether this payment instrument can be used for this payment
-  // request. It fires CanMakePaymentEvent to the payment app to do validation.
-  // The result is returned through callback. If the returned result is false,
-  // then this instrument should not be used for this payment request. This
-  // interface must be called before any other interfaces in this class.
+  // Validates whether this payment app can be used for this payment request. It
+  // fires CanMakePaymentEvent to the payment app to do validation. The result
+  // is returned through callback. If the returned result is false, then this
+  // app should not be used for this payment request. This interface must be
+  // called before any other interfaces in this class.
   void ValidateCanMakePayment(ValidateCanMakePaymentCallback callback);
 
-  // Returns the list of payment methods supported by this instrument.
-  std::vector<std::string> GetInstrumentMethodNames() const;
+  // Returns the list of payment methods supported by this app.
+  std::vector<std::string> GetAppMethodNames() const;
 
-  // PaymentInstrument:
+  // PaymentApp:
   void InvokePaymentApp(Delegate* delegate) override;
   void OnPaymentAppWindowClosed() override;
   bool IsCompleteForPayment() const override;
@@ -107,12 +107,12 @@
                           bool supported_types_specified,
                           const std::set<autofill::CreditCard::CardType>&
                               supported_types) const override;
-  // Only works for installed payment instruments. This will DCHECK if this
-  // instrument needs installation.
+  // Only works for installed payment apps. This will DCHECK if this app needs
+  // installation.
   void IsValidForPaymentMethodIdentifier(
       const std::string& payment_method_identifier,
       bool* is_valid) const override;
-  base::WeakPtr<PaymentInstrument> AsWeakPtr() override;
+  base::WeakPtr<PaymentApp> AsWeakPtr() override;
   gfx::ImageSkia icon_image_skia() const override;
   bool HandlesShippingAddress() const override;
   bool HandlesPayerName() const override;
@@ -125,7 +125,7 @@
   }
 
  private:
-  friend class ServiceWorkerPaymentInstrumentTest;
+  friend class ServiceWorkerPaymentAppTest;
 
   void OnPaymentAppInvoked(mojom::PaymentHandlerResponsePtr response);
   mojom::PaymentRequestEventDataPtr CreatePaymentRequestEventData();
@@ -155,22 +155,22 @@
 
   mojo::PendingRemote<mojom::PaymentHandlerHost> payment_handler_host_;
 
-  // PaymentAppProvider::CanMakePayment result of this payment instrument.
+  // PaymentAppProvider::CanMakePayment result of this payment app.
   bool can_make_payment_result_;
   bool has_enrolled_instrument_result_;
 
-  // Below variables are used for installable ServiceWorkerPaymentInstrument
+  // Below variables are used for installable ServiceWorkerPaymentApp
   // specifically.
   bool needs_installation_;
   content::WebContents* web_contents_;
   std::unique_ptr<WebAppInstallationInfo> installable_web_app_info_;
   std::string installable_enabled_method_;
 
-  base::WeakPtrFactory<ServiceWorkerPaymentInstrument> weak_ptr_factory_{this};
+  base::WeakPtrFactory<ServiceWorkerPaymentApp> weak_ptr_factory_{this};
 
-  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerPaymentInstrument);
+  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerPaymentApp);
 };
 
 }  // namespace payments
 
-#endif  // COMPONENTS_PAYMENTS_CONTENT_SERVICE_WORKER_PAYMENT_INSTRUMENT_H_
+#endif  // COMPONENTS_PAYMENTS_CONTENT_SERVICE_WORKER_PAYMENT_APP_H_
diff --git a/components/payments/content/service_worker_payment_instrument_unittest.cc b/components/payments/content/service_worker_payment_app_unittest.cc
similarity index 74%
rename from components/payments/content/service_worker_payment_instrument_unittest.cc
rename to components/payments/content/service_worker_payment_app_unittest.cc
index 17d0f2c..8b2c6f7 100644
--- a/components/payments/content/service_worker_payment_instrument_unittest.cc
+++ b/components/payments/content/service_worker_payment_app_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/payments/content/service_worker_payment_instrument.h"
+#include "components/payments/content/service_worker_payment_app.h"
 
 #include <memory>
 
@@ -18,11 +18,11 @@
 
 namespace payments {
 
-class ServiceWorkerPaymentInstrumentTest : public testing::Test,
-                                           public PaymentRequestSpec::Observer {
+class ServiceWorkerPaymentAppTest : public testing::Test,
+                                    public PaymentRequestSpec::Observer {
  public:
-  ServiceWorkerPaymentInstrumentTest() {}
-  ~ServiceWorkerPaymentInstrumentTest() override {}
+  ServiceWorkerPaymentAppTest() {}
+  ~ServiceWorkerPaymentAppTest() override {}
 
  protected:
   const SkBitmap* icon_bitmap() const { return icon_bitmap_; }
@@ -91,7 +91,7 @@
 
   void TearDown() override {}
 
-  void CreateServiceWorkerPaymentInstrument(bool with_url_method) {
+  void CreateServiceWorkerPaymentApp(bool with_url_method) {
     constexpr int kBitmapDimension = 16;
 
     std::unique_ptr<content::StoredPaymentApp> stored_app =
@@ -116,20 +116,20 @@
     stored_app->prefer_related_applications = false;
 
     icon_bitmap_ = stored_app->icon.get();
-    instrument_ = std::make_unique<ServiceWorkerPaymentInstrument>(
+    app_ = std::make_unique<ServiceWorkerPaymentApp>(
         &browser_context_, GURL("https://testmerchant.com"),
         GURL("https://testmerchant.com/bobpay"), spec_.get(),
         std::move(stored_app), &delegate_, identity_observer_.AsWeakPtr());
   }
 
-  ServiceWorkerPaymentInstrument* GetInstrument() { return instrument_.get(); }
+  ServiceWorkerPaymentApp* GetApp() { return app_.get(); }
 
   mojom::PaymentRequestEventDataPtr CreatePaymentRequestEventData() {
-    return instrument_->CreatePaymentRequestEventData();
+    return app_->CreatePaymentRequestEventData();
   }
 
   mojom::CanMakePaymentEventDataPtr CreateCanMakePaymentEventData() {
-    return instrument_->CreateCanMakePaymentEventData();
+    return app_->CreateCanMakePaymentEventData();
   }
 
  private:
@@ -139,31 +139,31 @@
   content::TestBrowserContext browser_context_;
 
   std::unique_ptr<PaymentRequestSpec> spec_;
-  std::unique_ptr<ServiceWorkerPaymentInstrument> instrument_;
+  std::unique_ptr<ServiceWorkerPaymentApp> app_;
   const SkBitmap* icon_bitmap_;
 
-  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerPaymentInstrumentTest);
+  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerPaymentAppTest);
 };
 
-// Test instrument info and status are correct.
-TEST_F(ServiceWorkerPaymentInstrumentTest, InstrumentInfo) {
-  CreateServiceWorkerPaymentInstrument(true);
+// Test app info and status are correct.
+TEST_F(ServiceWorkerPaymentAppTest, AppInfo) {
+  CreateServiceWorkerPaymentApp(true);
 
-  EXPECT_TRUE(GetInstrument()->IsCompleteForPayment());
-  EXPECT_TRUE(GetInstrument()->IsExactlyMatchingMerchantRequest());
+  EXPECT_TRUE(GetApp()->IsCompleteForPayment());
+  EXPECT_TRUE(GetApp()->IsExactlyMatchingMerchantRequest());
 
-  EXPECT_EQ(base::UTF16ToUTF8(GetInstrument()->GetLabel()), "bobpay");
-  EXPECT_EQ(base::UTF16ToUTF8(GetInstrument()->GetSublabel()), "bobpay.com");
+  EXPECT_EQ(base::UTF16ToUTF8(GetApp()->GetLabel()), "bobpay");
+  EXPECT_EQ(base::UTF16ToUTF8(GetApp()->GetSublabel()), "bobpay.com");
 
   const gfx::Size expected_size{icon_bitmap()->width(),
                                 icon_bitmap()->height()};
-  EXPECT_EQ(GetInstrument()->icon_image_skia().size(), expected_size);
+  EXPECT_EQ(GetApp()->icon_image_skia().size(), expected_size);
 }
 
 // Test payment request event data can be correctly constructed for invoking
 // InvokePaymentApp.
-TEST_F(ServiceWorkerPaymentInstrumentTest, CreatePaymentRequestEventData) {
-  CreateServiceWorkerPaymentInstrument(true);
+TEST_F(ServiceWorkerPaymentAppTest, CreatePaymentRequestEventData) {
+  CreateServiceWorkerPaymentApp(true);
 
   mojom::PaymentRequestEventDataPtr event_data =
       CreatePaymentRequestEventData();
@@ -195,13 +195,13 @@
 
 // Test CanMakePaymentEventData can be correctly constructed for invoking
 // Validate.
-TEST_F(ServiceWorkerPaymentInstrumentTest, CreateCanMakePaymentEvent) {
-  CreateServiceWorkerPaymentInstrument(false);
+TEST_F(ServiceWorkerPaymentAppTest, CreateCanMakePaymentEvent) {
+  CreateServiceWorkerPaymentApp(false);
   mojom::CanMakePaymentEventDataPtr event_data =
       CreateCanMakePaymentEventData();
   EXPECT_TRUE(event_data.is_null());
 
-  CreateServiceWorkerPaymentInstrument(true);
+  CreateServiceWorkerPaymentApp(true);
   event_data = CreateCanMakePaymentEventData();
   EXPECT_FALSE(event_data.is_null());
 
@@ -219,41 +219,38 @@
             "https://bobpay.com");
 }
 
-// Test the case when CanMakePaymentEvent cannot be fired. The instrument should
-// be considered valid, but not ready for payment.
-TEST_F(ServiceWorkerPaymentInstrumentTest, ValidateCanMakePayment) {
-  // CanMakePaymentEvent is not fired because this test instrument does not have
-  // any explicitly verified methods.
-  CreateServiceWorkerPaymentInstrument(/*with_url_method=*/true);
-  GetInstrument()->ValidateCanMakePayment(
-      base::BindOnce([](ServiceWorkerPaymentInstrument*, bool result) {
-        EXPECT_TRUE(result);
-      }));
-  EXPECT_FALSE(GetInstrument()->IsValidForCanMakePayment());
+// Test the case when CanMakePaymentEvent cannot be fired. The app should be
+// considered valid, but not ready for payment.
+TEST_F(ServiceWorkerPaymentAppTest, ValidateCanMakePayment) {
+  // CanMakePaymentEvent is not fired because this test app does not have any
+  // explicitly verified methods.
+  CreateServiceWorkerPaymentApp(/*with_url_method=*/true);
+  GetApp()->ValidateCanMakePayment(base::BindOnce(
+      [](ServiceWorkerPaymentApp*, bool result) { EXPECT_TRUE(result); }));
+  EXPECT_FALSE(GetApp()->IsValidForCanMakePayment());
 }
 
 // Test modifiers can be matched based on capabilities.
-TEST_F(ServiceWorkerPaymentInstrumentTest, IsValidForModifier) {
-  CreateServiceWorkerPaymentInstrument(true);
+TEST_F(ServiceWorkerPaymentAppTest, IsValidForModifier) {
+  CreateServiceWorkerPaymentApp(true);
+
+  EXPECT_TRUE(GetApp()->IsValidForModifier("basic-card", false, {}, false, {}));
 
   EXPECT_TRUE(
-      GetInstrument()->IsValidForModifier("basic-card", false, {}, false, {}));
+      GetApp()->IsValidForModifier("https://bobpay.com", true, {}, true, {}));
 
-  EXPECT_TRUE(GetInstrument()->IsValidForModifier("https://bobpay.com", true,
-                                                  {}, true, {}));
+  EXPECT_FALSE(GetApp()->IsValidForModifier("basic-card", true, {"mastercard"},
+                                            false, {}));
 
-  EXPECT_FALSE(GetInstrument()->IsValidForModifier("basic-card", true,
-                                                   {"mastercard"}, false, {}));
+  EXPECT_TRUE(GetApp()->IsValidForModifier("basic-card", true, {"unionpay"},
+                                           false, {}));
 
-  EXPECT_TRUE(GetInstrument()->IsValidForModifier("basic-card", true,
-                                                  {"unionpay"}, false, {}));
-
-  EXPECT_TRUE(GetInstrument()->IsValidForModifier(
+  EXPECT_TRUE(GetApp()->IsValidForModifier(
       "basic-card", true, {"unionpay"}, true,
       {autofill::CreditCard::CardType::CARD_TYPE_DEBIT,
        autofill::CreditCard::CardType::CARD_TYPE_CREDIT}));
 
-  EXPECT_FALSE(GetInstrument()->IsValidForModifier(
+  EXPECT_FALSE(GetApp()->IsValidForModifier(
       "basic-card", true, {"unionpay"}, true,
       {autofill::CreditCard::CardType::CARD_TYPE_CREDIT}));
 }
diff --git a/components/payments/core/BUILD.gn b/components/payments/core/BUILD.gn
index beff207..be4c319 100644
--- a/components/payments/core/BUILD.gn
+++ b/components/payments/core/BUILD.gn
@@ -46,14 +46,14 @@
     sources += [
       "autofill_card_validation.cc",
       "autofill_card_validation.h",
-      "autofill_payment_instrument.cc",
-      "autofill_payment_instrument.h",
+      "autofill_payment_app.cc",
+      "autofill_payment_app.h",
       "basic_card_response.cc",
       "basic_card_response.h",
       "payer_data.cc",
       "payer_data.h",
-      "payment_instrument.cc",
-      "payment_instrument.h",
+      "payment_app.cc",
+      "payment_app.h",
       "payment_options.cc",
       "payment_options.h",
       "payment_options_provider.h",
@@ -164,7 +164,7 @@
 
   if (!is_android) {
     sources += [
-      "autofill_payment_instrument_unittest.cc",
+      "autofill_payment_app_unittest.cc",
       "basic_card_response_unittest.cc",
       "payment_address_unittest.cc",
       "payment_options_unittest.cc",
diff --git a/components/payments/core/autofill_payment_instrument.cc b/components/payments/core/autofill_payment_app.cc
similarity index 81%
rename from components/payments/core/autofill_payment_instrument.cc
rename to components/payments/core/autofill_payment_app.cc
index 4210463..96b137d 100644
--- a/components/payments/core/autofill_payment_instrument.cc
+++ b/components/payments/core/autofill_payment_app.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
 
 #include <algorithm>
 #include <memory>
@@ -30,17 +30,16 @@
 
 namespace payments {
 
-AutofillPaymentInstrument::AutofillPaymentInstrument(
+AutofillPaymentApp::AutofillPaymentApp(
     const std::string& method_name,
     const autofill::CreditCard& card,
     bool matches_merchant_card_type_exactly,
     const std::vector<autofill::AutofillProfile*>& billing_profiles,
     const std::string& app_locale,
     PaymentRequestBaseDelegate* payment_request_delegate)
-    : PaymentInstrument(
-          autofill::data_util::GetPaymentRequestData(card.network())
-              .icon_resource_id,
-          PaymentInstrument::Type::AUTOFILL),
+    : PaymentApp(autofill::data_util::GetPaymentRequestData(card.network())
+                     .icon_resource_id,
+                 PaymentApp::Type::AUTOFILL),
       method_name_(method_name),
       credit_card_(card),
       matches_merchant_card_type_exactly_(matches_merchant_card_type_exactly),
@@ -48,10 +47,9 @@
       app_locale_(app_locale),
       delegate_(nullptr),
       payment_request_delegate_(payment_request_delegate) {}
-AutofillPaymentInstrument::~AutofillPaymentInstrument() {}
+AutofillPaymentApp::~AutofillPaymentApp() {}
 
-void AutofillPaymentInstrument::InvokePaymentApp(
-    PaymentInstrument::Delegate* delegate) {
+void AutofillPaymentApp::InvokePaymentApp(PaymentApp::Delegate* delegate) {
   DCHECK(delegate);
   // There can be only one FullCardRequest going on at a time. If |delegate_| is
   // not null, there's already an active request, which shouldn't happen.
@@ -74,56 +72,39 @@
   // Start the normalization of the billing address.
   payment_request_delegate_->GetAddressNormalizer()->NormalizeAddressAsync(
       billing_address_, /*timeout_seconds=*/5,
-      base::BindOnce(&AutofillPaymentInstrument::OnAddressNormalized,
+      base::BindOnce(&AutofillPaymentApp::OnAddressNormalized,
                      weak_ptr_factory_.GetWeakPtr()));
 
   payment_request_delegate_->DoFullCardRequest(credit_card_,
                                                weak_ptr_factory_.GetWeakPtr());
 }
 
-bool AutofillPaymentInstrument::IsCompleteForPayment() const {
+bool AutofillPaymentApp::IsCompleteForPayment() const {
   // COMPLETE or EXPIRED cards are considered valid for payment. The user will
   // be prompted to enter the new expiration at the CVC step.
   return GetCompletionStatusForCard(credit_card_, app_locale_,
                                     billing_profiles_) <= CREDIT_CARD_EXPIRED;
 }
 
-uint32_t AutofillPaymentInstrument::GetCompletenessScore() const {
+uint32_t AutofillPaymentApp::GetCompletenessScore() const {
   return ::payments::GetCompletenessScore(credit_card_, app_locale_,
                                           billing_profiles_);
 }
 
-bool AutofillPaymentInstrument::CanPreselect() const {
+bool AutofillPaymentApp::CanPreselect() const {
   return IsCompleteForPayment() && matches_merchant_card_type_exactly_;
 }
 
-void AutofillPaymentInstrument::RecordMissingFieldsForInstrument() const {
-  CreditCardCompletionStatus completion_status =
-      GetCompletionStatusForCard(credit_card_, app_locale_, billing_profiles_);
-  if (completion_status == CREDIT_CARD_COMPLETE &&
-      matches_merchant_card_type_exactly_) {
-    return;
-  }
-
-  // Record cases that the card type does not match the requested type(s) in
-  // addititon to missing fields from card completion status.
-  base::UmaHistogramSparse(
-      "PaymentRequest.MissingPaymentFields",
-      completion_status |
-          (matches_merchant_card_type_exactly_ ? 0
-                                               : CREDIT_CARD_TYPE_MISMATCH));
-}
-
-bool AutofillPaymentInstrument::IsExactlyMatchingMerchantRequest() const {
+bool AutofillPaymentApp::IsExactlyMatchingMerchantRequest() const {
   return matches_merchant_card_type_exactly_;
 }
 
-base::string16 AutofillPaymentInstrument::GetMissingInfoLabel() const {
+base::string16 AutofillPaymentApp::GetMissingInfoLabel() const {
   return GetCompletionMessageForCard(
       GetCompletionStatusForCard(credit_card_, app_locale_, billing_profiles_));
 }
 
-bool AutofillPaymentInstrument::IsValidForCanMakePayment() const {
+bool AutofillPaymentApp::IsValidForCanMakePayment() const {
   CreditCardCompletionStatus status =
       GetCompletionStatusForCard(credit_card_, app_locale_, billing_profiles_);
   if (PaymentsExperimentalFeatures::IsEnabled(
@@ -138,22 +119,22 @@
            status & CREDIT_CARD_NO_NUMBER);
 }
 
-void AutofillPaymentInstrument::RecordUse() {
+void AutofillPaymentApp::RecordUse() {
   // Record the use of the credit card.
   payment_request_delegate_->GetPersonalDataManager()->RecordUseOf(
       credit_card_);
 }
 
-base::string16 AutofillPaymentInstrument::GetLabel() const {
+base::string16 AutofillPaymentApp::GetLabel() const {
   return credit_card_.NetworkAndLastFourDigits();
 }
 
-base::string16 AutofillPaymentInstrument::GetSublabel() const {
+base::string16 AutofillPaymentApp::GetSublabel() const {
   return credit_card_.GetInfo(
       autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL), app_locale_);
 }
 
-bool AutofillPaymentInstrument::IsValidForModifier(
+bool AutofillPaymentApp::IsValidForModifier(
     const std::string& method,
     bool supported_networks_specified,
     const std::set<std::string>& supported_networks,
@@ -192,18 +173,34 @@
   return true;
 }
 
-void AutofillPaymentInstrument::IsValidForPaymentMethodIdentifier(
+void AutofillPaymentApp::IsValidForPaymentMethodIdentifier(
     const std::string& payment_method_identifier,
     bool* is_valid) const {
   // This instrument only matches basic-card.
   *is_valid = payment_method_identifier == methods::kBasicCard;
 }
 
-base::WeakPtr<PaymentInstrument> AutofillPaymentInstrument::AsWeakPtr() {
+base::WeakPtr<PaymentApp> AutofillPaymentApp::AsWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
 
-void AutofillPaymentInstrument::OnFullCardRequestSucceeded(
+bool AutofillPaymentApp::HandlesShippingAddress() const {
+  return false;
+}
+
+bool AutofillPaymentApp::HandlesPayerName() const {
+  return false;
+}
+
+bool AutofillPaymentApp::HandlesPayerEmail() const {
+  return false;
+}
+
+bool AutofillPaymentApp::HandlesPayerPhone() const {
+  return false;
+}
+
+void AutofillPaymentApp::OnFullCardRequestSucceeded(
     const autofill::payments::FullCardRequest& /* full_card_request */,
     const autofill::CreditCard& card,
     const base::string16& cvc) {
@@ -216,14 +213,31 @@
     GenerateBasicCardResponse();
 }
 
-void AutofillPaymentInstrument::OnFullCardRequestFailed() {
+void AutofillPaymentApp::OnFullCardRequestFailed() {
   // The user may have cancelled the unmask or something has gone wrong (e.g.,
   // the network request failed). In all cases, reset the |delegate_| so another
   // request can start.
   delegate_ = nullptr;
 }
 
-void AutofillPaymentInstrument::GenerateBasicCardResponse() {
+void AutofillPaymentApp::RecordMissingFieldsForApp() const {
+  CreditCardCompletionStatus completion_status =
+      GetCompletionStatusForCard(credit_card_, app_locale_, billing_profiles_);
+  if (completion_status == CREDIT_CARD_COMPLETE &&
+      matches_merchant_card_type_exactly_) {
+    return;
+  }
+
+  // Record cases that the card type does not match the requested type(s) in
+  // addititon to missing fields from card completion status.
+  base::UmaHistogramSparse(
+      "PaymentRequest.MissingPaymentFields",
+      completion_status |
+          (matches_merchant_card_type_exactly_ ? 0
+                                               : CREDIT_CARD_TYPE_MISMATCH));
+}
+
+void AutofillPaymentApp::GenerateBasicCardResponse() {
   DCHECK(!is_waiting_for_billing_address_normalization_);
   DCHECK(!is_waiting_for_card_unmask_);
   DCHECK(delegate_);
@@ -241,7 +255,7 @@
   cvc_ = base::UTF8ToUTF16("");
 }
 
-void AutofillPaymentInstrument::OnAddressNormalized(
+void AutofillPaymentApp::OnAddressNormalized(
     bool success,
     const autofill::AutofillProfile& normalized_profile) {
   DCHECK(is_waiting_for_billing_address_normalization_);
@@ -253,20 +267,4 @@
     GenerateBasicCardResponse();
 }
 
-bool AutofillPaymentInstrument::HandlesShippingAddress() const {
-  return false;
-}
-
-bool AutofillPaymentInstrument::HandlesPayerName() const {
-  return false;
-}
-
-bool AutofillPaymentInstrument::HandlesPayerEmail() const {
-  return false;
-}
-
-bool AutofillPaymentInstrument::HandlesPayerPhone() const {
-  return false;
-}
-
 }  // namespace payments
diff --git a/components/payments/core/autofill_payment_instrument.h b/components/payments/core/autofill_payment_app.h
similarity index 83%
rename from components/payments/core/autofill_payment_instrument.h
rename to components/payments/core/autofill_payment_app.h
index 3104f9f4..17ff7ecb 100644
--- a/components/payments/core/autofill_payment_instrument.h
+++ b/components/payments/core/autofill_payment_app.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_PAYMENTS_CORE_AUTOFILL_PAYMENT_INSTRUMENT_H_
-#define COMPONENTS_PAYMENTS_CORE_AUTOFILL_PAYMENT_INSTRUMENT_H_
+#ifndef COMPONENTS_PAYMENTS_CORE_AUTOFILL_PAYMENT_APP_H_
+#define COMPONENTS_PAYMENTS_CORE_AUTOFILL_PAYMENT_APP_H_
 
 #include <set>
 #include <string>
@@ -16,31 +16,30 @@
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/full_card_request.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 
 namespace payments {
 
 class PaymentRequestBaseDelegate;
 
-// Represents an Autofill/Payments credit card form of payment in Payment
-// Request.
-class AutofillPaymentInstrument
-    : public PaymentInstrument,
+// Represents an autofill credit card in Payment Request.
+class AutofillPaymentApp
+    : public PaymentApp,
       public autofill::payments::FullCardRequest::ResultDelegate {
  public:
   // |billing_profiles| is owned by the caller and should outlive this object.
   // |payment_request_delegate| must outlive this object.
-  AutofillPaymentInstrument(
+  AutofillPaymentApp(
       const std::string& method_name,
       const autofill::CreditCard& card,
       bool matches_merchant_card_type_exactly,
       const std::vector<autofill::AutofillProfile*>& billing_profiles,
       const std::string& app_locale,
       PaymentRequestBaseDelegate* payment_request_delegate);
-  ~AutofillPaymentInstrument() override;
+  ~AutofillPaymentApp() override;
 
-  // PaymentInstrument:
-  void InvokePaymentApp(PaymentInstrument::Delegate* delegate) override;
+  // PaymentApp:
+  void InvokePaymentApp(PaymentApp::Delegate* delegate) override;
   bool IsCompleteForPayment() const override;
   uint32_t GetCompletenessScore() const override;
   bool CanPreselect() const override;
@@ -59,7 +58,7 @@
   void IsValidForPaymentMethodIdentifier(
       const std::string& payment_method_identifier,
       bool* is_valid) const override;
-  base::WeakPtr<PaymentInstrument> AsWeakPtr() override;
+  base::WeakPtr<PaymentApp> AsWeakPtr() override;
   bool HandlesShippingAddress() const override;
   bool HandlesPayerName() const override;
   bool HandlesPayerEmail() const override;
@@ -72,7 +71,7 @@
       const base::string16& cvc) override;
   void OnFullCardRequestFailed() override;
 
-  void RecordMissingFieldsForInstrument() const;
+  void RecordMissingFieldsForApp() const;
 
   // Sets whether the complete and valid autofill data for merchant's request is
   // available.
@@ -108,7 +107,7 @@
 
   const std::string app_locale_;
 
-  PaymentInstrument::Delegate* delegate_;
+  PaymentApp::Delegate* delegate_;
   PaymentRequestBaseDelegate* payment_request_delegate_;
   autofill::AutofillProfile billing_address_;
 
@@ -122,11 +121,11 @@
   // variable is true only if the autofill data contains a valid email address.
   bool is_requested_autofill_data_available_ = false;
 
-  base::WeakPtrFactory<AutofillPaymentInstrument> weak_ptr_factory_{this};
+  base::WeakPtrFactory<AutofillPaymentApp> weak_ptr_factory_{this};
 
-  DISALLOW_COPY_AND_ASSIGN(AutofillPaymentInstrument);
+  DISALLOW_COPY_AND_ASSIGN(AutofillPaymentApp);
 };
 
 }  // namespace payments
 
-#endif  // COMPONENTS_PAYMENTS_CORE_AUTOFILL_PAYMENT_INSTRUMENT_H_
+#endif  // COMPONENTS_PAYMENTS_CORE_AUTOFILL_PAYMENT_APP_H_
diff --git a/components/payments/core/autofill_payment_app_unittest.cc b/components/payments/core/autofill_payment_app_unittest.cc
new file mode 100644
index 0000000..4a44c79
--- /dev/null
+++ b/components/payments/core/autofill_payment_app_unittest.cc
@@ -0,0 +1,393 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/payments/core/autofill_payment_app.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/address_normalizer.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/payments/full_card_request.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/test_address_normalizer.h"
+#include "components/autofill/core/browser/test_autofill_client.h"
+#include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/payments/core/test_payment_request_delegate.h"
+#include "components/strings/grit/components_strings.h"
+#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace payments {
+
+namespace {
+
+class FakePaymentAppDelegate : public PaymentApp::Delegate {
+ public:
+  FakePaymentAppDelegate() {}
+
+  void OnInstrumentDetailsReady(const std::string& method_name,
+                                const std::string& stringified_details,
+                                const PayerData& payer_data) override {
+    on_instrument_details_ready_called_ = true;
+  }
+
+  void OnInstrumentDetailsError(const std::string& error_message) override {
+    on_instrument_details_error_called_ = true;
+  }
+
+  bool WasOnInstrumentDetailsReadyCalled() {
+    return on_instrument_details_ready_called_;
+  }
+
+  bool WasOnInstrumentDetailsErrorCalled() {
+    return on_instrument_details_error_called_;
+  }
+
+ private:
+  bool on_instrument_details_ready_called_ = false;
+  bool on_instrument_details_error_called_ = false;
+};
+
+class FakePaymentRequestDelegate : public PaymentRequestDelegate {
+ public:
+  FakePaymentRequestDelegate()
+      : locale_("en-US"),
+        last_committed_url_("https://shop.com"),
+        personal_data_("en-US"),
+        test_shared_loader_factory_(
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_)),
+        payments_client_(test_shared_loader_factory_,
+                         /*identity_manager=*/nullptr,
+                         /*account_info_getter=*/nullptr),
+        full_card_request_(&autofill_client_,
+                           &payments_client_,
+                           &personal_data_) {}
+  void ShowDialog(PaymentRequest* request) override {}
+
+  void CloseDialog() override {}
+
+  void ShowErrorMessage() override {}
+
+  autofill::PersonalDataManager* GetPersonalDataManager() override {
+    return nullptr;
+  }
+
+  const std::string& GetApplicationLocale() const override { return locale_; }
+
+  bool IsIncognito() const override { return false; }
+
+  const GURL& GetLastCommittedURL() const override {
+    return last_committed_url_;
+  }
+
+  void DoFullCardRequest(
+      const autofill::CreditCard& credit_card,
+      base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
+          result_delegate) override {
+    full_card_request_card_ = credit_card;
+    full_card_result_delegate_ = result_delegate;
+  }
+
+  autofill::AddressNormalizer* GetAddressNormalizer() override {
+    return &address_normalizer_;
+  }
+
+  void CompleteFullCardRequest() {
+    full_card_result_delegate_->OnFullCardRequestSucceeded(
+        full_card_request_, full_card_request_card_, base::ASCIIToUTF16("123"));
+  }
+
+  autofill::RegionDataLoader* GetRegionDataLoader() override { return nullptr; }
+
+  ukm::UkmRecorder* GetUkmRecorder() override { return nullptr; }
+
+ private:
+  std::string locale_;
+  const GURL last_committed_url_;
+  autofill::TestAddressNormalizer address_normalizer_;
+  autofill::PersonalDataManager personal_data_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+  autofill::TestAutofillClient autofill_client_;
+  autofill::payments::PaymentsClient payments_client_;
+  autofill::payments::FullCardRequest full_card_request_;
+  autofill::CreditCard full_card_request_card_;
+  base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
+      full_card_result_delegate_;
+  DISALLOW_COPY_AND_ASSIGN(FakePaymentRequestDelegate);
+};
+
+}  // namespace
+
+class AutofillPaymentAppTest : public testing::Test {
+ protected:
+  AutofillPaymentAppTest()
+      : address_(autofill::test::GetFullProfile()),
+        local_card_(autofill::test::GetCreditCard()),
+        billing_profiles_({&address_}) {
+    local_card_.set_billing_address_id(address_.guid());
+  }
+
+  autofill::CreditCard& local_credit_card() { return local_card_; }
+  std::vector<autofill::AutofillProfile*>& billing_profiles() {
+    return billing_profiles_;
+  }
+
+ private:
+  autofill::AutofillProfile address_;
+  autofill::CreditCard local_card_;
+  std::vector<autofill::AutofillProfile*> billing_profiles_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillPaymentAppTest);
+};
+
+// A valid local credit card is a valid app for payment.
+TEST_F(AutofillPaymentAppTest, IsCompleteForPayment) {
+  AutofillPaymentApp app("visa", local_credit_card(),
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_TRUE(app.IsCompleteForPayment());
+  EXPECT_TRUE(app.GetMissingInfoLabel().empty());
+}
+
+// An expired local card is still a valid app for payment.
+TEST_F(AutofillPaymentAppTest, IsCompleteForPayment_Expired) {
+  autofill::CreditCard& card = local_credit_card();
+  card.SetExpirationYear(2016);  // Expired.
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_TRUE(app.IsCompleteForPayment());
+  EXPECT_EQ(base::string16(), app.GetMissingInfoLabel());
+}
+
+// A local card with no name is not a valid app for payment.
+TEST_F(AutofillPaymentAppTest, IsCompleteForPayment_NoName) {
+  autofill::CreditCard& card = local_credit_card();
+  card.SetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
+               base::ASCIIToUTF16(""), "en-US");
+  base::string16 missing_info;
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_FALSE(app.IsCompleteForPayment());
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_NAME_ON_CARD_REQUIRED),
+            app.GetMissingInfoLabel());
+}
+
+// A local card with no name is not a valid app for payment.
+TEST_F(AutofillPaymentAppTest, IsCompleteForPayment_NoNumber) {
+  autofill::CreditCard& card = local_credit_card();
+  card.SetNumber(base::ASCIIToUTF16(""));
+  base::string16 missing_info;
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_FALSE(app.IsCompleteForPayment());
+  EXPECT_EQ(l10n_util::GetStringUTF16(
+                IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE),
+            app.GetMissingInfoLabel());
+}
+
+// A local card with no billing address id is not a valid app for
+// payment.
+TEST_F(AutofillPaymentAppTest, IsCompleteForPayment_NoBillinbAddressId) {
+  autofill::CreditCard& card = local_credit_card();
+  card.set_billing_address_id("");
+  base::string16 missing_info;
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_FALSE(app.IsCompleteForPayment());
+  EXPECT_EQ(
+      l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
+      app.GetMissingInfoLabel());
+}
+
+// A local card with an invalid billing address id is not a valid app for
+// payment.
+TEST_F(AutofillPaymentAppTest, IsCompleteForPayment_InvalidBillinbAddressId) {
+  autofill::CreditCard& card = local_credit_card();
+  card.set_billing_address_id("InvalidBillingAddressId");
+  base::string16 missing_info;
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_FALSE(app.IsCompleteForPayment());
+  EXPECT_EQ(
+      l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
+      app.GetMissingInfoLabel());
+}
+
+// A local card with an incomplete billing address is not a complete app
+// for payment.
+TEST_F(AutofillPaymentAppTest, IsCompleteForPayment_IncompleteBillinbAddress) {
+  autofill::AutofillProfile incomplete_profile =
+      autofill::test::GetIncompleteProfile2();
+  billing_profiles()[0] = &incomplete_profile;
+  autofill::CreditCard& card = local_credit_card();
+  card.set_billing_address_id(incomplete_profile.guid());
+  base::string16 missing_info;
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_FALSE(app.IsCompleteForPayment());
+  EXPECT_EQ(
+      l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
+      app.GetMissingInfoLabel());
+}
+
+// A local card with no name and no number is not a valid app for
+// payment.
+TEST_F(AutofillPaymentAppTest, IsCompleteForPayment_MultipleThingsMissing) {
+  autofill::CreditCard& card = local_credit_card();
+  card.SetNumber(base::ASCIIToUTF16(""));
+  card.SetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
+               base::ASCIIToUTF16(""), "en-US");
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_FALSE(app.IsCompleteForPayment());
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_MORE_INFORMATION_REQUIRED),
+            app.GetMissingInfoLabel());
+}
+
+// A Masked (server) card is a valid app for payment.
+TEST_F(AutofillPaymentAppTest, IsCompleteForPayment_MaskedCard) {
+  autofill::CreditCard card = autofill::test::GetMaskedServerCard();
+  ASSERT_GT(billing_profiles().size(), 0UL);
+  card.set_billing_address_id(billing_profiles()[0]->guid());
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_TRUE(app.IsCompleteForPayment());
+  EXPECT_TRUE(app.GetMissingInfoLabel().empty());
+}
+
+// An expired masked (server) card is still a valid app for payment.
+TEST_F(AutofillPaymentAppTest, IsCompleteForPayment_ExpiredMaskedCard) {
+  autofill::CreditCard card = autofill::test::GetMaskedServerCard();
+  ASSERT_GT(billing_profiles().size(), 0UL);
+  card.set_billing_address_id(billing_profiles()[0]->guid());
+  card.SetExpirationYear(2016);  // Expired.
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_TRUE(app.IsCompleteForPayment());
+  EXPECT_EQ(base::string16(), app.GetMissingInfoLabel());
+}
+
+// An expired card is a valid app for canMakePayment.
+TEST_F(AutofillPaymentAppTest, IsValidForCanMakePayment_Minimal) {
+  autofill::CreditCard& card = local_credit_card();
+  card.SetExpirationYear(2016);  // Expired.
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_TRUE(app.IsValidForCanMakePayment());
+}
+
+// An expired Masked (server) card is a valid app for canMakePayment.
+TEST_F(AutofillPaymentAppTest, IsValidForCanMakePayment_MaskedCard) {
+  autofill::CreditCard card = autofill::test::GetMaskedServerCard();
+  card.SetExpirationYear(2016);  // Expired.
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_TRUE(app.IsValidForCanMakePayment());
+}
+
+// A card with no name is not a valid app for canMakePayment.
+TEST_F(AutofillPaymentAppTest, IsValidForCanMakePayment_NoName) {
+  autofill::CreditCard& card = local_credit_card();
+  card.SetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
+               base::ASCIIToUTF16(""), "en-US");
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_FALSE(app.IsValidForCanMakePayment());
+}
+
+// A card with no number is not a valid app for canMakePayment.
+TEST_F(AutofillPaymentAppTest, IsValidForCanMakePayment_NoNumber) {
+  autofill::CreditCard& card = local_credit_card();
+  card.SetNumber(base::ASCIIToUTF16(""));
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", nullptr);
+  EXPECT_FALSE(app.IsValidForCanMakePayment());
+}
+
+// Tests that the autofill app only calls OnappDetailsReady when
+// the billing address has been normalized and the card has been unmasked.
+TEST_F(AutofillPaymentAppTest, InvokePaymentApp_NormalizationBeforeUnmask) {
+  auto personal_data_manager =
+      std::make_unique<autofill::TestPersonalDataManager>();
+  TestPaymentRequestDelegate delegate(personal_data_manager.get());
+  delegate.DelayFullCardRequestCompletion();
+  delegate.test_address_normalizer()->DelayNormalization();
+
+  autofill::CreditCard& card = local_credit_card();
+  card.SetNumber(base::ASCIIToUTF16(""));
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", &delegate);
+
+  FakePaymentAppDelegate app_delegate;
+
+  app.InvokePaymentApp(&app_delegate);
+  EXPECT_FALSE(app_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(app_delegate.WasOnInstrumentDetailsErrorCalled());
+
+  delegate.test_address_normalizer()->CompleteAddressNormalization();
+  EXPECT_FALSE(app_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(app_delegate.WasOnInstrumentDetailsErrorCalled());
+
+  delegate.CompleteFullCardRequest();
+  EXPECT_TRUE(app_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(app_delegate.WasOnInstrumentDetailsErrorCalled());
+}
+
+// Tests that the autofill app only calls OnappDetailsReady when
+// the billing address has been normalized and the card has been unmasked.
+TEST_F(AutofillPaymentAppTest, InvokePaymentApp_UnmaskBeforeNormalization) {
+  auto personal_data_manager =
+      std::make_unique<autofill::TestPersonalDataManager>();
+  TestPaymentRequestDelegate delegate(personal_data_manager.get());
+  delegate.DelayFullCardRequestCompletion();
+  delegate.test_address_normalizer()->DelayNormalization();
+
+  autofill::CreditCard& card = local_credit_card();
+  card.SetNumber(base::ASCIIToUTF16(""));
+  AutofillPaymentApp app("visa", card,
+                         /*matches_merchant_card_type_exactly=*/true,
+                         billing_profiles(), "en-US", &delegate);
+
+  FakePaymentAppDelegate app_delegate;
+
+  app.InvokePaymentApp(&app_delegate);
+  EXPECT_FALSE(app_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(app_delegate.WasOnInstrumentDetailsErrorCalled());
+
+  delegate.CompleteFullCardRequest();
+  EXPECT_FALSE(app_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(app_delegate.WasOnInstrumentDetailsErrorCalled());
+
+  delegate.test_address_normalizer()->CompleteAddressNormalization();
+  EXPECT_TRUE(app_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(app_delegate.WasOnInstrumentDetailsErrorCalled());
+}
+
+}  // namespace payments
diff --git a/components/payments/core/autofill_payment_instrument_unittest.cc b/components/payments/core/autofill_payment_instrument_unittest.cc
deleted file mode 100644
index edae9dfd..0000000
--- a/components/payments/core/autofill_payment_instrument_unittest.cc
+++ /dev/null
@@ -1,399 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/payments/core/autofill_payment_instrument.h"
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "base/strings/utf_string_conversions.h"
-#include "components/autofill/core/browser/address_normalizer.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/autofill/core/browser/payments/full_card_request.h"
-#include "components/autofill/core/browser/payments/payments_client.h"
-#include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/autofill/core/browser/test_address_normalizer.h"
-#include "components/autofill/core/browser/test_autofill_client.h"
-#include "components/autofill/core/browser/test_personal_data_manager.h"
-#include "components/payments/core/test_payment_request_delegate.h"
-#include "components/strings/grit/components_strings.h"
-#include "net/url_request/url_request_test_util.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/l10n/l10n_util.h"
-
-namespace payments {
-
-namespace {
-
-class FakePaymentInstrumentDelegate : public PaymentInstrument::Delegate {
- public:
-  FakePaymentInstrumentDelegate() {}
-
-  void OnInstrumentDetailsReady(const std::string& method_name,
-                                const std::string& stringified_details,
-                                const PayerData& payer_data) override {
-    on_instrument_details_ready_called_ = true;
-  }
-
-  void OnInstrumentDetailsError(const std::string& error_message) override {
-    on_instrument_details_error_called_ = true;
-  }
-
-  bool WasOnInstrumentDetailsReadyCalled() {
-    return on_instrument_details_ready_called_;
-  }
-
-  bool WasOnInstrumentDetailsErrorCalled() {
-    return on_instrument_details_error_called_;
-  }
-
- private:
-  bool on_instrument_details_ready_called_ = false;
-  bool on_instrument_details_error_called_ = false;
-};
-
-class FakePaymentRequestDelegate : public PaymentRequestDelegate {
- public:
-  FakePaymentRequestDelegate()
-      : locale_("en-US"),
-        last_committed_url_("https://shop.com"),
-        personal_data_("en-US"),
-        test_shared_loader_factory_(
-            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-                &test_url_loader_factory_)),
-        payments_client_(test_shared_loader_factory_,
-                         /*identity_manager=*/nullptr,
-                         /*account_info_getter=*/nullptr),
-        full_card_request_(&autofill_client_,
-                           &payments_client_,
-                           &personal_data_) {}
-  void ShowDialog(PaymentRequest* request) override {}
-
-  void CloseDialog() override {}
-
-  void ShowErrorMessage() override {}
-
-  autofill::PersonalDataManager* GetPersonalDataManager() override {
-    return nullptr;
-  }
-
-  const std::string& GetApplicationLocale() const override { return locale_; }
-
-  bool IsIncognito() const override { return false; }
-
-  const GURL& GetLastCommittedURL() const override {
-    return last_committed_url_;
-  }
-
-  void DoFullCardRequest(
-      const autofill::CreditCard& credit_card,
-      base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
-          result_delegate) override {
-    full_card_request_card_ = credit_card;
-    full_card_result_delegate_ = result_delegate;
-  }
-
-  autofill::AddressNormalizer* GetAddressNormalizer() override {
-    return &address_normalizer_;
-  }
-
-  void CompleteFullCardRequest() {
-    full_card_result_delegate_->OnFullCardRequestSucceeded(
-        full_card_request_, full_card_request_card_, base::ASCIIToUTF16("123"));
-  }
-
-  autofill::RegionDataLoader* GetRegionDataLoader() override { return nullptr; }
-
-  ukm::UkmRecorder* GetUkmRecorder() override { return nullptr; }
-
- private:
-  std::string locale_;
-  const GURL last_committed_url_;
-  autofill::TestAddressNormalizer address_normalizer_;
-  autofill::PersonalDataManager personal_data_;
-  network::TestURLLoaderFactory test_url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
-  autofill::TestAutofillClient autofill_client_;
-  autofill::payments::PaymentsClient payments_client_;
-  autofill::payments::FullCardRequest full_card_request_;
-  autofill::CreditCard full_card_request_card_;
-  base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
-      full_card_result_delegate_;
-  DISALLOW_COPY_AND_ASSIGN(FakePaymentRequestDelegate);
-};
-
-}  // namespace
-
-class AutofillPaymentInstrumentTest : public testing::Test {
- protected:
-  AutofillPaymentInstrumentTest()
-      : address_(autofill::test::GetFullProfile()),
-        local_card_(autofill::test::GetCreditCard()),
-        billing_profiles_({&address_}) {
-    local_card_.set_billing_address_id(address_.guid());
-  }
-
-  autofill::CreditCard& local_credit_card() { return local_card_; }
-  std::vector<autofill::AutofillProfile*>& billing_profiles() {
-    return billing_profiles_;
-  }
-
- private:
-  autofill::AutofillProfile address_;
-  autofill::CreditCard local_card_;
-  std::vector<autofill::AutofillProfile*> billing_profiles_;
-
-  DISALLOW_COPY_AND_ASSIGN(AutofillPaymentInstrumentTest);
-};
-
-// A valid local credit card is a valid instrument for payment.
-TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment) {
-  AutofillPaymentInstrument instrument(
-      "visa", local_credit_card(),
-      /*matches_merchant_card_type_exactly=*/true, billing_profiles(), "en-US",
-      nullptr);
-  EXPECT_TRUE(instrument.IsCompleteForPayment());
-  EXPECT_TRUE(instrument.GetMissingInfoLabel().empty());
-}
-
-// An expired local card is still a valid instrument for payment.
-TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_Expired) {
-  autofill::CreditCard& card = local_credit_card();
-  card.SetExpirationYear(2016);  // Expired.
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_TRUE(instrument.IsCompleteForPayment());
-  EXPECT_EQ(base::string16(), instrument.GetMissingInfoLabel());
-}
-
-// A local card with no name is not a valid instrument for payment.
-TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_NoName) {
-  autofill::CreditCard& card = local_credit_card();
-  card.SetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
-               base::ASCIIToUTF16(""), "en-US");
-  base::string16 missing_info;
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_FALSE(instrument.IsCompleteForPayment());
-  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_NAME_ON_CARD_REQUIRED),
-            instrument.GetMissingInfoLabel());
-}
-
-// A local card with no name is not a valid instrument for payment.
-TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_NoNumber) {
-  autofill::CreditCard& card = local_credit_card();
-  card.SetNumber(base::ASCIIToUTF16(""));
-  base::string16 missing_info;
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_FALSE(instrument.IsCompleteForPayment());
-  EXPECT_EQ(l10n_util::GetStringUTF16(
-                IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE),
-            instrument.GetMissingInfoLabel());
-}
-
-// A local card with no billing address id is not a valid instrument for
-// payment.
-TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_NoBillinbAddressId) {
-  autofill::CreditCard& card = local_credit_card();
-  card.set_billing_address_id("");
-  base::string16 missing_info;
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_FALSE(instrument.IsCompleteForPayment());
-  EXPECT_EQ(
-      l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
-      instrument.GetMissingInfoLabel());
-}
-
-// A local card with an invalid billing address id is not a valid instrument for
-// payment.
-TEST_F(AutofillPaymentInstrumentTest,
-       IsCompleteForPayment_InvalidBillinbAddressId) {
-  autofill::CreditCard& card = local_credit_card();
-  card.set_billing_address_id("InvalidBillingAddressId");
-  base::string16 missing_info;
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_FALSE(instrument.IsCompleteForPayment());
-  EXPECT_EQ(
-      l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
-      instrument.GetMissingInfoLabel());
-}
-
-// A local card with an incomplete billing address is not a complete instrument
-// for payment.
-TEST_F(AutofillPaymentInstrumentTest,
-       IsCompleteForPayment_IncompleteBillinbAddress) {
-  autofill::AutofillProfile incomplete_profile =
-      autofill::test::GetIncompleteProfile2();
-  billing_profiles()[0] = &incomplete_profile;
-  autofill::CreditCard& card = local_credit_card();
-  card.set_billing_address_id(incomplete_profile.guid());
-  base::string16 missing_info;
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_FALSE(instrument.IsCompleteForPayment());
-  EXPECT_EQ(
-      l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
-      instrument.GetMissingInfoLabel());
-}
-
-// A local card with no name and no number is not a valid instrument for
-// payment.
-TEST_F(AutofillPaymentInstrumentTest,
-       IsCompleteForPayment_MultipleThingsMissing) {
-  autofill::CreditCard& card = local_credit_card();
-  card.SetNumber(base::ASCIIToUTF16(""));
-  card.SetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
-               base::ASCIIToUTF16(""), "en-US");
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_FALSE(instrument.IsCompleteForPayment());
-  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_MORE_INFORMATION_REQUIRED),
-            instrument.GetMissingInfoLabel());
-}
-
-// A Masked (server) card is a valid instrument for payment.
-TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_MaskedCard) {
-  autofill::CreditCard card = autofill::test::GetMaskedServerCard();
-  ASSERT_GT(billing_profiles().size(), 0UL);
-  card.set_billing_address_id(billing_profiles()[0]->guid());
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_TRUE(instrument.IsCompleteForPayment());
-  EXPECT_TRUE(instrument.GetMissingInfoLabel().empty());
-}
-
-// An expired masked (server) card is still a valid instrument for payment.
-TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_ExpiredMaskedCard) {
-  autofill::CreditCard card = autofill::test::GetMaskedServerCard();
-  ASSERT_GT(billing_profiles().size(), 0UL);
-  card.set_billing_address_id(billing_profiles()[0]->guid());
-  card.SetExpirationYear(2016);  // Expired.
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_TRUE(instrument.IsCompleteForPayment());
-  EXPECT_EQ(base::string16(), instrument.GetMissingInfoLabel());
-}
-
-// An expired card is a valid instrument for canMakePayment.
-TEST_F(AutofillPaymentInstrumentTest, IsValidForCanMakePayment_Minimal) {
-  autofill::CreditCard& card = local_credit_card();
-  card.SetExpirationYear(2016);  // Expired.
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_TRUE(instrument.IsValidForCanMakePayment());
-}
-
-// An expired Masked (server) card is a valid instrument for canMakePayment.
-TEST_F(AutofillPaymentInstrumentTest, IsValidForCanMakePayment_MaskedCard) {
-  autofill::CreditCard card = autofill::test::GetMaskedServerCard();
-  card.SetExpirationYear(2016);  // Expired.
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_TRUE(instrument.IsValidForCanMakePayment());
-}
-
-// A card with no name is not a valid instrument for canMakePayment.
-TEST_F(AutofillPaymentInstrumentTest, IsValidForCanMakePayment_NoName) {
-  autofill::CreditCard& card = local_credit_card();
-  card.SetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
-               base::ASCIIToUTF16(""), "en-US");
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_FALSE(instrument.IsValidForCanMakePayment());
-}
-
-// A card with no number is not a valid instrument for canMakePayment.
-TEST_F(AutofillPaymentInstrumentTest, IsValidForCanMakePayment_NoNumber) {
-  autofill::CreditCard& card = local_credit_card();
-  card.SetNumber(base::ASCIIToUTF16(""));
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", nullptr);
-  EXPECT_FALSE(instrument.IsValidForCanMakePayment());
-}
-
-// Tests that the autofill instrument only calls OnInstrumentDetailsReady when
-// the billing address has been normalized and the card has been unmasked.
-TEST_F(AutofillPaymentInstrumentTest,
-       InvokePaymentApp_NormalizationBeforeUnmask) {
-  auto personal_data_manager =
-      std::make_unique<autofill::TestPersonalDataManager>();
-  TestPaymentRequestDelegate delegate(personal_data_manager.get());
-  delegate.DelayFullCardRequestCompletion();
-  delegate.test_address_normalizer()->DelayNormalization();
-
-  autofill::CreditCard& card = local_credit_card();
-  card.SetNumber(base::ASCIIToUTF16(""));
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", &delegate);
-
-  FakePaymentInstrumentDelegate instrument_delegate;
-
-  instrument.InvokePaymentApp(&instrument_delegate);
-  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
-  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
-
-  delegate.test_address_normalizer()->CompleteAddressNormalization();
-  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
-  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
-
-  delegate.CompleteFullCardRequest();
-  EXPECT_TRUE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
-  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
-}
-
-// Tests that the autofill instrument only calls OnInstrumentDetailsReady when
-// the billing address has been normalized and the card has been unmasked.
-TEST_F(AutofillPaymentInstrumentTest,
-       InvokePaymentApp_UnmaskBeforeNormalization) {
-  auto personal_data_manager =
-      std::make_unique<autofill::TestPersonalDataManager>();
-  TestPaymentRequestDelegate delegate(personal_data_manager.get());
-  delegate.DelayFullCardRequestCompletion();
-  delegate.test_address_normalizer()->DelayNormalization();
-
-  autofill::CreditCard& card = local_credit_card();
-  card.SetNumber(base::ASCIIToUTF16(""));
-  AutofillPaymentInstrument instrument(
-      "visa", card, /*matches_merchant_card_type_exactly=*/true,
-      billing_profiles(), "en-US", &delegate);
-
-  FakePaymentInstrumentDelegate instrument_delegate;
-
-  instrument.InvokePaymentApp(&instrument_delegate);
-  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
-  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
-
-  delegate.CompleteFullCardRequest();
-  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
-  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
-
-  delegate.test_address_normalizer()->CompleteAddressNormalization();
-  EXPECT_TRUE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
-  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
-}
-
-}  // namespace payments
diff --git a/components/payments/core/payment_app.cc b/components/payments/core/payment_app.cc
new file mode 100644
index 0000000..ca73d5a
--- /dev/null
+++ b/components/payments/core/payment_app.cc
@@ -0,0 +1,107 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/payments/core/payment_app.h"
+
+#include <algorithm>
+
+#include "components/autofill/core/common/autofill_clock.h"
+#include "components/payments/core/autofill_payment_app.h"
+
+namespace payments {
+
+PaymentApp::PaymentApp(int icon_resource_id, Type type)
+    : icon_resource_id_(icon_resource_id), type_(type) {}
+
+PaymentApp::~PaymentApp() {}
+
+gfx::ImageSkia PaymentApp::icon_image_skia() const {
+  return gfx::ImageSkia();
+}
+
+// static
+void PaymentApp::SortApps(std::vector<std::unique_ptr<PaymentApp>>* apps) {
+  DCHECK(apps);
+  std::sort(apps->begin(), apps->end(),
+            [](const std::unique_ptr<PaymentApp>& lhs,
+               const std::unique_ptr<PaymentApp>& rhs) { return *lhs < *rhs; });
+}
+
+// static
+void PaymentApp::SortApps(std::vector<PaymentApp*>* apps) {
+  DCHECK(apps);
+  std::sort(
+      apps->begin(), apps->end(),
+      [](const PaymentApp* lhs, const PaymentApp* rhs) { return *lhs < *rhs; });
+}
+
+bool PaymentApp::operator<(const PaymentApp& other) const {
+  // Non-autofill apps before autofill.
+  if (type_ == Type::AUTOFILL && other.type() != Type::AUTOFILL)
+    return false;
+  if (type_ != Type::AUTOFILL && other.type() == Type::AUTOFILL)
+    return true;
+
+  // Non-autofill apps have max completeness score. Autofill cards are sorted
+  // based on completeness. (Each autofill card is considered an app.)
+  int completeness = GetCompletenessScore() - other.GetCompletenessScore();
+  if (completeness != 0)
+    return completeness > 0;
+
+  // Among equally complete cards, those with matching type come before unknown
+  // type cards.
+  if (IsExactlyMatchingMerchantRequest() !=
+      other.IsExactlyMatchingMerchantRequest()) {
+    return IsExactlyMatchingMerchantRequest();
+  }
+
+  // Sort autofill cards using their frecency scores as tie breaker.
+  if (type_ == Type::AUTOFILL) {
+    DCHECK_EQ(other.type(), Type::AUTOFILL);
+    return static_cast<const AutofillPaymentApp*>(this)
+        ->credit_card()
+        ->HasGreaterFrecencyThan(
+            static_cast<const AutofillPaymentApp*>(&other)->credit_card(),
+            autofill::AutofillClock::Now());
+  }
+
+  // SW based payment apps are sorted based on whether they will handle shipping
+  // delegation or not (i.e. shipping address is requested and the app supports
+  // the delegation.).
+  if (HandlesShippingAddress() != other.HandlesShippingAddress())
+    return HandlesShippingAddress();
+
+  // SW based payment apps are sorted based on the number of the contact field
+  // delegations that they will handle (i.e. number of contact fields which are
+  // requested and the apps support their delegations.)
+  int supported_contact_delegations_num = 0;
+  if (HandlesPayerEmail())
+    supported_contact_delegations_num++;
+  if (HandlesPayerName())
+    supported_contact_delegations_num++;
+  if (HandlesPayerPhone())
+    supported_contact_delegations_num++;
+
+  int other_supported_contact_delegations_num = 0;
+  if (other.HandlesPayerEmail())
+    other_supported_contact_delegations_num++;
+  if (other.HandlesPayerName())
+    other_supported_contact_delegations_num++;
+  if (other.HandlesPayerPhone())
+    other_supported_contact_delegations_num++;
+
+  int contact_delegations_diff = supported_contact_delegations_num -
+                                 other_supported_contact_delegations_num;
+  if (contact_delegations_diff != 0)
+    return contact_delegations_diff > 0;
+
+  // SW based payment apps are sorted based on whether they can be pre-selected
+  // or not. Note that autofill cards are already sorted by CanPreselect() since
+  // they are sorted by completeness and type matching.
+  if (CanPreselect() != other.CanPreselect())
+    return CanPreselect();
+  return false;
+}
+
+}  // namespace payments
diff --git a/components/payments/core/payment_app.h b/components/payments/core/payment_app.h
new file mode 100644
index 0000000..95c95a29
--- /dev/null
+++ b/components/payments/core/payment_app.h
@@ -0,0 +1,130 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAYMENTS_CORE_PAYMENT_APP_H_
+#define COMPONENTS_PAYMENTS_CORE_PAYMENT_APP_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "build/build_config.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/payments/core/payer_data.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace payments {
+
+// Base class which represents a payment app in Payment Request.
+class PaymentApp {
+ public:
+  // The type of this app instance.
+  enum class Type { AUTOFILL, NATIVE_MOBILE_APP, SERVICE_WORKER_APP };
+
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+
+    // Should be called with method name (e.g., "https://google.com/pay") and
+    // json-serialized stringified details.
+    virtual void OnInstrumentDetailsReady(
+        const std::string& method_name,
+        const std::string& stringified_details,
+        const PayerData& payer_data) = 0;
+
+    // Should be called with a developer-facing error message to be used when
+    // rejecting PaymentRequest.show().
+    virtual void OnInstrumentDetailsError(const std::string& error_message) = 0;
+  };
+
+  virtual ~PaymentApp();
+
+  // Will call into the |delegate| (can't be null) on success or error.
+  virtual void InvokePaymentApp(Delegate* delegate) = 0;
+  // Called when the payment app window has closed.
+  virtual void OnPaymentAppWindowClosed() {}
+  // Returns whether the app is complete to be used for payment without further
+  // editing.
+  virtual bool IsCompleteForPayment() const = 0;
+  // Returns the calculated completeness score. Used to sort the list of
+  // available apps.
+  virtual uint32_t GetCompletenessScore() const = 0;
+  // Returns whether the app can be preselected in the payment sheet. If none of
+  // the apps can be preselected, the user must explicitly select an app from a
+  // list.
+  virtual bool CanPreselect() const = 0;
+  // Returns whether the app is exactly matching all filters provided by the
+  // merchant. For example, this can return "false" for unknown autofill card
+  // types, if the merchant requested only debit cards from "basic-card".
+  virtual bool IsExactlyMatchingMerchantRequest() const = 0;
+  // Returns a message to indicate to the user what's missing for the app to be
+  // complete for payment.
+  virtual base::string16 GetMissingInfoLabel() const = 0;
+  // Returns whether the app is valid for the purposes of responding to
+  // canMakePayment.
+  // TODO(crbug.com/915907): rename to IsValidForHasEnrolledInstrument.
+  virtual bool IsValidForCanMakePayment() const = 0;
+  // Records the use of this payment app.
+  virtual void RecordUse() = 0;
+  // Return the sub/label of payment app, to be displayed to the user.
+  virtual base::string16 GetLabel() const = 0;
+  virtual base::string16 GetSublabel() const = 0;
+  virtual gfx::ImageSkia icon_image_skia() const;
+
+  // Returns true if this payment app can be used to fulfill a request
+  // specifying |method| as supported method of payment. The parsed basic-card
+  // specific data (supported_networks, supported_types, etc) is relevant only
+  // for the AutofillPaymentApp, which runs inside of the browser process and
+  // thus should not be parsing untrusted JSON strings from the renderer.
+  virtual bool IsValidForModifier(
+      const std::string& method,
+      bool supported_networks_specified,
+      const std::set<std::string>& supported_networks,
+      bool supported_types_specified,
+      const std::set<autofill::CreditCard::CardType>& supported_types)
+      const = 0;
+
+  // Sets |is_valid| to true if this payment app can handle payments for the
+  // given |payment_method_identifier|. The |is_valid| is an out-param instead
+  // of the return value to enable binding this method with a base::WeakPtr,
+  // which prohibits non-void methods.
+  virtual void IsValidForPaymentMethodIdentifier(
+      const std::string& payment_method_identifier,
+      bool* is_valid) const = 0;
+
+  // Returns a WeakPtr to this payment app.
+  virtual base::WeakPtr<PaymentApp> AsWeakPtr() = 0;
+
+  // Returns true if this payment app can collect and return the required
+  // information. This is used to show/hide shipping/contact sections in payment
+  // sheet view depending on the selected app.
+  virtual bool HandlesShippingAddress() const = 0;
+  virtual bool HandlesPayerName() const = 0;
+  virtual bool HandlesPayerEmail() const = 0;
+  virtual bool HandlesPayerPhone() const = 0;
+
+  // Sorts the apps using the overloaded < operator.
+  static void SortApps(std::vector<std::unique_ptr<PaymentApp>>* apps);
+  static void SortApps(std::vector<PaymentApp*>* apps);
+
+  int icon_resource_id() const { return icon_resource_id_; }
+  Type type() const { return type_; }
+
+ protected:
+  PaymentApp(int icon_resource_id, Type type);
+
+ private:
+  bool operator<(const PaymentApp& other) const;
+  int icon_resource_id_;
+  Type type_;
+
+  DISALLOW_COPY_AND_ASSIGN(PaymentApp);
+};
+
+}  // namespace payments
+
+#endif  // COMPONENTS_PAYMENTS_CORE_PAYMENT_APP_H_
diff --git a/components/payments/core/payment_instrument.cc b/components/payments/core/payment_instrument.cc
deleted file mode 100644
index 6105827a..0000000
--- a/components/payments/core/payment_instrument.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/payments/core/payment_instrument.h"
-
-#include <algorithm>
-
-#include "components/autofill/core/common/autofill_clock.h"
-#include "components/payments/core/autofill_payment_instrument.h"
-
-namespace payments {
-
-PaymentInstrument::PaymentInstrument(int icon_resource_id, Type type)
-    : icon_resource_id_(icon_resource_id), type_(type) {}
-
-PaymentInstrument::~PaymentInstrument() {}
-
-gfx::ImageSkia PaymentInstrument::icon_image_skia() const {
-  return gfx::ImageSkia();
-}
-
-// static
-void PaymentInstrument::SortInstruments(
-    std::vector<std::unique_ptr<PaymentInstrument>>* instruments) {
-  DCHECK(instruments);
-  std::sort(instruments->begin(), instruments->end(),
-            [](const std::unique_ptr<PaymentInstrument>& lhs,
-               const std::unique_ptr<PaymentInstrument>& rhs) {
-              return *lhs < *rhs;
-            });
-}
-
-// static
-void PaymentInstrument::SortInstruments(
-    std::vector<PaymentInstrument*>* instruments) {
-  DCHECK(instruments);
-  std::sort(instruments->begin(), instruments->end(),
-            [](const PaymentInstrument* lhs, const PaymentInstrument* rhs) {
-              return *lhs < *rhs;
-            });
-}
-
-bool PaymentInstrument::operator<(const PaymentInstrument& other) const {
-  // Non-autofill instruments before autofill.
-  if (type_ == Type::AUTOFILL && other.type() != Type::AUTOFILL)
-    return false;
-  if (type_ != Type::AUTOFILL && other.type() == Type::AUTOFILL)
-    return true;
-
-  // Non-autofill instruments have max completeness score. Autofill cards are
-  // sorted based on completeness.
-  int completeness = GetCompletenessScore() - other.GetCompletenessScore();
-  if (completeness != 0)
-    return completeness > 0;
-
-  // Among equally complete cards, those with matching type come before unknown
-  // type cards.
-  if (IsExactlyMatchingMerchantRequest() !=
-      other.IsExactlyMatchingMerchantRequest()) {
-    return IsExactlyMatchingMerchantRequest();
-  }
-
-  // Sort autofill instruments using their frecency scores as tie breaker.
-  if (type_ == Type::AUTOFILL) {
-    DCHECK_EQ(other.type(), Type::AUTOFILL);
-    return static_cast<const AutofillPaymentInstrument*>(this)
-        ->credit_card()
-        ->HasGreaterFrecencyThan(
-            static_cast<const AutofillPaymentInstrument*>(&other)
-                ->credit_card(),
-            autofill::AutofillClock::Now());
-  }
-
-  // SW based payment instruments are sorted based on whether they will handle
-  // shipping delegation or not (i.e. shipping address is requested and the
-  // instrument supports the delegation.).
-  if (HandlesShippingAddress() != other.HandlesShippingAddress())
-    return HandlesShippingAddress();
-
-  // SW based payment instruments are sorted based on the number of the contact
-  // field delegations that they will handle (i.e. number of contact fields
-  // which are requested and the instruments support their delegations.)
-  int supported_contact_delegations_num = 0;
-  if (HandlesPayerEmail())
-    supported_contact_delegations_num++;
-  if (HandlesPayerName())
-    supported_contact_delegations_num++;
-  if (HandlesPayerPhone())
-    supported_contact_delegations_num++;
-
-  int other_supported_contact_delegations_num = 0;
-  if (other.HandlesPayerEmail())
-    other_supported_contact_delegations_num++;
-  if (other.HandlesPayerName())
-    other_supported_contact_delegations_num++;
-  if (other.HandlesPayerPhone())
-    other_supported_contact_delegations_num++;
-
-  int contact_delegations_diff = supported_contact_delegations_num -
-                                 other_supported_contact_delegations_num;
-  if (contact_delegations_diff != 0)
-    return contact_delegations_diff > 0;
-
-  // SW based payment instruments are sorted based on whether they can be
-  // pre-selected or not. Note that autofill based instruments are already
-  // sorted by CanPreselect() since they are sorted by completeness and type
-  // matching.
-  if (CanPreselect() != other.CanPreselect())
-    return CanPreselect();
-  return false;
-}
-
-}  // namespace payments
diff --git a/components/payments/core/payment_instrument.h b/components/payments/core/payment_instrument.h
deleted file mode 100644
index 398a5fb..0000000
--- a/components/payments/core/payment_instrument.h
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_PAYMENTS_CORE_PAYMENT_INSTRUMENT_H_
-#define COMPONENTS_PAYMENTS_CORE_PAYMENT_INSTRUMENT_H_
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/string16.h"
-#include "build/build_config.h"
-#include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/payments/core/payer_data.h"
-#include "ui/gfx/image/image_skia.h"
-
-namespace payments {
-
-// Base class which represents a form of payment in Payment Request.
-class PaymentInstrument {
- public:
-  // The type of this instrument instance.
-  enum class Type { AUTOFILL, NATIVE_MOBILE_APP, SERVICE_WORKER_APP };
-
-  class Delegate {
-   public:
-    virtual ~Delegate() {}
-
-    // Should be called with method name (e.g., "visa") and json-serialized
-    // stringified details.
-    virtual void OnInstrumentDetailsReady(
-        const std::string& method_name,
-        const std::string& stringified_details,
-        const PayerData& payer_data) = 0;
-
-    // Should be called with a developer-facing error message to be used when
-    // rejecting PaymentRequest.show().
-    virtual void OnInstrumentDetailsError(const std::string& error_message) = 0;
-  };
-
-  virtual ~PaymentInstrument();
-
-  // Will call into the |delegate| (can't be null) on success or error.
-  virtual void InvokePaymentApp(Delegate* delegate) = 0;
-  // Called when the payment app window has closed.
-  virtual void OnPaymentAppWindowClosed() {}
-  // Returns whether the instrument is complete to be used as a payment method
-  // without further editing.
-  virtual bool IsCompleteForPayment() const = 0;
-  // Returns the calculated completeness score. Used to sort the list of
-  // available instruments.
-  virtual uint32_t GetCompletenessScore() const = 0;
-  // Returns whether the instrument can be preselected in the payment sheet or
-  // not. If none of the instruments can be preselected, the user must
-  // explicitly select an instrument from a list.
-  virtual bool CanPreselect() const = 0;
-  // Returns whether the instrument is exactly matching all filters provided by
-  // the merchant. For example, this can return "false" for unknown card types,
-  // if the merchant requested only debit cards.
-  virtual bool IsExactlyMatchingMerchantRequest() const = 0;
-  // Returns a message to indicate to the user what's missing for the instrument
-  // to be complete for payment.
-  virtual base::string16 GetMissingInfoLabel() const = 0;
-  // Returns whether the instrument is valid for the purposes of responding to
-  // canMakePayment.
-  // TODO(crbug.com/915907): rename to IsValidForHasEnrolledInstrument.
-  virtual bool IsValidForCanMakePayment() const = 0;
-  // Records the use of this payment instrument.
-  virtual void RecordUse() = 0;
-  // Return the sub/label of payment instrument, to be displayed to the user.
-  virtual base::string16 GetLabel() const = 0;
-  virtual base::string16 GetSublabel() const = 0;
-  virtual gfx::ImageSkia icon_image_skia() const;
-
-  // Returns true if this payment instrument can be used to fulfill a request
-  // specifying |method| as supported method of payment, false otherwise. The
-  // parsed basic-card specific data (supported_networks, supported_types, etc)
-  // is relevant only for the AutofillPaymentInstrument, which runs inside of
-  // the browser process and thus should not be parsing untrusted JSON strings
-  // from the renderer.
-  virtual bool IsValidForModifier(
-      const std::string& method,
-      bool supported_networks_specified,
-      const std::set<std::string>& supported_networks,
-      bool supported_types_specified,
-      const std::set<autofill::CreditCard::CardType>& supported_types)
-      const = 0;
-
-  // Sets |is_valid| to true if this payment instrument can handle payments for
-  // the given |payment_method_identifier|. The |is_valid| is an an out-param
-  // instead of the return value to enable binding this method with a
-  // base::WeakPtr, which prohibits non-void methods.
-  virtual void IsValidForPaymentMethodIdentifier(
-      const std::string& payment_method_identifier,
-      bool* is_valid) const = 0;
-
-  // Returns a WeakPtr to this payment instrument.
-  virtual base::WeakPtr<PaymentInstrument> AsWeakPtr() = 0;
-
-  // Returns true if this payment instrument can collect and return the required
-  // information. This is used to show/hide shipping/contact sections in payment
-  // sheet view depending on the selected instrument.
-  virtual bool HandlesShippingAddress() const = 0;
-  virtual bool HandlesPayerName() const = 0;
-  virtual bool HandlesPayerEmail() const = 0;
-  virtual bool HandlesPayerPhone() const = 0;
-
-  // Sorts the instruments using the overloaded < operator.
-  static void SortInstruments(
-      std::vector<std::unique_ptr<PaymentInstrument>>* instruments);
-  static void SortInstruments(std::vector<PaymentInstrument*>* instruments);
-
-  int icon_resource_id() const { return icon_resource_id_; }
-  Type type() const { return type_; }
-
- protected:
-  PaymentInstrument(int icon_resource_id, Type type);
-
- private:
-  bool operator<(const PaymentInstrument& other) const;
-  int icon_resource_id_;
-  Type type_;
-
-  DISALLOW_COPY_AND_ASSIGN(PaymentInstrument);
-};
-
-}  // namespace payments
-
-#endif  // COMPONENTS_PAYMENTS_CORE_PAYMENT_INSTRUMENT_H_
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 84e45e9..0cd17f80 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -14859,7 +14859,7 @@
 
       Over this time period, the user will be repeatedly informed of the need for an update. For <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> devices, a restart notification appears in the system tray according to the <ph name="RELAUNCH_HEADS_UP_PERIOD_POLICY_NAME">RelaunchHeadsUpPeriod</ph> policy. For <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> browsers, the app menu changes to indicate that a relaunch is needed once one third of the notification period passes. This notification changes color once two thirds of the notification period passes, and again once the full notification period has passed. The additional notifications enabled by the <ph name="RELAUNCH_NOTIFICATION_POLICY_NAME">RelaunchNotification</ph> policy follow this same schedule.
 
-      If not set, the default period of 345600000 milliseconds (four days) is used for <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> devices and 604800000 milliseconds (one week) for <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>.''',
+      If not set, the default period of 604800000 milliseconds (one week) is used.''',
     },
     {
       'id': 567,
@@ -14878,7 +14878,7 @@
       'example_value': 86400000,
       'desc': '''Allows you to set the time period, in milliseconds, between the first notification that a <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> device must be restarted to apply a pending update and the end of the time period specified by the <ph name="RELAUNCH_NOTIFICATION_PERIOD_POLICY_NAME">RelaunchNotificationPeriod</ph> policy.
 
-      If not set, the default period of 86400000 milliseconds (one day) is used for <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> devices.''',
+      If not set, the default period of 259200000 milliseconds (three days) is used for <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> devices.''',
     },
     {
       'name': 'VirtualMachinesAllowed',
@@ -18987,6 +18987,30 @@
       If this policy is set to False or not set, the Web Components v0 features will be disabled by default, starting in M80.
 
       This policy will be removed after Chrome 84.'''
+    },
+    {
+      'name': 'ClickToCallEnabled',
+      'owners': ['knollr@chromium.org', 'mvanouwerkerk@chromium.org'],
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'supported_on': ['chrome.*:79-', 'chrome_os:79-'],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'example_value': True,
+      'id': 645,
+      'caption': '''Enable the Click to Call Feature''',
+      'tags': [],
+      'desc': '''Enable the Click to Call feature which allows users to send phone numbers from Chrome Desktops to an Android device when the user is Signed-in. For more information, see help center article: https://support.google.com/chrome/answer/9430554?hl=en.
+
+      If this policy is set to enabled, the capability of sending phone numbers to Android devices will be enabled for the Chrome user.
+
+      If this policy is set to disabled, the capability of sending phone numbers to Android devices will be disabled for the Chrome user.
+
+      If you set this policy, users cannot change or override it.
+
+      If this policy is left unset, the Click to Call feature is enabled by default.''',
     }
   ],
 
@@ -19811,6 +19835,6 @@
   ],
   'placeholders': [],
   'deleted_policy_ids': [412, 546, 562, 569],
-  'highest_id_currently_used': 644,
+  'highest_id_currently_used': 645,
   'highest_atomic_group_id_currently_used': 38
 }
diff --git a/components/spellcheck/common/spellcheck_common.cc b/components/spellcheck/common/spellcheck_common.cc
index 82db6b998..0eac1e1a 100644
--- a/components/spellcheck/common/spellcheck_common.cc
+++ b/components/spellcheck/common/spellcheck_common.cc
@@ -111,10 +111,11 @@
   // number if you're updating either dic or aff files. Increment the minor
   // version number if you're updating only dic_delta files.
   static constexpr LanguageVersion kSpecialVersionString[] = {
-      {"tr-TR",
-       "-4-0"},  // Jan 9, 2013: Add "FLAG num" to aff to avoid heapcheck
-                 // crash.
-      {"tg-TG", "-5-0"},  // Mar 4, 2014: Add Tajik dictionary.
+      // Jan 9, 2013: Add "FLAG num" to aff to avoid heapcheck crash.
+      {"tr-TR", "-4-0"},
+
+      // Mar 4, 2014: Add Tajik dictionary.
+      {"tg-TG", "-5-0"},
 
       // October 2017: Update from upstream.
       {"en-AU", "-8-0"},
@@ -130,6 +131,10 @@
 
       // April 2019: Update Persian
       {"fa-IR", "-8-0"},
+
+      // November 2019: Update Serbian-Latin and Serbian-Cyrillic
+      {"sh", "-4-0"},
+      {"sr", "-4-0"},
   };
 
   // Generate the bdict file name using default version string or special
diff --git a/components/spellcheck/renderer/spellcheck_unittest.cc b/components/spellcheck/renderer/spellcheck_unittest.cc
index cf56067..e6db96bb 100644
--- a/components/spellcheck/renderer/spellcheck_unittest.cc
+++ b/components/spellcheck/renderer/spellcheck_unittest.cc
@@ -736,21 +736,20 @@
     }, {
       // Serbo-Croatian (Serbian Latin)
       "sh",
-      L"Google-ova misija je da organizuje sve informacije na svetu i "
-      L"u\x010dini ih univerzal-no dostupnim i korisnim."
+      L"Guglova misija je organizirati svjetske informacije i u\x010diniti ih "
+      L"univerzalno dostupnim i korisnim."
     }, {
       // Serbian
       "sr",
-      L"\x0047\x006f\x006f\x0067\x006c\x0065\x002d\x043e\x0432\x0430 "
-      L"\x043c\x0438\x0441\x0438\x0458\x0430 \x0458\x0435 \x0434\x0430 "
-      L"\x043e\x0440\x0433\x0430\x043d\x0438\x0437\x0443\x0458\x0435 "
-      L"\x0441\x0432\x0435 "
-      L"\x0438\x043d\x0444\x043e\x0440\x043c\x0430\x0446\x0438\x0458\x0435 "
-      L"\x043d\x0430 \x0441\x0432\x0435\x0442\x0443 \x0438 "
-      L"\x0443\x0447\x0438\x043d\x0438 \x0438\x0445 "
-      L"\x0443\x043d\x0438\x0432\x0435\x0440\x0437\x0430\x043b\x043d\x043e "
-      L"\x0434\x043e\x0441\x0442\x0443\x043f\x043d\x0438\x043c \x0438 "
-      L"\x043a\x043e\x0440\x0438\x0441\x043d\x0438\x043c."
+      L"\x0413\x0443\x0433\x043B\x043E\x0432\x0430 "
+      L"\x043C\x0438\x0441\x0438\x0458\x0430 \x0458\x0435 \x0434\x0430 "
+      L"\x043E\x0440\x0433\x0430\x043D\x0438\x0437\x0443\x0458\x0435 "
+      L"\x0441\x0432\x0435\x0442\x0441\x043A\x0435 "
+      L"\x0438\x043D\x0444\x043E\x0440\x043C\x0430\x0446\x0438\x0458\x0435 "
+      L"\x0438 \x0443\x0447\x0438\x043D\x0438 \x0438\x0445 "
+      L"\x0443\x043D\x0438\x0432\x0435\x0440\x0437\x0430\x043B\x043D\x0438"
+      L"\x043C \x0434\x043E\x0441\x0442\x0443\x043F\x043D\x0438\x043C \x0438 "
+      L"\x043A\x043E\x0440\x0438\x0441\x043D\x0438\x043C."
     }, {
       // Slovak
       "sk-SK",
diff --git a/components/sync/protocol/sharing_remote_copy_message.proto b/components/sync/protocol/sharing_remote_copy_message.proto
index 92f2f72..c4743f1 100644
--- a/components/sync/protocol/sharing_remote_copy_message.proto
+++ b/components/sync/protocol/sharing_remote_copy_message.proto
@@ -11,6 +11,12 @@
 
 // Message to pass content for the remote copy feature.
 message RemoteCopyMessage {
-  // optional
-  string text = 1;
+  // The content to write to the clipboard. Required.
+  oneof content {
+    // Plain text.
+    string text = 1;
+
+    // The URL of an image to download and write to the clipboard.
+    string image_url = 2;
+  }
 }
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index 18b3f6d..18f04fff 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -39,6 +39,10 @@
     "//ui/gfx:buffer_types",
     "//ui/gfx/geometry:geometry",
   ]
+
+  if (skia_use_dawn) {
+    deps += [ "//third_party/dawn/src/dawn:dawncpp_headers" ]
+  }
 }
 
 # TODO(sgilhuly): To reduce link times, merge these context provider components
diff --git a/components/viz/common/gpu/dawn_context_provider.cc b/components/viz/common/gpu/dawn_context_provider.cc
index 38fa7cd0..d57ef4c 100644
--- a/components/viz/common/gpu/dawn_context_provider.cc
+++ b/components/viz/common/gpu/dawn_context_provider.cc
@@ -34,6 +34,10 @@
 }
 
 DawnContextProvider::DawnContextProvider() {
+  // TODO(sgilhuly): This may return a GPU that is not the active one. Currently
+  // the only known way to avoid this is platform-specific; e.g. on Mac, create
+  // a Dawn device, get the actual Metal device from it, and compare against
+  // MTLCreateSystemDefaultDevice().
   device_ = CreateDevice(GetDefaultBackendType());
   if (device_)
     gr_context_ = GrContext::MakeDawn(device_);
diff --git a/components/viz/common/resources/DEPS b/components/viz/common/resources/DEPS
index 972774c..7fbfdb3 100644
--- a/components/viz/common/resources/DEPS
+++ b/components/viz/common/resources/DEPS
@@ -10,6 +10,8 @@
   "+gpu/GLES2",
   "+mojo/public/cpp/system/buffer.h",
   "+mojo/public/cpp/system/platform_handle.h",
+  "+skia/buildflags.h",
+  "+third_party/dawn/src/include",
   "+third_party/khronos/GLES2",
   "+third_party/skia",
   "+third_party/vulkan",
diff --git a/components/viz/common/resources/resource_format_utils.cc b/components/viz/common/resources/resource_format_utils.cc
index c145ddf..1991654 100644
--- a/components/viz/common/resources/resource_format_utils.cc
+++ b/components/viz/common/resources/resource_format_utils.cc
@@ -488,4 +488,40 @@
 }
 #endif
 
+#if BUILDFLAG(SKIA_USE_DAWN)
+dawn::TextureFormat ToDawnFormat(ResourceFormat format) {
+  switch (format) {
+    case RGBA_8888:
+    case RGBX_8888:
+      return dawn::TextureFormat::RGBA8Unorm;
+    case BGRA_8888:
+    case BGRX_8888:
+      return dawn::TextureFormat::BGRA8Unorm;
+    case RED_8:
+    case ALPHA_8:
+    case LUMINANCE_8:
+      return dawn::TextureFormat::R8Unorm;
+    case RG_88:
+      return dawn::TextureFormat::RG8Unorm;
+    case RGBA_F16:
+      return dawn::TextureFormat::RGBA16Float;
+    case RGBX_1010102:
+      return dawn::TextureFormat::RGB10A2Unorm;
+    case RGBA_4444:
+    case RGB_565:
+    case BGR_565:
+    case R16_EXT:
+    case BGRX_1010102:
+    case YVU_420:
+    case YUV_420_BIPLANAR:
+    case ETC1:
+    case LUMINANCE_F16:
+    case P010:
+      break;
+  }
+  NOTREACHED() << "Unsupported format " << format;
+  return dawn::TextureFormat::Undefined;
+}
+#endif
+
 }  // namespace viz
diff --git a/components/viz/common/resources/resource_format_utils.h b/components/viz/common/resources/resource_format_utils.h
index 45866c0..eb8748f9 100644
--- a/components/viz/common/resources/resource_format_utils.h
+++ b/components/viz/common/resources/resource_format_utils.h
@@ -8,6 +8,7 @@
 #include "components/viz/common/resources/resource_format.h"
 #include "components/viz/common/viz_resource_format_export.h"
 #include "gpu/vulkan/buildflags.h"
+#include "skia/buildflags.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/gpu/GrTypes.h"
 #include "ui/gfx/buffer_types.h"
@@ -16,6 +17,10 @@
 #include "third_party/vulkan/include/vulkan/vulkan.h"
 #endif
 
+#if BUILDFLAG(SKIA_USE_DAWN)
+#include "third_party/dawn/src/include/dawn/dawncpp.h"  // nogncheck
+#endif
+
 namespace viz {
 
 VIZ_RESOURCE_FORMAT_EXPORT SkColorType
@@ -62,6 +67,11 @@
 VIZ_RESOURCE_FORMAT_EXPORT VkFormat ToVkFormat(ResourceFormat format);
 #endif
 
+#if BUILDFLAG(SKIA_USE_DAWN)
+VIZ_RESOURCE_FORMAT_EXPORT dawn::TextureFormat ToDawnFormat(
+    ResourceFormat format);
+#endif
+
 }  // namespace viz
 
 #endif  // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index fd7d8e8..d929601 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -2265,6 +2265,9 @@
   cc::FakeOutputSurfaceClient output_surface_client;
   std::unique_ptr<FakeOutputSurface> output_surface(
       FakeOutputSurface::Create3d());
+#if defined(OS_WIN)
+  output_surface->set_supports_dc_layers(true);
+#endif
   output_surface->BindToClient(&output_surface_client);
 
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index e692643..0deeecb 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -99,41 +99,6 @@
                                : OutputSurface::Type::kOpenGL;
 }
 
-#if BUILDFLAG(SKIA_USE_DAWN)
-// TODO(sgilhuly): Resolve conflicts with X11/X.h so that this can be moved to
-// resource_format_utils.cc.
-dawn::TextureFormat ToDawnFormat(ResourceFormat format) {
-  switch (format) {
-    case RGBA_8888:
-    case RGBX_8888:
-      return dawn::TextureFormat::RGBA8Unorm;
-    case BGRA_8888:
-    case BGRX_8888:
-      return dawn::TextureFormat::BGRA8Unorm;
-    case RED_8:
-    case ALPHA_8:
-    case LUMINANCE_8:
-      return dawn::TextureFormat::R8Unorm;
-    case RGBA_4444:
-    case RGB_565:
-    case BGR_565:
-    case RG_88:
-    case RGBA_F16:
-    case R16_EXT:
-    case RGBX_1010102:
-    case BGRX_1010102:
-    case YVU_420:
-    case YUV_420_BIPLANAR:
-    case ETC1:
-    case LUMINANCE_F16:
-    case P010:
-      break;
-  }
-  NOTREACHED() << "Unsupported format " << format;
-  return dawn::TextureFormat::Undefined;
-}
-#endif
-
 }  // namespace
 
 SkiaOutputSurfaceImpl::ScopedPaint::ScopedPaint(
diff --git a/content/browser/accessibility/accessibility_auralinux_browsertest.cc b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
index c40504b..d0abf05 100644
--- a/content/browser/accessibility/accessibility_auralinux_browsertest.cc
+++ b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
@@ -144,8 +144,9 @@
     int expected_end_offset,
     const char* expected_text) {
   testing::Message message;
-  message << "While checking for \'" << expected_text << "\' at "
-          << expected_start_offset << '-' << expected_end_offset << '.';
+  message << "While checking at index \'" << offset << "\' for \'"
+          << expected_text << "\' at " << expected_start_offset << '-'
+          << expected_end_offset << '.';
   SCOPED_TRACE(message);
 
   int start_offset = 0;
@@ -299,6 +300,49 @@
   g_object_unref(atk_text);
 }
 
+IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
+                       TestParagraphTextAtOffsetWithBoundarySentence) {
+  LoadInitialAccessibilityTreeFromHtml(std::string(
+      R"HTML(<!DOCTYPE html>
+          <html>
+          <body>
+            <div>One sentence. Two sentences. Three sentences!</div>
+          </body>
+          </html>)HTML"));
+
+  AtkObject* document = GetRendererAccessible();
+  EXPECT_EQ(1, atk_object_get_n_accessible_children(document));
+
+  AtkText* div_element = ATK_TEXT(atk_object_ref_accessible_child(document, 0));
+  EXPECT_EQ(1, atk_object_get_n_accessible_children(ATK_OBJECT(div_element)));
+  AtkText* atk_text =
+      ATK_TEXT(atk_object_ref_accessible_child(ATK_OBJECT(div_element), 0));
+
+  int first_sentence_offset = 14;
+  int second_sentence_offset = first_sentence_offset + 15;
+  int third_sentence_offset = second_sentence_offset + 16;
+
+  for (int i = 0; i < first_sentence_offset; ++i) {
+    CheckTextAtOffset(atk_text, i, ATK_TEXT_BOUNDARY_SENTENCE_START, 0,
+                      first_sentence_offset, "One sentence. ");
+  }
+
+  for (int i = first_sentence_offset + 1; i < second_sentence_offset; ++i) {
+    CheckTextAtOffset(atk_text, i, ATK_TEXT_BOUNDARY_SENTENCE_START,
+                      first_sentence_offset, second_sentence_offset,
+                      "Two sentences. ");
+  }
+
+  for (int i = second_sentence_offset + 1; i < third_sentence_offset; ++i) {
+    CheckTextAtOffset(atk_text, i, ATK_TEXT_BOUNDARY_SENTENCE_START,
+                      second_sentence_offset, third_sentence_offset,
+                      "Three sentences!");
+  }
+
+  g_object_unref(atk_text);
+  g_object_unref(div_element);
+}
+
 #if defined(ATK_CHECK_VERSION) && ATK_CHECK_VERSION(2, 30, 0)
 #define ATK_230
 #endif
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index 61b0d9f..f8404b21 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -129,9 +129,9 @@
   DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest);
 };
 
-AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() {}
+AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() = default;
 
-AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() {}
+AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() = default;
 
 base::string16 AccessibilityWinBrowserTest::PrintAXTree() const {
   std::unique_ptr<AccessibilityTreeFormatter> formatter(
@@ -143,10 +143,7 @@
 
   base::string16 str;
   formatter->FormatAccessibilityTree(
-      static_cast<WebContentsImpl*>(shell()->web_contents())
-          ->GetRootBrowserAccessibilityManager()
-          ->GetRoot(),
-      &str);
+      GetRootAccessibilityNode(shell()->web_contents()), &str);
   return str;
 }
 
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 488d31a0..264ae2a8 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -186,6 +186,7 @@
 }
 
 BrowserAccessibilityManager::~BrowserAccessibilityManager() {
+  delegate_ = nullptr;  // Guard against reentrancy by screen reader.
   if (last_focused_node_tree_id_ &&
       ax_tree_id_ == *last_focused_node_tree_id_) {
     SetLastFocusedNode(nullptr);
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
index d7078fd9..9210695 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.cc
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
@@ -133,12 +133,9 @@
       PropertyFilter(base::ASCIIToUTF16("*"), PropertyFilter::ALLOW));
   formatter->SetPropertyFilters(property_filters);
   formatter->set_show_ids(true);
-  WebContentsImpl* web_contents =
-      static_cast<WebContentsImpl*>(shell()->web_contents());
   base::string16 ax_tree_dump;
   formatter->FormatAccessibilityTree(
-      web_contents->GetRootBrowserAccessibilityManager()->GetRoot(),
-      &ax_tree_dump);
+      GetRootAccessibilityNode(shell()->web_contents()), &ax_tree_dump);
   return ax_tree_dump;
 }
 
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.h b/content/browser/accessibility/dump_accessibility_browsertest_base.h
index cbea701b..187c8c6 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.h
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_BROWSER_ACCESSIBILITY_DUMP_ACCESSIBILITY_BROWSERTEST_BASE_H_
 #define CONTENT_BROWSER_ACCESSIBILITY_DUMP_ACCESSIBILITY_BROWSERTEST_BASE_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 85c4bf68..fbf4de5 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -15,7 +15,6 @@
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_blink.h"
-#include "content/browser/accessibility/browser_accessibility.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/dump_accessibility_browsertest_base.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -147,10 +146,8 @@
     formatter->SetPropertyFilters(property_filters_);
     formatter->SetNodeFilters(node_filters_);
     base::string16 actual_contents_utf16;
-    WebContentsImpl* web_contents =
-        static_cast<WebContentsImpl*>(shell()->web_contents());
     formatter->FormatAccessibilityTree(
-        web_contents->GetRootBrowserAccessibilityManager()->GetRoot(),
+        GetRootAccessibilityNode(shell()->web_contents()),
         &actual_contents_utf16);
     std::string actual_contents = base::UTF16ToUTF8(actual_contents_utf16);
     return base::SplitString(actual_contents, "\n", base::KEEP_WHITESPACE,
diff --git a/content/browser/android/content_feature_list.cc b/content/browser/android/content_feature_list.cc
index ed536eb8..7fba4a3 100644
--- a/content/browser/android/content_feature_list.cc
+++ b/content/browser/android/content_feature_list.cc
@@ -7,7 +7,7 @@
 #include "base/android/jni_string.h"
 #include "base/feature_list.h"
 #include "base/stl_util.h"
-#include "content/public/android/content_jni_headers/ContentFeatureList_jni.h"
+#include "content/public/android/content_jni_headers/ContentFeatureListImpl_jni.h"
 #include "content/public/common/content_features.h"
 
 using base::android::ConvertJavaStringToUTF8;
@@ -23,6 +23,7 @@
 // in other locations in the code base (e.g. content_features.h).
 const base::Feature* kFeaturesExposedToJava[] = {
     &features::kBackgroundMediaRendererHasModerateBinding,
+    &features::kWebNfc,
     &kServiceGroupImportance,
 };
 
@@ -42,7 +43,7 @@
 const base::Feature kServiceGroupImportance{"ServiceGroupImportance",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
-static jboolean JNI_ContentFeatureList_IsEnabled(
+static jboolean JNI_ContentFeatureListImpl_IsEnabled(
     JNIEnv* env,
     const JavaParamRef<jstring>& jfeature_name) {
   const base::Feature* feature =
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 61f5109..b05a8a8 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -375,7 +375,7 @@
 #if defined(OS_CHROMEOS)
   if (chromeos::switches::MemoryPressureHandlingEnabled())
     monitor = std::make_unique<util::MultiSourceMemoryPressureMonitor>();
-#elif defined(OS_MACOSX) || defined(OS_WIN)
+#elif defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_FUCHSIA)
   monitor = std::make_unique<util::MultiSourceMemoryPressureMonitor>();
 #endif
   // No memory monitor on other platforms...
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index c4fcde9..1fd6d7e 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1126,6 +1126,10 @@
   for (auto& iter : visual_state_callbacks_)
     std::move(iter.second).Run(false);
 
+  // Delete this before destroying the widget, to guard against reentrancy
+  // by in-process screen readers such as JAWS.
+  browser_accessibility_manager_.reset();
+
   // Note: The RenderWidgetHost of the main frame is owned by the RenderViewHost
   // instead. In this case the RenderViewHost is responsible for shutting down
   // its RenderViewHost.
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc
index e85bea68..a849ef1 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -998,10 +998,12 @@
 }
 
 void GpuDataManagerImplPrivate::FallBackToNextGpuMode() {
-#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
+#if defined(OS_ANDROID) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
   // Android and Chrome OS can't switch to software compositing. If the GPU
   // process initialization fails or GPU process is too unstable then crash the
   // browser process to reset everything.
+  // On Fuchsia Vulkan must be used when it's enabled by the WebEngine embedder.
+  // Falling back to SW compositing in that case is not supported.
 #if defined(OS_ANDROID)
   FatalGpuProcessLaunchFailureOnBackground();
 #endif
diff --git a/content/browser/media/cdm_storage_impl.cc b/content/browser/media/cdm_storage_impl.cc
index 898e5377..cd9bfc1a 100644
--- a/content/browser/media/cdm_storage_impl.cc
+++ b/content/browser/media/cdm_storage_impl.cc
@@ -35,9 +35,10 @@
 namespace content {
 
 // static
-void CdmStorageImpl::Create(RenderFrameHost* render_frame_host,
-                            const std::string& cdm_file_system_id,
-                            media::mojom::CdmStorageRequest request) {
+void CdmStorageImpl::Create(
+    RenderFrameHost* render_frame_host,
+    const std::string& cdm_file_system_id,
+    mojo::PendingReceiver<media::mojom::CdmStorage> receiver) {
   DVLOG(3) << __func__;
   DCHECK(!render_frame_host->GetLastCommittedOrigin().opaque())
       << "Invalid origin specified for CdmStorageImpl::Create";
@@ -49,9 +50,9 @@
   if (storage_partition)
     file_system_context = storage_partition->GetFileSystemContext();
 
-  // The created object is bound to (and owned by) |request|.
+  // The created object is bound to (and owned by) |receiver|.
   new CdmStorageImpl(render_frame_host, cdm_file_system_id,
-                     std::move(file_system_context), std::move(request));
+                     std::move(file_system_context), std::move(receiver));
 }
 
 // static
@@ -75,8 +76,8 @@
     RenderFrameHost* render_frame_host,
     const std::string& cdm_file_system_id,
     scoped_refptr<storage::FileSystemContext> file_system_context,
-    media::mojom::CdmStorageRequest request)
-    : FrameServiceBase(render_frame_host, std::move(request)),
+    mojo::PendingReceiver<media::mojom::CdmStorage> receiver)
+    : FrameServiceBase(render_frame_host, std::move(receiver)),
       cdm_file_system_id_(cdm_file_system_id),
       file_system_context_(std::move(file_system_context)),
       child_process_id_(render_frame_host->GetProcess()->GetID()) {}
diff --git a/content/browser/media/cdm_storage_impl.h b/content/browser/media/cdm_storage_impl.h
index 423d9df..49f7886e 100644
--- a/content/browser/media/cdm_storage_impl.h
+++ b/content/browser/media/cdm_storage_impl.h
@@ -16,6 +16,7 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/frame_service_base.h"
 #include "media/mojo/mojom/cdm_storage.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/unique_associated_receiver_set.h"
 
 namespace storage {
@@ -40,7 +41,7 @@
   // |request|.
   static void Create(RenderFrameHost* render_frame_host,
                      const std::string& cdm_file_system_id,
-                     media::mojom::CdmStorageRequest request);
+                     mojo::PendingReceiver<media::mojom::CdmStorage> receiver);
 
   // media::mojom::CdmStorage implementation.
   void Open(const std::string& file_name, OpenCallback callback) final;
@@ -54,7 +55,7 @@
   CdmStorageImpl(RenderFrameHost* render_frame_host,
                  const std::string& cdm_file_system_id,
                  scoped_refptr<storage::FileSystemContext> file_system_context,
-                 media::mojom::CdmStorageRequest request);
+                 mojo::PendingReceiver<media::mojom::CdmStorage> receiver);
   ~CdmStorageImpl() final;
 
   // Called when the file system is opened.
diff --git a/content/browser/media/cdm_storage_impl_unittest.cc b/content/browser/media/cdm_storage_impl_unittest.cc
index 41ff33c..2d96743 100644
--- a/content/browser/media/cdm_storage_impl_unittest.cc
+++ b/content/browser/media/cdm_storage_impl_unittest.cc
@@ -16,13 +16,13 @@
 #include "media/mojo/mojom/cdm_storage.mojom.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
 using media::mojom::CdmFile;
 using media::mojom::CdmStorage;
-using media::mojom::CdmStoragePtr;
 
 namespace content {
 
@@ -92,7 +92,7 @@
     // Create the CdmStorageImpl object. |cdm_storage_| will own the resulting
     // object.
     CdmStorageImpl::Create(rfh_, file_system_id,
-                           mojo::MakeRequest(&cdm_storage_));
+                           cdm_storage_.BindNewPipeAndPassReceiver());
   }
 
   // Open the file |name|. Returns true if the file returned is valid, false
@@ -209,7 +209,7 @@
   }
 
   RenderFrameHost* rfh_ = nullptr;
-  CdmStoragePtr cdm_storage_;
+  mojo::Remote<CdmStorage> cdm_storage_;
   std::unique_ptr<RunLoopWithExpectedCount> run_loop_with_count_;
 };
 
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index b058995..df8313db 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -130,7 +130,7 @@
     "java/src/org/chromium/content/browser/ContentApiHelperForM.java",
     "java/src/org/chromium/content/browser/ContentChildProcessConstants.java",
     "java/src/org/chromium/content/browser/ContentClassFactory.java",
-    "java/src/org/chromium/content/browser/ContentFeatureList.java",
+    "java/src/org/chromium/content/browser/ContentFeatureListImpl.java",
     "java/src/org/chromium/content/browser/ContentNfcDelegate.java",
     "java/src/org/chromium/content/browser/ContentUiEventHandler.java",
     "java/src/org/chromium/content/browser/ContentViewStaticsImpl.java",
@@ -247,6 +247,7 @@
     "java/src/org/chromium/content_public/browser/ActionModeCallbackHelper.java",
     "java/src/org/chromium/content_public/browser/BrowserStartupController.java",
     "java/src/org/chromium/content_public/browser/BrowserTaskExecutor.java",
+    "java/src/org/chromium/content_public/browser/ContentFeatureList.java",
     "java/src/org/chromium/content_public/browser/ContentViewStatics.java",
     "java/src/org/chromium/content_public/browser/DeviceUtils.java",
     "java/src/org/chromium/content_public/browser/InputMethodManagerWrapper.java",
@@ -400,7 +401,7 @@
     "java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java",
     "java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java",
     "java/src/org/chromium/content/browser/ContactsDialogHost.java",
-    "java/src/org/chromium/content/browser/ContentFeatureList.java",
+    "java/src/org/chromium/content/browser/ContentFeatureListImpl.java",
     "java/src/org/chromium/content/browser/ContentNfcDelegate.java",
     "java/src/org/chromium/content/browser/ContentUiEventHandler.java",
     "java/src/org/chromium/content/browser/ContentViewStaticsImpl.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
index d851e89..90ba61b 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
@@ -35,6 +35,7 @@
 import org.chromium.content.app.SandboxedProcessService;
 import org.chromium.content.common.ContentSwitchUtils;
 import org.chromium.content_public.browser.ChildProcessImportance;
+import org.chromium.content_public.browser.ContentFeatureList;
 import org.chromium.content_public.common.ContentSwitches;
 
 import java.io.IOException;
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentFeatureList.java b/content/public/android/java/src/org/chromium/content/browser/ContentFeatureListImpl.java
similarity index 65%
rename from content/public/android/java/src/org/chromium/content/browser/ContentFeatureList.java
rename to content/public/android/java/src/org/chromium/content/browser/ContentFeatureListImpl.java
index 300c8b3..706fc0c 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentFeatureList.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentFeatureListImpl.java
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -9,14 +9,12 @@
 import org.chromium.base.annotations.NativeMethods;
 
 /**
+ * Implementation of {@link ContentFeatureList}.
  * Java accessor for base/feature_list.h state.
  */
 @JNINamespace("content::android")
 @MainDex
-public abstract class ContentFeatureList {
-    // Prevent instantiation.
-    private ContentFeatureList() {}
-
+public class ContentFeatureListImpl {
     /**
      * Returns whether the specified feature is enabled or not.
      *
@@ -27,15 +25,9 @@
      * @return Whether the feature is enabled or not.
      */
     public static boolean isEnabled(String featureName) {
-        return ContentFeatureListJni.get().isEnabled(featureName);
+        return ContentFeatureListImplJni.get().isEnabled(featureName);
     }
 
-    // Alphabetical:
-    public static final String BACKGROUND_MEDIA_RENDERER_HAS_MODERATE_BINDING =
-            "BackgroundMediaRendererHasModerateBinding";
-
-    public static final String SERVICE_GROUP_IMPORTANCE = "ServiceGroupImportance";
-
     @NativeMethods
     interface Natives {
         boolean isEnabled(String featureName);
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
new file mode 100644
index 0000000..39293ff6d
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
@@ -0,0 +1,32 @@
+// 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.content_public.browser;
+
+import org.chromium.content.browser.ContentFeatureListImpl;
+
+/**
+ * Static public methods for ContentFeatureList.
+ */
+public class ContentFeatureList {
+    private ContentFeatureList() {}
+
+    /**
+     * Returns whether the specified feature is enabled or not.
+     *
+     * @param featureName The name of the feature to query.
+     * @return Whether the feature is enabled or not.
+     */
+    public static boolean isEnabled(String featureName) {
+        return ContentFeatureListImpl.isEnabled(featureName);
+    }
+
+    // Alphabetical:
+    public static final String BACKGROUND_MEDIA_RENDERER_HAS_MODERATE_BINDING =
+            "BackgroundMediaRendererHasModerateBinding";
+
+    public static final String SERVICE_GROUP_IMPORTANCE = "ServiceGroupImportance";
+
+    public static final String WEB_NFC = "WebNFC";
+}
diff --git a/device/bluetooth/public/cpp/bluetooth_uuid.h b/device/bluetooth/public/cpp/bluetooth_uuid.h
index e3d5b44..0c27ae7 100644
--- a/device/bluetooth/public/cpp/bluetooth_uuid.h
+++ b/device/bluetooth/public/cpp/bluetooth_uuid.h
@@ -6,6 +6,7 @@
 #define DEVICE_BLUETOOTH_PUBLIC_CPP_BLUETOOTH_UUID_H_
 
 #include <string>
+#include <vector>
 
 #include "build/build_config.h"
 
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom
index a054e81..28aa8ba 100644
--- a/device/vr/public/mojom/vr_service.mojom
+++ b/device/vr/public/mojom/vr_service.mojom
@@ -208,6 +208,13 @@
   bool pose_reset;
 };
 
+// An entity's pose. Used by entities for which both position and orientation is
+// always known (e.g. anchors, planes).
+struct Pose {
+  gfx.mojom.Quaternion orientation;
+  gfx.mojom.Point3F position;
+};
+
 struct XRRay {
   gfx.mojom.Point3F origin;
   gfx.mojom.Vector3dF direction;
@@ -328,7 +335,7 @@
   // Pose of the plane's center. Defines new coordinate space.
   // Y axis of the coordinate space describes plane's normal, the rotation of
   // X and Z around the Y axis is arbitrary.
-  VRPose pose;
+  Pose pose;
 
   // Vertices of 2D convex polygon approximating the plane.
   array<XRPlanePointData> polygon;
@@ -358,7 +365,7 @@
   uint64 id;
 
   // Pose of the anchor.
-  VRPose pose;
+  Pose pose;
 };
 
 // Struct containing information about all tracked & updated anchors in a given
@@ -554,8 +561,8 @@
   // Issues a request to create an anchor attached to a session.
   // |result| will contain status code of the request. |anchor_id| will be valid
   // only if the |result| is SUCCESS.
-  CreateAnchor(VRPose anchor_pose) => (CreateAnchorResult result,
-                                       uint64 anchor_id);
+  CreateAnchor(Pose anchor_pose) => (CreateAnchorResult result,
+                                     uint64 anchor_id);
   // TODO(https://crbug.com/657632): Switch anchor_id to nullable integer once
   // that's available. This will allow us to remove CreateAnchorResult if we're
   // not interested in obtaining detailed error information from the device.
@@ -563,7 +570,7 @@
   // Issues a request to create an anchor attached to a plane.
   // |result| will contain status code of the request. |anchor_id| will be valid
   // only if the |result| is SUCCESS.
-  CreatePlaneAnchor(VRPose anchor_pose, uint64 plane_id) =>
+  CreatePlaneAnchor(Pose anchor_pose, uint64 plane_id) =>
     (CreateAnchorResult result, uint64 anchor_id);
   // TODO(https://crbug.com/657632): Ditto - make anchor_id a nullable integer..
 
diff --git a/docs/gpu/pixel_wrangling.md b/docs/gpu/pixel_wrangling.md
index e54c98f..2ac2958 100644
--- a/docs/gpu/pixel_wrangling.md
+++ b/docs/gpu/pixel_wrangling.md
@@ -52,8 +52,7 @@
 [Chromium GPU]: https://ci.chromium.org/p/chromium/g/chromium.gpu/console?reload=120
 [Chromium GPU FYI]: https://ci.chromium.org/p/chromium/g/chromium.gpu.fyi/console?reload=120
 [ANGLE tryservers]: https://build.chromium.org/p/tryserver.chromium.angle/waterfall
-<!-- TODO(kainino): update link when the page is migrated -->
-[ANGLE Wrangler]: https://sites.google.com/a/chromium.org/dev/developers/how-tos/angle-wrangling
+[ANGLE Wrangler]: https://chromium.googlesource.com/angle/angle/+/master/infra/ANGLEWrangling.md
 
 ## Test Suites
 
diff --git a/extensions/common/image_util.cc b/extensions/common/image_util.cc
index 0b2cd46a..e84bf58 100644
--- a/extensions/common/image_util.cc
+++ b/extensions/common/image_util.cc
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include <algorithm>
-#include <vector>
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -65,9 +64,9 @@
       formatted_color += color_string[i];
     }
   } else if (color_string.length() == 7) {
-    formatted_color = color_string.substr(1, 6);
+    formatted_color.assign(color_string, 1, 6);
   } else if (color_string.length() == 9) {
-    formatted_color = color_string.substr(1, 8);
+    formatted_color.assign(color_string, 1, 8);
   } else {
     return false;
   }
@@ -77,10 +76,9 @@
     formatted_color += "FF";
   }
 
-  // Convert the string to an integer and make sure it is in the correct value
-  // range.
-  std::vector<uint8_t> color_bytes;
-  if (!base::HexStringToBytes(formatted_color, &color_bytes))
+  // Convert the hex string to an integer.
+  std::array<uint8_t, 4> color_bytes;
+  if (!base::HexStringToSpan(formatted_color, color_bytes))
     return false;
 
   *result = SkColorSetARGB(color_bytes[3], color_bytes[0], color_bytes[1],
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index cd9e0afb9..e1294b4 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -70,6 +70,7 @@
     ":web_engine_pak",
     "//base",
     "//base:base_static",
+    "//base/util/memory_pressure",
     "//components/cdm/renderer",
     "//components/version_info",
     "//content/public/app:both",
@@ -146,6 +147,8 @@
     "browser/web_engine_content_browser_client.h",
     "browser/web_engine_devtools_controller.cc",
     "browser/web_engine_devtools_controller.h",
+    "browser/web_engine_memory_pressure_evaluator.cc",
+    "browser/web_engine_memory_pressure_evaluator.h",
     "browser/web_engine_net_log.cc",
     "browser/web_engine_net_log.h",
     "browser/web_engine_permission_manager.cc",
diff --git a/fuchsia/engine/browser/context_impl.cc b/fuchsia/engine/browser/context_impl.cc
index fe8d1a0..785e05d 100644
--- a/fuchsia/engine/browser/context_impl.cc
+++ b/fuchsia/engine/browser/context_impl.cc
@@ -10,11 +10,13 @@
 
 #include "base/bind.h"
 #include "base/fuchsia/fuchsia_logging.h"
+#include "base/memory/memory_pressure_monitor.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "fuchsia/engine/browser/frame_impl.h"
 #include "fuchsia/engine/browser/web_engine_devtools_controller.h"
+#include "fuchsia/engine/browser/web_engine_memory_pressure_evaluator.h"
 
 ContextImpl::ContextImpl(content::BrowserContext* browser_context,
                          WebEngineDevToolsController* devtools_controller)
@@ -27,6 +29,15 @@
   DCHECK(browser_context_);
   DCHECK(devtools_controller_);
   devtools_controller_->OnContextCreated();
+
+  // In browser tests there will be no MemoryPressureMonitor.
+  if (base::MemoryPressureMonitor::Get()) {
+    memory_pressure_evaluator_ =
+        std::make_unique<WebEngineMemoryPressureEvaluator>(
+            static_cast<util::MultiSourceMemoryPressureMonitor*>(
+                base::MemoryPressureMonitor::Get())
+                ->CreateVoter());
+  }
 }
 
 ContextImpl::~ContextImpl() {
diff --git a/fuchsia/engine/browser/context_impl.h b/fuchsia/engine/browser/context_impl.h
index 48ee4a49..2a3728af 100644
--- a/fuchsia/engine/browser/context_impl.h
+++ b/fuchsia/engine/browser/context_impl.h
@@ -22,6 +22,7 @@
 
 class FrameImpl;
 class WebEngineDevToolsController;
+class WebEngineMemoryPressureEvaluator;
 
 // Implementation of Context from fuchsia.web.
 // Owns a BrowserContext instance and uses it to create new WebContents/Frames.
@@ -86,6 +87,10 @@
   // destruction when this ContextImpl is destroyed.
   std::set<std::unique_ptr<FrameImpl>, base::UniquePtrComparator> frames_;
 
+  // Synthesizes MemoryPressureLevel values & notifications to manage the
+  // Context process' memory footprint.
+  std::unique_ptr<WebEngineMemoryPressureEvaluator> memory_pressure_evaluator_;
+
   DISALLOW_COPY_AND_ASSIGN(ContextImpl);
 };
 
diff --git a/fuchsia/engine/browser/web_engine_memory_pressure_evaluator.cc b/fuchsia/engine/browser/web_engine_memory_pressure_evaluator.cc
new file mode 100644
index 0000000..faa44138
--- /dev/null
+++ b/fuchsia/engine/browser/web_engine_memory_pressure_evaluator.cc
@@ -0,0 +1,118 @@
+// 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 "fuchsia/engine/browser/web_engine_memory_pressure_evaluator.h"
+
+#include <lib/zx/job.h>
+#include <lib/zx/process.h>
+#include <zircon/rights.h>
+
+#include "base/fuchsia/default_job.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/util/memory_pressure/memory_pressure_voter.h"
+
+namespace {
+
+// TODO(https://crbug.com/1020698): Connect the evaluator to OS-provided metrics
+// or signals, rather than hard-wiring a target Context memory usage.
+constexpr size_t kContextMemoryCriticalBytes = 150 * 1024 * 1024;
+constexpr size_t kContextMemoryModerateBytes =
+    (kContextMemoryCriticalBytes * 3) / 4;
+
+// Match the moderate-pressure "cooldown" used on Windows & ChromeOS.
+constexpr auto kModerateCooldownTime = base::TimeDelta::FromSeconds(10);
+
+size_t QueryJobMemoryBytes(zx::unowned_job job) {
+  size_t total_bytes = 0u;
+
+  // Enumerate size of processes under |job|.
+  zx_koid_t koids[128]{};
+  size_t actual = 0u;
+  zx_status_t status = job->get_info(ZX_INFO_JOB_PROCESSES, koids,
+                                     sizeof(koids), &actual, nullptr);
+  ZX_CHECK(status == ZX_OK, status) << "get_info(PROCESSES)";
+  for (size_t i = 0; i < actual; ++i) {
+    zx::process process;
+    if (job->get_child(koids[i], ZX_RIGHT_SAME_RIGHTS, &process) != ZX_OK)
+      continue;
+    zx_info_task_stats_t context_stats{};
+    zx_status_t status =
+        process.get_info(ZX_INFO_TASK_STATS, &context_stats,
+                         sizeof(context_stats), nullptr, nullptr);
+    if (status != ZX_OK)
+      continue;
+    total_bytes +=
+        context_stats.mem_private_bytes + context_stats.mem_scaled_shared_bytes;
+  }
+
+  // Recursively enumerate jobs under |job|.
+  status = job->get_info(ZX_INFO_JOB_CHILDREN, koids, sizeof(koids), &actual,
+                         nullptr);
+  ZX_CHECK(status == ZX_OK, status) << "get_info(CHILDREN)";
+  for (size_t i = 0; i < actual; ++i) {
+    zx::job child_job;
+    if (job->get_child(koids[i], ZX_RIGHT_SAME_RIGHTS, &child_job) != ZX_OK)
+      continue;
+    total_bytes += QueryJobMemoryBytes(zx::unowned_job(child_job));
+  }
+
+  return total_bytes;
+}
+
+}  // namespace
+
+WebEngineMemoryPressureEvaluator::WebEngineMemoryPressureEvaluator(
+    std::unique_ptr<util::MemoryPressureVoter> voter)
+    : util::SystemMemoryPressureEvaluator(std::move(voter)) {
+  timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1),
+               base::BindRepeating(
+                   &WebEngineMemoryPressureEvaluator::CheckMemoryPressure,
+                   base::Unretained(this)));
+  CheckMemoryPressure();
+}
+
+WebEngineMemoryPressureEvaluator::~WebEngineMemoryPressureEvaluator() = default;
+
+void WebEngineMemoryPressureEvaluator::CheckMemoryPressure() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::MemoryPressureListener::MemoryPressureLevel old_level = current_vote();
+
+  // Query the private and shared memory usage of processes in the Context job.
+  size_t context_bytes = QueryJobMemoryBytes(base::GetDefaultJob());
+
+  // Calculate a MemoryPressureLevel based on current Context usage.
+  base::MemoryPressureListener::MemoryPressureLevel new_level =
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
+  if (context_bytes >= kContextMemoryCriticalBytes) {
+    new_level = base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
+  } else if (context_bytes > kContextMemoryModerateBytes) {
+    new_level = base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
+  }
+
+  VLOG(1) << "Context memory: " << context_bytes << ", pressure=" << new_level;
+
+  // Set the new vote, and determine whether to notify listeners.
+  SetCurrentVote(new_level);
+  switch (new_level) {
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+      // By convention no notifications are sent when returning to NONE level.
+      SendCurrentVote(false);
+      break;
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: {
+      // Moderate pressure signals trigger some cleanup work, so to avoid
+      // impacting performance, only deliver MODERATE signals if it that level
+      // is sustained.
+      if (old_level != new_level)
+        next_moderate_notify_time_ = base::TimeTicks();
+      base::TimeTicks now = base::TimeTicks::Now();
+      SendCurrentVote(now >= next_moderate_notify_time_);
+      next_moderate_notify_time_ = now + kModerateCooldownTime;
+      break;
+    }
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+      SendCurrentVote(true);
+      break;
+  }
+}
diff --git a/fuchsia/engine/browser/web_engine_memory_pressure_evaluator.h b/fuchsia/engine/browser/web_engine_memory_pressure_evaluator.h
new file mode 100644
index 0000000..aea6f3f3
--- /dev/null
+++ b/fuchsia/engine/browser/web_engine_memory_pressure_evaluator.h
@@ -0,0 +1,44 @@
+// 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 FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_MEMORY_PRESSURE_EVALUATOR_H_
+#define FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_MEMORY_PRESSURE_EVALUATOR_H_
+
+#include "base/memory/memory_pressure_listener.h"
+#include "base/sequence_checker.h"
+#include "base/timer/timer.h"
+#include "base/util/memory_pressure/system_memory_pressure_evaluator.h"
+
+namespace util {
+class MemoryPressureVoter;
+}
+
+// Synthesizes MemoryPressureLevel values & notifications by comparing the total
+// memory usage of the web Context processes against a target total.
+class WebEngineMemoryPressureEvaluator
+    : public util::SystemMemoryPressureEvaluator {
+ public:
+  explicit WebEngineMemoryPressureEvaluator(
+      std::unique_ptr<util::MemoryPressureVoter> voter);
+
+  ~WebEngineMemoryPressureEvaluator() override;
+
+  WebEngineMemoryPressureEvaluator(const WebEngineMemoryPressureEvaluator&) =
+      delete;
+  WebEngineMemoryPressureEvaluator& operator=(
+      const WebEngineMemoryPressureEvaluator&) = delete;
+
+ private:
+  void CheckMemoryPressure();
+
+  // Periodic timer used to trigger sampling of memory usage.
+  base::RepeatingTimer timer_;
+
+  // Next time at which to notify moderate memory pressure.
+  base::TimeTicks next_moderate_notify_time_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+#endif  // FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_MEMORY_PRESSURE_EVALUATOR_H_
diff --git a/fuchsia/engine/context_provider_impl.cc b/fuchsia/engine/context_provider_impl.cc
index 759ad917..57835345 100644
--- a/fuchsia/engine/context_provider_impl.cc
+++ b/fuchsia/engine/context_provider_impl.cc
@@ -247,14 +247,28 @@
   }
 
   if (enable_vulkan) {
+    DLOG(ERROR) << "Enabling Vulkan GPU acceleration.";
+
+    // Vulkan requires use of SkiaRenderer, configured to a use Vulkan context.
     launch_command.AppendSwitch(switches::kUseVulkan);
     launch_command.AppendSwitchASCII(switches::kEnableFeatures,
                                      features::kUseSkiaRenderer.name);
-    launch_command.AppendSwitch(switches::kEnableOopRasterization);
-    launch_command.AppendSwitchASCII(switches::kUseGL,
-                                     gl::kGLImplementationStubName);
     launch_command.AppendSwitchASCII(switches::kGrContextType,
                                      switches::kGrContextTypeVulkan);
+
+    // SkiaRenderer requires out-of-process rasterization be enabled.
+    launch_command.AppendSwitch(switches::kEnableOopRasterization);
+
+    // TODO(https://crbug.com/766360): Provide a no-op GL implementation until
+    // vANGLE is available.
+    launch_command.AppendSwitchASCII(switches::kUseGL,
+                                     gl::kGLImplementationStubName);
+  } else {
+    DLOG(ERROR) << "Disabling GPU acceleration.";
+    // Disable use of Vulkan GPU, and use of the software-GL rasterizer. The
+    // Context will still run a GPU process, but will not support WebGL.
+    launch_command.AppendSwitch(switches::kDisableGpu);
+    launch_command.AppendSwitch(switches::kDisableSoftwareRasterizer);
   }
 
   if (enable_widevine) {
diff --git a/fuchsia/runners/cast/cast_runner_integration_test.cc b/fuchsia/runners/cast/cast_runner_integration_test.cc
index 5ae4aba..73410997 100644
--- a/fuchsia/runners/cast/cast_runner_integration_test.cc
+++ b/fuchsia/runners/cast/cast_runner_integration_test.cc
@@ -172,7 +172,8 @@
             TestTimeouts::action_timeout(),
             base::MakeExpectedNotRunClosure(FROM_HERE, "Run() timed out.")) {
     // Create the CastRunner, published into |outgoing_directory_|.
-    constexpr fuchsia::web::ContextFeatureFlags kFeatures = {};
+    constexpr fuchsia::web::ContextFeatureFlags kFeatures = {
+        fuchsia::web::ContextFeatureFlags::NETWORK};
     fuchsia::web::CreateContextParams create_context_params =
         WebContentRunner::BuildCreateContextParams(
             fidl::InterfaceHandle<fuchsia::io::Directory>(), kFeatures);
diff --git a/gin/BUILD.gn b/gin/BUILD.gn
index 9698cb8..e49ca6c2 100644
--- a/gin/BUILD.gn
+++ b/gin/BUILD.gn
@@ -63,6 +63,8 @@
     "v8_isolate_memory_dump_provider.cc",
     "v8_isolate_memory_dump_provider.h",
     "v8_platform.cc",
+    "v8_shared_memory_dump_provider.cc",
+    "v8_shared_memory_dump_provider.h",
     "wrappable.cc",
     "wrappable.h",
     "wrapper_info.cc",
@@ -138,6 +140,7 @@
     "test/run_all_unittests.cc",
     "v8_isolate_memory_dump_provider_unittest.cc",
     "v8_platform_unittest.cc",
+    "v8_shared_memory_dump_provider_unittest.cc",
     "wrappable_unittest.cc",
   ]
 
diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc
index 9298a9c..5d07ecaa 100644
--- a/gin/v8_initializer.cc
+++ b/gin/v8_initializer.cc
@@ -23,9 +23,12 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/threading/platform_thread.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "base/trace_event/memory_dump_manager.h"
 #include "build/build_config.h"
 #include "gin/gin_features.h"
+#include "gin/v8_shared_memory_dump_provider.h"
 
 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
 #if defined(OS_ANDROID)
@@ -270,6 +273,10 @@
   v8::V8::SetEntropySource(&GenerateEntropy);
   v8::V8::Initialize();
 
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      gin::V8SharedMemoryDumpProvider::Instance(), "V8SharedMemory",
+      base::ThreadTaskRunnerHandle::Get());
+
   v8_is_initialized = true;
 }
 
diff --git a/gin/v8_shared_memory_dump_provider.cc b/gin/v8_shared_memory_dump_provider.cc
new file mode 100644
index 0000000..b4edbcc
--- /dev/null
+++ b/gin/v8_shared_memory_dump_provider.cc
@@ -0,0 +1,51 @@
+// 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 "gin/v8_shared_memory_dump_provider.h"
+
+#include <string>
+
+#include "base/memory/singleton.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+V8SharedMemoryDumpProvider::V8SharedMemoryDumpProvider() {}
+
+// static
+V8SharedMemoryDumpProvider* V8SharedMemoryDumpProvider::Instance() {
+  return base::Singleton<
+      V8SharedMemoryDumpProvider,
+      base::LeakySingletonTraits<V8SharedMemoryDumpProvider>>::get();
+}
+
+// Called at trace dump point time. Creates a snapshot with the memory counters
+// for the current isolate.
+bool V8SharedMemoryDumpProvider::OnMemoryDump(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* process_memory_dump) {
+  v8::SharedMemoryStatistics shared_memory_statistics;
+  v8::V8::GetSharedMemoryStatistics(&shared_memory_statistics);
+
+  std::string dump_base_name = "v8/shared";
+  auto* shared_memory_dump = process_memory_dump->CreateAllocatorDump(
+      dump_base_name + "/read_only_space");
+  shared_memory_dump->AddScalar(
+      base::trace_event::MemoryAllocatorDump::kNameSize,
+      base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+      shared_memory_statistics.read_only_space_physical_size());
+  shared_memory_dump->AddScalar(
+      "allocated_objects_size",
+      base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+      shared_memory_statistics.read_only_space_used_size());
+  shared_memory_dump->AddScalar(
+      "virtual_size", base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+      shared_memory_statistics.read_only_space_size());
+
+  return true;
+}
+
+}  // namespace gin
diff --git a/gin/v8_shared_memory_dump_provider.h b/gin/v8_shared_memory_dump_provider.h
new file mode 100644
index 0000000..1049198d
--- /dev/null
+++ b/gin/v8_shared_memory_dump_provider.h
@@ -0,0 +1,37 @@
+// 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 GIN_V8_SHARED_MEMORY_DUMP_PROVIDER_H_
+#define GIN_V8_SHARED_MEMORY_DUMP_PROVIDER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "gin/gin_export.h"
+
+namespace gin {
+
+// Memory dump provider for the chrome://tracing infrastructure. It dumps
+// summarized memory stats about V8 Memory shared between Isolates in the same
+// process.
+class GIN_EXPORT V8SharedMemoryDumpProvider
+    : public base::trace_event::MemoryDumpProvider {
+ public:
+  V8SharedMemoryDumpProvider();
+
+  // MemoryDumpProvider implementation.
+  bool OnMemoryDump(
+      const base::trace_event::MemoryDumpArgs& args,
+      base::trace_event::ProcessMemoryDump* process_memory_dump) override;
+
+  static V8SharedMemoryDumpProvider* Instance();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(V8SharedMemoryDumpProvider);
+};
+
+}  // namespace gin
+
+#endif  // GIN_V8_SHARED_MEMORY_DUMP_PROVIDER_H_
diff --git a/gin/v8_shared_memory_dump_provider_unittest.cc b/gin/v8_shared_memory_dump_provider_unittest.cc
new file mode 100644
index 0000000..df0ff7e
--- /dev/null
+++ b/gin/v8_shared_memory_dump_provider_unittest.cc
@@ -0,0 +1,45 @@
+// 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 "gin/v8_shared_memory_dump_provider.h"
+
+#include <memory>
+
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event.h"
+#include "gin/test/v8_test.h"
+
+namespace gin {
+
+typedef V8Test V8SharedMemoryDumpProviderTest;
+
+// Checks if the dump provider runs without crashing and dumps root objects.
+TEST_F(V8SharedMemoryDumpProviderTest, DumpStatistics) {
+  V8SharedMemoryDumpProvider provider;
+
+  base::trace_event::MemoryDumpArgs dump_args = {
+      base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
+  std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump(
+      new base::trace_event::ProcessMemoryDump(dump_args));
+  provider.OnMemoryDump(dump_args, process_memory_dump.get());
+  const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap&
+      allocator_dumps = process_memory_dump->allocator_dumps();
+
+  bool did_dump_shared_memory_stats = false;
+  bool did_dump_read_only_space = false;
+  for (const auto& name_dump : allocator_dumps) {
+    const std::string& name = name_dump.first;
+    if (name.find("v8/shared") != std::string::npos) {
+      did_dump_shared_memory_stats = true;
+    }
+    if (name.find("v8/shared/read_only_space") != std::string::npos) {
+      did_dump_read_only_space = true;
+    }
+  }
+
+  ASSERT_TRUE(did_dump_shared_memory_stats);
+  ASSERT_TRUE(did_dump_read_only_space);
+}
+
+}  // namespace gin
diff --git a/ios/chrome/browser/payments/ios_payment_instrument.h b/ios/chrome/browser/payments/ios_payment_instrument.h
index b5efa913..b213e63 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument.h
+++ b/ios/chrome/browser/payments/ios_payment_instrument.h
@@ -13,7 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #include "url/gurl.h"
 
@@ -30,7 +30,7 @@
 const std::map<std::string, std::string>& GetMethodNameToSchemeName();
 
 // Represents an iOS Native App as a form of payment in Payment Request.
-class IOSPaymentInstrument : public PaymentInstrument {
+class IOSPaymentInstrument : public PaymentApp {
  public:
   // Initializes an IOSPaymentInstrument. |method_name| is the url payment
   // method identifier for this instrument. |universal_link| is the unique
@@ -48,8 +48,8 @@
 
   ~IOSPaymentInstrument() override;
 
-  // PaymentInstrument:
-  void InvokePaymentApp(PaymentInstrument::Delegate* delegate) override;
+  // PaymentApp:
+  void InvokePaymentApp(PaymentApp::Delegate* delegate) override;
   bool IsCompleteForPayment() const override;
   uint32_t GetCompletenessScore() const override;
   bool CanPreselect() const override;
@@ -72,7 +72,7 @@
   bool HandlesPayerName() const override;
   bool HandlesPayerEmail() const override;
   bool HandlesPayerPhone() const override;
-  base::WeakPtr<PaymentInstrument> AsWeakPtr() override;
+  base::WeakPtr<PaymentApp> AsWeakPtr() override;
 
   // Given that the icon for the iOS payment instrument can only be determined
   // at run-time, the icon is obtained using this UIImage object rather than
diff --git a/ios/chrome/browser/payments/ios_payment_instrument.mm b/ios/chrome/browser/payments/ios_payment_instrument.mm
index 139bd1f..5db152c1 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument.mm
@@ -37,8 +37,8 @@
     const std::string& app_name,
     UIImage* icon_image,
     id<PaymentRequestUIDelegate> payment_request_ui_delegate)
-    : PaymentInstrument(-1 /* resource id not used */,
-                        PaymentInstrument::Type::NATIVE_MOBILE_APP),
+    : PaymentApp(-1 /* resource id not used */,
+                 PaymentApp::Type::NATIVE_MOBILE_APP),
       method_name_(method_name),
       universal_link_(universal_link),
       app_name_(app_name),
@@ -46,8 +46,7 @@
       payment_request_ui_delegate_(payment_request_ui_delegate) {}
 IOSPaymentInstrument::~IOSPaymentInstrument() {}
 
-void IOSPaymentInstrument::InvokePaymentApp(
-    PaymentInstrument::Delegate* delegate) {
+void IOSPaymentInstrument::InvokePaymentApp(PaymentApp::Delegate* delegate) {
   DCHECK(delegate);
   [payment_request_ui_delegate_ paymentInstrument:this
                        launchAppWithUniversalLink:universal_link_
@@ -134,7 +133,7 @@
   return false;
 }
 
-base::WeakPtr<PaymentInstrument> IOSPaymentInstrument::AsWeakPtr() {
+base::WeakPtr<PaymentApp> IOSPaymentInstrument::AsWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
 
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher.h b/ios/chrome/browser/payments/ios_payment_instrument_launcher.h
index 62c9a84b..eb2d2ab9 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_launcher.h
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher.h
@@ -39,11 +39,10 @@
   // indicating if it made an attempt to launch the IOSPaymentInstrument. The
   // only instance when the launcher will not attempt a launch is when there is
   // another in-flight request already happening.
-  bool LaunchIOSPaymentInstrument(
-      payments::PaymentRequest* payment_request,
-      web::WebState* active_web_state,
-      GURL& universal_link,
-      payments::PaymentInstrument::Delegate* delegate);
+  bool LaunchIOSPaymentInstrument(payments::PaymentRequest* payment_request,
+                                  web::WebState* active_web_state,
+                                  GURL& universal_link,
+                                  payments::PaymentApp::Delegate* delegate);
 
   // Callback for when an iOS payment app sends a response back to Chrome.
   // |response| is a base-64 encodeded string. When decoded, |response| is
@@ -58,10 +57,10 @@
 
   // Before invoking ReceieveResponseFromIOSPaymentInstrument, callers can
   // use delegate() to ensure that the delegate property is valid.
-  payments::PaymentInstrument::Delegate* delegate() { return delegate_; }
+  payments::PaymentApp::Delegate* delegate() { return delegate_; }
 
   // Sets the delegate for the current IOSPaymentInstrumentLauncher request.
-  void set_delegate(payments::PaymentInstrument::Delegate* delegate) {
+  void set_delegate(payments::PaymentApp::Delegate* delegate) {
     delegate_ = delegate;
   }
 
@@ -100,7 +99,7 @@
   void CompleteLaunchRequest(const std::string& method_name,
                              const std::string& details);
 
-  payments::PaymentInstrument::Delegate* delegate_;
+  payments::PaymentApp::Delegate* delegate_;
   std::string payment_request_id_;
 };
 
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm b/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
index 4f61ca8..1679852 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
@@ -14,8 +14,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "components/payments/core/native_error_strings.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_details.h"
-#include "components/payments/core/payment_instrument.h"
 #import "ios/chrome/browser/payments/payment_request_constants.h"
 #include "ios/web/public/navigation/navigation_item.h"
 #include "ios/web/public/navigation/navigation_manager.h"
@@ -60,7 +60,7 @@
     payments::PaymentRequest* payment_request,
     web::WebState* active_web_state,
     GURL& universal_link,
-    payments::PaymentInstrument::Delegate* delegate) {
+    payments::PaymentApp::Delegate* delegate) {
   DCHECK(delegate);
 
   // Only one request can be handled at a time.
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm b/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm
index 5269e9f8..8097ca5a 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm
@@ -13,7 +13,7 @@
 #include "base/test/task_environment.h"
 #include "base/values.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/web_payment_request.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
@@ -31,7 +31,7 @@
 
 namespace {
 
-class FakePaymentInstrumentDelegate : public PaymentInstrument::Delegate {
+class FakePaymentInstrumentDelegate : public PaymentApp::Delegate {
  public:
   FakePaymentInstrumentDelegate() {}
 
diff --git a/ios/chrome/browser/payments/payment_request.h b/ios/chrome/browser/payments/payment_request.h
index 3ec10f1c..4cbd9912 100644
--- a/ios/chrome/browser/payments/payment_request.h
+++ b/ios/chrome/browser/payments/payment_request.h
@@ -17,7 +17,7 @@
 #include "components/autofill/core/browser/address_normalizer_impl.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/payments/core/journey_logger.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_options_provider.h"
 #include "components/payments/core/payment_request_base_delegate.h"
 #include "components/payments/core/payments_profile_comparator.h"
@@ -34,7 +34,7 @@
 }  // namespace autofill
 
 namespace payments {
-class AutofillPaymentInstrument;
+class AutofillPaymentApp;
 class CurrencyFormatter;
 class PaymentDetails;
 class PaymentDetailsModifier;
@@ -68,7 +68,7 @@
 // Called when a native iOS payment app should be launched.
 - (void)paymentInstrument:(payments::IOSPaymentInstrument*)paymentInstrument
     launchAppWithUniversalLink:(GURL)universalLink
-            instrumentDelegate:(payments::PaymentInstrument::Delegate*)delegate;
+            instrumentDelegate:(payments::PaymentApp::Delegate*)delegate;
 
 @end
 
@@ -149,12 +149,12 @@
 
   // Returns the total object of this payment request, taking into account the
   // applicable modifier for |selected_instrument|, if any.
-  const PaymentItem& GetTotal(PaymentInstrument* selected_instrument) const;
+  const PaymentItem& GetTotal(PaymentApp* selected_instrument) const;
 
   // Returns the display items for this payment request, taking into account the
   // applicable modifier for |selected_instrument|, if any.
   std::vector<PaymentItem> GetDisplayItems(
-      PaymentInstrument* selected_instrument) const;
+      PaymentApp* selected_instrument) const;
 
   // Updates the payment details of the |web_payment_request_|. It also updates
   // the cached references to the shipping options in |web_payment_request_| as
@@ -249,9 +249,9 @@
     return supported_card_types_set_;
   }
 
-  // Creates and adds an AutofillPaymentInstrument to the list of payment
+  // Creates and adds an AutofillPaymentApp to the list of payment
   // instruments by making a copy of |credit_card|.
-  virtual AutofillPaymentInstrument* CreateAndAddAutofillPaymentInstrument(
+  virtual AutofillPaymentApp* CreateAndAddAutofillPaymentInstrument(
       const autofill::CreditCard& credit_card);
 
   // Updates the given |credit_card| in the PersonalDataManager if the user is
@@ -261,18 +261,18 @@
 
   // Returns the available payment methods for this user that match a supported
   // type specified in |web_payment_request_|.
-  const std::vector<PaymentInstrument*>& payment_methods() const {
+  const std::vector<PaymentApp*>& payment_methods() const {
     return payment_methods_;
   }
 
   // Returns the currently selected payment method for this PaymentRequest flow
   // if there is one. Returns nullptr if there is no selected payment method.
-  PaymentInstrument* selected_payment_method() const {
+  PaymentApp* selected_payment_method() const {
     return selected_payment_method_;
   }
 
   // Sets the currently selected payment method for this PaymentRequest flow.
-  void set_selected_payment_method(PaymentInstrument* payment_method) {
+  void set_selected_payment_method(PaymentApp* payment_method) {
     selected_payment_method_ = payment_method;
   }
 
@@ -322,7 +322,7 @@
   // Returns the first applicable modifier in the Payment Request for the
   // |selected_instrument|.
   const PaymentDetailsModifier* GetApplicableModifier(
-      PaymentInstrument* selected_instrument) const;
+      PaymentApp* selected_instrument) const;
 
   // Fetches the autofill profiles for this user from the PersonalDataManager,
   // and stores copies of them, owned by this PaymentRequest, in profile_cache_.
@@ -352,10 +352,10 @@
   // methods.
   void PopulateAvailablePaymentMethods();
 
-  // Creates and adds an AutofillPaymentInstrument to the list of payment
+  // Creates and adds an AutofillPaymentApp to the list of payment
   // instruments by making a copy of |credit_card|. Updates PersonalDataManager
   // if not in incognito mode and |may_update_personal_data_manager| is true.
-  AutofillPaymentInstrument* CreateAndAddAutofillPaymentInstrument(
+  AutofillPaymentApp* CreateAndAddAutofillPaymentInstrument(
       const autofill::CreditCard& credit_card,
       bool may_update_personal_data_manager);
 
@@ -420,10 +420,10 @@
   // them. Therefore, payment methods are fetched once and their copies are
   // cached here. Whenever payment methods are requested a vector of pointers to
   // these copies are returned.
-  std::vector<std::unique_ptr<PaymentInstrument>> payment_method_cache_;
+  std::vector<std::unique_ptr<PaymentApp>> payment_method_cache_;
 
-  std::vector<PaymentInstrument*> payment_methods_;
-  PaymentInstrument* selected_payment_method_;
+  std::vector<PaymentApp*> payment_methods_;
+  PaymentApp* selected_payment_method_;
 
   // A vector of supported basic card networks.
   std::vector<std::string> supported_card_networks_;
diff --git a/ios/chrome/browser/payments/payment_request.mm b/ios/chrome/browser/payments/payment_request.mm
index c37e1e72..5bb44ae3 100644
--- a/ios/chrome/browser/payments/payment_request.mm
+++ b/ios/chrome/browser/payments/payment_request.mm
@@ -18,7 +18,7 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/validation.h"
 #import "components/autofill/ios/browser/credit_card_util.h"
-#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
 #include "components/payments/core/currency_formatter.h"
 #include "components/payments/core/features.h"
 #include "components/payments/core/method_strings.h"
@@ -189,7 +189,7 @@
 }
 
 const PaymentItem& PaymentRequest::GetTotal(
-    PaymentInstrument* selected_instrument) const {
+    PaymentApp* selected_instrument) const {
   const PaymentDetailsModifier* modifier =
       GetApplicableModifier(selected_instrument);
   if (modifier && modifier->total) {
@@ -201,7 +201,7 @@
 }
 
 std::vector<PaymentItem> PaymentRequest::GetDisplayItems(
-    PaymentInstrument* selected_instrument) const {
+    PaymentApp* selected_instrument) const {
   std::vector<PaymentItem> display_items =
       web_payment_request_.details.display_items;
 
@@ -294,7 +294,7 @@
 }
 
 const PaymentDetailsModifier* PaymentRequest::GetApplicableModifier(
-    PaymentInstrument* selected_instrument) const {
+    PaymentApp* selected_instrument) const {
   if (!selected_instrument ||
       !base::FeatureList::IsEnabled(features::kWebPaymentsModifiers)) {
     return nullptr;
@@ -362,15 +362,13 @@
       profile_comparator_.FilterProfilesForShipping(raw_profiles_for_filtering);
 }
 
-AutofillPaymentInstrument*
-PaymentRequest::CreateAndAddAutofillPaymentInstrument(
+AutofillPaymentApp* PaymentRequest::CreateAndAddAutofillPaymentInstrument(
     const autofill::CreditCard& credit_card) {
   return CreateAndAddAutofillPaymentInstrument(
       credit_card, /*may_update_personal_data_manager=*/true);
 }
 
-AutofillPaymentInstrument*
-PaymentRequest::CreateAndAddAutofillPaymentInstrument(
+AutofillPaymentApp* PaymentRequest::CreateAndAddAutofillPaymentInstrument(
     const autofill::CreditCard& credit_card,
     bool may_update_personal_data_manager) {
   std::string basic_card_issuer_network =
@@ -400,9 +398,9 @@
       credit_card.card_type() != autofill::CreditCard::CARD_TYPE_UNKNOWN ||
       supported_card_types_set_.size() == kTotalNumberOfCardTypes;
 
-  // AutofillPaymentInstrument makes a copy of |credit_card| so it is
+  // AutofillPaymentApp makes a copy of |credit_card| so it is
   // effectively owned by this object.
-  payment_method_cache_.push_back(std::make_unique<AutofillPaymentInstrument>(
+  payment_method_cache_.push_back(std::make_unique<AutofillPaymentApp>(
       method_name, credit_card, matches_merchant_card_type_exactly,
       billing_profiles(), GetApplicationLocale(), this));
 
@@ -411,8 +409,7 @@
   if (may_update_personal_data_manager && !IsIncognito())
     personal_data_manager_->AddCreditCard(credit_card);
 
-  return static_cast<AutofillPaymentInstrument*>(
-      payment_method_cache_.back().get());
+  return static_cast<AutofillPaymentApp*>(payment_method_cache_.back().get());
 }
 
 void PaymentRequest::UpdateAutofillPaymentInstrument(
@@ -437,7 +434,7 @@
 }
 
 bool PaymentRequest::CanMakePayment() const {
-  for (PaymentInstrument* payment_method : payment_methods_) {
+  for (PaymentApp* payment_method : payment_methods_) {
     if (payment_method->IsValidForCanMakePayment()) {
       return true;
     }
@@ -542,14 +539,15 @@
     // We only want to add the credit cards read from the PersonalDataManager to
     // the list of payment instrument. Don't re-add them to PersonalDataManager.
     CreateAndAddAutofillPaymentInstrument(
-        *credit_card, /*may_update_personal_data_manager=*/false);
+        *credit_card,
+        /*may_update_personal_data_manager=*/false);
   }
 
   PopulateAvailablePaymentMethods();
 
   const auto first_complete_payment_method =
       std::find_if(payment_methods_.begin(), payment_methods_.end(),
-                   [](PaymentInstrument* payment_method) {
+                   [](PaymentApp* payment_method) {
                      return payment_method->IsCompleteForPayment() &&
                             payment_method->IsExactlyMatchingMerchantRequest();
                    });
diff --git a/ios/chrome/browser/payments/payment_request_unittest.mm b/ios/chrome/browser/payments/payment_request_unittest.mm
index 7623daf..454362b 100644
--- a/ios/chrome/browser/payments/payment_request_unittest.mm
+++ b/ios/chrome/browser/payments/payment_request_unittest.mm
@@ -13,7 +13,7 @@
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
-#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
 #include "components/payments/core/currency_formatter.h"
 #include "components/payments/core/features.h"
 #include "components/payments/core/payment_details.h"
@@ -315,7 +315,7 @@
   EXPECT_EQ(0U, payment_request.payment_methods().size());
 
   autofill::CreditCard credit_card_1 = autofill::test::GetCreditCard();
-  AutofillPaymentInstrument* added_credit_card_1 =
+  AutofillPaymentApp* added_credit_card_1 =
       payment_request.CreateAndAddAutofillPaymentInstrument(credit_card_1);
   EXPECT_EQ(credit_card_1, *added_credit_card_1->credit_card());
 
@@ -341,7 +341,7 @@
   payment_request.set_is_incognito(true);
 
   autofill::CreditCard credit_card_1 = autofill::test::GetCreditCard();
-  AutofillPaymentInstrument* added_credit_card_1 =
+  AutofillPaymentApp* added_credit_card_1 =
       payment_request.CreateAndAddAutofillPaymentInstrument(credit_card_1);
   EXPECT_EQ(credit_card_1, *added_credit_card_1->credit_card());
 
@@ -752,10 +752,9 @@
                                      chrome_browser_state_.get(), &web_state_,
                                      &test_personal_data_manager_);
   EXPECT_EQ(payment_request.selected_payment_method()->type(),
-            PaymentInstrument::Type::AUTOFILL);
-  AutofillPaymentInstrument* payment_instrument =
-      static_cast<AutofillPaymentInstrument*>(
-          payment_request.selected_payment_method());
+            PaymentApp::Type::AUTOFILL);
+  AutofillPaymentApp* payment_instrument = static_cast<AutofillPaymentApp*>(
+      payment_request.selected_payment_method());
   EXPECT_EQ(credit_card.guid(), payment_instrument->credit_card()->guid());
 }
 
@@ -780,9 +779,8 @@
   TestPaymentRequest payment_request(web_payment_request,
                                      chrome_browser_state_.get(), &web_state_,
                                      &test_personal_data_manager_);
-  AutofillPaymentInstrument* payment_instrument =
-      static_cast<AutofillPaymentInstrument*>(
-          payment_request.selected_payment_method());
+  AutofillPaymentApp* payment_instrument = static_cast<AutofillPaymentApp*>(
+      payment_request.selected_payment_method());
   EXPECT_EQ(credit_card2.guid(), payment_instrument->credit_card()->guid());
 }
 
@@ -806,9 +804,8 @@
   TestPaymentRequest payment_request(web_payment_request,
                                      chrome_browser_state_.get(), &web_state_,
                                      &test_personal_data_manager_);
-  AutofillPaymentInstrument* payment_instrument =
-      static_cast<AutofillPaymentInstrument*>(
-          payment_request.selected_payment_method());
+  AutofillPaymentApp* payment_instrument = static_cast<AutofillPaymentApp*>(
+      payment_request.selected_payment_method());
   EXPECT_EQ(credit_card.guid(), payment_instrument->credit_card()->guid());
 }
 
@@ -837,9 +834,8 @@
   TestPaymentRequest payment_request(web_payment_request,
                                      chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
-  AutofillPaymentInstrument* payment_instrument =
-      static_cast<AutofillPaymentInstrument*>(
-          payment_request.selected_payment_method());
+  AutofillPaymentApp* payment_instrument = static_cast<AutofillPaymentApp*>(
+      payment_request.selected_payment_method());
   EXPECT_EQ(address.guid(),
             payment_request.selected_shipping_profile()->guid());
   EXPECT_EQ(contact_info.guid(),
@@ -874,9 +870,8 @@
   TestPaymentRequest payment_request(web_payment_request,
                                      chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
-  AutofillPaymentInstrument* payment_instrument =
-      static_cast<AutofillPaymentInstrument*>(
-          payment_request.selected_payment_method());
+  AutofillPaymentApp* payment_instrument = static_cast<AutofillPaymentApp*>(
+      payment_request.selected_payment_method());
   EXPECT_EQ(address.guid(),
             payment_request.selected_shipping_profile()->guid());
   EXPECT_EQ(address.guid(), payment_request.selected_contact_profile()->guid());
@@ -912,9 +907,8 @@
   TestPaymentRequest payment_request(web_payment_request,
                                      chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
-  AutofillPaymentInstrument* payment_instrument =
-      static_cast<AutofillPaymentInstrument*>(
-          payment_request.selected_payment_method());
+  AutofillPaymentApp* payment_instrument = static_cast<AutofillPaymentApp*>(
+      payment_request.selected_payment_method());
   EXPECT_EQ(address.guid(),
             payment_request.selected_shipping_profile()->guid());
   EXPECT_EQ(nullptr, payment_request.selected_contact_profile());
@@ -946,9 +940,8 @@
   TestPaymentRequest payment_request(web_payment_request,
                                      chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
-  AutofillPaymentInstrument* payment_instrument =
-      static_cast<AutofillPaymentInstrument*>(
-          payment_request.selected_payment_method());
+  AutofillPaymentApp* payment_instrument = static_cast<AutofillPaymentApp*>(
+      payment_request.selected_payment_method());
   EXPECT_EQ(nullptr, payment_request.selected_shipping_profile());
   EXPECT_EQ(address.guid(), payment_request.selected_contact_profile()->guid());
   EXPECT_EQ(credit_card.guid(), payment_instrument->credit_card()->guid());
@@ -982,9 +975,8 @@
   TestPaymentRequest payment_request(web_payment_request,
                                      chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
-  AutofillPaymentInstrument* payment_instrument =
-      static_cast<AutofillPaymentInstrument*>(
-          payment_request.selected_payment_method());
+  AutofillPaymentApp* payment_instrument = static_cast<AutofillPaymentApp*>(
+      payment_request.selected_payment_method());
   EXPECT_EQ(nullptr, payment_request.selected_shipping_profile());
   EXPECT_EQ(nullptr, payment_request.selected_contact_profile());
   EXPECT_EQ(credit_card.guid(), payment_instrument->credit_card()->guid());
@@ -1026,8 +1018,8 @@
   TestPaymentRequest payment_request(web_payment_request,
                                      chrome_browser_state_.get(), &web_state_,
                                      &test_personal_data_manager_);
-  AutofillPaymentInstrument* selected_payment_method =
-      static_cast<AutofillPaymentInstrument*>(
+  AutofillPaymentApp* selected_payment_method =
+      static_cast<AutofillPaymentApp*>(
           payment_request.selected_payment_method());
   EXPECT_EQ("Total", payment_request.GetTotal(selected_payment_method).label);
   EXPECT_EQ("1.00",
@@ -1063,8 +1055,8 @@
   TestPaymentRequest payment_request(web_payment_request,
                                      chrome_browser_state_.get(), &web_state_,
                                      &test_personal_data_manager_);
-  AutofillPaymentInstrument* selected_payment_method =
-      static_cast<AutofillPaymentInstrument*>(
+  AutofillPaymentApp* selected_payment_method =
+      static_cast<AutofillPaymentApp*>(
           payment_request.selected_payment_method());
   EXPECT_EQ("Discounted Total",
             payment_request.GetTotal(selected_payment_method).label);
@@ -1112,8 +1104,8 @@
   TestPaymentRequest payment_request(web_payment_request,
                                      chrome_browser_state_.get(), &web_state_,
                                      &test_personal_data_manager_);
-  AutofillPaymentInstrument* selected_payment_method =
-      static_cast<AutofillPaymentInstrument*>(
+  AutofillPaymentApp* selected_payment_method =
+      static_cast<AutofillPaymentApp*>(
           payment_request.selected_payment_method());
   EXPECT_EQ("Total", payment_request.GetTotal(selected_payment_method).label);
   EXPECT_EQ("1.00",
@@ -1158,8 +1150,8 @@
   TestPaymentRequest payment_request(web_payment_request,
                                      chrome_browser_state_.get(), &web_state_,
                                      &test_personal_data_manager_);
-  AutofillPaymentInstrument* selected_payment_method =
-      static_cast<AutofillPaymentInstrument*>(
+  AutofillPaymentApp* selected_payment_method =
+      static_cast<AutofillPaymentApp*>(
           payment_request.selected_payment_method());
   EXPECT_EQ("Discounted Total",
             payment_request.GetTotal(selected_payment_method).label);
diff --git a/ios/chrome/browser/payments/payment_request_util.h b/ios/chrome/browser/payments/payment_request_util.h
index a91e732..981fc40 100644
--- a/ios/chrome/browser/payments/payment_request_util.h
+++ b/ios/chrome/browser/payments/payment_request_util.h
@@ -19,7 +19,7 @@
 }  // namespace autofill
 
 namespace payments {
-class PaymentInstrument;
+class PaymentApp;
 class PaymentRequest;
 class PaymentResponse;
 }  // namespace payments
@@ -64,7 +64,7 @@
 // Helper function to create a notification label for what's missing from a
 // payment method. Returns nil if the resulting label is empty.
 NSString* GetPaymentMethodNotificationLabelFromPaymentMethod(
-    const payments::PaymentInstrument& payment_method,
+    const payments::PaymentApp& payment_method,
     const std::vector<autofill::AutofillProfile*>& billing_profiles);
 
 // Returns the title for the shipping section of the payment summary view given
diff --git a/ios/chrome/browser/payments/payment_request_util.mm b/ios/chrome/browser/payments/payment_request_util.mm
index ceb04cd..b771d808 100644
--- a/ios/chrome/browser/payments/payment_request_util.mm
+++ b/ios/chrome/browser/payments/payment_request_util.mm
@@ -16,7 +16,7 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/validation.h"
 #include "components/payments/core/payment_address.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/strings_util.h"
 #include "components/payments/core/web_payment_request.h"
@@ -118,7 +118,7 @@
 }
 
 NSString* GetPaymentMethodNotificationLabelFromPaymentMethod(
-    const payments::PaymentInstrument& payment_method,
+    const payments::PaymentApp& payment_method,
     const std::vector<autofill::AutofillProfile*>& billing_profiles) {
   base::string16 label = payment_method.GetMissingInfoLabel();
   return !label.empty() ? base::SysUTF16ToNSString(label) : nil;
diff --git a/ios/chrome/browser/payments/payment_response_helper.h b/ios/chrome/browser/payments/payment_response_helper.h
index 5331ac4..17f3dd28 100644
--- a/ios/chrome/browser/payments/payment_response_helper.h
+++ b/ios/chrome/browser/payments/payment_response_helper.h
@@ -12,7 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_response.h"
 #include "components/payments/core/web_payment_request.h"
 
@@ -37,14 +37,14 @@
 
 // A helper class to facilitate creation of the PaymentResponse.
 class PaymentResponseHelper
-    : public PaymentInstrument::Delegate,
+    : public PaymentApp::Delegate,
       public base::SupportsWeakPtr<PaymentResponseHelper> {
  public:
   PaymentResponseHelper(id<PaymentResponseHelperConsumer> consumer,
                         PaymentRequest* payment_request);
   ~PaymentResponseHelper() override;
 
-  // PaymentInstrument::Delegate
+  // PaymentApp::Delegate
   void OnInstrumentDetailsReady(const std::string& method_name,
                                 const std::string& stringified_details,
                                 const PayerData& payer_data) override;
diff --git a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.h b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.h
index 3a9a3fb..00039b5a 100644
--- a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.h
+++ b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.h
@@ -11,7 +11,7 @@
 #import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller.h"
 
 namespace payments {
-class AutofillPaymentInstrument;
+class AutofillPaymentApp;
 }
 
 namespace payments {
@@ -28,8 +28,7 @@
 // PaymentRequest object if no payment method instance was provided to the
 // coordinator. Otherwise, it will be the same edited instance.
 - (void)creditCardEditCoordinator:(CreditCardEditCoordinator*)coordinator
-    didFinishEditingPaymentMethod:
-        (payments::AutofillPaymentInstrument*)paymentMethod;
+    didFinishEditingPaymentMethod:(payments::AutofillPaymentApp*)paymentMethod;
 
 // Notifies the delegate that the user has chosen to cancel editing or creating
 // a credit card and return to the previous screen.
@@ -48,7 +47,7 @@
 
 // The payment method to be edited, if any. This pointer is not owned by this
 // class and should outlive it.
-@property(nonatomic, assign) payments::AutofillPaymentInstrument* paymentMethod;
+@property(nonatomic, assign) payments::AutofillPaymentApp* paymentMethod;
 
 // The PaymentRequest object owning an instance of payments::WebPaymentRequest
 // as provided by the page invoking the Payment Request API. This pointer is not
diff --git a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm
index f80d597a..ec27b348 100644
--- a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm
@@ -13,8 +13,8 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/common/autofill_constants.h"
 #import "components/autofill/ios/browser/credit_card_util.h"
-#include "components/payments/core/autofill_payment_instrument.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
+#include "components/payments/core/payment_app.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
 #import "ios/chrome/browser/ui/payments/credit_card_edit_mediator.h"
diff --git a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm
index f127f90..e6d61de 100644
--- a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm
@@ -12,8 +12,8 @@
 #import "base/test/ios/wait_util.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
-#include "components/payments/core/autofill_payment_instrument.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
+#include "components/payments/core/payment_app.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/payment_request_unittest_base.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
@@ -45,9 +45,8 @@
                                      browser_state,
                                      web_state,
                                      personal_data_manager) {}
-  MOCK_METHOD1(
-      CreateAndAddAutofillPaymentInstrument,
-      payments::AutofillPaymentInstrument*(const autofill::CreditCard&));
+  MOCK_METHOD1(CreateAndAddAutofillPaymentInstrument,
+               payments::AutofillPaymentApp*(const autofill::CreditCard&));
   MOCK_METHOD1(UpdateAutofillPaymentInstrument,
                void(const autofill::CreditCard&));
 };
@@ -191,8 +190,7 @@
       mockForProtocol:@protocol(CreditCardEditCoordinatorDelegate)];
   [[delegate expect]
           creditCardEditCoordinator:coordinator
-      didFinishEditingPaymentMethod:static_cast<
-                                        payments::AutofillPaymentInstrument*>(
+      didFinishEditingPaymentMethod:static_cast<payments::AutofillPaymentApp*>(
                                         [OCMArg anyPointer])];
   [coordinator setDelegate:delegate];
 
@@ -246,8 +244,7 @@
       mockForProtocol:@protocol(CreditCardEditCoordinatorDelegate)];
   [[delegate expect]
           creditCardEditCoordinator:coordinator
-      didFinishEditingPaymentMethod:static_cast<
-                                        payments::AutofillPaymentInstrument*>(
+      didFinishEditingPaymentMethod:static_cast<payments::AutofillPaymentApp*>(
                                         [OCMArg anyPointer])];
   [coordinator setDelegate:delegate];
 
@@ -294,7 +291,7 @@
 
   // Set the payment method to be edited.
   autofill::CreditCard credit_card;
-  payments::AutofillPaymentInstrument payment_method(
+  payments::AutofillPaymentApp payment_method(
       "", credit_card, false, payment_request_->billing_profiles(), "", nil);
   [coordinator setPaymentMethod:&payment_method];
 
@@ -303,8 +300,7 @@
       mockForProtocol:@protocol(CreditCardEditCoordinatorDelegate)];
   [[delegate expect]
           creditCardEditCoordinator:coordinator
-      didFinishEditingPaymentMethod:static_cast<
-                                        payments::AutofillPaymentInstrument*>(
+      didFinishEditingPaymentMethod:static_cast<payments::AutofillPaymentApp*>(
                                         [OCMArg anyPointer])];
   [coordinator setDelegate:delegate];
 
diff --git a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.h b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.h
index 0151502..049f9660 100644
--- a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.h
+++ b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.h
@@ -13,7 +13,7 @@
 #import "ios/chrome/browser/ui/payments/payment_request_selector_view_controller.h"
 
 namespace payments {
-class PaymentInstrument;
+class PaymentApp;
 class PaymentRequest;
 }  // namespace payments
 
@@ -25,8 +25,7 @@
 // Notifies the delegate that the user has selected a payment method.
 - (void)paymentMethodSelectionCoordinator:
             (PaymentMethodSelectionCoordinator*)coordinator
-                   didSelectPaymentMethod:
-                       (payments::PaymentInstrument*)paymentMethod;
+                   didSelectPaymentMethod:(payments::PaymentApp*)paymentMethod;
 
 // Notifies the delegate that the user has chosen to return to the previous
 // screen without making a selection.
diff --git a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.mm b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.mm
index 6a6cc9b..b1d3eb6 100644
--- a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.mm
@@ -8,8 +8,8 @@
 
 #include "base/logging.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/payments/core/autofill_payment_instrument.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
+#include "components/payments/core/payment_app.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #import "ios/chrome/browser/ui/payments/cells/payment_method_item.h"
@@ -38,14 +38,13 @@
 // Initializes and starts the CreditCardEditCoordinator. Sets
 // |autofillInstrument| as the autofill payment instrument to be edited.
 - (void)startCreditCardEditCoordinatorWithAutofillPaymentInstrument:
-    (payments::AutofillPaymentInstrument*)autofillInstrument;
+    (payments::AutofillPaymentApp*)autofillInstrument;
 
 // Called when the user selects a payment method. The cell is checked, the
 // UI is locked so that the user can't interact with it, then the delegate is
 // notified. The delay is here to let the user get a visual feedback of the
 // selection before this view disappears.
-- (void)delayedNotifyDelegateOfSelection:
-    (payments::PaymentInstrument*)paymentMethod;
+- (void)delayedNotifyDelegateOfSelection:(payments::PaymentApp*)paymentMethod;
 
 @end
 
@@ -85,12 +84,12 @@
             (PaymentRequestSelectorViewController*)controller
                         didSelectItemAtIndex:(NSUInteger)index {
   DCHECK(index < self.paymentRequest->payment_methods().size());
-  payments::PaymentInstrument* paymentMethod =
+  payments::PaymentApp* paymentMethod =
       self.paymentRequest->payment_methods()[index];
 
   // Proceed with item selection only if the item has all required info, or
   // else bring up the credit card editor. A payment method can be incomplete
-  // only if it is an AutofillPaymentInstrument.
+  // only if it is an AutofillPaymentApp.
   CollectionViewItem<PaymentsIsSelectable>* selectedItem =
       self.mediator.selectableItems[index];
   if (selectedItem.complete) {
@@ -99,10 +98,9 @@
     [self delayedNotifyDelegateOfSelection:paymentMethod];
     return YES;
   } else {
-    DCHECK(paymentMethod->type() ==
-           payments::PaymentInstrument::Type::AUTOFILL);
+    DCHECK(paymentMethod->type() == payments::PaymentApp::Type::AUTOFILL);
     [self startCreditCardEditCoordinatorWithAutofillPaymentInstrument:
-              static_cast<payments::AutofillPaymentInstrument*>(paymentMethod)];
+              static_cast<payments::AutofillPaymentApp*>(paymentMethod)];
     return NO;
   }
 }
@@ -129,11 +127,11 @@
   DCHECK(index < self.paymentRequest->payment_methods().size());
 
   // We should only edit the payment instrument if it is an
-  // AutofillPaymentInstrument.
+  // AutofillPaymentApp.
   if (self.paymentRequest->payment_methods()[index]->type() ==
-      payments::PaymentInstrument::Type::AUTOFILL) {
+      payments::PaymentApp::Type::AUTOFILL) {
     [self startCreditCardEditCoordinatorWithAutofillPaymentInstrument:
-              static_cast<payments::AutofillPaymentInstrument*>(
+              static_cast<payments::AutofillPaymentApp*>(
                   self.paymentRequest->payment_methods()[index])];
   }
 }
@@ -141,14 +139,13 @@
 #pragma mark - CreditCardEditCoordinatorDelegate
 
 - (void)creditCardEditCoordinator:(CreditCardEditCoordinator*)coordinator
-    didFinishEditingPaymentMethod:
-        (payments::AutofillPaymentInstrument*)creditCard {
+    didFinishEditingPaymentMethod:(payments::AutofillPaymentApp*)creditCard {
   BOOL isEditing = [self.viewController isEditing];
 
   // Update the data source with the new data.
   [self.mediator loadItems];
 
-  const std::vector<payments::PaymentInstrument*>& paymentMethods =
+  const std::vector<payments::PaymentApp*>& paymentMethods =
       self.paymentRequest->payment_methods();
   auto position =
       std::find(paymentMethods.begin(), paymentMethods.end(), creditCard);
@@ -197,7 +194,7 @@
 #pragma mark - Helper methods
 
 - (void)startCreditCardEditCoordinatorWithAutofillPaymentInstrument:
-    (payments::AutofillPaymentInstrument*)autofillInstrument {
+    (payments::AutofillPaymentApp*)autofillInstrument {
   self.creditCardEditCoordinator = [[CreditCardEditCoordinator alloc]
       initWithBaseViewController:self.viewController];
   self.creditCardEditCoordinator.paymentRequest = self.paymentRequest;
@@ -206,8 +203,7 @@
   [self.creditCardEditCoordinator start];
 }
 
-- (void)delayedNotifyDelegateOfSelection:
-    (payments::PaymentInstrument*)paymentMethod {
+- (void)delayedNotifyDelegateOfSelection:(payments::PaymentApp*)paymentMethod {
   self.viewController.view.userInteractionEnabled = NO;
   __weak PaymentMethodSelectionCoordinator* weakSelf = self;
   dispatch_after(
diff --git a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm
index f40635c..c7c27ce 100644
--- a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm
@@ -10,7 +10,7 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #import "ios/chrome/browser/payments/payment_request_unittest_base.h"
 #import "ios/chrome/browser/ui/payments/payment_request_selector_view_controller.h"
diff --git a/ios/chrome/browser/ui/payments/payment_method_selection_mediator.mm b/ios/chrome/browser/ui/payments/payment_method_selection_mediator.mm
index a30734d..afe1a983 100644
--- a/ios/chrome/browser/ui/payments/payment_method_selection_mediator.mm
+++ b/ios/chrome/browser/ui/payments/payment_method_selection_mediator.mm
@@ -11,8 +11,8 @@
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/payments/core/autofill_payment_instrument.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/strings_util.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/payments/ios_payment_instrument.h"
@@ -102,11 +102,11 @@
 #pragma mark - Public methods
 
 - (void)loadItems {
-  const std::vector<payments::PaymentInstrument*>& paymentMethods =
+  const std::vector<payments::PaymentApp*>& paymentMethods =
       _paymentRequest->payment_methods();
   _items = [NSMutableArray arrayWithCapacity:paymentMethods.size()];
   for (size_t index = 0; index < paymentMethods.size(); ++index) {
-    payments::PaymentInstrument* paymentMethod = paymentMethods[index];
+    payments::PaymentApp* paymentMethod = paymentMethods[index];
     DCHECK(paymentMethod);
     PaymentMethodItem* item = [[PaymentMethodItem alloc] init];
     item.methodID = base::SysUTF16ToNSString(paymentMethod->GetLabel());
@@ -116,9 +116,9 @@
     item.complete = paymentMethod->IsCompleteForPayment();
 
     switch (paymentMethod->type()) {
-      case payments::PaymentInstrument::Type::AUTOFILL: {
-        payments::AutofillPaymentInstrument* autofillInstrument =
-            static_cast<payments::AutofillPaymentInstrument*>(paymentMethod);
+      case payments::PaymentApp::Type::AUTOFILL: {
+        payments::AutofillPaymentApp* autofillInstrument =
+            static_cast<payments::AutofillPaymentApp*>(paymentMethod);
         autofill::AutofillProfile* billingAddress =
             autofill::PersonalDataManager::GetProfileFromProfilesByGUID(
                 autofillInstrument->credit_card()->billing_address_id(),
@@ -130,13 +130,13 @@
         item.methodTypeIcon = NativeImage(paymentMethod->icon_resource_id());
         break;
       }
-      case payments::PaymentInstrument::Type::NATIVE_MOBILE_APP: {
+      case payments::PaymentApp::Type::NATIVE_MOBILE_APP: {
         payments::IOSPaymentInstrument* mobileApp =
             static_cast<payments::IOSPaymentInstrument*>(paymentMethod);
         item.methodTypeIcon = mobileApp->icon_image();
         break;
       }
-      case payments::PaymentInstrument::Type::SERVICE_WORKER_APP: {
+      case payments::PaymentApp::Type::SERVICE_WORKER_APP: {
         NOTIMPLEMENTED();
         break;
       }
diff --git a/ios/chrome/browser/ui/payments/payment_request_coordinator.mm b/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
index 3eb3950..316896e 100644
--- a/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
@@ -9,10 +9,10 @@
 #include "base/json/json_reader.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
 #include "components/payments/core/payment_address.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_details.h"
-#include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/payment_item.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/payment_shipping_option.h"
@@ -427,8 +427,7 @@
 
 - (void)paymentMethodSelectionCoordinator:
             (PaymentMethodSelectionCoordinator*)coordinator
-                   didSelectPaymentMethod:
-                       (payments::PaymentInstrument*)paymentMethod {
+                   didSelectPaymentMethod:(payments::PaymentApp*)paymentMethod {
   DCHECK(paymentMethod);
   DCHECK(paymentMethod->IsCompleteForPayment());
   _paymentRequest->set_selected_payment_method(paymentMethod);
@@ -449,8 +448,7 @@
 #pragma mark - CreditCardEditCoordinatorDelegate
 
 - (void)creditCardEditCoordinator:(CreditCardEditCoordinator*)coordinator
-    didFinishEditingPaymentMethod:
-        (payments::AutofillPaymentInstrument*)paymentMethod {
+    didFinishEditingPaymentMethod:(payments::AutofillPaymentApp*)paymentMethod {
   DCHECK(paymentMethod);
   DCHECK(paymentMethod->IsCompleteForPayment());
   _paymentRequest->set_selected_payment_method(paymentMethod);
diff --git a/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm
index 6ed5089..699e4f10 100644
--- a/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm
@@ -12,9 +12,9 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
 #include "components/payments/core/payment_address.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/payment_shipping_option.h"
 #include "components/payments/core/payments_test_util.h"
diff --git a/ios/chrome/browser/ui/payments/payment_request_manager.mm b/ios/chrome/browser/ui/payments/payment_request_manager.mm
index 82bbc5b..9e88ec02 100644
--- a/ios/chrome/browser/ui/payments/payment_request_manager.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_manager.mm
@@ -29,9 +29,9 @@
 #include "components/payments/core/features.h"
 #include "components/payments/core/journey_logger.h"
 #include "components/payments/core/payment_address.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_details.h"
 #include "components/payments/core/payment_details_validation.h"
-#include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/payment_prefs.h"
 #include "components/payments/core/payment_request_base_delegate.h"
 #include "components/payments/core/payment_request_data_util.h"
@@ -1033,8 +1033,7 @@
 
 - (void)paymentInstrument:(payments::IOSPaymentInstrument*)paymentInstrument
     launchAppWithUniversalLink:(GURL)universalLink
-            instrumentDelegate:
-                (payments::PaymentInstrument::Delegate*)delegate {
+            instrumentDelegate:(payments::PaymentApp::Delegate*)delegate {
   DCHECK(_pendingPaymentRequest);
   DCHECK(_activeWebState);
 
@@ -1062,7 +1061,7 @@
       payments::JourneyLogger::EVENT_PAY_CLICKED);
   coordinator.paymentRequest->journey_logger().SetEventOccurred(
       coordinator.paymentRequest->selected_payment_method()->type() ==
-              payments::PaymentInstrument::Type::AUTOFILL
+              payments::PaymentApp::Type::AUTOFILL
           ? payments::JourneyLogger::EVENT_SELECTED_CREDIT_CARD
           : payments::JourneyLogger::EVENT_SELECTED_OTHER);
 
diff --git a/ios/chrome/browser/ui/payments/payment_request_mediator.mm b/ios/chrome/browser/ui/payments/payment_request_mediator.mm
index 22636370..11223cce 100644
--- a/ios/chrome/browser/ui/payments/payment_request_mediator.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_mediator.mm
@@ -12,7 +12,7 @@
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/field_types.h"
-#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/autofill_payment_app.h"
 #include "components/payments/core/currency_formatter.h"
 #include "components/payments/core/payment_item.h"
 #include "components/payments/core/payment_prefs.h"
@@ -191,7 +191,7 @@
 }
 
 - (CollectionViewItem*)paymentMethodItem {
-  payments::PaymentInstrument* paymentMethod =
+  payments::PaymentApp* paymentMethod =
       self.paymentRequest->selected_payment_method();
   if (paymentMethod) {
     PaymentMethodItem* item = [[PaymentMethodItem alloc] init];
@@ -199,17 +199,17 @@
     item.methodDetail = base::SysUTF16ToNSString(paymentMethod->GetSublabel());
 
     switch (paymentMethod->type()) {
-      case payments::PaymentInstrument::Type::AUTOFILL: {
+      case payments::PaymentApp::Type::AUTOFILL: {
         item.methodTypeIcon = NativeImage(paymentMethod->icon_resource_id());
         break;
       }
-      case payments::PaymentInstrument::Type::NATIVE_MOBILE_APP: {
+      case payments::PaymentApp::Type::NATIVE_MOBILE_APP: {
         payments::IOSPaymentInstrument* mobileApp =
             static_cast<payments::IOSPaymentInstrument*>(paymentMethod);
         item.methodTypeIcon = mobileApp->icon_image();
         break;
       }
-      case payments::PaymentInstrument::Type::SERVICE_WORKER_APP: {
+      case payments::PaymentApp::Type::SERVICE_WORKER_APP: {
         NOTIMPLEMENTED();
         break;
       }
diff --git a/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm b/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
index 49fbb99..344f18f6 100644
--- a/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
@@ -11,7 +11,7 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_prefs.h"
 #include "components/payments/core/payment_shipping_option.h"
 #include "components/payments/core/strings_util.h"
@@ -129,7 +129,7 @@
   EXPECT_TRUE([mediator() canPay]);
 
   // Payment cannot be completed if there is no selected payment method.
-  payments::PaymentInstrument* selected_payment_method =
+  payments::PaymentApp* selected_payment_method =
       payment_request()->selected_payment_method();
   payment_request()->set_selected_payment_method(nullptr);
   EXPECT_FALSE([mediator() canPay]);
diff --git a/media/gpu/test/image_processor/image_processor_client.cc b/media/gpu/test/image_processor/image_processor_client.cc
index 819fbb9..10a8efe 100644
--- a/media/gpu/test/image_processor/image_processor_client.cc
+++ b/media/gpu/test/image_processor/image_processor_client.cc
@@ -32,6 +32,15 @@
 namespace media {
 namespace test {
 namespace {
+
+#define ASSERT_TRUE_OR_RETURN_NULLPTR(predicate) \
+  do {                                           \
+    if (!(predicate)) {                          \
+      ADD_FAILURE();                             \
+      return nullptr;                            \
+    }                                            \
+  } while (0)
+
 base::Optional<VideoFrameLayout> CreateLayout(
     const ImageProcessor::PortConfig& config) {
   const VideoPixelFormat pixel_format = config.fourcc.ToVideoPixelFormat();
@@ -127,15 +136,15 @@
 scoped_refptr<VideoFrame> ImageProcessorClient::CreateInputFrame(
     const Image& input_image) const {
   DCHECK_CALLED_ON_VALID_THREAD(test_main_thread_checker_);
-  LOG_ASSERT(image_processor_);
-  LOG_ASSERT(input_image.IsLoaded());
+  ASSERT_TRUE_OR_RETURN_NULLPTR(image_processor_);
+  ASSERT_TRUE_OR_RETURN_NULLPTR(input_image.IsLoaded());
 
   const ImageProcessor::PortConfig& input_config =
       image_processor_->input_config();
   const VideoFrame::StorageType input_storage_type =
       input_config.storage_type();
   base::Optional<VideoFrameLayout> input_layout = CreateLayout(input_config);
-  LOG_ASSERT(input_layout);
+  ASSERT_TRUE_OR_RETURN_NULLPTR(input_layout);
 
   if (VideoFrame::IsStorageTypeMappable(input_storage_type)) {
     return CloneVideoFrame(gpu_memory_buffer_factory_.get(),
@@ -143,8 +152,9 @@
                            *input_layout, VideoFrame::STORAGE_OWNED_MEMORY);
   } else {
 #if defined(OS_CHROMEOS)
-    LOG_ASSERT(input_storage_type == VideoFrame::STORAGE_DMABUFS ||
-               input_storage_type == VideoFrame::STORAGE_GPU_MEMORY_BUFFER);
+    ASSERT_TRUE_OR_RETURN_NULLPTR(
+        input_storage_type == VideoFrame::STORAGE_DMABUFS ||
+        input_storage_type == VideoFrame::STORAGE_GPU_MEMORY_BUFFER);
     // NV12 and YV12 are the only formats that can be allocated with
     // gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE. So
     // gfx::BufferUsage::GPU_READ_CPU_READ_WRITE is specified for RGB formats.
@@ -163,23 +173,24 @@
 scoped_refptr<VideoFrame> ImageProcessorClient::CreateOutputFrame(
     const Image& output_image) const {
   DCHECK_CALLED_ON_VALID_THREAD(test_main_thread_checker_);
-  LOG_ASSERT(output_image.IsMetadataLoaded());
-  LOG_ASSERT(image_processor_);
+  ASSERT_TRUE_OR_RETURN_NULLPTR(output_image.IsMetadataLoaded());
+  ASSERT_TRUE_OR_RETURN_NULLPTR(image_processor_);
 
   const ImageProcessor::PortConfig& output_config =
       image_processor_->output_config();
   const VideoFrame::StorageType output_storage_type =
       output_config.storage_type();
   base::Optional<VideoFrameLayout> output_layout = CreateLayout(output_config);
-  LOG_ASSERT(output_layout);
+  ASSERT_TRUE_OR_RETURN_NULLPTR(output_layout);
   if (VideoFrame::IsStorageTypeMappable(output_storage_type)) {
     return VideoFrame::CreateFrameWithLayout(
         *output_layout, gfx::Rect(output_image.Size()), output_image.Size(),
         base::TimeDelta(), false /* zero_initialize_memory*/);
   } else {
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
-    LOG_ASSERT(output_storage_type == VideoFrame::STORAGE_DMABUFS ||
-               output_storage_type == VideoFrame::STORAGE_GPU_MEMORY_BUFFER);
+    ASSERT_TRUE_OR_RETURN_NULLPTR(
+        output_storage_type == VideoFrame::STORAGE_DMABUFS ||
+        output_storage_type == VideoFrame::STORAGE_GPU_MEMORY_BUFFER);
     scoped_refptr<VideoFrame> output_frame = CreatePlatformVideoFrame(
         gpu_memory_buffer_factory_.get(), output_layout->format(),
         output_layout->coded_size(), gfx::Rect(output_image.Size()),
diff --git a/media/gpu/test/video_frame_helpers.cc b/media/gpu/test/video_frame_helpers.cc
index d0fb485..498c257e 100644
--- a/media/gpu/test/video_frame_helpers.cc
+++ b/media/gpu/test/video_frame_helpers.cc
@@ -32,19 +32,19 @@
 
 namespace {
 
-#define ASSERT_TRUE_AND_RETURN(predicate, return_value) \
-  do {                                                  \
-    if (!(predicate)) {                                 \
-      ADD_FAILURE();                                    \
-      return (return_value);                            \
-    }                                                   \
+#define ASSERT_TRUE_OR_RETURN(predicate, return_value) \
+  do {                                                 \
+    if (!(predicate)) {                                \
+      ADD_FAILURE();                                   \
+      return (return_value);                           \
+    }                                                  \
   } while (0)
 
 bool ConvertVideoFrameToI420(const VideoFrame* src_frame,
                              VideoFrame* dst_frame) {
-  ASSERT_TRUE_AND_RETURN(src_frame->visible_rect() == dst_frame->visible_rect(),
-                         false);
-  ASSERT_TRUE_AND_RETURN(dst_frame->format() == PIXEL_FORMAT_I420, false);
+  ASSERT_TRUE_OR_RETURN(src_frame->visible_rect() == dst_frame->visible_rect(),
+                        false);
+  ASSERT_TRUE_OR_RETURN(dst_frame->format() == PIXEL_FORMAT_I420, false);
 
   const auto& visible_rect = src_frame->visible_rect();
   const int width = visible_rect.width();
@@ -91,9 +91,9 @@
 
 bool ConvertVideoFrameToARGB(const VideoFrame* src_frame,
                              VideoFrame* dst_frame) {
-  ASSERT_TRUE_AND_RETURN(src_frame->visible_rect() == dst_frame->visible_rect(),
-                         false);
-  ASSERT_TRUE_AND_RETURN(dst_frame->format() == PIXEL_FORMAT_ARGB, false);
+  ASSERT_TRUE_OR_RETURN(src_frame->visible_rect() == dst_frame->visible_rect(),
+                        false);
+  ASSERT_TRUE_OR_RETURN(dst_frame->format() == PIXEL_FORMAT_ARGB, false);
 
   const auto& visible_rect = src_frame->visible_rect();
   const int width = visible_rect.width();
@@ -137,7 +137,7 @@
 // Copy memory based |src_frame| buffer to |dst_frame| buffer.
 bool CopyVideoFrame(const VideoFrame* src_frame,
                     scoped_refptr<VideoFrame> dst_frame) {
-  ASSERT_TRUE_AND_RETURN(src_frame->IsMappable(), false);
+  ASSERT_TRUE_OR_RETURN(src_frame->IsMappable(), false);
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
   // If |dst_frame| is a Dmabuf-backed VideoFrame, we need to map its underlying
   // buffer into memory. We use a VideoFrameMapper to create a memory-based
@@ -145,7 +145,7 @@
   if (dst_frame->storage_type() == VideoFrame::STORAGE_DMABUFS) {
     auto video_frame_mapper = VideoFrameMapperFactory::CreateMapper(
         dst_frame->format(), VideoFrame::STORAGE_DMABUFS, true);
-    ASSERT_TRUE_AND_RETURN(video_frame_mapper, false);
+    ASSERT_TRUE_OR_RETURN(video_frame_mapper, false);
     dst_frame = video_frame_mapper->Map(std::move(dst_frame));
     if (!dst_frame) {
       LOG(ERROR) << "Failed to map DMABuf video frame.";
@@ -153,15 +153,15 @@
     }
   }
 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
-  ASSERT_TRUE_AND_RETURN(dst_frame->IsMappable(), false);
-  ASSERT_TRUE_AND_RETURN(src_frame->format() == dst_frame->format(), false);
+  ASSERT_TRUE_OR_RETURN(dst_frame->IsMappable(), false);
+  ASSERT_TRUE_OR_RETURN(src_frame->format() == dst_frame->format(), false);
 
   // Copy every plane's content from |src_frame| to |dst_frame|.
   const size_t num_planes = VideoFrame::NumPlanes(dst_frame->format());
-  ASSERT_TRUE_AND_RETURN(dst_frame->layout().planes().size() == num_planes,
-                         false);
-  ASSERT_TRUE_AND_RETURN(src_frame->layout().planes().size() == num_planes,
-                         false);
+  ASSERT_TRUE_OR_RETURN(dst_frame->layout().planes().size() == num_planes,
+                        false);
+  ASSERT_TRUE_OR_RETURN(src_frame->layout().planes().size() == num_planes,
+                        false);
   for (size_t i = 0; i < num_planes; ++i) {
     // |width| in libyuv::CopyPlane() is in bytes, not pixels.
     gfx::Size plane_size = VideoFrame::PlaneSize(dst_frame->format(), i,
@@ -177,10 +177,10 @@
 }  // namespace
 
 bool ConvertVideoFrame(const VideoFrame* src_frame, VideoFrame* dst_frame) {
-  ASSERT_TRUE_AND_RETURN(src_frame->visible_rect() == dst_frame->visible_rect(),
-                         false);
-  ASSERT_TRUE_AND_RETURN(src_frame->IsMappable() && dst_frame->IsMappable(),
-                         false);
+  ASSERT_TRUE_OR_RETURN(src_frame->visible_rect() == dst_frame->visible_rect(),
+                        false);
+  ASSERT_TRUE_OR_RETURN(src_frame->IsMappable() && dst_frame->IsMappable(),
+                        false);
 
   // Writing into non-owned memory might produce some unexpected side effects.
   if (dst_frame->storage_type() != VideoFrame::STORAGE_OWNED_MEMORY)
@@ -362,14 +362,14 @@
 size_t CompareFramesWithErrorDiff(const VideoFrame& frame1,
                                   const VideoFrame& frame2,
                                   uint8_t tolerance) {
-  ASSERT_TRUE_AND_RETURN(frame1.format() == frame2.format(),
-                         std::numeric_limits<std::size_t>::max());
-  ASSERT_TRUE_AND_RETURN(frame1.visible_rect() == frame2.visible_rect(),
-                         std::numeric_limits<std::size_t>::max());
-  ASSERT_TRUE_AND_RETURN(frame1.visible_rect().origin() == gfx::Point(0, 0),
-                         std::numeric_limits<std::size_t>::max());
-  ASSERT_TRUE_AND_RETURN(frame1.IsMappable() && frame2.IsMappable(),
-                         std::numeric_limits<std::size_t>::max());
+  ASSERT_TRUE_OR_RETURN(frame1.format() == frame2.format(),
+                        std::numeric_limits<std::size_t>::max());
+  ASSERT_TRUE_OR_RETURN(frame1.visible_rect() == frame2.visible_rect(),
+                        std::numeric_limits<std::size_t>::max());
+  ASSERT_TRUE_OR_RETURN(frame1.visible_rect().origin() == gfx::Point(0, 0),
+                        std::numeric_limits<std::size_t>::max());
+  ASSERT_TRUE_OR_RETURN(frame1.IsMappable() && frame2.IsMappable(),
+                        std::numeric_limits<std::size_t>::max());
   size_t diff_cnt = 0;
 
   const VideoPixelFormat format = frame1.format();
diff --git a/media/mojo/services/mojo_cdm_helper_unittest.cc b/media/mojo/services/mojo_cdm_helper_unittest.cc
index f93b9eaf..fcdec73 100644
--- a/media/mojo/services/mojo_cdm_helper_unittest.cc
+++ b/media/mojo/services/mojo_cdm_helper_unittest.cc
@@ -11,7 +11,8 @@
 #include "media/cdm/api/content_decryption_module.h"
 #include "media/mojo/mojom/cdm_storage.mojom.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -65,9 +66,9 @@
   mojo::AssociatedReceiver<mojom::CdmFile> client_receiver_{&cdm_file_};
 };
 
-void CreateCdmStorage(mojom::CdmStorageRequest request) {
-  mojo::MakeStrongBinding(std::make_unique<MockCdmStorage>(),
-                          std::move(request));
+void CreateCdmStorage(mojo::PendingReceiver<mojom::CdmStorage> receiver) {
+  mojo::MakeSelfOwnedReceiver(std::make_unique<MockCdmStorage>(),
+                              std::move(receiver));
 }
 
 class TestInterfaceProvider : public service_manager::mojom::InterfaceProvider {
diff --git a/media/webrtc/audio_processor.cc b/media/webrtc/audio_processor.cc
index ea4830a4..32ff97e 100644
--- a/media/webrtc/audio_processor.cc
+++ b/media/webrtc/audio_processor.cc
@@ -4,8 +4,8 @@
 
 #include "media/webrtc/audio_processor.h"
 
+#include <array>
 #include <utility>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -18,6 +18,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
+#include "media/base/limits.h"
 #include "media/webrtc/helpers.h"
 #include "media/webrtc/webrtc_switches.h"
 #include "third_party/webrtc/api/audio/echo_canceller3_factory.h"
@@ -98,22 +99,16 @@
 
   render_delay_ = playout_time - base::TimeTicks::Now();
 
-  constexpr int kMaxChannels = 2;
   DCHECK_GE(parameters.channels(), 1);
-  const float* channel_ptrs[kMaxChannels];
-  channel_ptrs[0] = audio.channel(0);
-  webrtc::AudioProcessing::ChannelLayout webrtc_layout =
-      webrtc::AudioProcessing::ChannelLayout::kMono;
-  // Limit the number of channels to two (stereo) even in a multi-channel case.
-  // TODO(crbug.com/982276): process all channels when multi-channel AEC is
-  // supported.
-  if (parameters.channels() > 1) {
-    channel_ptrs[1] = audio.channel(1);
-    webrtc_layout = webrtc::AudioProcessing::ChannelLayout::kStereo;
+  DCHECK_LE(parameters.channels(), audio.channels());
+  DCHECK_LE(parameters.channels(), media::limits::kMaxChannels);
+  std::array<const float*, media::limits::kMaxChannels> input_ptrs;
+  for (int i = 0; i < parameters.channels(); ++i) {
+    input_ptrs[i] = audio.channel(i);
   }
 
   const int apm_error = audio_processing_->AnalyzeReverseStream(
-      channel_ptrs, CreateStreamConfig(parameters));
+      input_ptrs.data(), CreateStreamConfig(parameters));
 
   DCHECK_EQ(apm_error, webrtc::AudioProcessing::kNoError);
 }
@@ -308,7 +303,8 @@
 }
 
 void AudioProcessor::FeedDataToAPM(const AudioBus& source) {
-  std::vector<const float*> input_ptrs(source.channels());
+  DCHECK_LE(source.channels(), media::limits::kMaxChannels);
+  std::array<const float*, media::limits::kMaxChannels> input_ptrs;
   for (int i = 0; i < source.channels(); ++i) {
     input_ptrs[i] = source.channel(i);
   }
diff --git a/media/webrtc/audio_processor_unittest.cc b/media/webrtc/audio_processor_unittest.cc
index aaa2171..c8663af 100644
--- a/media/webrtc/audio_processor_unittest.cc
+++ b/media/webrtc/audio_processor_unittest.cc
@@ -40,7 +40,6 @@
 
 // The number of packers used for testing.
 const int kNumberOfPacketsForTest = 100;
-const int kMaxNumberOfPlayoutDataChannels = 2;
 
 void ReadDataFromSpeechFile(char* data, int length) {
   base::FilePath file;
@@ -83,15 +82,16 @@
     std::unique_ptr<media::AudioBus> data_bus =
         media::AudioBus::Create(params.channels(), params.frames_per_buffer());
 
-    // |data_bus_playout| is used if the number of capture channels is larger
-    // than max allowed playout channels. |data_bus_playout_to_use| points to
-    // the AudioBus to use, either |data_bus| or |data_bus_playout|.
+    // |data_bus_playout| is used if the capture channels include a keyboard
+    // channel. |data_bus_playout_to_use| points to the AudioBus to use, either
+    // |data_bus| or |data_bus_playout|.
     std::unique_ptr<media::AudioBus> data_bus_playout;
     media::AudioBus* data_bus_playout_to_use = data_bus.get();
     media::AudioParameters playout_params = params;
-    if (params.channels() > kMaxNumberOfPlayoutDataChannels) {
-      data_bus_playout =
-          media::AudioBus::CreateWrapper(kMaxNumberOfPlayoutDataChannels);
+    const bool has_keyboard_mic = params.channel_layout() ==
+                                  media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC;
+    if (has_keyboard_mic) {
+      data_bus_playout = media::AudioBus::CreateWrapper(2);
       data_bus_playout->set_frames(params.frames_per_buffer());
       data_bus_playout_to_use = data_bus_playout.get();
       playout_params.Reset(params.format(), CHANNEL_LAYOUT_STEREO,
@@ -108,8 +108,8 @@
       webrtc::AudioProcessing* ap = audio_processor->audio_processing_.get();
       const bool is_aec_enabled = ap && ap->GetConfig().echo_canceller.enabled;
       if (is_aec_enabled) {
-        if (params.channels() > kMaxNumberOfPlayoutDataChannels) {
-          for (int i = 0; i < kMaxNumberOfPlayoutDataChannels; ++i) {
+        if (has_keyboard_mic) {
+          for (int i = 0; i < data_bus_playout->channels(); ++i) {
             data_bus_playout->SetChannelData(
                 i, const_cast<float*>(data_bus->channel(i)));
           }
diff --git a/media/webrtc/helpers.cc b/media/webrtc/helpers.cc
index 814446b..5dfddf0 100644
--- a/media/webrtc/helpers.cc
+++ b/media/webrtc/helpers.cc
@@ -8,9 +8,14 @@
 
 webrtc::StreamConfig CreateStreamConfig(const AudioParameters& parameters) {
   const int rate = parameters.sample_rate();
-  const int channels = std::min(parameters.channels(), 2);
   const bool has_keyboard = parameters.channel_layout() ==
                             media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC;
+  int channels =
+      media::ChannelLayoutToChannelCount(parameters.channel_layout());
+  // webrtc::StreamConfig requires that the keyboard mic channel is not included
+  // in the channel count. It may still be used.
+  if (has_keyboard)
+    channels -= 1;
   return webrtc::StreamConfig(rate, channels, has_keyboard);
 }
 
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index cce155e..f3f054a9 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -4410,7 +4410,6 @@
     { "name": "linuxbierwanderung.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lmintlcx.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "locomore.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "logfile.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lusis.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lusis.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "macgeneral.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -5963,7 +5962,6 @@
     { "name": "jupp0r.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kalami.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kantankye.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "kaputt.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "karguine.in", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "katericke.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kausch.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -6353,7 +6351,6 @@
     { "name": "the-earth-yui.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "thehonorguard.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "theinvisibletrailer.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "themerchandiser.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "theodorejones.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "thescientists.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "thesled.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -6741,7 +6738,6 @@
     { "name": "jettlarue.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jie.dance", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "joerss.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "joostbovee.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "joostrijneveld.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "joworld.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jr5devdoug.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -9557,7 +9553,6 @@
     { "name": "octanio.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "octocat.ninja", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "oddtime.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "ohiohealthfortune100.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ohsocool.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "oishioffice.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "okutama.in.th", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -13646,7 +13641,6 @@
     { "name": "techcultivation.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "techcultivation.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "techmasters.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "technogroup.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "techwords.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "telefonnummer.online", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tendermaster.com.ua", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -14390,7 +14384,6 @@
     { "name": "foolwealth.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "elan-organics.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "freethetv.ie", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "florismoo.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "eyedarts.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "frasesparaface.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "exousiakaidunamis.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -15718,7 +15711,6 @@
     { "name": "assindia.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "btrb.ml", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "antonchen.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "andrea-wirthensohn.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "7x24servis.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bobobox.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "amsterdamian.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -17494,7 +17486,6 @@
     { "name": "buildingclouds.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dynamictostatic.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dopfer-fenstertechnik.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "egoroof.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "e-rickroll-r.pw", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ebraph.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dragfiles.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -17528,7 +17519,6 @@
     { "name": "ecc-kaufbeuren.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ecotruck-pooling.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "buildingclouds.es", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "drogueriaelbarco.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "eqim.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "electricoperaduo.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "devpsy.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -17887,7 +17877,6 @@
     { "name": "inme.ga", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "interhosts.co.za", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "interessiert-uns.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "itsgoingdown.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ikzoekeengoedkopeauto.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ikarate.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ipfs.ink", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -19517,7 +19506,6 @@
     { "name": "business-garden.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "buricloud.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cadooz.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "bragasoft.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cdmhp.org.nz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bsktweetup.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "canalsidehouse.be", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -22438,7 +22426,6 @@
     { "name": "secondbyte.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "section-31.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "secwise.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "seeworkdone.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sellajoch.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "semjonov.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sendai-sisters.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -27737,7 +27724,6 @@
     { "name": "j-eck.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "internetinhetbuitengebied.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jabergrutschi.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "horizonshypnosis.ca", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "it-labor.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "insolent.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jaberg-rutschi.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -28023,7 +28009,6 @@
     { "name": "littledisney.ro", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lotuscloud.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lensdoctor.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "lukaszorn.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lachainedesentrepreneurs.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "luffyhair.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "littleqiu.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -28178,7 +28163,6 @@
     { "name": "luxescreenprotector.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mkfs.be", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "molunerfinn.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "milktea.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mohanmekap.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "miguel.pw", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mobil-bei-uns.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -28198,16 +28182,13 @@
     { "name": "mireillewendling.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "moojp.co.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "momstableonline.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "miguelmartinez.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "monteurzimmerfrei.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "moneytoday.se", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "moonrhythm.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mojoco.co.za", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "moppeleinhorn.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mojefilmy.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "mrca-sharp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mosaique-lachenaie.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "migueldominguez.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "modcasts.video", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "multipleservers.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "moritztremmel.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -34569,7 +34550,6 @@
     { "name": "xoda.pw", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "xonn.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yaru.one", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "yephy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yibaoweilong.top", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yinga.ga", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yorcool.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -34687,7 +34667,6 @@
     { "name": "ardor.noip.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "artemis.re", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "artratio.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "assetict.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "australien-tipps.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "autobedrijfschalkoort.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "averageinspired.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -35928,7 +35907,6 @@
     { "name": "henker.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "henleybouncycastles.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "here4funpartysolutions.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "heritagebaptistchurch.com.ph", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "herohirehq.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hertsbouncycastles.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hiddenhillselectrical.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -39157,7 +39135,6 @@
     { "name": "fliino.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fliino.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fliino.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "flipbell.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "flirtycourts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "flmortgagebank.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "florenceapp.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -40537,7 +40514,6 @@
     { "name": "fifei.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fireshellsecurity.team", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "flugstadplasticsurgery.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "fnncat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fomopop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "foto-pro.by", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fourashesgolfcentre.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -40639,13 +40615,11 @@
     { "name": "incoherent.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "infobae.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "innohb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "inscomers.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "intae.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "intpforum.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ip-tanz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ipv6.jetzt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "isakssons.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "issala.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "itaiferber.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jak-na-les.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jalogisch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -41077,7 +41051,6 @@
     { "name": "vx.hn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wala-floor.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "walkingrehabilitation.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "wallinger-online.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "water-addict.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "watoo.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "webharvest.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -41262,7 +41235,6 @@
     { "name": "businessfactors.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "buytermpaper.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "byte128.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bytelog.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "calatoruldigital.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "calculateaspectratio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "carspneu.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -41319,7 +41291,6 @@
     { "name": "dailyxenang.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "danielstiner.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "danifabi.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dansk777.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "darknight.blog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "daubecity.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "decompiled.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -44603,7 +44574,6 @@
     { "name": "netflixlife.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "netrewrite.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nevermore.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "nextcasino.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nf4.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nibo.blog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nicsezcheckfbi.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -44669,7 +44639,6 @@
     { "name": "po.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "politiezoneriho.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ponere.dz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "pop3.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "porpcr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "posalji.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "principalship.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -44937,7 +44906,6 @@
     { "name": "casadasportasejanelas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "catalystapp.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "certfa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "cfdcre5.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chapelaria.tf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chaussenot.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cjhzp.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -44959,7 +44927,6 @@
     { "name": "cyrating.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dartetdemetiers.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "datumstudio.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ddays2008.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "derkuki.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "desveja.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "detroitrocs.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -46129,7 +46096,6 @@
     { "name": "walpu.ski", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "walpuski.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "webdesignsandiego.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "webgreat.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "website-engineering.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "weiming.ddns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wer.sh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -47702,7 +47668,6 @@
     { "name": "sucretown.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sunyataherb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "supertutorial.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "surveyhealthcare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "suzukikazuki.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "swankism.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "swetrust.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -48975,7 +48940,6 @@
     { "name": "somersetscr.nhs.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "soundscrate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sparkresearch.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sparkreviewcenter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "spd-pulheim-mitte.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "speletrodomesticos.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "st-shakyo.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -50437,7 +50401,6 @@
     { "name": "carhunters.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "carlot-j.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "carolina.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "carroceriascarluis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "carseatchecks.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "carsoug.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "casteloinformatica.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -50660,7 +50623,6 @@
     { "name": "fracreazioni.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "frankbellamy.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "free.ac.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "freedom.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "freedom35.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "freehao123.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "frontierdiscount.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -51921,7 +51883,6 @@
     { "name": "pwnedpass.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pxl.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pyrios.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "qiaohong.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "qnq.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "qr.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "qrbird.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -52711,7 +52672,6 @@
     { "name": "clearbookscdn.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "clearvoice.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "clinicasmedicas.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "cloudse.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "com-news.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "compactchess.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "comptu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -54844,7 +54804,6 @@
     { "name": "stfrancisnaugatuck.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "stgabrielstowepa.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sthenryrc.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "stm32f4.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "stmichaelunion.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "studio44.fit", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "studiohomebase.amsterdam", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -56081,7 +56040,6 @@
     { "name": "olmcnewark.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "olphseaside.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "olqoa.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "omicron3069.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "onepercentrentals.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ongiaenegogoa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "openpresentes.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -56457,7 +56415,6 @@
     { "name": "wegonnagetsued.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "welcometoscottsdalehomes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wellness-bonbon.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "weltmeister.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "westernpadermatologist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "westside-pediatrics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "weswitch4u.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -59132,7 +59089,6 @@
     { "name": "sentiments.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sentirmebien.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "servidoresweb.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "seven-shadows.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sharing-kyoto.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "shimi.blog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "shimi.guru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -60178,7 +60134,6 @@
     { "name": "mikusa.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "milkypond.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "minican.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "miraste.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mirazperu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mircarfinder.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mirete.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -60218,7 +60173,6 @@
     { "name": "naarakah.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nabbar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "naganithin.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "nagata.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "naijaxnet.com.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nais0ne.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nakayama.industries", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63178,7 +63132,6 @@
     { "name": "idbs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "impactingsports.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "infraredradiant.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "iningrui.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "insegne.roma.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "instagib.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "iondrey.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -66244,7 +66197,6 @@
     { "name": "bjut.photos", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bobasy.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brocinema.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "buildiffuse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bustany.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bwgjms.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bwgjms.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -66643,7 +66595,6 @@
     { "name": "3809944.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "380zz8989.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "38138938.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "3886aa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "39661463.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "4999016.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "4monar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -66907,7 +66858,6 @@
     { "name": "fallenmoons.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fallin.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "familleseux.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "fattailcall.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fdfz.edu.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ferienstpeter.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "filebox.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -67671,7 +67621,6 @@
     { "name": "fantasmesexuel.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "femmesaupluriel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fenhl.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ferrada.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ff5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ff9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ff9397.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -70372,7 +70321,6 @@
     { "name": "36594.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "39w66.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "518k8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "660887.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "66619991.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "666k8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "668k8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -71128,7 +71076,6 @@
     { "name": "ordermygear.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "osterlensyd.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pandiora.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "pay.mg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pcr24.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pixelabs.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "planet.live", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -71190,7 +71137,6 @@
     { "name": "uze-mobility.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "uze-mobility.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "uzemobility.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "vademekum.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "valuecashhomes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "valuecashoffers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "verified.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -72665,7 +72611,6 @@
     { "name": "petnow.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "petrovich.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "philanima.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "phongthuyanthinh.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "photosaloncontest.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pignus.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "podsvojostreho.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -72810,7 +72755,6 @@
     { "name": "yuer.sytes.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "z8079.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zi5.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "zl-49.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zl-59.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zl-69.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zl-79.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -73480,7 +73424,6 @@
     { "name": "yuzu-tee.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "z8017.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "z8023.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "z8078.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "z8106.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "z8109.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "z8132.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -73503,7 +73446,6 @@
     { "name": "zd239.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zd252.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zd253.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "zd257.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zd258.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zd259.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zd262.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -76378,9 +76320,6 @@
     { "name": "test-school.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "testthis.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "textpages.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tgo3333.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tgo4444.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tgo5555.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thaiboystory.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thaihotmodels.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thaiportal.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -76414,7 +76353,6 @@
     { "name": "timmi6790.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tips4india.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tirteafuera.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "titanforged.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tixio.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tobiasfischer.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tolerance-zero.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -76855,56 +76793,9 @@
     { "name": "y7091.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "y7092.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "y7093.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y890000.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y891111.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y892222.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y893333.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y894444.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y895555.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y896666.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y897777.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y898888.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89a.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89aaa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89b.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89bbb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89c.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89ccc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89d.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89dd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89ddd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89e.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89ee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "y89eee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89f.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "y89fff.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89gg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "y89ggg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89hh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89hhh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89i.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89ii.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89iii.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89j.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89jj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89jjj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89k.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89kk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89l.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89ll.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89m.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89n.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89o.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89q.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89r.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89s.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89u.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89v.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89z.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89zz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yagoda-malina.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yangfamily.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yantox.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -77610,7 +77501,6 @@
     { "name": "guiacursos.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "guys-reviews.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "habitable.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "hafer.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hairpins.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hakkariradyo.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hallcouture.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -78136,7 +78026,6 @@
     { "name": "p333ll.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p333lll.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p333m.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "p333mm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p333mmm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p333n.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p333nn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -78146,7 +78035,6 @@
     { "name": "p333ooo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p333ppp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p333q.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "p333qq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p333qqq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p333r.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p333rr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -79301,7 +79189,6 @@
     { "name": "intranetcrowd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "introes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "iplist.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ippawards.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ipvbook.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "irxoo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "itqh0pk67wngbob5suh-c7glbmvtfa0dqhokufs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -79461,7 +79348,6 @@
     { "name": "seguimosganando.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "seicochimica.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "shoparbonne.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "simplydesk.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "smartmones.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "smartpheromones.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "smartsitio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -80035,17 +79921,6 @@
     { "name": "x81818.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xn--strandhaus-hinter-der-dne-1wc.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xunleiyy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89a.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89b.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89c.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89d.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89e.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89f.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89g.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89h.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89i.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89j.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "y89ww.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yay.cam", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yolocast.wtf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yourpocketbook.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -80076,7 +79951,6 @@
     { "name": "1net.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "25percent.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "2jhb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "40666888.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "500promocodes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "500promokodov.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "52062d.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -80430,7 +80304,6 @@
     { "name": "wintzenterprise.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wuyiwa.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xc9988.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "xerbo.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xiaojicdn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xlunastore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xn--80ageukloel.xn--p1ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -80701,7 +80574,6 @@
     { "name": "openwrt-dist.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "operr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "operrtel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "operrwork.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "orangelandgaming.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ouest-annonces.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "parasca7.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -80767,7 +80639,6 @@
     { "name": "srfloki.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "srkb.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "stainhaufen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "studiovictorialimited.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "summusglobal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "suniru.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sunnistan.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -81171,7 +81042,6 @@
     { "name": "snizl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "songesdeplumes.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sphacks.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sporttomorrow.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "spoters.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "srimakc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sunshinecoastplumbingcompany.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -82839,7 +82709,6 @@
     { "name": "z6912.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "z8870.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zanjirzanane-shanbeghazan.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "zd623.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zd635.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zd652.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zd653.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -84680,7 +84549,6 @@
     { "name": "bet44409.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bet44410.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bienhacerlimpiezas.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bionezis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blaargh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bloggingtipsfornewblogger.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bongocams.webcam", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -85732,7 +85600,6 @@
     { "name": "018k8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "025k8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "031373.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "06lc.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "075k8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "089k8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "08lc.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -85742,7 +85609,6 @@
     { "name": "098k8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "0cp8778.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "0lc8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "0lc8.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "110k8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "115lc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "116lc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86152,7 +86018,6 @@
     { "name": "jurojin.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "juxin08.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k-jtan.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k5k.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8-1.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8-2.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8-facai.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86161,7 +86026,6 @@
     { "name": "k801.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8029.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8031.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8032.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k805.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8050.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8052.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86173,21 +86037,17 @@
     { "name": "k80725.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8073.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8079.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8086.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k809.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8097.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8098.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k80998.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8100.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8103.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8105.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8107.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8109.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k811.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k811.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8111.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k811111.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8121.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k814.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8158.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k81788.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86324,14 +86184,12 @@
     { "name": "k87136.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k87137.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k87138.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k87288.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k873.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k873.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8736.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k875.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8771.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k87777.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8780.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8804.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k88101.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k88102.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86369,7 +86227,6 @@
     { "name": "k88207.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k88210.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k88213.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k88214.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k88233.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k88236.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k88237.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86425,12 +86282,10 @@
     { "name": "k88890.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k889.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8892.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k89.app", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8901119.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8927.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k894.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k895.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k8955.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k89595.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8974.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8994.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86468,8 +86323,6 @@
     { "name": "k8v29.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8v30.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kaibo.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kaifa.gs", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kaifa199.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kanyingba.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "karger.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kassa.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86499,13 +86352,10 @@
     { "name": "kf1313.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf188.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf196.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf199.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf200.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf2000.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf201988.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf2020g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf2121g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf2222g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf260.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf268.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf282.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86576,7 +86426,6 @@
     { "name": "kf6830.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf6831.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf6835.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf688.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf707.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf7171.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf7272.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86626,7 +86475,6 @@
     { "name": "kf8658.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf8659.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf8686.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8787g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf8801.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf8803.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf8805.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86638,7 +86486,6 @@
     { "name": "kf8819.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf8825.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf8828.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kf8830.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf8835.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf8850.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf8851.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86669,15 +86516,12 @@
     { "name": "kf9797.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf981.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kf997.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kfa6.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kfkf999.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kimkyzcrs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kingstake.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kneli.co.il", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "koladeogunleye.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kritikahotels.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "larsson-ornmark.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lb266.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc0101.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc0188.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc040.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86690,20 +86534,16 @@
     { "name": "lc1010g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc1212g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc1313.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc1414.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc1616.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc1616g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc171.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc1717.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc18.fun", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc18.ph", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc18.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc1800.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc1818.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc1904.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc204.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc2121g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc221.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc2222g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc2323g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc2424.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86780,8 +86620,6 @@
     { "name": "lc5188.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc530.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc5353.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc5454.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc5454g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc555.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc5555g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc5668.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86832,7 +86670,6 @@
     { "name": "lc6683.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc6686.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc6698.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc6767.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc68.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc6800.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc6801.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86861,8 +86698,6 @@
     { "name": "lc68697.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc68698.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc6880.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc68880.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc68881.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc68882.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc68884.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc68888.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86873,7 +86708,6 @@
     { "name": "lc7575.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc7676.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc7676g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc777.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc7979.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc7979g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8.life", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86896,11 +86730,8 @@
     { "name": "lc8585g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc859.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc869.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc871.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc873.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc875.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc876.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc8787.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc879.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc88.fun", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8812.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86972,7 +86803,6 @@
     { "name": "lc8935.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8936.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc895.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc896.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc897.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8c.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8dc04.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86984,26 +86814,20 @@
     { "name": "lc8dc16.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8dc20.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8dc21.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc8dc22.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8dc24.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8dc26.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8dc27.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8dc28.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8dc29.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8md00.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc8md01.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8md02.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc8md08.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8md11.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8md26.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8md28.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc8md31.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc8md33.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8md35.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8md55.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8md66.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8md77.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc8md88.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9.app", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc90000.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9090.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -87015,10 +86839,8 @@
     { "name": "lc9256.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9292.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9393g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc9494.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9494g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc973.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc9797.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9862.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9899.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9900.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -87026,7 +86848,6 @@
     { "name": "lc9930.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9938.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9939.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc9940.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9950.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9960.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9968.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -87039,7 +86860,6 @@
     { "name": "lcvip6.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lcvip7.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lcvip8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lcvip8.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lcvip9.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "le052.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "le056.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -87099,8 +86919,6 @@
     { "name": "md52lc8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "md56lc8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "md5lc8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "md8lc8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "md9lc8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mealcast.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "melissagalt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mellika.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -87208,7 +87026,6 @@
     { "name": "sethlmatarassomd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "shdw.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "shopikal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "shoujik8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "simpleprojects.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sitelmexico.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skynetstores.ae", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -87261,10 +87078,8 @@
     { "name": "viplc4.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "viplc5.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "viplc6.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "viplc66.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "viplc68.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "viplc7.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "viplc98.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vmautorajkot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wakf123.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wakf123.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -87548,7 +87363,6 @@
     { "name": "huntertechsolution.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "iinfin.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "imkindofabigdeal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "infotech24.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "infoyaracuy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "inmedic.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "inovacallis.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -87912,7 +87726,6 @@
     { "name": "apirest.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "appliancepronwi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "arox.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "artchic.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "arturli.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "arx.vg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "astifan.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -88000,7 +87813,6 @@
     { "name": "depoker.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deteken.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deutschland-dsl.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "devstores.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dieti-natura.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dimomaint.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dimomaint.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -88039,7 +87851,6 @@
     { "name": "eutiximo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "evemagazineonline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eventprazdnik.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "everichspice.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "evlorin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "evony.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ewaf.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -88308,7 +88119,6 @@
     { "name": "phinphanatic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "phonefilter.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "phonetikos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "phpkoru.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "piercing.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pinpointline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "plekker.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -88495,7 +88305,6 @@
     { "name": "wijaya2u.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wildcatproductions.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wismile.lu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "wolfhowl.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wolftain.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wolvesvtc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wordadmin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -88521,6 +88330,592 @@
     { "name": "zifoapptest.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zoomplumbing.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zoyride.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "1337.vg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2022class1.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2habc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "5icsb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "9118.hk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "9968.ag", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "9968.love", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "9jabase.com.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "a-tes-cotes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abdelaliezzyn.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abdelaliezzyn.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "acronis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adamdorman.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adhocracy.plus", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adimplere.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adultwebcams1.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aeroacademia.com.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aficards.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "agence-wazacom.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "agilesurvey.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "agroconsultoraplus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ahollamby.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ahu.la", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alfratehotelcampiglio.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alitec.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "altertek.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "amforst-ha.ddns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "amforst.ddns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "amrff.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "anblik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "andrija-i-andjelka.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "annabelcinemas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "appers.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "appsaraby.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "apptesters.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "archframe.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arcinapoli.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arkantos.agency", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "asociaciontrastea.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "assistenciamultitec.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "atelier-origami.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "atis-ars.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "autowise.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "avalonbelltown.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ayecode.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "badcreditcarsfinance.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bailleux.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bamanshop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bank-yahav.co.il", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "barnvets.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "batitrakya.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bayoleth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "beanbox.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "beautyinweb.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "besensi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bet333111.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bet333222.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bet33app.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bewegtes-lagern.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bewegtes-lagern.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bewegtes-lagern.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bewegtes-lagern.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bewegteslagern.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bewegteslagern.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bewegteslagern.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bhi.consulting", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bimacitizen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "biolmarket.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "birdie.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blackstump.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blackzebra.audio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blaulicht-giessen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blinds.media", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blogofapps.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blrjmt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "boats.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "botcamp.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "botmedia.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brainstobrand.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brandwidth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bravobet.et", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brendansbits.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brookes.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brutecloud.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "burotec-sarl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "buster.me.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "buycurious.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bxegypt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bysgo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bytheswordinc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "byxong.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "c2m-staging.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cabalacoach.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cajalosandes.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "calitateavietii-ardeal.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "calucon.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "caoliu.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cardanoinvestment.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "carolineball.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cashenvoy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cbt.tj", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "centurion-consulting.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "centurion-consulting.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "checkra.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chika.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chimpmatic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chr1sbin.works", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cissofitness.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "citizenkevin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cixiaoya.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cixiaoya1.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cixiaoya2.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cleaningsolutionn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "clinicadentalvinateros.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "clinicainfinitydental.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "clouddesk.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "coinclickz.fun", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "coldren.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "colinespinas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "comparecompensationclaims.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "consultingconnection.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "courvix.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cpars.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cpls.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cry-sys.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cswebi.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cumagini.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cyberdyne.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "d3a.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dabai.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dabai.photo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "daie-inc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dailychristianpodcast.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dailyrenewblog.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "danndorf.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "davidgroup.co.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "davidops.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dbtsai.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dcave.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dcmarvelunited.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "deerwoodrvpark.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "defendbearbutte.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "designrhome.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "devtea.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "digicasso.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "distrivalle.ec", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dktq2hj81vknv.cloudfront.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dokhuyenmaigiatot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "domicile-clean.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dominicanosenpr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drherndonent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drpure.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dtune.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dug.net.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "e-klempir.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "e-privat.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ebashim.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "echo.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edelweiss-pinzolo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "educa2.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "egglestonyouthcenter.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ekocleaningllc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "electronicayseguridadmonserrate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eletminosegert.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elsuccionador.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "emkode.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "emptyadjacentpossible.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "en-este.link", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "en0.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "enjoymondayofficial.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "envman.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ernearmetx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eruga.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eusolar.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "evnt.team", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "execbar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "exl-english.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "exoticaz.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ezftrs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f8cp0.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fabulosa.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "faca.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fachversand-hennes.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fanbot.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fapiis.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "farallonesrentacar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fenom.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "finethin.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fluglektuere.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "foonly.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "frag.works", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "freebsd.la", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "freebsd.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "freebsd.wiki", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "freelancerhub.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "galj.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "galleonwaymedical.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gavr.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gender-summit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ggiveilig.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gitube.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "glassofgrape.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "globalipaction.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "goaudits.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gotravel.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gozaars.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gpl-elite.store", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "grafia.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "graspingtech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gregmc.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "grouindev.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "guidesorbetiere.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gxpconsultora.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hads0m.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "halilyagcioglu.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hargamobilmu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "harrisandharris.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hartleighclyde.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hawkargentina.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hazelglow.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hceu-performance.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hennesshop.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hethakhout.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "himpler.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hiteshjoshi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hitfront.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hiwannz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hly0928.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hofstaetter.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "holdengreene.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hotelcorporatecodes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hotelpresident.co.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hotelpromo.codes", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hrumka.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "huawenyy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hubspot.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hubspot.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hubspot.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hubspot.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hw923.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "idee-lq.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "idee-lq.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "idee-lq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "idee-lq.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "idee-lq.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iedison.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "imwjc.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "indoor-kletterwald.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infinity-uitvaartzorg.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "informatiger.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ingadesign.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "inpector.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "insurediy.com.sg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "integralsalud.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "involic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iotsys.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iranwiki.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "italiensk-tolk.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ivais.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ivoryandgrace.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "izumi-ryokan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "j-k-fischer-verlag.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jasnowidzkajowi.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jaspersreef.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jessem.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "joaojunior.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jobty.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "joernwendland.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jolee.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "joomla-leipzig.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jorgeto.ddns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jouons-aux-echecs.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jzwebdesign.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "karopapier.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "karrselfstorage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kashflowcoupon.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kashflowpromocode.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kawiarnia.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "keepdecor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kenkou-kitakyusyu.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kf086.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kf117.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kf3131g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kik.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kiwiflowershop.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "knca.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "koji-tsujitani.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kollross.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "konyaescortsiteler.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kopidingin.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kreativklinik.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "krypto-geld.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kurhotel-am-reischberg.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kweb.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "laab.gv.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "laby.life", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lajkatheme.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lancers.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "larete.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "launchgroup.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lawrenceklepinger.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leddingplasticsurgery.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leemac.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leisurepools.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lemagauto.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lifeguatemala.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lintasi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linux.farm", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "livresetmanuscrits.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "locksmithdriftwood.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "logico.ar", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lomerhouse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "long139.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "loshogares.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lsl.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lushan.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "m8593.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maisan.best", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "manageprefs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mariagealamontagne.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maringalazer.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "marketplacestrategy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "masdemexico.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "masterwayhealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "matthiasmueller.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mattrude.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maywoodpark.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mbc.asn.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mclouds.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mdclass.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "me7878.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "medasset.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "meganruggiero.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "metanumbers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "metzgermark.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mi1k.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "miamiaquatours.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "microwavezone.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mieldemexico.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "milleron.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mindvalley.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "minimonies.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "modbom.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "molleron.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "monotai.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "monthlyfukuoka.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "moonlightdesign.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mosternaut.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "muscularbabes.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "muskokavoltz.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "my-profile.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mybillie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mycarinsurance123.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mydenverhomesource.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mygear.live", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myjarofhope.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myloanmanager.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nashikmatka.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nbook.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nectardigit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "netliste.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "netzwerk-lq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "newyorkhiltonmidtown.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nextechoax.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nguyendiep.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "noranowak.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "notedinstyle.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nougat-anduze.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nrv-linux.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nubehogar.nsupdate.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "oacloud.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "oh-leg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "olibomb.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "oliver-wiedemann.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "omahmebel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "on-targettrainingcourses.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "on-this.link", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "onlineltctraining.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "op3y.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "openstakes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "opstory.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ornsyn.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "osuarez3.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "overijsselsemerentocht.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "oz-style.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "p333kk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pacificbeachpub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pathsha.re", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "patlis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "patrickcurl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pcrab.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pedigreetechnologies.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pendrivelinux.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "performancepiers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "permista.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "persiennkompaniet.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "photomaniastore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "phuductms.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "piffer.ind.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pighouse.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pinpromosisemarang.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pjgj16.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "playtzolk.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pluginsetemaswp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "polestar.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "porn7.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "portalz.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "portiaweb.org.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "postoffices.co.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "prawnikdlaanglii.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "precisionhockey.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "precisionicerinks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "primecursos.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "primetrial.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "primetrialfree.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pro-lq.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pro-lq.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pro-lq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pro-lq.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pro-lq.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pro-lq.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pro-lq.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pro-lq.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "prodentalsantacruz.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "proextra.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "prograce.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "punematka.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pupset.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pusehusetkattehotell.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pusehusetmalvik.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "putrawijayatours.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pvc-stolarija.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "queenbeer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "quizhub.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "radiosdeguate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rajasatour.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ratirl.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "raynersorchard.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rdr2natives.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "recruit.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rekurasi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "repalcateia.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "resch.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "retirest.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "revistadiscover.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ricardotaakehb.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ridadihouse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "riklewis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rjrplay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rockefellergroup.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rohde.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rsa-erp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rugsandmore.co.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sahilm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sanabproperties.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sbaten.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "scoach475k.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "scottshorter.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "searx.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "seoexpert.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sergiogas.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "serveur.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sexytagram.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shawnz.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shellopolis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sheremetka.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shin-yo.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shiningbright.co.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shitcountries.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopjek.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "siecledigital.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "signup.ly", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "siteweb-seo.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "siwek.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "skilloutlook.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "skynetstores.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "slymak.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "snowrippers.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "soket.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "southmill.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spd-porta-westfalica.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spm.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "springboardsandmore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "squareforums.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stefanknobel.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stenhojmedia.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stiff.wang", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stiftung-lq.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stiftung-lq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stiftung-lq.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stiftunglq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "storyoneforty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "streemprn.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "studioxii.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "superglidewardrobes.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "svo-intranet.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "swissinternationalva.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "symbo.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "taskhorizon.audio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tebebo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tech4arab.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tekingb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tektouch.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theangelfishfoundation.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theclonker.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "themattresswarehouse.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thisislaikipia.co.ke", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tiamarcia.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tipranks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tomandsonya.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tomandsonya.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tomandsonya.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tool.lu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "totallclean.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tours.co.th", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tradesafe.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tradingoptioncloud.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "traumaheilung.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "travelinc.pk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "treasuredandloved.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "treasuredandloved.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trendfrisuren-bongard.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trendycrowds.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tryprime.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tsutawal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tubedesire.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tusharwalaskar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tvnow.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tvoe-delo24.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tx299.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "udtunnel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ukpropertyrescue.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uksb.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "underwoodpatents.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "unidostransportes.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "upawg.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "usamultimeters.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uvtcinemas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vagueetvent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "valverdedelcamino.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vechainstats.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "verlag-lq.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "verlag-lq.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "verlag-lq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "verlag-lq.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "verlag-lq.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "verlaglq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "verloskundigepraktijktolmiea.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "video-adult-clips-mobile.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "videogamecoupons.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "videopornoitaliana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "videoskaseros.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vinco.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vir.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "virtueinfo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "visarewardprogramplatform.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "visatitans.ae", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "visatitans.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "visatitans.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wear-referrals.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webbricks.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webce.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webcloud.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webrox.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "weymouthslowik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "whi.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wohlraj.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "worcestervets.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wxhbts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wzp.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--80aafaxhj3c.xn--p1ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--9xa.fun", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--rb-fka.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yann.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ymatyt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yodababy.com.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yourname.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yuan.nctu.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yuxiangyuan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zfj.la", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zhongxigo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zihun.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zngay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zqzx.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zvive.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zz342.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     // END OF 1-YEAR BULK HSTS ENTRIES
 
     // Only eTLD+1 domains can be submitted automatically to hstspreload.org,
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 056d24e..9644c28 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -73,14 +73,14 @@
 QUIC_FLAG(int32_t, FLAGS_quic_anti_amplification_factor, 3)
 
 // Enables 3 new connection options to make PROBE_RTT more aggressive
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_less_probe_rtt, true)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_less_probe_rtt, false)
 
 // If true, enable QUIC v99.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_99, true)
 
 // When true, set the initial congestion control window from connection options
 // in QuicSentPacketManager rather than TcpCubicSenderBytes.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_unified_iw_options, true)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_unified_iw_options, false)
 
 // Number of packets that the pacing sender allows in bursts during pacing.
 QUIC_FLAG(int32_t, FLAGS_quic_lumpy_pacing_size, 2)
@@ -109,18 +109,18 @@
 QUIC_FLAG(
     bool,
     FLAGS_quic_reloadable_flag_quic_donot_reset_ideal_next_packet_send_time,
-    true)
+    false)
 
 // If true, enable experiment for testing PCC congestion-control.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_pcc3, false)
 
 // When true, ensure BBR allows at least one MSS to be sent in response to an
 // ACK in packet conservation.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_one_mss_conservation, true)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_one_mss_conservation, false)
 
 // Enables the BBQ5 connection option, which forces saved aggregation values to
 // expire when the bandwidth increases more than 25% in QUIC BBR STARTUP.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_slower_startup4, true)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_slower_startup4, false)
 
 // When true and the BBR9 connection option is present, BBR only considers
 // bandwidth samples app-limited if they're not filling the pipe.
@@ -130,31 +130,31 @@
 // will cause the sequencer to discard future data.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_stop_reading_when_level_triggered,
-          true)
+          false)
 
 // When the STMP connection option is sent by the client, timestamps in the QUIC
 // ACK frame are sent and processed.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_send_timestamps, true)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_send_timestamps, false)
 
 // When in STARTUP and recovery, do not add bytes_acked to QUIC BBR's CWND in
 // CalculateCongestionWindow()
 QUIC_FLAG(
     bool,
     FLAGS_quic_reloadable_flag_quic_bbr_no_bytes_acked_in_startup_recovery,
-    true)
+    false)
 
 // If true, use common code for checking whether a new stream ID may be
 // allocated.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_common_stream_check, true)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_common_stream_check, false)
 
 // If true, QuicEpollClock::Now() will monotonically increase.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_monotonic_epoll_clock, true)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_monotonic_epoll_clock, false)
 
 // If true, enables the BBS4 and BBS5 connection options, which reduce BBR's
 // pacing rate in STARTUP as more losses occur as a fraction of CWND.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_bbr_startup_rate_reduction,
-          true)
+          false)
 
 // If true and using Leto for QUIC shared-key calculations, GFE will react to a
 // failure to contact Leto by sending a REJ containing a fallback ServerConfig,
@@ -177,7 +177,7 @@
 // it's received.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_do_not_accept_stop_waiting,
-          true)
+          false)
 
 // If true, set burst token to 2 in cwnd bootstrapping experiment.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_conservative_bursts, false)
@@ -205,7 +205,7 @@
 QUIC_FLAG(
     bool,
     FLAGS_quic_reloadable_flag_quic_reject_unprocessable_packets_statelessly,
-    false)
+    true)
 
 // Maximum number of tracked packets.
 QUIC_FLAG(int64_t, FLAGS_quic_max_tracked_packet_count, 10000)
@@ -214,40 +214,24 @@
 // descendents) will be automatically converted to lower case.
 QUIC_FLAG(bool, FLAGS_quic_client_convert_http_header_name_to_lowercase, true)
 
-// If true, do not send STOP_WAITING if no_stop_waiting_frame_.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_simplify_stop_waiting, true)
-
 // If true, allow client to enable BBRv2 on server via connection option 'B2ON'.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_allow_client_enabled_bbr_v2,
           false)
 
 // If true, will negotiate the ACK delay time.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_negotiate_ack_delay_time, true)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_negotiate_ack_delay_time, false)
 
 // If true, QuicFramer::WriteClientVersionNegotiationProbePacket uses
 // length-prefixed connection IDs.
 QUIC_FLAG(bool, FLAGS_quic_prober_uses_length_prefixed_connection_ids, false)
 
-// When true, QuicFramer allows parsing failures of source connection ID for
-// the PROX version.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_parse_prox_source_connection_id,
-          true)
-
 // If true and H2PR connection option is received, write_blocked_streams_ uses
 // HTTP2 (tree-style) priority write scheduler.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_use_http2_priority_write_scheduler,
           true)
 
-// If true, close connection if there are too many (> 1000) buffered control
-// frames.
-QUIC_FLAG(
-    bool,
-    FLAGS_quic_reloadable_flag_quic_add_upper_limit_of_buffered_control_frames3,
-    true)
-
 // If true and FIFO connection option is received, write_blocked_streams uses
 // FIFO(stream with smallest ID has highest priority) write scheduler.
 QUIC_FLAG(bool,
@@ -289,11 +273,6 @@
 // If true, enable HTTP/2 default scheduling(round robin).
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_rr_write_scheduler, true)
 
-// If true, combine QuicPacketGenerator and QuicPacketCreator.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_combine_generator_and_creator,
-          true)
-
 // If true, QuicFramer does not create an encrypter/decrypter for the
 // ENCRYPTION_INITIAL level.
 QUIC_FLAG(
@@ -301,11 +280,6 @@
     FLAGS_quic_reloadable_flag_quic_framer_doesnt_create_initial_encrypter,
     true)
 
-// If true, server drops client initial packets in datagrams < 1200 bytes.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_donot_process_small_initial_packets,
-          true)
-
 // If true, treat queued QUIC packets as sent.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_treat_queued_packets_as_sent,
@@ -366,7 +340,7 @@
           false)
 
 // If true, re-calculate pacing rate when cwnd gets bootstrapped.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_fix_pacing_rate, false)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_fix_pacing_rate, true)
 
 // The maximum congestion window in packets.
 QUIC_FLAG(int32_t, FLAGS_quic_max_congestion_window, 2000)
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc
index 2f6a4abd..00f2582e 100644
--- a/net/url_request/url_request_http_job_unittest.cc
+++ b/net/url_request/url_request_http_job_unittest.cc
@@ -454,6 +454,10 @@
 }
 
 TEST_F(URLRequestHttpJobWithMockSocketsTest, TestSuccessfulCachedHeadRequest) {
+  const url::Origin kOrigin1 =
+      url::Origin::Create(GURL("http://www.example.com"));
+  const NetworkIsolationKey kTestNetworkIsolationKey(kOrigin1, kOrigin1);
+
   // Cache the response.
   {
     MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
@@ -469,6 +473,7 @@
         GURL("http://www.example.com"), DEFAULT_PRIORITY, &delegate,
         TRAFFIC_ANNOTATION_FOR_TESTS);
 
+    request->set_network_isolation_key(kTestNetworkIsolationKey);
     request->Start();
     ASSERT_TRUE(request->is_pending());
     delegate.RunUntilComplete();
@@ -497,6 +502,7 @@
     // Use the cached version.
     request->SetLoadFlags(LOAD_SKIP_CACHE_VALIDATION);
     request->set_method("HEAD");
+    request->set_network_isolation_key(kTestNetworkIsolationKey);
     request->Start();
     ASSERT_TRUE(request->is_pending());
     delegate.RunUntilComplete();
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 5df08d1..6a06aa9 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -2716,7 +2716,17 @@
 
 class URLRequestTestHTTP : public URLRequestTest {
  public:
-  URLRequestTestHTTP() : test_server_(base::FilePath(kTestFilePath)) {}
+  const url::Origin origin1_;
+  const url::Origin origin2_;
+  const NetworkIsolationKey network_isolation_key1_;
+  const NetworkIsolationKey network_isolation_key2_;
+
+  URLRequestTestHTTP()
+      : origin1_(url::Origin::Create(GURL("https://foo.test/"))),
+        origin2_(url::Origin::Create(GURL("https://bar.test/"))),
+        network_isolation_key1_(NetworkIsolationKey(origin1_, origin1_)),
+        network_isolation_key2_(NetworkIsolationKey(origin2_, origin2_)),
+        test_server_(base::FilePath(kTestFilePath)) {}
 
  protected:
   // ProtocolHandler for the scheme that's unsafe to redirect to.
@@ -5488,6 +5498,7 @@
   TestDelegate d;
   std::unique_ptr<URLRequest> request(context.CreateRequest(
       request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+  request->set_network_isolation_key(network_isolation_key1_);
   request->Start();
   d.RunUntilComplete();
 
@@ -5500,6 +5511,7 @@
 
   request = context.CreateRequest(request_url, DEFAULT_PRIORITY, &d,
                                   TRAFFIC_ANNOTATION_FOR_TESTS);
+  request->set_network_isolation_key(network_isolation_key1_);
   request->Start();
   d.RunUntilComplete();
 
@@ -5667,6 +5679,7 @@
     d.set_credentials(AuthCredentials(kUser, kSecret));
     std::unique_ptr<URLRequest> r(context.CreateRequest(
         request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+    r->set_network_isolation_key(network_isolation_key1_);
     r->Start();
     d.RunUntilComplete();
   }
@@ -5691,6 +5704,7 @@
     std::unique_ptr<URLRequest> r(context.CreateRequest(
         request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
     r->SetLoadFlags(LOAD_VALIDATE_CACHE);
+    r->set_network_isolation_key(network_isolation_key1_);
     r->Start();
     d.RunUntilComplete();
 
@@ -5886,6 +5900,7 @@
     TestDelegate d;
     std::unique_ptr<URLRequest> req(default_context().CreateRequest(
         redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+    req->set_network_isolation_key(network_isolation_key1_);
     req->Start();
     d.RunUntilComplete();
     EXPECT_EQ(OK, d.request_status());
@@ -5897,6 +5912,7 @@
     TestDelegate d;
     std::unique_ptr<URLRequest> req(default_context().CreateRequest(
         redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+    req->set_network_isolation_key(network_isolation_key1_);
     req->Start();
     d.RunUntilRedirect();
 
@@ -6386,6 +6402,7 @@
     HttpRequestHeaders headers;
     headers.SetHeader("foo", "1");
     req->SetExtraRequestHeaders(headers);
+    req->set_network_isolation_key(network_isolation_key1_);
     req->Start();
     d.RunUntilComplete();
 
@@ -6403,6 +6420,7 @@
     HttpRequestHeaders headers;
     headers.SetHeader("foo", "1");
     req->SetExtraRequestHeaders(headers);
+    req->set_network_isolation_key(network_isolation_key1_);
     req->Start();
     d.RunUntilComplete();
 
@@ -6422,6 +6440,7 @@
     HttpRequestHeaders headers;
     headers.SetHeader("foo", "2");
     req->SetExtraRequestHeaders(headers);
+    req->set_network_isolation_key(network_isolation_key1_);
     req->Start();
     d.RunUntilComplete();
 
@@ -6444,6 +6463,7 @@
     std::unique_ptr<URLRequest> r(default_context().CreateRequest(
         http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d,
         TRAFFIC_ANNOTATION_FOR_TESTS));
+    r->set_network_isolation_key(network_isolation_key1_);
     r->Start();
 
     d.RunUntilComplete();
@@ -6462,6 +6482,7 @@
         http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d,
         TRAFFIC_ANNOTATION_FOR_TESTS));
     r->SetLoadFlags(LOAD_VALIDATE_CACHE);
+    r->set_network_isolation_key(network_isolation_key1_);
     r->Start();
 
     d.RunUntilComplete();
@@ -6584,11 +6605,6 @@
 
 // Tests that |key_auth_cache_by_network_isolation_key| is respected.
 TEST_F(URLRequestTestHTTP, AuthWithNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
-
   ASSERT_TRUE(http_test_server()->Start());
 
   for (bool key_auth_cache_by_network_isolation_key : {false, true}) {
@@ -6613,7 +6629,7 @@
       std::unique_ptr<URLRequest> r(url_request_context.CreateRequest(
           url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
       r->SetLoadFlags(LOAD_BYPASS_CACHE);
-      r->set_network_isolation_key(kNetworkIsolationKey1);
+      r->set_network_isolation_key(network_isolation_key1_);
       r->Start();
 
       d.RunUntilComplete();
@@ -6633,7 +6649,7 @@
           http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d,
           TRAFFIC_ANNOTATION_FOR_TESTS));
       r->SetLoadFlags(LOAD_BYPASS_CACHE);
-      r->set_network_isolation_key(kNetworkIsolationKey2);
+      r->set_network_isolation_key(network_isolation_key2_);
       r->Start();
 
       d.RunUntilComplete();
@@ -6976,6 +6992,7 @@
     std::unique_ptr<URLRequest> r(default_context().CreateRequest(
         http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d,
         TRAFFIC_ANNOTATION_FOR_TESTS));
+    r->set_network_isolation_key(network_isolation_key1_);
     r->Start();
     d.RunUntilAuthRequired();
 
@@ -7010,6 +7027,7 @@
         http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d,
         TRAFFIC_ANNOTATION_FOR_TESTS));
     r->SetLoadFlags(LOAD_VALIDATE_CACHE);
+    r->set_network_isolation_key(network_isolation_key1_);
     r->Start();
 
     d.RunUntilComplete();
@@ -7794,6 +7812,7 @@
   std::unique_ptr<URLRequest> req(default_context().CreateRequest(
       http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d,
       TRAFFIC_ANNOTATION_FOR_TESTS));
+  req->set_network_isolation_key(network_isolation_key1_);
   req->Start();
   d.RunUntilComplete();
 
@@ -7804,6 +7823,7 @@
   req = default_context().CreateRequest(
       http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d,
       TRAFFIC_ANNOTATION_FOR_TESTS);
+  req->set_network_isolation_key(network_isolation_key1_);
   req->Start();
   d.RunUntilComplete();
 
@@ -10954,6 +10974,7 @@
         [](scoped_refptr<const HttpResponseHeaders>* left,
            scoped_refptr<const HttpResponseHeaders> right) { *left = right; },
         base::Unretained(&raw_resp_headers)));
+    r->set_network_isolation_key(network_isolation_key1_);
     r->Start();
     while (!delegate.response_started_count())
       base::RunLoop().RunUntilIdle();
@@ -10979,6 +11000,7 @@
         base::Bind([](scoped_refptr<const HttpResponseHeaders>) {
           FAIL() << "Callback should not be called unless request is sent";
         }));
+    r->set_network_isolation_key(network_isolation_key1_);
     r->Start();
     delegate.RunUntilComplete();
     EXPECT_TRUE(r->was_cached());
@@ -11086,6 +11108,7 @@
   r->SetExtraRequestHeaders(extra_headers);
   r->SetRequestHeadersCallback(req_headers_callback);
   r->SetResponseHeadersCallback(resp_headers_callback);
+  r->set_network_isolation_key(network_isolation_key1_);
   r->Start();
   delegate.RunUntilComplete();
   EXPECT_FALSE(r->is_pending());
@@ -11109,6 +11132,7 @@
   r2->SetRequestHeadersCallback(req_headers_callback);
   r2->SetResponseHeadersCallback(resp_headers_callback);
   r2->SetLoadFlags(LOAD_VALIDATE_CACHE);
+  r2->set_network_isolation_key(network_isolation_key1_);
   r2->Start();
   delegate.RunUntilComplete();
   EXPECT_FALSE(r2->is_pending());
diff --git a/testing/buildbot/chromium.webrtc.fyi.json b/testing/buildbot/chromium.webrtc.fyi.json
index 6dad5f3..ca5f7af 100644
--- a/testing/buildbot/chromium.webrtc.fyi.json
+++ b/testing/buildbot/chromium.webrtc.fyi.json
@@ -112,7 +112,6 @@
   },
   "WebRTC Chromium FYI Linux Builder": {
     "additional_compile_targets": [
-      "frame_analyzer",
       "remoting/webapp:webapp"
     ]
   },
@@ -122,7 +121,6 @@
       "capture_unittests",
       "content_browsertests",
       "content_unittests",
-      "frame_analyzer",
       "jingle_unittests",
       "remoting_unittests",
       "remoting/webapp:webapp"
@@ -273,7 +271,6 @@
   },
   "WebRTC Chromium FYI Mac Builder": {
     "additional_compile_targets": [
-      "frame_analyzer",
       "remoting/webapp:webapp"
     ]
   },
@@ -283,7 +280,6 @@
       "capture_unittests",
       "content_browsertests",
       "content_unittests",
-      "frame_analyzer",
       "jingle_unittests",
       "remoting_unittests",
       "remoting/webapp:webapp"
@@ -434,7 +430,6 @@
   },
   "WebRTC Chromium FYI Win Builder": {
     "additional_compile_targets": [
-      "frame_analyzer",
       "remoting/webapp:webapp"
     ]
   },
@@ -444,7 +439,6 @@
       "capture_unittests",
       "content_browsertests",
       "content_unittests",
-      "frame_analyzer",
       "jingle_unittests",
       "remoting_unittests",
       "remoting/webapp:webapp"
diff --git a/testing/buildbot/chromium.webrtc.json b/testing/buildbot/chromium.webrtc.json
index 2f03c75..658f540 100644
--- a/testing/buildbot/chromium.webrtc.json
+++ b/testing/buildbot/chromium.webrtc.json
@@ -85,7 +85,6 @@
   },
   "WebRTC Chromium Linux Builder": {
     "additional_compile_targets": [
-      "frame_analyzer",
       "jingle_unittests",
       "remoting_unittests"
     ]
@@ -265,7 +264,6 @@
   },
   "WebRTC Chromium Mac Builder": {
     "additional_compile_targets": [
-      "frame_analyzer",
       "jingle_unittests",
       "remoting_unittests"
     ]
@@ -405,7 +403,6 @@
   },
   "WebRTC Chromium Win Builder": {
     "additional_compile_targets": [
-      "frame_analyzer",
       "jingle_unittests",
       "remoting_unittests"
     ]
diff --git a/testing/buildbot/manage.py b/testing/buildbot/manage.py
index 7f0e896..e0ad3aed 100755
--- a/testing/buildbot/manage.py
+++ b/testing/buildbot/manage.py
@@ -152,7 +152,6 @@
   'audio_decoder_unittests',
   'common_audio_unittests',
   'common_video_unittests',
-  'frame_analyzer',
   'libjingle_peerconnection_android_unittest',
   'modules_tests',
   'modules_unittests',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 4f15b1d..73f7d3f3 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3920,7 +3920,6 @@
       },
       'WebRTC Chromium Linux Builder': {
         'additional_compile_targets': [
-          'frame_analyzer',
           'jingle_unittests',
           'remoting_unittests',
         ],
@@ -3935,7 +3934,6 @@
       },
       'WebRTC Chromium Mac Builder': {
         'additional_compile_targets': [
-          'frame_analyzer',
           'jingle_unittests',
           'remoting_unittests',
         ],
@@ -3947,7 +3945,6 @@
       },
       'WebRTC Chromium Win Builder': {
         'additional_compile_targets': [
-          'frame_analyzer',
           'jingle_unittests',
           'remoting_unittests',
         ],
@@ -4007,7 +4004,6 @@
       },
       'WebRTC Chromium FYI Linux Builder': {
         'additional_compile_targets': [
-          'frame_analyzer',
           'remoting/webapp:webapp',
         ],
       },
@@ -4017,7 +4013,6 @@
           'capture_unittests',
           'content_browsertests',
           'content_unittests',
-          'frame_analyzer',
           'jingle_unittests',
           'remoting_unittests',
           'remoting/webapp:webapp',
@@ -4034,7 +4029,6 @@
       },
       'WebRTC Chromium FYI Mac Builder': {
         'additional_compile_targets': [
-          'frame_analyzer',
           'remoting/webapp:webapp',
         ],
       },
@@ -4044,7 +4038,6 @@
           'capture_unittests',
           'content_browsertests',
           'content_unittests',
-          'frame_analyzer',
           'jingle_unittests',
           'remoting_unittests',
           'remoting/webapp:webapp',
@@ -4060,7 +4053,6 @@
       },
       'WebRTC Chromium FYI Win Builder': {
         'additional_compile_targets': [
-          'frame_analyzer',
           'remoting/webapp:webapp',
         ],
       },
@@ -4070,7 +4062,6 @@
           'capture_unittests',
           'content_browsertests',
           'content_unittests',
-          'frame_analyzer',
           'jingle_unittests',
           'remoting_unittests',
           'remoting/webapp:webapp',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 7c79f16..c82e501e 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3167,6 +3167,26 @@
             ]
         }
     ],
+    "KeepaliveRequestPriority": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_20191107",
+                    "enable_features": [
+                        "SetLowPriorityForBeacon"
+                    ]
+                }
+            ]
+        }
+    ],
     "KeyboardAccessoryAddressIPH": [
         {
             "platforms": [
@@ -5749,25 +5769,6 @@
             ]
         }
     ],
-    "SetLowPriorityForBeacon": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20191107",
-                    "enable_features": [
-                        "SetLowPriorityForBeacon"
-                    ]
-                }
-            ]
-        }
-    ],
     "SettingsEnforcement": [
         {
             "platforms": [
diff --git a/third_party/blink/public/platform/web_rtc_stats.h b/third_party/blink/public/platform/web_rtc_stats.h
index b332e095..8ed2dc9 100644
--- a/third_party/blink/public/platform/web_rtc_stats.h
+++ b/third_party/blink/public/platform/web_rtc_stats.h
@@ -19,48 +19,13 @@
 
 namespace webrtc {
 class RTCStatsCollectorCallback;
-class RTCStatsMemberInterface;
-class RTCStatsReport;
 enum class NonStandardGroupId;
 }  // namespace webrtc
 
 namespace blink {
 
-class WebRTCStatsMember;
 class RTCStatsReportPlatform;
 
-class BLINK_PLATFORM_EXPORT WebRTCStatsMember {
- public:
-  virtual ~WebRTCStatsMember();
-
-  virtual WebString GetName() const = 0;
-  virtual webrtc::RTCStatsMemberInterface::Type GetType() const = 0;
-  virtual bool IsDefined() const = 0;
-
-  // Value getters. No conversion is performed; the function must match the
-  // member's |type|.
-  virtual bool ValueBool() const = 0;
-  virtual int32_t ValueInt32() const = 0;
-  virtual uint32_t ValueUint32() const = 0;
-  virtual int64_t ValueInt64() const = 0;
-  virtual uint64_t ValueUint64() const = 0;
-  virtual double ValueDouble() const = 0;
-  virtual WebString ValueString() const = 0;
-  // |WebVector<int> because |WebVector| is incompatible with |bool|.
-  virtual WebVector<int> ValueSequenceBool() const = 0;
-  virtual WebVector<int32_t> ValueSequenceInt32() const = 0;
-  virtual WebVector<uint32_t> ValueSequenceUint32() const = 0;
-  virtual WebVector<int64_t> ValueSequenceInt64() const = 0;
-  virtual WebVector<uint64_t> ValueSequenceUint64() const = 0;
-  virtual WebVector<double> ValueSequenceDouble() const = 0;
-  virtual WebVector<WebString> ValueSequenceString() const = 0;
-};
-
-BLINK_PLATFORM_EXPORT
-std::unique_ptr<WebRTCStatsMember> CreateRTCStatsMember(
-    const scoped_refptr<const webrtc::RTCStatsReport>& stats_owner,
-    const webrtc::RTCStatsMemberInterface* member);
-
 using WebRTCStatsReportCallback =
     base::OnceCallback<void(std::unique_ptr<RTCStatsReportPlatform>)>;
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
index e5deb70..c69cfeb 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
@@ -51,6 +51,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_idle_task_runner.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_trusted_script.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_wasm_response_extensions.h"
 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -382,6 +383,67 @@
   return false;
 }
 
+static std::pair<bool, v8::MaybeLocal<v8::String>>
+TrustedTypesCodeGenerationCheck(v8::Local<v8::Context> context,
+                                v8::Local<v8::Value> source) {
+  v8::Isolate* isolate = context->GetIsolate();
+  ExceptionState exception_state(isolate, ExceptionState::kExecutionContext,
+                                 "eval", "");
+
+  // If the input is not a string or TrustedScript, pass it through.
+  if (!source->IsString() && !V8TrustedScript::HasInstance(source, isolate)) {
+    return {true, v8::MaybeLocal<v8::String>()};
+  }
+
+  StringOrTrustedScript string_or_trusted_script;
+  V8StringOrTrustedScript::ToImpl(
+      context->GetIsolate(), source, string_or_trusted_script,
+      UnionTypeConversionMode::kNotNullable, exception_state);
+  if (exception_state.HadException()) {
+    exception_state.ClearException();
+    // The input was a string or TrustedScript but the conversion failed.
+    // Block, just in case.
+    return {false, v8::MaybeLocal<v8::String>()};
+  }
+
+  String stringified_source = GetStringFromTrustedScript(
+      string_or_trusted_script, ToExecutionContext(context), exception_state);
+  if (exception_state.HadException()) {
+    exception_state.ClearException();
+    return {false, v8::MaybeLocal<v8::String>()};
+  }
+
+  return {true, V8String(context->GetIsolate(), stringified_source)};
+}
+
+static v8::ModifyCodeGenerationFromStringsResult
+CodeGenerationCheckCallbackInMainThread(v8::Local<v8::Context> context,
+                                        v8::Local<v8::Value> source) {
+  // With Trusted Types, we always run the TT check first because of reporting,
+  // and because a default policy might want to stringify or modify the original
+  // source. When TT enforcement is disabled, codegen is always allowed, and we
+  // just use the check to stringify any trusted type source.
+  bool codegen_allowed_by_tt = false;
+  v8::MaybeLocal<v8::String> stringified_source;
+  std::tie(codegen_allowed_by_tt, stringified_source) =
+      TrustedTypesCodeGenerationCheck(context, source);
+
+  if (!codegen_allowed_by_tt) {
+    return {false, v8::MaybeLocal<v8::String>()};
+  }
+
+  if (stringified_source.IsEmpty()) {
+    return {true, v8::MaybeLocal<v8::String>()};
+  }
+
+  if (!ContentSecurityPolicyCodeGenerationCheck(
+          context, stringified_source.ToLocalChecked())) {
+    return {false, v8::MaybeLocal<v8::String>()};
+  }
+
+  return {true, std::move(stringified_source)};
+}
+
 static bool WasmCodeGenerationCheckCallbackInMainThread(
     v8::Local<v8::Context> context,
     v8::Local<v8::String> source) {
@@ -639,8 +701,8 @@
           v8::Isolate::kMessageLog);
   isolate->SetFailedAccessCheckCallbackFunction(
       FailedAccessCheckCallbackInMainThread);
-  isolate->SetAllowCodeGenerationFromStringsCallback(
-      ContentSecurityPolicyCodeGenerationCheck);
+  isolate->SetModifyCodeGenerationFromStringsCallback(
+      CodeGenerationCheckCallbackInMainThread);
   isolate->SetAllowWasmCodeGenerationCallback(
       WasmCodeGenerationCheckCallbackInMainThread);
   if (RuntimeEnabledFeatures::V8IdleTasksEnabled()) {
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 4d1144d..aa917a98 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1205,6 +1205,7 @@
     "frame/csp/csp_source_test.cc",
     "frame/csp/media_list_directive_test.cc",
     "frame/csp/source_list_directive_test.cc",
+    "frame/deprecation_report_body_test.cc",
     "frame/document_loading_rendering_test.cc",
     "frame/dom_timer_test.cc",
     "frame/find_in_page_test.cc",
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index 4ee0fa6..80aae8f 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -218,8 +218,8 @@
 
 // Returns the start time of an animation given the start delay. A negative
 // start delay results in the animation starting with non-zero progress.
-double StartTimeFromDelay(double start_delay) {
-  return start_delay < 0 ? -start_delay : 0;
+AnimationTimeDelta StartTimeFromDelay(double start_delay) {
+  return AnimationTimeDelta::FromSecondsD(start_delay < 0 ? -start_delay : 0);
 }
 
 }  // namespace
@@ -1104,7 +1104,7 @@
 void CSSAnimations::AnimationEventDelegate::MaybeDispatch(
     Document::ListenerType listener_type,
     const AtomicString& event_name,
-    double elapsed_time) {
+    const AnimationTimeDelta& elapsed_time) {
   if (animation_target_->GetDocument().HasListenerType(listener_type)) {
     String pseudo_element_name = PseudoElement::PseudoElementNameForEvents(
         animation_target_->GetPseudoId());
@@ -1132,7 +1132,8 @@
       (previous_phase_ == Timing::kPhaseNone ||
        previous_phase_ == Timing::kPhaseBefore)) {
     const double start_delay = animation_node.SpecifiedTiming().start_delay;
-    const double elapsed_time = start_delay < 0 ? -start_delay : 0;
+    const auto& elapsed_time =
+        AnimationTimeDelta::FromSecondsD(start_delay < 0 ? -start_delay : 0);
     MaybeDispatch(Document::kAnimationStartListener,
                   event_type_names::kAnimationstart, elapsed_time);
   }
@@ -1149,15 +1150,15 @@
         animation_node.SpecifiedTiming().iteration_duration.value() *
         (previous_iteration_.value() + 1);
     MaybeDispatch(Document::kAnimationIterationListener,
-                  event_type_names::kAnimationiteration,
-                  elapsed_time.InSecondsF());
+                  event_type_names::kAnimationiteration, elapsed_time);
   }
 
   if (current_phase == Timing::kPhaseAfter &&
       previous_phase_ != Timing::kPhaseAfter) {
     MaybeDispatch(Document::kAnimationEndListener,
                   event_type_names::kAnimationend,
-                  animation_node.SpecifiedTiming().ActiveDuration());
+                  AnimationTimeDelta::FromSecondsD(
+                      animation_node.SpecifiedTiming().ActiveDuration()));
   }
 
   previous_phase_ = current_phase;
@@ -1200,9 +1201,9 @@
                previous_phase_ == Timing::kPhaseAfter) {
       // If the transition is progressing backwards it is considered to have
       // started at the end position.
-      EnqueueEvent(
-          event_type_names::kTransitionstart,
-          animation_node.SpecifiedTiming().iteration_duration->InSecondsF());
+      DCHECK(animation_node.SpecifiedTiming().iteration_duration.has_value());
+      EnqueueEvent(event_type_names::kTransitionstart,
+                   animation_node.SpecifiedTiming().iteration_duration.value());
     }
   }
 
@@ -1211,9 +1212,9 @@
         (previous_phase_ == Timing::kPhaseActive ||
          previous_phase_ == Timing::kPhaseBefore ||
          previous_phase_ == Timing::kPhaseNone)) {
-      EnqueueEvent(
-          event_type_names::kTransitionend,
-          animation_node.SpecifiedTiming().iteration_duration->InSecondsF());
+      DCHECK(animation_node.SpecifiedTiming().iteration_duration.has_value());
+      EnqueueEvent(event_type_names::kTransitionend,
+                   animation_node.SpecifiedTiming().iteration_duration.value());
     } else if (current_phase == Timing::kPhaseBefore &&
                (previous_phase_ == Timing::kPhaseActive ||
                 previous_phase_ == Timing::kPhaseAfter)) {
@@ -1241,7 +1242,7 @@
       // current_phase != previous_phase_ (see early return at the beginning).
       DCHECK(cancel_active_time);
       EnqueueEvent(event_type_names::kTransitioncancel,
-                   cancel_active_time->InSecondsF());
+                   cancel_active_time.value());
     }
   }
 
@@ -1250,7 +1251,7 @@
 
 void CSSAnimations::TransitionEventDelegate::EnqueueEvent(
     const WTF::AtomicString& type,
-    double elapsed_time) {
+    const AnimationTimeDelta& elapsed_time) {
   String property_name =
       property_.IsCSSCustomProperty()
           ? property_.CustomPropertyName()
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.h b/third_party/blink/renderer/core/animation/css/css_animations.h
index f64a400..159ad965 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.h
+++ b/third_party/blink/renderer/core/animation/css/css_animations.h
@@ -213,7 +213,7 @@
 
     void MaybeDispatch(Document::ListenerType,
                        const AtomicString& event_name,
-                       double elapsed_time);
+                       const AnimationTimeDelta& elapsed_time);
     Member<Element> animation_target_;
     const AtomicString name_;
     Timing::Phase previous_phase_;
@@ -234,7 +234,8 @@
     void Trace(blink::Visitor*) override;
 
    private:
-    void EnqueueEvent(const WTF::AtomicString& type, double elapsed_time);
+    void EnqueueEvent(const WTF::AtomicString& type,
+                      const AnimationTimeDelta& elapsed_time);
 
     const Element& TransitionTarget() const { return *transition_target_; }
     EventTarget* GetEventTarget() const;
diff --git a/third_party/blink/renderer/core/css/css_group_config.json5 b/third_party/blink/renderer/core/css/css_group_config.json5
index d1e33c0..d00e862 100644
--- a/third_party/blink/renderer/core/css/css_group_config.json5
+++ b/third_party/blink/renderer/core/css/css_group_config.json5
@@ -5,15 +5,15 @@
         {
             "name": "rare_non_inherited_properties_rule",
             "cumulative_distribution": [
-                0.134,
-                0.327,
+                0.11925287356321838,
+                0.23275862068965517,
                 1.0
             ]
         },
         {
             "name": "rare_inherited_properties_rule",
             "cumulative_distribution": [
-                0.4,
+                0.5718390804597702,
                 1.0
             ]
         }
diff --git a/third_party/blink/renderer/core/css/css_properties_ranking.json5 b/third_party/blink/renderer/core/css/css_properties_ranking.json5
index dbc8226..855eceaa 100644
--- a/third_party/blink/renderer/core/css/css_properties_ranking.json5
+++ b/third_party/blink/renderer/core/css/css_properties_ranking.json5
@@ -1,578 +1,703 @@
 // The popularity ranking of all css properties the first properties is the most
 // used property according to: https://www.chromestatus.com/metrics/css/popularity
 {
-    properties: {
-    },
-    data: [
-        "display",
+    "data": [
         "width",
-        "margin",
         "height",
-        "overflow",
-        "font-size",
-        "color",
-        "background-color",
+        "display",
         "padding",
         "position",
-        "font-family",
+        "border",
+        "margin",
+        "top",
+        "left",
+        "margin-top",
+        "color",
+        "font-size",
+        "background-color",
         "text-align",
         "opacity",
-        "font-weight",
-        "max-width",
-        "box-sizing",
-        "max-height",
-        "webkit-user-select",
-        "webkit-user-select",
-        "border",
-        "cursor",
-        "top",
-        "margin-top",
-        "left",
-        "background",
-        "margin-left",
-        "padding-left",
-        "text-decoration",
-        "line-height",
         "float",
+        "font-weight",
+        "background",
+        "font-family",
+        "overflow",
+        "line-height",
+        "text-decoration",
         "z-index",
+        "box-sizing",
+        "vertical-align",
+        "margin-left",
+        "cursor",
+        "margin-bottom",
+        "border-radius",
+        "margin-right",
+        "padding-left",
+        "right",
         "padding-top",
         "background-image",
-        "vertical-align",
-        "margin-bottom",
+        "max-width",
+        "padding-right",
+        "bottom",
+        "content",
         "white-space",
-        "margin-right",
         "border-bottom",
         "padding-bottom",
-        "border-radius",
-        "right",
-        "align-items",
-        "padding-right",
+        "box-shadow",
         "visibility",
-        "border-top",
-        "bottom",
-        "background-repeat",
         "min-height",
         "background-position",
-        "outline",
-        "content",
-        "justify-content",
-        "box-shadow",
-        "clear",
-        "border-color",
-        "min-width",
-        "border-left",
-        "font-style",
-        "background-size",
-        "border-right",
-        "font",
-        "list-style",
-        "transition",
-        "text-overflow",
-        "border-width",
+        "border-top",
         "transform",
-        "border-style",
-        "border-collapse",
+        "outline",
+        "clear",
+        "min-width",
+        "transition",
+        "border-color",
+        "background-repeat",
         "text-transform",
+        "background-size",
+        "list-style",
+        "-webkit-transform",
+        "max-height",
+        "font",
+        "font-style",
+        "border-left",
+        "-webkit-box-sizing",
+        "border-right",
+        "border-width",
+        "text-overflow",
+        "-webkit-user-select",
+        "-webkit-user-select",
+        "-webkit-transition",
         "overflow-y",
-        "flex-direction",
-        "text-shadow",
-        "alias-webkit-transform",
-        "text-indent",
-        "zoom",
-        "alias-webkit-border-radius",
-        "alias-webkit-box-shadow",
-        "alias-webkit-box-sizing",
-        "webkit-appearance",
-        "alias-webkit-transition",
-        "word-wrap",
+        "justify-content",
+        "align-items",
+        "border-style",
+        "-webkit-font-smoothing",
         "pointer-events",
-        "border-bottom-left-radius",
-        "border-spacing",
-        "overflow-x",
-        "list-style-type",
-        "border-top-left-radius",
-        "border-bottom-right-radius",
-        "alias-webkit-animation",
-        "border-top-right-radius",
-        "border-top-color",
-        "animation",
-        "border-bottom-color",
         "letter-spacing",
-        "fill",
-        "alias-webkit-text-size-adjust",
-        "webkit-font-smoothing",
-        "src",
-        "border-right-width",
-        "clip",
-        "user-select",
-        "border-bottom-width",
-        "webkit-tap-highlight-color",
-        "direction",
-        "border-top-width",
-        "border-right-color",
-        "border-left-width",
-        "border-left-color",
-        "word-break",
-        "table-layout",
-        "background-clip",
-        "transform-origin",
-        "filter",
+        "-webkit-appearance",
+        "animation",
+        "border-collapse",
+        "overflow-x",
+        "zoom",
+        "word-wrap",
+        "word-wrap",
+        "-webkit-tap-highlight-color",
+        "flex-direction",
+        "-webkit-box-shadow",
+        "border-bottom-left-radius",
+        "list-style-type",
         "flex",
-        "alias-webkit-background-size",
-        "resize",
-        "webkit-box-orient",
-        "alias-webkit-transform-origin",
-        "webkit-filter",
-        "webkit-filter",
-        "alias-webkit-align-items",
-        "alias-webkit-backface-visibility",
-        "transition-property",
-        "alias-webkit-justify-content",
-        "text-rendering",
-        "font-variant",
-        "transition-duration",
-        "outline-offset",
-        "unicode-range",
-        "transition-timing-function",
-        "alias-webkit-animation-duration",
-        "alias-webkit-flex",
-        "alias-webkit-animation-name",
-        "flex-grow",
-        "alias-webkit-animation-timing-function",
-        "alias-webkit-transition-property",
-        "order",
-        "webkit-line-clamp",
-        "word-spacing",
-        "alias-webkit-border-top-right-radius",
-        "webkit-box-align",
-        "touch-action",
-        "transition-delay",
-        "animation-duration",
-        "alias-webkit-border-top-left-radius",
-        "backface-visibility",
-        "animation-name",
-        "stroke",
-        "alias-webkit-transition-duration",
+        "-webkit-text-size-adjust",
+        "text-shadow",
+        "user-select",
+        "text-indent",
+        "border-top-left-radius",
+        "border-spacing",
+        "transform-origin",
+        "border-bottom-right-radius",
+        "fill",
+        "-webkit-border-radius",
+        "-webkit-animation",
+        "clip",
+        "border-top-right-radius",
         "flex-wrap",
-        "alias-webkit-transition-timing-function",
-        "border-bottom-style",
-        "webkit-background-clip",
-        "webkit-box-pack",
-        "quotes",
-        "outline-width",
-        "alias-webkit-border-bottom-right-radius",
-        "alias-webkit-border-bottom-left-radius",
-        "alias-webkit-animation-fill-mode",
-        "alias-webkit-animation-delay",
-        "animation-timing-function",
-        "alias-webkit-flex-direction",
-        "webkit-box-flex",
-        "animation-delay",
-        "animation-fill-mode",
-        "flex-shrink",
-        "unicode-bidi",
-        "page-break-inside",
-        "alias-webkit-perspective",
-        "border-top-style",
-        "page-break-after",
-        "will-change",
-        "list-style-position",
-        "speak",
-        "alias-webkit-order",
-        "alias-webkit-flex-wrap",
-        "orphans",
-        "widows",
-        "outline-style",
-        "list-style-image",
+        "border-top-color",
+        "word-break",
+        "border-bottom-color",
+        "flex-grow",
+        "text-rendering",
+        "direction",
+        "-webkit-box-orient",
         "align-self",
-        "image-rendering",
-        "background-origin",
-        "alias-webkit-animation-iteration-count",
-        "border-left-style",
-        "fill-opacity",
-        "perspective",
-        "border-right-style",
-        "alias-webkit-transition-delay",
-        "outline-color",
-        "overflow-wrap",
-        "animation-iteration-count",
-        "webkit-animation-direction",
-        "webkit-animation-direction",
-        "stroke-width",
-        "stroke-width",
-        "align-content",
-        "webkit-box-direction",
-        "background-attachment",
-        "alias-webkit-animation-play-state",
-        "transform-style",
+        "background-clip",
+        "filter",
+        "src",
+        "flex-shrink",
+        "animation-timing-function",
+        "-webkit-transform-origin",
+        "touch-action",
+        "-webkit-box-align",
+        "border-right-color",
+        "-webkit-align-items",
+        "border-left-color",
+        "-webkit-justify-content",
+        "font-variant",
+        "-webkit-line-clamp",
+        "-webkit-background-size",
+        "transition-property",
+        "animation-name",
+        "animation-duration",
+        "-webkit-flex",
+        "table-layout",
+        "transition-delay",
+        "stroke",
+        "-webkit-backface-visibility",
+        "will-change",
+        "border-bottom-width",
+        "border-top-width",
+        "-webkit-box-flex",
         "flex-basis",
-        "alias-webkit-transform-style",
-        "alias-webkit-flex-grow",
-        "counter-increment",
-        "webkit-box-ordinal-group",
-        "counter-reset",
-        "alias-webkit-opacity",
-        "alias-webkit-flex-shrink",
-        "webkit-mask-image",
-        "webkit-margin-start",
-        "perspective-origin",
-        "alias-webkit-flex-flow",
-        "alias-webkit-perspective-origin",
-        "flex-flow",
-        "alias-webkit-align-self",
-        "background-position-x",
-        "background-position-y",
-        "border-image",
-        "font-stretch",
-        "webkit-user-drag",
+        "outline-width",
+        "order",
+        "-webkit-box-pack",
+        "overflow-wrap",
+        "animation-fill-mode",
+        "outline-style",
+        "outline-offset",
+        "border-right-width",
+        "transition-duration",
         "object-fit",
-        "alias-webkit-align-content",
-        "text-size-adjust",
-        "alias-webkit-flex-basis",
-        "animation-direction",
-        "column-count",
-        "webkit-font-feature-settings",
-        "webkit-font-feature-settings",
-        "webkit-margin-after",
-        "alias-webkit-column-count",
-        "webkit-margin-end",
-        "contain",
-        "animation-play-state",
-        "webkit-text-fill-color",
-        "stroke-opacity",
-        "empty-cells",
-        "webkit-mask-size",
-        "column-gap",
-        "hyphens",
-        "alias-webkit-column-gap",
-        "line-break",
-        "line-break",
-        "webkit-mask-repeat",
-        "stroke-dasharray",
-        "font-feature-settings",
-        "webkit-mask-position",
-        "fill-rule",
-        "webkit-padding-end",
-        "webkit-user-modify",
-        "stroke-linecap",
+        "-webkit-animation-timing-function",
+        "stroke-width",
+        "speak",
+        "-webkit-border-top-right-radius",
+        "-webkit-filter",
+        "-webkit-filter",
+        "resize",
+        "-webkit-flex-direction",
+        "unicode-bidi",
+        "animation-delay",
+        "transition-timing-function",
         "stroke-dashoffset",
-        "columns",
-        "tab-size",
-        "stroke-miterlimit",
-        "webkit-border-image",
-        "stroke-linejoin",
-        "border-image-slice",
-        "webkit-padding-start",
-        "webkit-margin-before",
-        "webkit-column-break-inside",
-        "alias-webkit-columns",
-        "page-break-before",
-        "break-inside",
+        "border-left-width",
+        "unicode-range",
+        "backface-visibility",
+        "word-spacing",
+        "flex-flow",
+        "-webkit-animation-name",
+        "animation-iteration-count",
+        "stroke-dasharray",
+        "-webkit-animation-duration",
+        "-webkit-flex-wrap",
+        "variable",
+        "-webkit-border-top-left-radius",
+        "align-content",
+        "image-rendering",
+        "-webkit-box-direction",
+        "-webkit-transition-property",
+        "border-bottom-style",
+        "-webkit-transition-duration",
+        "-webkit-transition-timing-function",
+        "-webkit-animation-fill-mode",
+        "perspective",
+        "border-top-style",
+        "-webkit-animation-delay",
+        "-webkit-flex-grow",
+        "-webkit-flex-shrink",
+        "animation-direction",
+        "-webkit-border-bottom-left-radius",
+        "-webkit-border-bottom-right-radius",
+        "-webkit-animation-iteration-count",
+        "transform-style",
+        "text-size-adjust",
+        "-webkit-transform-style",
+        "list-style-position",
+        "-webkit-transition-delay",
+        "-webkit-background-clip",
+        "-webkit-background-clip",
+        "background-origin",
+        "animation-play-state",
+        "grid-template-columns",
+        "background-position-x",
+        "background-attachment",
+        "-webkit-flex-flow",
+        "-webkit-flex-basis",
+        "-webkit-perspective",
+        "hyphens",
+        "quotes",
+        "-webkit-align-self",
+        "-webkit-order",
+        "-webkit-opacity",
+        "font-feature-settings",
+        "border-left-style",
+        "border-right-style",
+        "outline-color",
+        "scroll-boundary-behavior",
+        "scroll-boundary-behavior",
+        "font-stretch",
+        "background-position-y",
         "clip-path",
-        "webkit-app-region",
-        "column-width",
-        "webkit-text-stroke-width",
-        "caption-side",
-        "webkit-margin-top-collapse",
-        "border-image-width",
+        "-webkit-mask-image",
+        "-webkit-box-ordinal-group",
+        "-webkit-animation-direction",
+        "-webkit-animation-direction",
+        "list-style-image",
+        "font-display",
+        "counter-increment",
+        "counter-reset",
+        "-webkit-align-content",
+        "column-count",
+        "border-image",
         "size",
-        "font-kerning",
-        "stop-color",
-        "alias-webkit-column-width",
-        "border-image-repeat",
+        "-webkit-font-feature-settings",
+        "-webkit-font-feature-settings",
+        "-webkit-user-drag",
         "text-decoration-color",
-        "border-image-outset",
-        "webkit-text-stroke-color",
-        "webkit-text-stroke",
-        "clip-rule",
-        "text-align-last",
-        "border-image-source",
-        "writing-mode",
-        "mix-blend-mode",
-        "webkit-print-color-adjust",
-        "webkit-clip-path",
-        "webkit-clip-path",
-        "shape-rendering",
-        "webkit-column-break-before",
-        "column-rule",
-        "font-variant-ligatures",
-        "text-anchor",
-        "overflow-anchor",
-        "webkit-line-break",
-        "webkit-box-decoration-break",
-        "column-fill",
-        "webkit-writing-mode",
-        "stop-opacity",
-        "column-rule-color",
-        "alias-webkit-column-rule",
-        "webkit-perspective-origin-x",
-        "webkit-perspective-origin-y",
-        "object-position",
-        "webkit-text-security",
-        "webkit-mask",
-        "text-decoration-style",
-        "background-blend-mode",
-        "column-rule-style",
-        "dominant-baseline",
-        "webkit-mask-box-image",
-        "alias-webkit-column-rule-color",
-        "text-decoration-line",
-        "font-variant-caps",
-        "webkit-background-origin",
-        "webkit-padding-before",
-        "shape-outside",
-        "webkit-padding-after",
+        "page-break-inside",
+        "empty-cells",
+        "column-gap",
+        "-webkit-animation-play-state",
+        "-webkit-clip-path",
+        "-webkit-clip-path",
+        "stroke-linecap",
+        "page-break-after",
         "all",
-        "webkit-text-emphasis-color",
-        "mask",
+        "object-position",
+        "contain",
+        "fill-opacity",
+        "-webkit-column-count",
+        "orphans",
+        "margin-block-end",
+        "padding-inline-start",
+        "font-variant-ligatures",
+        "margin-inline-start",
+        "widows",
+        "margin-inline-end",
+        "grid-gap",
+        "stroke-opacity",
+        "caption-side",
+        "-webkit-text-fill-color",
+        "grid-template-rows",
+        "padding-inline-end",
+        "perspective-origin",
+        "column-width",
+        "grid-column",
+        "shape-outside",
+        "mix-blend-mode",
+        "-webkit-column-gap",
+        "scroll-behavior",
+        "line-break",
+        "line-break",
+        "text-align-last",
+        "columns",
+        "caret-color",
+        "grid-auto-rows",
+        "column-rule",
+        "stroke-miterlimit",
+        "border-image-repeat",
+        "break-inside",
+        "grid-column-gap",
+        "border-image-width",
+        "justify-self",
+        "text-decoration-skip",
+        "fill-rule",
+        "border-image-slice",
+        "overflow-anchor",
+        "border-image-outset",
+        "column-fill",
+        "stroke-linejoin",
+        "tab-size",
+        "justify-items",
+        "-webkit-user-modify",
+        "page-break-before",
+        "grid-row",
+        "-webkit-column-width",
+        "-webkit-margin-before",
+        "-webkit-margin-before",
+        "text-decoration-line",
+        "font-kerning",
+        "grid-area",
+        "-webkit-perspective-origin",
+        "scroll-snap-type",
+        "scroll-boundary-behavior-y",
+        "scroll-boundary-behavior-y",
+        "grid-template-areas",
+        "-webkit-box-decoration-break",
+        "scroll-snap-align",
+        "border-image-source",
+        "text-decoration-style",
+        "-webkit-box-lines",
+        "grid-row-gap",
+        "-webkit-padding-start",
+        "-webkit-padding-start",
+        "-webkit-margin-after",
+        "-webkit-margin-after",
+        "-webkit-margin-start",
+        "-webkit-margin-start",
+        "-webkit-print-color-adjust",
+        "-webkit-column-break-inside",
+        "grid-column-start",
+        "-webkit-mask-size",
+        "backdrop-filter",
+        "-webkit-text-stroke-width",
+        "grid-auto-flow",
+        "-webkit-margin-end",
+        "-webkit-margin-end",
+        "-webkit-columns",
+        "-webkit-box-reflect",
+        "stop-color",
+        "background-blend-mode",
+        "clip-rule",
+        "-webkit-border-image",
+        "column-rule-color",
+        "grid-column-end",
         "font-variant-numeric",
         "isolation",
-        "column-rule-width",
+        "-webkit-mask-box-image",
+        "shape-rendering",
+        "writing-mode",
+        "column-rule-style",
+        "dominant-baseline",
+        "-webkit-mask-position",
         "break-after",
-        "webkit-column-break-after",
-        "webkit-border-horizontal-spacing",
+        "-webkit-text-stroke",
         "break-before",
-        "alignment-baseline",
-        "webkit-box-reflect",
-        "page",
-        "baseline-shift",
-        "text-orientation",
-        "alias-webkit-column-rule-style",
-        "webkit-text-orientation",
-        "rx",
-        "ry",
-        "alias-webkit-column-rule-width",
-        "justify-self",
-        "webkit-highlight",
-        "background-repeat-y",
-        "webkit-rtl-ordering",
-        "background-repeat-x",
-        "webkit-transform-origin-x",
-        "column-span",
-        "webkit-transform-origin-y",
-        "webkit-text-emphasis",
-        "webkit-border-vertical-spacing",
-        "x",
-        "alias-webkit-column-span",
+        "margin-block-start",
+        "-webkit-column-rule",
+        "-webkit-line-break",
         "text-decoration-skip-ink",
-        "webkit-margin-collapse",
-        "y",
-        "r",
-        "grid-template-columns",
-        "webkit-text-combine",
-        "caret-color",
-        "color-rendering",
-        "vector-effect",
-        "color-interpolation-filters",
-        "mask-type",
-        "color-interpolation",
-        "webkit-mask-clip",
-        "marker",
-        "justify-items",
-        "text-underline-position",
-        "marker-end",
-        "marker-start",
-        "webkit-border-start-width",
-        "marker-mid",
-        "webkit-border-start-color",
-        "flood-color",
-        "cy",
-        "shape-margin",
-        "flood-opacity",
-        "webkit-text-decorations-in-effect",
-        "cx",
-        "webkit-border-after-width",
-        "webkit-border-end-width",
-        "webkit-border-before-width",
-        "grid-template-rows",
-        "webkit-hyphenate-character",
-        "paint-order",
-        "webkit-border-end-color",
-        "webkit-border-before-color",
-        "webkit-border-after-color",
-        "webkit-border-end",
-        "webkit-locale",
-        "webkit-mask-composite",
-        "lighting-color",
-        "grid-auto-flow",
-        "d",
-        "shape-image-threshold",
-        "grid-column-gap",
-        "buffered-rendering",
-        "webkit-text-emphasis-position",
-        "gap",
-        "grid-gap",
-        "grid-column-start",
-        "grid-auto-columns",
-        "webkit-mask-box-image-source",
-        "webkit-mask-box-image-outset",
-        "grid-template-areas",
-        "webkit-margin-bottom-collapse",
-        "webkit-mask-box-image-width",
-        "webkit-mask-box-image-repeat",
-        "webkit-mask-box-image-slice",
-        "offset-rotate",
-        "grid-auto-rows",
-        "grid-column-end",
-        "grid-column",
-        "webkit-text-emphasis-style",
-        "webkit-border-start",
-        "grid-row",
-        "grid-row-gap",
-        "row-gap",
-        "webkit-mask-origin",
+        "alignment-baseline",
         "grid-row-start",
+        "-webkit-mask-repeat",
+        "grid-auto-columns",
+        "font-variant-caps",
+        "-webkit-text-stroke-color",
+        "-webkit-text-security",
+        "baseline-shift",
+        "background-repeat-x",
+        "page",
         "grid-row-end",
-        "webkit-margin-before-collapse",
-        "webkit-margin-after-collapse",
-        "offset-path",
-        "offset-distance",
-        "offset-rotation",
-        "grid-template",
-        "alias-webkit-shape-outside",
-        "webkit-border-end-style",
-        "grid-area",
-        "webkit-min-logical-width",
-        "alias-webkit-shape-margin",
-        "place-content",
-        "alias-epub-word-break",
-        "motion",
-        "place-items",
-        "font-display",
-        "webkit-mask-position-x",
-        "enable-background",
-        "text-combine-upright",
-        "webkit-mask-position-y",
+        "-webkit-hyphenate-character",
+        "text-anchor",
+        "-webkit-writing-mode",
+        "stop-opacity",
+        "column-span",
+        "-webkit-mask",
+        "-webkit-background-origin",
+        "-webkit-background-origin",
+        "mask",
+        "border-inline-start-width",
+        "border-inline-end-width",
+        "mask-type",
+        "-webkit-highlight",
+        "font-variant-east-asian",
+        "text-underline-position",
+        "marker",
+        "-webkit-padding-end",
+        "-webkit-padding-end",
+        "column-rule-width",
+        "vector-effect",
+        "-webkit-margin-top-collapse",
+        "background-repeat-y",
+        "text-orientation",
+        "-webkit-text-emphasis-color",
+        "-webkit-rtl-ordering",
+        "r",
+        "shape-margin",
+        "shape-image-threshold",
+        "scroll-boundary-behavior-x",
+        "scroll-boundary-behavior-x",
         "inline-size",
-        "block-size",
-        "min-block-size",
-        "webkit-border-after",
-        "webkit-border-before",
-        "max-inline-size",
+        "-webkit-mask-composite",
+        "-webkit-mask-clip",
+        "ry",
         "min-inline-size",
-        "max-block-size",
-        "webkit-max-logical-width",
-        "webkit-transform-origin-z",
-        "webkit-min-logical-height",
-        "webkit-logical-height",
-        "grid",
-        "webkit-max-logical-height",
-        "webkit-logical-width",
-        "webkit-font-size-delta",
-        "motion-path",
-        "motion-path",
-        "offset",
-        "webkit-ruby-position",
-        "text-justify",
-        "motion-offset",
-        "motion-offset",
-        "motion-rotation",
-        "motion-rotation",
-        "webkit-background-composite",
-        "alias-webkit-shape-image-threshold",
-        "scroll-behavior",
-        "webkit-border-start-style",
-        "font-size-adjust",
-        "glyph-orientation-vertical",
-        "glyph-orientation-horizontal",
-        "webkit-line-box-contain",
-        "webkit-border-fit",
-        "alias-epub-text-transform",
-        "alias-epub-writing-mode",
-        "webkit-border-after-style",
-        "webkit-border-before-style",
-        "place-self",
-        "max-zoom",
-        "min-zoom",
-        "backdrop-filter",
-        "webkit-region-break-inside",
-        "user-zoom",
-        "color-profile",
-        "rotate",
-        "translate",
-        "scale",
-        "alias-epub-text-combine",
-        "touch-action-delay",
-        "snap-height",
-        "snap-height",
-        "orientation",
-        "offset-position",
-        "mask-source-type",
-        "offset-anchor",
-        "kerning",
-        "webkit-mask-repeat-y",
-        "webkit-mask-repeat-x",
-        "alias-epub-text-emphasis-style",
-        "text-underline-style",
-        "webkit-column-progression",
-        "webkit-column-axis",
-        "webkit-line-align",
-        "webkit-line-grid",
-        "webkit-line-snap",
-        "font-variation-settings",
-        "alias-epub-text-orientation",
-        "text-overline",
+        "rx",
+        "-webkit-mask-position-y",
+        "-webkit-mask-origin",
+        "block-size",
+        "-webkit-app-region",
+        "text-combine-upright",
+        "padding-block-start",
+        "row-gap",
+        "grid-template",
+        "min-block-size",
+        "padding-block-end",
+        "border-inline-start-color",
+        "border-inline-end-color",
+        "-webkit-column-span",
+        "-webkit-border-vertical-spacing",
+        "cx",
+        "border-inline-end-style",
+        "border-block-end-width",
+        "border-block-start-width",
+        "border-inline-start-style",
+        "border-block-end-style",
+        "border-block-end-color",
+        "border-block-start-color",
+        "border-block-start-style",
+        "-webkit-padding-before",
+        "-webkit-padding-before",
+        "cy",
+        "-webkit-border-horizontal-spacing",
+        "x",
+        "-webkit-padding-after",
+        "-webkit-padding-after",
+        "-webkit-mask-position-x",
+        "y",
+        "-webkit-text-decorations-in-effect",
+        "color-interpolation-filters",
+        "paint-order",
+        "color-interpolation",
+        "-webkit-transform-origin-x",
+        "-webkit-transform-origin-y",
+        "-webkit-border-before-color",
+        "-webkit-border-before-color",
+        "-webkit-border-before-width",
+        "-webkit-border-before-width",
+        "flood-color",
+        "flood-opacity",
+        "lighting-color",
+        "color-rendering",
+        "gap",
+        "-webkit-text-emphasis-position",
+        "-webkit-column-break-before",
+        "-webkit-text-orientation",
+        "marker-end",
+        "-webkit-locale",
+        "marker-start",
+        "buffered-rendering",
+        "marker-mid",
+        "scroll-snap-stop",
+        "-webkit-text-emphasis",
+        "-webkit-text-emphasis-style",
+        "-webkit-text-combine",
+        "place-content",
+        "offset-path",
+        "offset-rotate",
+        "offset-distance",
+        "-webkit-border-before-style",
+        "-webkit-border-before-style",
         "transform-box",
-        "text-underline-color",
-        "text-overline-width",
-        "alias-epub-text-emphasis-color",
-        "scroll-snap-destination",
-        "image-orientation",
-        "webkit-box-decoration-break",
-        "image-orientation",
-        "webkit-overflow-scrolling",
-        "webkit-dashboard-region",
+        "-webkit-mask-box-image-width",
+        "scroll-padding-left",
+        "-webkit-margin-after-collapse",
+        "-webkit-mask-box-image-source",
+        "-webkit-mask-box-image-repeat",
+        "-webkit-mask-box-image-slice",
+        "-webkit-mask-box-image-outset",
+        "-webkit-margin-before-collapse",
+        "-webkit-column-rule-color",
+        "font-variation-settings",
+        "max-inline-size",
+        "max-block-size",
+        "-webkit-column-rule-style",
+        "-webkit-perspective-origin-x",
+        "-webkit-perspective-origin-y",
+        "scroll-padding",
+        "d",
+        "scroll-padding-top",
+        "scroll-snap-margin",
+        "scroll-snap-margin",
+        "-webkit-box-flex-group",
+        "-webkit-column-break-after",
+        "-webkit-margin-bottom-collapse",
+        "scroll-padding-bottom",
+        "scroll-padding-right",
+        "scroll-snap-margin-top",
+        "scroll-snap-margin-top",
+        "-webkit-ruby-position",
+        "grid",
+        "scroll-snap-margin-right",
+        "scroll-snap-margin-right",
+        "scroll-padding-inline-start",
+        "scroll-snap-margin-left",
+        "scroll-snap-margin-left",
+        "scroll-snap-margin-bottom",
+        "scroll-snap-margin-bottom",
+        "scroll-snap-margin-block-start",
+        "scroll-snap-margin-block-start",
+        "scroll-snap-margin-block-end",
+        "scroll-snap-margin-block-end",
+        "scroll-padding-block-start",
+        "scroll-padding-inline-end",
+        "scroll-snap-margin-inline-end",
+        "scroll-snap-margin-inline-end",
+        "scroll-padding-block-end",
+        "scroll-snap-margin-inline-start",
+        "scroll-snap-margin-inline-start",
+        "place-items",
+        "place-self",
+        "-webkit-margin-collapse",
+        "-webkit-column-rule-width",
+        "font-optical-sizing",
+        "-webkit-border-end-color",
+        "-webkit-border-end-color",
+        "-webkit-border-end-width",
+        "-webkit-border-end-width",
+        "-webkit-border-start-color",
+        "-webkit-border-start-color",
+        "-webkit-border-start-width",
+        "-webkit-border-start-width",
+        "-webkit-border-after-color",
+        "-webkit-border-after-color",
+        "-webkit-border-after-width",
+        "-webkit-border-after-width",
+        "border-block-end",
+        "border-inline-start",
+        "border-block-start",
+        "border-inline-end",
+        "-webkit-shape-outside",
+        "offset",
+        "-webkit-border-before",
+        "-webkit-border-before",
+        "-webkit-border-after",
+        "-webkit-border-after",
+        "-webkit-border-start",
+        "-webkit-border-start",
+        "-webkit-border-end",
+        "-webkit-border-end",
+        "offset-rotation",
+        "-webkit-transform-origin-z",
+        "-webkit-logical-height",
+        "-webkit-logical-height",
+        "-webkit-min-logical-width",
+        "-webkit-min-logical-width",
+        "-webkit-font-size-delta",
+        "-webkit-logical-width",
+        "-webkit-logical-width",
+        "-webkit-max-logical-width",
+        "-webkit-max-logical-width",
+        "-webkit-min-logical-height",
+        "-webkit-min-logical-height",
+        "-webkit-max-logical-height",
+        "-webkit-max-logical-height",
+        "scroll-padding-block",
+        "scroll-padding-inline",
+        "scroll-snap-margin-block",
+        "scroll-snap-margin-block",
+        "scroll-snap-margin-inline",
+        "scroll-snap-margin-inline",
+        "-webkit-border-end-style",
+        "-webkit-border-end-style",
+        "overscroll-behavior-inline",
+        "overscroll-behavior-block",
+        "-webkit-border-start-style",
+        "-webkit-border-start-style",
+        "motion",
+        "-webkit-border-after-style",
+        "-webkit-border-after-style",
+        "-webkit-shape-margin",
+        "-epub-word-break",
+        "motion-path",
+        "motion-path",
+        "font-size-adjust",
+        "text-justify",
+        "-webkit-shape-image-threshold",
+        "-epub-writing-mode",
+        "motion-offset",
+        "motion-offset",
+        "-epub-text-transform",
+        "inset",
+        "scale",
+        "inset-inline-start",
+        "inset-inline-end",
+        "translate",
+        "rotate",
+        "-epub-text-combine",
+        "mask-source-type",
+        "snap-height",
+        "snap-height",
+        "offset-anchor",
+        "offset-position",
+        "margin-block",
+        "user-zoom",
+        "padding-block",
+        "-webkit-mask-repeat-x",
+        "-webkit-mask-repeat-y",
+        "-epub-text-orientation",
+        "border-block-color",
+        "orientation",
+        "text-overline-mode",
+        "text-overline",
+        "-epub-caption-side",
+        "-epub-text-emphasis-color",
+        "-epub-text-emphasis-style",
+        "margin-inline",
+        "padding-inline",
+        "border-block",
+        "border-inline",
+        "inset-block-end",
+        "inset-block",
+        "inset-inline",
+        "content-size",
         "text-line-through",
         "text-line-through-color",
         "text-line-through-mode",
         "text-line-through-style",
         "text-line-through-width",
         "text-overline-color",
-        "text-overline-mode",
         "text-overline-style",
+        "text-overline-width",
         "text-underline",
+        "text-underline-color",
         "text-underline-mode",
+        "text-underline-style",
         "text-underline-width",
-        "webkit-aspect-ratio",
-        "webkit-color-correction",
-        "webkit-filter",
-        "webkit-hyphenate-limit-after",
-        "webkit-hyphenate-limit-before",
-        "webkit-hyphenate-limit-lines",
-        "webkit-hyphens",
-        "webkit-marquee",
-        "webkit-marquee-direction",
-        "webkit-marquee-increment",
-        "webkit-marquee-repetition",
-        "webkit-marquee-speed",
-        "webkit-marquee-style",
-        "webkit-nbsp-mode",
-        "webkit-flow-into",
-        "webkit-flow-from",
-        "webkit-region-fragment",
-        "webkit-region-break-after",
-        "webkit-region-break-before",
+        "-webkit-aspect-ratio",
+        "-webkit-background-composite",
+        "-webkit-border-fit",
+        "-webkit-color-correction",
+        "-webkit-column-axis",
+        "-webkit-column-progression",
+        "-webkit-box-decoration-break",
+        "-webkit-filter",
+        "-webkit-hyphenate-limit-after",
+        "-webkit-hyphenate-limit-before",
+        "-webkit-hyphenate-limit-lines",
+        "-webkit-hyphens",
+        "-webkit-line-box-contain",
+        "-webkit-line-align",
+        "-webkit-line-grid",
+        "-webkit-line-snap",
+        "-webkit-marquee",
+        "-webkit-marquee-direction",
+        "-webkit-marquee-increment",
+        "-webkit-marquee-repetition",
+        "-webkit-marquee-speed",
+        "-webkit-marquee-style",
+        "-webkit-nbsp-mode",
+        "-webkit-flow-into",
+        "-webkit-flow-from",
+        "-webkit-region-fragment",
+        "-webkit-region-break-after",
+        "-webkit-region-break-before",
+        "-webkit-region-break-inside",
         "shape-inside",
         "shape-padding",
-        "webkit-wrap-flow",
-        "webkit-wrap-through",
-        "webkit-wrap",
-        "webkit-tap-highlight-color",
-        "webkit-app-region",
-        "webkit-svg-shadow",
-        "webkit-cursor-visibility",
+        "-webkit-wrap-flow",
+        "-webkit-wrap-through",
+        "-webkit-wrap",
+        "-webkit-tap-highlight-color",
+        "-webkit-app-region",
+        "enable-background",
+        "color-profile",
+        "glyph-orientation-horizontal",
+        "glyph-orientation-vertical",
+        "kerning",
+        "-webkit-svg-shadow",
+        "-webkit-cursor-visibility",
+        "image-orientation",
         "image-resolution",
-        "webkit-blend-mode",
-        "webkit-background-blend-mode",
+        "-webkit-blend-mode",
+        "-webkit-background-blend-mode",
+        "max-zoom",
+        "min-zoom",
+        "-webkit-dashboard-region",
+        "-webkit-overflow-scrolling",
         "internal-callback",
+        "touch-action-delay",
         "scroll-blocks-on",
-        "alias-epub-caption-side",
-        "alias-epub-text-emphasis",
-        "scroll-snap-type",
+        "motion-rotation",
+        "motion-rotation",
+        "-epub-text-emphasis",
         "scroll-snap-points-x",
         "scroll-snap-points-y",
         "scroll-snap-coordinate",
-        "variable",
+        "scroll-snap-destination",
+        "image-orientation",
+        "apply-at-rule",
+        "viewport-fit",
+        "border-block-style",
+        "border-block-width",
+        "border-inline-color",
+        "border-inline-style",
+        "border-inline-width",
+        "inset-block-start",
+        "syntax"
     ],
     "properties": {}
 }
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index 59a366b2..22c9f03 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -2369,4 +2369,32 @@
             innermost_style->VisitedDependentColor(GetCSSPropertyColor()));
 }
 
+TEST_F(StyleEngineTest, MoveSlottedOutsideFlatTreeCrash) {
+  ScopedFlatTreeStyleRecalcForTest feature_scope(true);
+
+  GetDocument().body()->SetInnerHTMLFromString(R"HTML(
+    <div id="host1"><span></span></div>
+    <div id="host2"></div>
+  )HTML");
+
+  auto* host1 = GetDocument().getElementById("host1");
+  auto* host2 = GetDocument().getElementById("host2");
+  auto* span = host1->firstChild();
+
+  ShadowRoot& shadow_root =
+      host1->AttachShadowRootInternal(ShadowRootType::kOpen);
+  shadow_root.SetInnerHTMLFromString("<slot></slot>");
+  host2->AttachShadowRootInternal(ShadowRootType::kOpen);
+
+  UpdateAllLifecyclePhases();
+
+  host2->appendChild(span);
+  EXPECT_EQ(span, GetStyleRecalcRoot());
+
+  // Removing the span will fall back to using the host parent as the new style
+  // recalc root.
+  span->remove();
+  EXPECT_EQ(host2, GetStyleRecalcRoot());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/style_recalc_root.cc b/third_party/blink/renderer/core/css/style_recalc_root.cc
index 43c5e17..546b1509 100644
--- a/third_party/blink/renderer/core/css/style_recalc_root.cc
+++ b/third_party/blink/renderer/core/css/style_recalc_root.cc
@@ -64,6 +64,8 @@
     // V0. It is too complicated to try to find the closest flat tree parent.
     return base::nullopt;
   }
+  if (!root->HasSlotAssignment())
+    return base::nullopt;
   // The child has already been removed, so we cannot look up its slot
   // assignment directly. Find the slot which was part of the ancestor chain
   // before the removal by checking the child-dirty bits. Since the recalc root
diff --git a/third_party/blink/renderer/core/dom/BUILD.gn b/third_party/blink/renderer/core/dom/BUILD.gn
index 76061e82..16a1255 100644
--- a/third_party/blink/renderer/core/dom/BUILD.gn
+++ b/third_party/blink/renderer/core/dom/BUILD.gn
@@ -168,8 +168,6 @@
     "mutation_observer.h",
     "mutation_observer_interest_group.cc",
     "mutation_observer_interest_group.h",
-    "mutation_observer_notifier.cc",
-    "mutation_observer_notifier.h",
     "mutation_observer_registration.cc",
     "mutation_observer_registration.h",
     "mutation_record.cc",
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index ba7f272..cc66dbc 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -120,7 +120,6 @@
 #include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
 #include "third_party/blink/renderer/core/dom/live_node_list.h"
 #include "third_party/blink/renderer/core/dom/mutation_observer.h"
-#include "third_party/blink/renderer/core/dom/mutation_observer_notifier.h"
 #include "third_party/blink/renderer/core/dom/node_child_removal_tracker.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/dom/node_iterator.h"
@@ -7271,7 +7270,7 @@
   if (scripted_animation_controller_)
     scripted_animation_controller_->Unpause();
 
-  GetWindowAgent().GetMutationObserverNotifier().ResumeSuspendedObservers();
+  MutationObserver::ResumeSuspendedObservers();
   if (dom_window_)
     DOMWindowPerformance::performance(*dom_window_)->ResumeSuspendedObservers();
 }
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 79b6719..af3ccf8 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -405,8 +405,6 @@
     return true;
   if (name == html_names::kAriaFlowtoAttr)
     return true;
-  if (name == html_names::kAriaLabeledbyAttr)
-    return true;
   if (name == html_names::kAriaLabelledbyAttr)
     return true;
   if (name == html_names::kAriaOwnsAttr)
@@ -416,7 +414,7 @@
 
 HeapVector<Member<Element>>* GetExplicitlySetElementsForAttr(
     Element* element,
-    const QualifiedName& name) {
+    QualifiedName name) {
   ExplicitlySetAttrElementsMap* element_attribute_map =
       element->GetDocument().GetExplicitlySetAttrElementsMap(element);
   return element_attribute_map->at(name);
@@ -669,11 +667,6 @@
     removeAttribute(name);
 }
 
-bool Element::HasExplicitlySetAttrAssociatedElements(
-    const QualifiedName& name) {
-  return GetExplicitlySetElementsForAttr(this, name);
-}
-
 void Element::SynchronizeContentAttributeAndElementReference(
     const QualifiedName& name) {
   ExplicitlySetAttrElementsMap* element_attribute_map =
@@ -750,7 +743,6 @@
   ExplicitlySetAttrElementsMap* element_attribute_map =
       GetDocument().GetExplicitlySetAttrElementsMap(this);
 
-  // Not sure if this can happen, as FrozenArrays aren't nullable?
   if (is_null) {
     element_attribute_map->erase(name);
     removeAttribute(name);
@@ -802,10 +794,6 @@
   }
 
   setAttribute(name, value.SerializeToString());
-  if (isConnected()) {
-    if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache())
-      cache->HandleAttributeChanged(name, this);
-  }
   element_attribute_map->Set(name, elements);
 }
 
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 08869ea..99a505c 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -204,7 +204,6 @@
   void SetFloatingPointAttribute(const QualifiedName& attribute_name,
                                  double value);
 
-  bool HasExplicitlySetAttrAssociatedElements(const QualifiedName& name);
   Element* GetElementAttribute(const QualifiedName& name);
   void SetElementAttribute(const QualifiedName&, Element*);
   HeapVector<Member<Element>> GetElementArrayAttribute(
diff --git a/third_party/blink/renderer/core/dom/mutation_observer.cc b/third_party/blink/renderer/core/dom/mutation_observer.cc
index bc40b03..7635ab4 100644
--- a/third_party/blink/renderer/core/dom/mutation_observer.cc
+++ b/third_party/blink/renderer/core/dom/mutation_observer.cc
@@ -36,7 +36,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_mutation_callback.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/mutation_observer_init.h"
-#include "third_party/blink/renderer/core/dom/mutation_observer_notifier.h"
 #include "third_party/blink/renderer/core/dom/mutation_observer_registration.h"
 #include "third_party/blink/renderer/core/dom/mutation_record.h"
 #include "third_party/blink/renderer/core/dom/node.h"
@@ -85,6 +84,14 @@
   Member<V8MutationCallback> callback_;
 };
 
+static unsigned g_observer_priority = 0;
+struct MutationObserver::ObserverLessThan {
+  bool operator()(const Member<MutationObserver>& lhs,
+                  const Member<MutationObserver>& rhs) {
+    return lhs->priority_ < rhs->priority_;
+  }
+};
+
 MutationObserver* MutationObserver::Create(Delegate* delegate) {
   DCHECK(IsMainThread());
   return MakeGarbageCollected<MutationObserver>(delegate->GetExecutionContext(),
@@ -102,10 +109,7 @@
 MutationObserver::MutationObserver(ExecutionContext* execution_context,
                                    Delegate* delegate)
     : ContextClient(execution_context), delegate_(delegate) {
-  priority_ = GetDocument()
-                  ->GetWindowAgent()
-                  .GetMutationObserverNotifier()
-                  .NextObserverPriority();
+  priority_ = g_observer_priority++;
 }
 
 MutationObserver::~MutationObserver() = default;
@@ -215,43 +219,64 @@
   registrations_.erase(registration);
 }
 
+static MutationObserverSet& ActiveMutationObservers() {
+  DEFINE_STATIC_LOCAL(Persistent<MutationObserverSet>, active_observers,
+                      (MakeGarbageCollected<MutationObserverSet>()));
+  return *active_observers;
+}
+using SlotChangeList = HeapVector<Member<HTMLSlotElement>>;
+// TODO(hayato): We should have a SlotChangeList for each unit of related
+// similar-origin browsing context.
+// https://html.spec.whatwg.org/C/#unit-of-related-similar-origin-browsing-contexts
+static SlotChangeList& ActiveSlotChangeList() {
+  DEFINE_STATIC_LOCAL(Persistent<SlotChangeList>, slot_change_list,
+                      (MakeGarbageCollected<SlotChangeList>()));
+  return *slot_change_list;
+}
+static MutationObserverSet& SuspendedMutationObservers() {
+  DEFINE_STATIC_LOCAL(Persistent<MutationObserverSet>, suspended_observers,
+                      (MakeGarbageCollected<MutationObserverSet>()));
+  return *suspended_observers;
+}
+static void EnsureEnqueueMicrotask() {
+  if (ActiveMutationObservers().IsEmpty() && ActiveSlotChangeList().IsEmpty())
+    Microtask::EnqueueMicrotask(WTF::Bind(&MutationObserver::DeliverMutations));
+}
+
 // static
 void MutationObserver::EnqueueSlotChange(HTMLSlotElement& slot) {
   DCHECK(IsMainThread());
-  slot.GetDocument()
-      .GetWindowAgent()
-      .GetMutationObserverNotifier()
-      .EnqueueSlotChange(slot);
+  EnsureEnqueueMicrotask();
+  ActiveSlotChangeList().push_back(&slot);
 }
 
 // static
 void MutationObserver::CleanSlotChangeList(Document& document) {
-  document.GetWindowAgent().GetMutationObserverNotifier().CleanSlotChangeList(
-      document);
+  SlotChangeList kept;
+  kept.ReserveCapacity(ActiveSlotChangeList().size());
+  for (auto& slot : ActiveSlotChangeList()) {
+    if (slot->GetDocument() != document)
+      kept.push_back(slot);
+  }
+  ActiveSlotChangeList().swap(kept);
 }
 
-void MutationObserver::Activate() {
-  GetDocument()
-      ->GetWindowAgent()
-      .GetMutationObserverNotifier()
-      .ActivateObserver(this);
+static void ActivateObserver(MutationObserver* observer) {
+  EnsureEnqueueMicrotask();
+  ActiveMutationObservers().insert(observer);
 }
 
 void MutationObserver::EnqueueMutationRecord(MutationRecord* mutation) {
   DCHECK(IsMainThread());
-  ExecutionContext* execution_context = delegate_->GetExecutionContext();
-  // This might get called when the execution context is gone. crbug.com/982850
-  if (!execution_context)
-    return;
   records_.push_back(mutation);
-  Activate();
-  probe::AsyncTaskScheduled(execution_context, mutation->type(),
+  ActivateObserver(this);
+  probe::AsyncTaskScheduled(delegate_->GetExecutionContext(), mutation->type(),
                             mutation->async_task_id());
 }
 
 void MutationObserver::SetHasTransientRegistration() {
   DCHECK(IsMainThread());
-  Activate();
+  ActivateObserver(this);
 }
 
 HeapHashSet<Member<Node>> MutationObserver::GetObservedNodes() const {
@@ -299,6 +324,49 @@
   delegate_->Deliver(records, *this);
 }
 
+// static
+void MutationObserver::ResumeSuspendedObservers() {
+  DCHECK(IsMainThread());
+  if (SuspendedMutationObservers().IsEmpty())
+    return;
+  MutationObserverVector suspended;
+  CopyToVector(SuspendedMutationObservers(), suspended);
+  for (const auto& observer : suspended) {
+    if (!observer->ShouldBeSuspended()) {
+      SuspendedMutationObservers().erase(observer);
+      ActivateObserver(observer);
+    }
+  }
+}
+
+// static
+void MutationObserver::DeliverMutations() {
+  // These steps are defined in DOM Standard's "notify mutation observers".
+  // https://dom.spec.whatwg.org/#notify-mutation-observers
+  DCHECK(IsMainThread());
+  MutationObserverVector observers;
+  CopyToVector(ActiveMutationObservers(), observers);
+  ActiveMutationObservers().clear();
+  SlotChangeList slots;
+  slots.swap(ActiveSlotChangeList());
+  for (const auto& slot : slots)
+    slot->ClearSlotChangeEventEnqueued();
+  std::sort(observers.begin(), observers.end(), ObserverLessThan());
+  for (const auto& observer : observers) {
+    if (!observer->GetExecutionContext()) {
+      // The observer's execution context is already gone, as active observers
+      // intentionally do not hold their execution context. Do nothing then.
+      continue;
+    }
+    if (observer->ShouldBeSuspended())
+      SuspendedMutationObservers().insert(observer);
+    else
+      observer->Deliver();
+  }
+  for (const auto& slot : slots)
+    slot->DispatchSlotChangeEvent();
+}
+
 void MutationObserver::Trace(Visitor* visitor) {
   visitor->Trace(delegate_);
   visitor->Trace(records_);
diff --git a/third_party/blink/renderer/core/dom/mutation_observer.h b/third_party/blink/renderer/core/dom/mutation_observer.h
index 02dc9dd..04f5c8b 100644
--- a/third_party/blink/renderer/core/dom/mutation_observer.h
+++ b/third_party/blink/renderer/core/dom/mutation_observer.h
@@ -56,8 +56,10 @@
 class ScriptState;
 class V8MutationCallback;
 
+using MutationObserverSet = HeapHashSet<Member<MutationObserver>>;
 using MutationObserverRegistrationSet =
     HeapHashSet<WeakMember<MutationObserverRegistration>>;
+using MutationObserverVector = HeapVector<Member<MutationObserver>>;
 using MutationRecordVector = HeapVector<Member<MutationRecord>>;
 
 class CORE_EXPORT MutationObserver final
@@ -94,6 +96,8 @@
 
   static MutationObserver* Create(Delegate*);
   static MutationObserver* Create(ScriptState*, V8MutationCallback*);
+  static void ResumeSuspendedObservers();
+  static void DeliverMutations();
   static void EnqueueSlotChange(HTMLSlotElement&);
   static void CleanSlotChangeList(Document&);
 
@@ -114,14 +118,12 @@
 
   void Trace(Visitor*) override;
 
-  // Methods to be used by MutationObserverNotifier
+ private:
+  struct ObserverLessThan;
+
   void Deliver();
   bool ShouldBeSuspended() const;
   void CancelInspectorAsyncTasks();
-  unsigned priority() { return priority_; }
-
- private:
-  void Activate();
 
   Member<Delegate> delegate_;
   HeapVector<Member<MutationRecord>> records_;
diff --git a/third_party/blink/renderer/core/dom/mutation_observer_notifier.cc b/third_party/blink/renderer/core/dom/mutation_observer_notifier.cc
deleted file mode 100644
index f84fccf4..0000000
--- a/third_party/blink/renderer/core/dom/mutation_observer_notifier.cc
+++ /dev/null
@@ -1,116 +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 "third_party/blink/renderer/core/dom/mutation_observer_notifier.h"
-
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/mutation_observer.h"
-#include "third_party/blink/renderer/core/html/html_slot_element.h"
-#include "third_party/blink/renderer/platform/bindings/microtask.h"
-
-namespace blink {
-
-MutationObserverNotifier::MutationObserverNotifier() {}
-
-void MutationObserverNotifier::EnqueueSlotChange(HTMLSlotElement& slot) {
-  EnqueueMicrotaskIfNecessary();
-  active_slot_change_list_.push_back(&slot);
-}
-
-void MutationObserverNotifier::CleanSlotChangeList(Document& document) {
-  SlotChangeList kept;
-  kept.ReserveCapacity(active_slot_change_list_.size());
-  for (auto& slot : active_slot_change_list_) {
-    if (slot->GetDocument() != document)
-      kept.push_back(slot);
-  }
-  active_slot_change_list_.swap(kept);
-}
-
-void MutationObserverNotifier::ActivateObserver(MutationObserver* observer) {
-  EnqueueMicrotaskIfNecessary();
-  active_mutation_observers_.insert(observer);
-}
-
-void MutationObserverNotifier::EnqueueMicrotaskIfNecessary() {
-  if (microtask_enqueued_) {
-    return;
-  }
-
-  // If this is called before inserting the observer or slot, they should both
-  // be empty.
-  DCHECK(active_mutation_observers_.IsEmpty());
-  DCHECK(active_slot_change_list_.IsEmpty());
-
-  microtask_enqueued_ = true;
-  Microtask::EnqueueMicrotask(WTF::Bind(
-      &MutationObserverNotifier::DeliverMutations, WrapPersistent(this)));
-}
-
-struct MutationObserverNotifier::ObserverLessThan {
-  bool operator()(const Member<MutationObserver>& lhs,
-                  const Member<MutationObserver>& rhs) {
-    return lhs->priority() < rhs->priority();
-  }
-};
-
-void MutationObserverNotifier::DeliverMutations() {
-  // These steps are defined in DOM Standard's "notify mutation observers".
-  // https://dom.spec.whatwg.org/#notify-mutation-observers
-  DCHECK(IsMainThread());
-
-  microtask_enqueued_ = false;
-
-  MutationObserverVector observers;
-  CopyToVector(active_mutation_observers_, observers);
-  active_mutation_observers_.clear();
-
-  SlotChangeList slots;
-  slots.swap(active_slot_change_list_);
-  for (const auto& slot : slots)
-    slot->ClearSlotChangeEventEnqueued();
-
-  std::sort(observers.begin(), observers.end(), ObserverLessThan());
-  for (const auto& observer : observers) {
-    if (!observer->GetExecutionContext()) {
-      // The observer's execution context is already gone, as active observers
-      // intentionally do not hold their execution context. Do nothing then.
-      continue;
-    }
-
-    if (observer->ShouldBeSuspended())
-      suspended_mutation_observers_.insert(observer);
-    else
-      observer->Deliver();
-  }
-  for (const auto& slot : slots)
-    slot->DispatchSlotChangeEvent();
-}
-
-void MutationObserverNotifier::ResumeSuspendedObservers() {
-  DCHECK(IsMainThread());
-  if (suspended_mutation_observers_.IsEmpty())
-    return;
-
-  MutationObserverVector suspended;
-  CopyToVector(suspended_mutation_observers_, suspended);
-  for (const auto& observer : suspended) {
-    if (!observer->ShouldBeSuspended()) {
-      suspended_mutation_observers_.erase(observer);
-      ActivateObserver(observer);
-    }
-  }
-}
-
-unsigned MutationObserverNotifier::NextObserverPriority() {
-  return ++last_observer_priority_;
-}
-
-void MutationObserverNotifier::Trace(Visitor* visitor) {
-  visitor->Trace(active_mutation_observers_);
-  visitor->Trace(suspended_mutation_observers_);
-  visitor->Trace(active_slot_change_list_);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/mutation_observer_notifier.h b/third_party/blink/renderer/core/dom/mutation_observer_notifier.h
deleted file mode 100644
index 497245e..0000000
--- a/third_party/blink/renderer/core/dom/mutation_observer_notifier.h
+++ /dev/null
@@ -1,54 +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.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_MUTATION_OBSERVER_NOTIFIER_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_MUTATION_OBSERVER_NOTIFIER_H_
-
-#include "third_party/blink/renderer/platform/heap/handle.h"
-
-namespace blink {
-
-class Document;
-class MutationObserver;
-class HTMLSlotElement;
-
-// In charge of mutation observer related state that should be managed per
-// WindowAgent.
-class MutationObserverNotifier final
-    : public GarbageCollected<MutationObserverNotifier> {
- public:
-  MutationObserverNotifier();
-
-  void Trace(Visitor*);
-
-  void EnqueueSlotChange(HTMLSlotElement&);
-  void CleanSlotChangeList(Document&);
-  void ActivateObserver(MutationObserver*);
-  void ResumeSuspendedObservers();
-  // Returns the priority that should be assigned to the next MutationObserver.
-  unsigned NextObserverPriority();
-
-  // Callback called to deliver the accumulated mutations.
-  void DeliverMutations();
-
- private:
-  struct ObserverLessThan;
-
-  // Schedules the DeliverMutations() callback if necessary.
-  void EnqueueMicrotaskIfNecessary();
-
-  using MutationObserverSet = HeapHashSet<Member<MutationObserver>>;
-  using SlotChangeList = HeapVector<Member<HTMLSlotElement>>;
-  using MutationObserverVector = HeapVector<Member<MutationObserver>>;
-
-  bool microtask_enqueued_ = false;
-  MutationObserverSet active_mutation_observers_;
-  MutationObserverSet suspended_mutation_observers_;
-  SlotChangeList active_slot_change_list_;
-  unsigned last_observer_priority_ = 0;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_MUTATION_OBSERVER_NOTIFIER_H_
diff --git a/third_party/blink/renderer/core/events/animation_event.cc b/third_party/blink/renderer/core/events/animation_event.cc
index b633a20..2f7f9ad7 100644
--- a/third_party/blink/renderer/core/events/animation_event.cc
+++ b/third_party/blink/renderer/core/events/animation_event.cc
@@ -29,18 +29,19 @@
 
 namespace blink {
 
-AnimationEvent::AnimationEvent() : elapsed_time_(0.0) {}
+AnimationEvent::AnimationEvent() = default;
 
 AnimationEvent::AnimationEvent(const AtomicString& type,
                                const AnimationEventInit* initializer)
     : Event(type, initializer),
       animation_name_(initializer->animationName()),
-      elapsed_time_(initializer->elapsedTime()),
+      elapsed_time_(
+          AnimationTimeDelta::FromSecondsD(initializer->elapsedTime())),
       pseudo_element_(initializer->pseudoElement()) {}
 
 AnimationEvent::AnimationEvent(const AtomicString& type,
                                const String& animation_name,
-                               double elapsed_time,
+                               const AnimationTimeDelta& elapsed_time,
                                const String& pseudo_element)
     : Event(type, Bubbles::kYes, Cancelable::kYes),
       animation_name_(animation_name),
@@ -54,7 +55,7 @@
 }
 
 double AnimationEvent::elapsedTime() const {
-  return elapsed_time_;
+  return elapsed_time_.InSecondsF();
 }
 
 const String& AnimationEvent::pseudoElement() const {
diff --git a/third_party/blink/renderer/core/events/animation_event.h b/third_party/blink/renderer/core/events/animation_event.h
index 9f468cae..69d57cc 100644
--- a/third_party/blink/renderer/core/events/animation_event.h
+++ b/third_party/blink/renderer/core/events/animation_event.h
@@ -26,6 +26,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_ANIMATION_EVENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_ANIMATION_EVENT_H_
 
+#include "third_party/blink/renderer/core/animation/animation_time_delta.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/events/animation_event_init.h"
 
@@ -40,7 +41,7 @@
   }
   static AnimationEvent* Create(const AtomicString& type,
                                 const String& animation_name,
-                                double elapsed_time,
+                                const AnimationTimeDelta& elapsed_time,
                                 const String& pseudo_element) {
     return MakeGarbageCollected<AnimationEvent>(type, animation_name,
                                                 elapsed_time, pseudo_element);
@@ -53,7 +54,7 @@
   AnimationEvent();
   AnimationEvent(const AtomicString& type,
                  const String& animation_name,
-                 double elapsed_time,
+                 const AnimationTimeDelta& elapsed_time,
                  const String& pseudo_element);
   AnimationEvent(const AtomicString&, const AnimationEventInit*);
   ~AnimationEvent() override;
@@ -68,7 +69,7 @@
 
  private:
   String animation_name_;
-  double elapsed_time_;
+  AnimationTimeDelta elapsed_time_;
   String pseudo_element_;
 };
 
diff --git a/third_party/blink/renderer/core/events/transition_event.cc b/third_party/blink/renderer/core/events/transition_event.cc
index 8b3dd28e..f784087 100644
--- a/third_party/blink/renderer/core/events/transition_event.cc
+++ b/third_party/blink/renderer/core/events/transition_event.cc
@@ -30,11 +30,11 @@
 
 namespace blink {
 
-TransitionEvent::TransitionEvent() : elapsed_time_(0) {}
+TransitionEvent::TransitionEvent() = default;
 
 TransitionEvent::TransitionEvent(const AtomicString& type,
                                  const String& property_name,
-                                 double elapsed_time,
+                                 const AnimationTimeDelta& elapsed_time,
                                  const String& pseudo_element)
     : Event(type, Bubbles::kYes, Cancelable::kYes),
       property_name_(property_name),
@@ -43,11 +43,13 @@
 
 TransitionEvent::TransitionEvent(const AtomicString& type,
                                  const TransitionEventInit* initializer)
-    : Event(type, initializer), elapsed_time_(0) {
+    : Event(type, initializer) {
   if (initializer->hasPropertyName())
     property_name_ = initializer->propertyName();
-  if (initializer->hasElapsedTime())
-    elapsed_time_ = initializer->elapsedTime();
+  if (initializer->hasElapsedTime()) {
+    elapsed_time_ =
+        AnimationTimeDelta::FromSecondsD(initializer->elapsedTime());
+  }
   if (initializer->hasPseudoElement())
     pseudo_element_ = initializer->pseudoElement();
 }
@@ -59,7 +61,7 @@
 }
 
 double TransitionEvent::elapsedTime() const {
-  return elapsed_time_;
+  return elapsed_time_.InSecondsF();
 }
 
 const String& TransitionEvent::pseudoElement() const {
diff --git a/third_party/blink/renderer/core/events/transition_event.h b/third_party/blink/renderer/core/events/transition_event.h
index 4185a60..6038290 100644
--- a/third_party/blink/renderer/core/events/transition_event.h
+++ b/third_party/blink/renderer/core/events/transition_event.h
@@ -27,6 +27,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_TRANSITION_EVENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_TRANSITION_EVENT_H_
 
+#include "third_party/blink/renderer/core/animation/animation_time_delta.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/events/transition_event_init.h"
 
@@ -41,7 +42,7 @@
   }
   static TransitionEvent* Create(const AtomicString& type,
                                  const String& property_name,
-                                 double elapsed_time,
+                                 const AnimationTimeDelta& elapsed_time,
                                  const String& pseudo_element) {
     return MakeGarbageCollected<TransitionEvent>(type, property_name,
                                                  elapsed_time, pseudo_element);
@@ -54,7 +55,7 @@
   TransitionEvent();
   TransitionEvent(const AtomicString& type,
                   const String& property_name,
-                  double elapsed_time,
+                  const AnimationTimeDelta& elapsed_time,
                   const String& pseudo_element);
   TransitionEvent(const AtomicString& type,
                   const TransitionEventInit* initializer);
@@ -70,7 +71,7 @@
 
  private:
   String property_name_;
-  double elapsed_time_;
+  AnimationTimeDelta elapsed_time_;
   String pseudo_element_;
 };
 
diff --git a/third_party/blink/renderer/core/execution_context/window_agent.cc b/third_party/blink/renderer/core/execution_context/window_agent.cc
index 2f8b44bf..ffc29f7 100644
--- a/third_party/blink/renderer/core/execution_context/window_agent.cc
+++ b/third_party/blink/renderer/core/execution_context/window_agent.cc
@@ -4,20 +4,16 @@
 
 #include "third_party/blink/renderer/core/execution_context/window_agent.h"
 
-#include "third_party/blink/renderer/core/dom/mutation_observer_notifier.h"
 
 namespace blink {
 
 WindowAgent::WindowAgent(v8::Isolate* isolate)
-    : Agent(isolate, base::UnguessableToken::Create()),
-      mutation_observer_notifier_(
-          MakeGarbageCollected<MutationObserverNotifier>()) {}
+    : Agent(isolate, base::UnguessableToken::Create()) {}
 
 WindowAgent::~WindowAgent() = default;
 
 void WindowAgent::Trace(Visitor* visitor) {
   Agent::Trace(visitor);
-  visitor->Trace(mutation_observer_notifier_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/execution_context/window_agent.h b/third_party/blink/renderer/core/execution_context/window_agent.h
index 332b4ab..c5d2215 100644
--- a/third_party/blink/renderer/core/execution_context/window_agent.h
+++ b/third_party/blink/renderer/core/execution_context/window_agent.h
@@ -13,8 +13,6 @@
 
 namespace blink {
 
-class MutationObserverNotifier;
-
 // This corresponds to similar-origin window agent, that is shared by a group
 // of Documents that are mutually reachable and have the same-site origins.
 // https://html.spec.whatwg.org/C#similar-origin-window-agent
@@ -39,16 +37,10 @@
 
   void Trace(blink::Visitor*) override;
 
-  MutationObserverNotifier& GetMutationObserverNotifier() {
-    return *mutation_observer_notifier_;
-  }
-
  private:
-  // For MutationObserver.
-  Member<MutationObserverNotifier> mutation_observer_notifier_;
-
-  // TODO(tzik): Move per-agent data here with the correct granularity.
-  // E.g. CustomElementReactionStack should move here.
+  // TODO(keishi): Move per-agent data here with the correct granularity.
+  // E.g. ActiveMutationObservers and CustomElementReactionStack should move
+  // here.
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/BUILD.gn b/third_party/blink/renderer/core/frame/BUILD.gn
index 381effd..109f359 100644
--- a/third_party/blink/renderer/core/frame/BUILD.gn
+++ b/third_party/blink/renderer/core/frame/BUILD.gn
@@ -19,6 +19,7 @@
     "csp/csp_directive_list.h",
     "csp/csp_source.cc",
     "csp/csp_source.h",
+    "csp/csp_violation_report_body.cc",
     "csp/csp_violation_report_body.h",
     "csp/execution_context_csp_delegate.cc",
     "csp/execution_context_csp_delegate.h",
@@ -36,6 +37,7 @@
     "deprecated_schedule_style_recalc_during_layout.h",
     "deprecation.cc",
     "deprecation.h",
+    "deprecation_report_body.cc",
     "deprecation_report_body.h",
     "device_single_window_event_controller.cc",
     "device_single_window_event_controller.h",
@@ -54,6 +56,7 @@
     "event_handler_registry.cc",
     "event_handler_registry.h",
     "external.h",
+    "feature_policy_violation_report_body.cc",
     "feature_policy_violation_report_body.h",
     "find_in_page.cc",
     "find_in_page.h",
@@ -83,6 +86,7 @@
     "hosts_using_features.h",
     "intervention.cc",
     "intervention.h",
+    "intervention_report_body.cc",
     "intervention_report_body.h",
     "layout_subtree_root_list.cc",
     "layout_subtree_root_list.h",
@@ -97,6 +101,7 @@
     "local_frame_view.h",
     "location.cc",
     "location.h",
+    "location_report_body.cc",
     "location_report_body.h",
     "navigation_rate_limiter.cc",
     "navigation_rate_limiter.h",
@@ -146,6 +151,7 @@
     "remote_frame_view.h",
     "report.cc",
     "report.h",
+    "report_body.cc",
     "report_body.h",
     "reporting_context.cc",
     "reporting_context.h",
@@ -171,6 +177,7 @@
     "settings_delegate.h",
     "smart_clip.cc",
     "smart_clip.h",
+    "test_report_body.cc",
     "test_report_body.h",
     "use_counter_helper.cc",
     "use_counter_helper.h",
diff --git a/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.cc b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.cc
new file mode 100644
index 0000000..a552bc3
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.cc
@@ -0,0 +1,21 @@
+// 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 "third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h"
+
+namespace blink {
+
+void CSPViolationReportBody::BuildJSONValue(V8ObjectBuilder& builder) const {
+  LocationReportBody::BuildJSONValue(builder);
+  builder.AddString("documentURL", documentURL());
+  builder.AddStringOrNull("referrer", referrer());
+  builder.AddStringOrNull("blockedURL", blockedURL());
+  builder.AddString("effectiveDirective", effectiveDirective());
+  builder.AddString("originalPolicy", originalPolicy());
+  builder.AddStringOrNull("sample", sample());
+  builder.AddString("disposition", disposition());
+  builder.AddNumber("statusCode", statusCode());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h
index 8573ec36..2e57116 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h
+++ b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_CSP_CSP_VIOLATION_REPORT_BODY_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/events/security_policy_violation_event_init.h"
 #include "third_party/blink/renderer/core/frame/location_report_body.h"
 
@@ -45,6 +46,8 @@
   String disposition() const { return disposition_; }
   uint16_t statusCode() const { return status_code_; }
 
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
+
  private:
   const String document_url_;
   const String referrer_;
diff --git a/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.idl b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.idl
index 0ab4e1d..d65ebecf 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.idl
+++ b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.idl
@@ -18,4 +18,5 @@
   readonly attribute unsigned short statusCode;
   readonly attribute unsigned long? lineNumber;
   readonly attribute unsigned long? columnNumber;
+  [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/deprecation_report_body.cc b/third_party/blink/renderer/core/frame/deprecation_report_body.cc
new file mode 100644
index 0000000..353dcc60
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/deprecation_report_body.cc
@@ -0,0 +1,38 @@
+// 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 "third_party/blink/renderer/core/frame/deprecation_report_body.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+void DeprecationReportBody::BuildJSONValue(V8ObjectBuilder& builder) const {
+  LocationReportBody::BuildJSONValue(builder);
+  builder.AddString("id", id());
+  builder.AddString("message", message());
+
+  bool is_null = false;
+  double anticipated_removal_value = anticipatedRemoval(is_null);
+  if (is_null) {
+    builder.AddNull("anticipatedRemoval");
+  } else {
+    DateComponents anticipated_removal_date;
+    bool is_valid =
+        anticipated_removal_date.SetMillisecondsSinceEpochForDateTimeLocal(
+            anticipated_removal_value);
+    if (!is_valid) {
+      builder.AddNull("anticipatedRemoval");
+    } else {
+      // Adding extra 'Z' here to ensure that the string gives the same result
+      // as JSON.stringify(anticipatedRemoval) in javascript. Note here
+      // anticipatedRemoval will become a Date object in javascript.
+      String iso8601_date =
+          anticipated_removal_date.ToString(DateComponents::kMillisecond) + "Z";
+      builder.AddString("anticipatedRemoval", iso8601_date);
+    }
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/deprecation_report_body.h b/third_party/blink/renderer/core/frame/deprecation_report_body.h
index 2029cd5..7d238996 100644
--- a/third_party/blink/renderer/core/frame/deprecation_report_body.h
+++ b/third_party/blink/renderer/core/frame/deprecation_report_body.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_DEPRECATION_REPORT_BODY_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/frame/location_report_body.h"
 
 namespace blink {
@@ -28,6 +29,8 @@
     return anticipatedRemoval_;
   }
 
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
+
  private:
   const String id_;
   const String message_;
diff --git a/third_party/blink/renderer/core/frame/deprecation_report_body.idl b/third_party/blink/renderer/core/frame/deprecation_report_body.idl
index 5b76ce4..7c9916b4 100644
--- a/third_party/blink/renderer/core/frame/deprecation_report_body.idl
+++ b/third_party/blink/renderer/core/frame/deprecation_report_body.idl
@@ -13,4 +13,5 @@
   readonly attribute DOMString? sourceFile;
   readonly attribute unsigned long? lineNumber;
   readonly attribute unsigned long? columnNumber;
+  [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/deprecation_report_body_test.cc b/third_party/blink/renderer/core/frame/deprecation_report_body_test.cc
new file mode 100644
index 0000000..0f2d5e9
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/deprecation_report_body_test.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 "third_party/blink/renderer/core/frame/deprecation_report_body.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+
+namespace blink {
+
+namespace {
+
+TEST(DeprecationReportBodyJSONTest, noAnticipatedRemoval) {
+  DeprecationReportBody body("test_id", 0, "test_message");
+  V8TestingScope scope;
+  ScriptState* script_state = scope.GetScriptState();
+  V8ObjectBuilder builder(script_state);
+  body.BuildJSONValue(builder);
+  ScriptValue json_object = builder.GetScriptValue();
+  EXPECT_TRUE(json_object.IsObject());
+
+  String json_string = ToBlinkString<String>(
+      v8::JSON::Stringify(scope.GetContext(),
+                          json_object.V8Value().As<v8::Object>())
+          .ToLocalChecked(),
+      kDoNotExternalize);
+
+  String expected =
+      "{\"sourceFile\":null,\"lineNumber\":null,\"columnNumber\":null,\"id\":"
+      "\"test_id\",\"message\":\"test_message\",\"anticipatedRemoval\":null}";
+  EXPECT_EQ(expected, json_string);
+}
+
+TEST(DeprecationReportBodyJSONTest, actualAnticipatedRemoval) {
+  DeprecationReportBody body("test_id", 1575950400000, "test_message");
+  V8TestingScope scope;
+  ScriptState* script_state = scope.GetScriptState();
+  V8ObjectBuilder builder(script_state);
+  body.BuildJSONValue(builder);
+  ScriptValue json_object = builder.GetScriptValue();
+  EXPECT_TRUE(json_object.IsObject());
+
+  String json_string = ToBlinkString<String>(
+      v8::JSON::Stringify(scope.GetContext(),
+                          json_object.V8Value().As<v8::Object>())
+          .ToLocalChecked(),
+      kDoNotExternalize);
+
+  String expected =
+      "{\"sourceFile\":null,\"lineNumber\":null,\"columnNumber\":null,\"id\":"
+      "\"test_id\",\"message\":\"test_message\",\"anticipatedRemoval\":\"2019-"
+      "12-10T04:00:00.000Z\"}";
+  EXPECT_EQ(expected, json_string);
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.cc b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.cc
new file mode 100644
index 0000000..8edb646
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.cc
@@ -0,0 +1,17 @@
+// 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 "third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h"
+
+namespace blink {
+
+void FeaturePolicyViolationReportBody::BuildJSONValue(
+    V8ObjectBuilder& builder) const {
+  LocationReportBody::BuildJSONValue(builder);
+  builder.AddString("featureId", featureId());
+  builder.AddString("disposition", disposition());
+  builder.AddStringOrNull("message", message());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h
index 430da4f6d..85251a46 100644
--- a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h
+++ b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FEATURE_POLICY_VIOLATION_REPORT_BODY_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/frame/location_report_body.h"
 
 namespace blink {
@@ -33,6 +34,7 @@
   String featureId() const { return feature_id_; }
   String disposition() const { return disposition_; }
   String message() const { return message_; }
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
 
   ~FeaturePolicyViolationReportBody() override = default;
 
diff --git a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl
index b5184ea..dca179c 100644
--- a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl
+++ b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl
@@ -13,4 +13,5 @@
   readonly attribute unsigned long? columnNumber;
   readonly attribute DOMString disposition;
   readonly attribute DOMString? message;
+  [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/intervention_report_body.cc b/third_party/blink/renderer/core/frame/intervention_report_body.cc
new file mode 100644
index 0000000..6ad9854
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/intervention_report_body.cc
@@ -0,0 +1,15 @@
+// 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 "third_party/blink/renderer/core/frame/intervention_report_body.h"
+
+namespace blink {
+
+void InterventionReportBody::BuildJSONValue(V8ObjectBuilder& builder) const {
+  LocationReportBody::BuildJSONValue(builder);
+  builder.AddString("id", id());
+  builder.AddString("message", message());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/intervention_report_body.h b/third_party/blink/renderer/core/frame/intervention_report_body.h
index 873a319..3b7fdfb 100644
--- a/third_party/blink/renderer/core/frame/intervention_report_body.h
+++ b/third_party/blink/renderer/core/frame/intervention_report_body.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_INTERVENTION_REPORT_BODY_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/frame/location_report_body.h"
 
 namespace blink {
@@ -21,6 +22,7 @@
 
   String id() const { return id_; }
   String message() const { return message_; }
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
 
  private:
   const String id_;
diff --git a/third_party/blink/renderer/core/frame/intervention_report_body.idl b/third_party/blink/renderer/core/frame/intervention_report_body.idl
index 156fbc2..1626e2a 100644
--- a/third_party/blink/renderer/core/frame/intervention_report_body.idl
+++ b/third_party/blink/renderer/core/frame/intervention_report_body.idl
@@ -12,4 +12,5 @@
   readonly attribute DOMString? sourceFile;
   readonly attribute unsigned long? lineNumber;
   readonly attribute unsigned long? columnNumber;
+  [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/location_report_body.cc b/third_party/blink/renderer/core/frame/location_report_body.cc
new file mode 100644
index 0000000..6525fb03
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/location_report_body.cc
@@ -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.
+
+#include "third_party/blink/renderer/core/frame/location_report_body.h"
+
+namespace blink {
+
+void LocationReportBody::BuildJSONValue(V8ObjectBuilder& builder) const {
+  builder.AddStringOrNull("sourceFile", sourceFile());
+  bool is_null = false;
+  uint32_t line_number = lineNumber(is_null);
+  if (is_null) {
+    builder.AddNull("lineNumber");
+  } else {
+    builder.AddNumber("lineNumber", line_number);
+  }
+  is_null = true;
+  uint32_t column_number = columnNumber(is_null);
+  if (is_null) {
+    builder.AddNull("columnNumber");
+  } else {
+    builder.AddNumber("columnNumber", column_number);
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/location_report_body.h b/third_party/blink/renderer/core/frame/location_report_body.h
index 5a87d761..208ea7b 100644
--- a/third_party/blink/renderer/core/frame/location_report_body.h
+++ b/third_party/blink/renderer/core/frame/location_report_body.h
@@ -9,11 +9,12 @@
 #include <utility>
 
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/frame/report_body.h"
 
 namespace blink {
 
-class LocationReportBody : public ReportBody {
+class CORE_EXPORT LocationReportBody : public ReportBody {
  public:
   explicit LocationReportBody(std::unique_ptr<SourceLocation> location)
       : source_file_(location->Url()),
@@ -47,6 +48,8 @@
     return column_number_.value_or(0);
   }
 
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
+
  protected:
   const String source_file_;
   base::Optional<uint32_t> line_number_;
diff --git a/third_party/blink/renderer/core/frame/report.cc b/third_party/blink/renderer/core/frame/report.cc
index 38b7c5e0..46d331d 100644
--- a/third_party/blink/renderer/core/frame/report.cc
+++ b/third_party/blink/renderer/core/frame/report.cc
@@ -11,4 +11,14 @@
 constexpr const char ReportType::kIntervention[];
 constexpr const char ReportType::kCSPViolation[];
 
+ScriptValue Report::toJSON(ScriptState* script_state) const {
+  V8ObjectBuilder builder(script_state);
+  builder.AddString("type", type());
+  builder.AddString("url", url());
+  V8ObjectBuilder body_builder(script_state);
+  body()->BuildJSONValue(body_builder);
+  builder.Add("body", body_builder);
+  return builder.GetScriptValue();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/report.h b/third_party/blink/renderer/core/frame/report.h
index 3aba915a..3cadb41 100644
--- a/third_party/blink/renderer/core/frame/report.h
+++ b/third_party/blink/renderer/core/frame/report.h
@@ -39,6 +39,8 @@
     ScriptWrappable::Trace(visitor);
   }
 
+  ScriptValue toJSON(ScriptState* script_state) const;
+
  private:
   const String type_;
   const String url_;
diff --git a/third_party/blink/renderer/core/frame/report.idl b/third_party/blink/renderer/core/frame/report.idl
index 40d70444..2178073 100644
--- a/third_party/blink/renderer/core/frame/report.idl
+++ b/third_party/blink/renderer/core/frame/report.idl
@@ -10,4 +10,5 @@
     readonly attribute DOMString type;
     readonly attribute DOMString url;
     readonly attribute ReportBody? body;
+    [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/report_body.cc b/third_party/blink/renderer/core/frame/report_body.cc
new file mode 100644
index 0000000..91f8e26d
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/report_body.cc
@@ -0,0 +1,15 @@
+// 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 "third_party/blink/renderer/core/frame/report_body.h"
+
+namespace blink {
+
+ScriptValue ReportBody::toJSON(ScriptState* script_state) const {
+  V8ObjectBuilder result(script_state);
+  BuildJSONValue(result);
+  return result.GetScriptValue();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/report_body.h b/third_party/blink/renderer/core/frame/report_body.h
index 82a6dae..b1dfeee 100644
--- a/third_party/blink/renderer/core/frame/report_body.h
+++ b/third_party/blink/renderer/core/frame/report_body.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_REPORT_BODY_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_REPORT_BODY_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 namespace blink {
@@ -14,6 +15,11 @@
 
  public:
   ~ReportBody() override = default;
+
+  ScriptValue toJSON(ScriptState* script_state) const;
+
+  // This function is public for use in Report::toJSON
+  virtual void BuildJSONValue(V8ObjectBuilder& builder) const = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/report_body.idl b/third_party/blink/renderer/core/frame/report_body.idl
index b11ae3f..022f160 100644
--- a/third_party/blink/renderer/core/frame/report_body.idl
+++ b/third_party/blink/renderer/core/frame/report_body.idl
@@ -7,4 +7,5 @@
 [
     NoInterfaceObject
 ] interface ReportBody {
+    [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/test_report_body.cc b/third_party/blink/renderer/core/frame/test_report_body.cc
new file mode 100644
index 0000000..99c0eba
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/test_report_body.cc
@@ -0,0 +1,13 @@
+// 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 "third_party/blink/renderer/core/frame/test_report_body.h"
+
+namespace blink {
+
+void TestReportBody::BuildJSONValue(V8ObjectBuilder& builder) const {
+  builder.AddString("message", message());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/test_report_body.h b/third_party/blink/renderer/core/frame/test_report_body.h
index 4ce9240..7e60bc8e 100644
--- a/third_party/blink/renderer/core/frame/test_report_body.h
+++ b/third_party/blink/renderer/core/frame/test_report_body.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_TEST_REPORT_BODY_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_TEST_REPORT_BODY_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/frame/report_body.h"
 
 namespace blink {
@@ -19,6 +20,8 @@
 
   String message() const { return message_; }
 
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
+
  private:
   const String message_;
 };
diff --git a/third_party/blink/renderer/core/frame/test_report_body.idl b/third_party/blink/renderer/core/frame/test_report_body.idl
index 2bffa24..afbd820 100644
--- a/third_party/blink/renderer/core/frame/test_report_body.idl
+++ b/third_party/blink/renderer/core/frame/test_report_body.idl
@@ -8,4 +8,5 @@
     NoInterfaceObject
 ] interface TestReportBody : ReportBody {
   readonly attribute DOMString message;
+  [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
index b852281..336e6204 100644
--- a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
@@ -277,22 +277,11 @@
   // tree, as the snap_container is found by walking up the layout tree in
   // FindSnapContainer(). Under this assumption,
   // snap_area.LocalToAncestorRect() returns the snap_area's position relative
-  // to its container's border box. And the |area| below represents the
-  // snap_area rect in respect to the snap_container.
+  // to the snap container's border box, while ignoring scroll offset.
   PhysicalRect area_rect = snap_area.PhysicalBorderBoxRect();
-  area_rect = snap_area.LocalToAncestorRect(area_rect, &snap_container,
-                                            kTraverseDocumentBoundaries);
-  ScrollableArea* scrollable_area =
-      ScrollableArea::GetForScrolling(&snap_container);
-
-  if (scrollable_area) {
-    if (snap_container.IsLayoutView()) {
-      area_rect = snap_container.GetFrameView()->FrameToDocument(area_rect);
-    } else {
-      area_rect.Move(PhysicalOffset::FromFloatPointRound(
-          scrollable_area->ScrollPosition()));
-    }
-  }
+  area_rect = snap_area.LocalToAncestorRect(
+      area_rect, &snap_container,
+      kTraverseDocumentBoundaries | kIgnoreScrollOffset);
 
   LayoutRectOutsets area_margin(
       area_style->ScrollMarginTop(), area_style->ScrollMarginRight(),
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 3d527be..9b17ea3c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -2424,12 +2424,7 @@
     return false;
 
   // Step 2B from: http://www.w3.org/TR/accname-aam-1.1
-  bool is_null = false;
-  HeapVector<Member<Element>> elements = GetElement()->GetElementArrayAttribute(
-      html_names::kAriaLabelledbyAttr, is_null);
-  if (!is_null && elements.size() > 1)
-    return false;
-
+  HeapVector<Member<Element>> elements;
   Vector<String> ids;
   AriaLabelledbyElementVector(elements, ids);
   if (ids.size() > 0)
@@ -3033,17 +3028,6 @@
     return;
   }
 
-  Element* element = GetElement();
-  if (element && element->HasExplicitlySetAttrAssociatedElements(
-                     html_names::kAriaOwnsAttr)) {
-    bool is_null = false;
-    AXObjectCache().UpdateAriaOwnsFromAttrAssociatedElements(
-        this,
-        element->GetElementArrayAttribute(html_names::kAriaOwnsAttr, is_null),
-        owned_children);
-    return;
-  }
-
   if (!HasAttribute(html_names::kAriaOwnsAttr))
     return;
 
@@ -3614,43 +3598,28 @@
 
   // aria-describedby overrides any other accessible description, from:
   // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html
-  bool is_null = false;
-  Element* element = GetElement();
-  if (element) {
-    HeapVector<Member<Element>> attr_associated_elements =
-        GetElement()->GetElementArrayAttribute(html_names::kAriaDescribedbyAttr,
-                                               is_null);
-    if (!is_null) {
-      // TODO(meredithl): Determine description sources when |aria_describedby|
-      // is the empty string.
+  const AtomicString& aria_describedby =
+      GetAttribute(html_names::kAriaDescribedbyAttr);
+  if (!aria_describedby.IsNull()) {
+    if (description_sources)
+      description_sources->back().attribute_value = aria_describedby;
+
+    Vector<String> ids;
+    description = TextFromAriaDescribedby(related_objects, ids);
+    AXObjectCache().UpdateReverseRelations(this, ids);
+
+    if (!description.IsNull()) {
       if (description_sources) {
-        description_sources->back().attribute_value =
-            GetAttribute(html_names::kAriaDescribedbyAttr);
+        DescriptionSource& source = description_sources->back();
+        source.type = description_from;
+        source.related_objects = *related_objects;
+        source.text = description;
+        found_description = true;
+      } else {
+        return description;
       }
-      AXObjectSet visited;
-      description = TextFromElements(true, visited, attr_associated_elements,
-                                     related_objects);
-
-      Vector<String> ids;
-      for (auto& element : attr_associated_elements)
-        ids.push_back(element->GetIdAttribute());
-
-      TokenVectorFromAttribute(ids, html_names::kAriaDescribedbyAttr);
-      AXObjectCache().UpdateReverseRelations(this, ids);
-
-      if (!description.IsNull()) {
-        if (description_sources) {
-          DescriptionSource& source = description_sources->back();
-          source.type = description_from;
-          source.related_objects = *related_objects;
-          source.text = description;
-          found_description = true;
-        } else {
-          return description;
-        }
-      } else if (description_sources) {
-        description_sources->back().invalid = true;
-      }
+    } else if (description_sources) {
+      description_sources->back().invalid = true;
     }
   }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index e1d91a7b..1fc81de 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -1691,43 +1691,33 @@
       name_sources->back().type = name_from;
     }
 
-    bool is_null = false;
-    Element* element = GetElement();
-    if (element) {
-      HeapVector<Member<Element>> attr_associated_elements =
-          element->GetElementArrayAttribute(attr, is_null);
-      const AtomicString& aria_labelledby = GetAttribute(attr);
+    const AtomicString& aria_labelledby = GetAttribute(attr);
+    if (!aria_labelledby.IsNull()) {
+      if (name_sources)
+        name_sources->back().attribute_value = aria_labelledby;
 
-      if (!aria_labelledby.IsNull()) {
-        if (name_sources)
-          name_sources->back().attribute_value = aria_labelledby;
-
-        // Operate on a copy of |visited| so that if |nameSources| is not
-        // null, the set of visited objects is preserved unmodified for future
-        // calculations.
-        AXObjectSet visited_copy = visited;
-        Vector<String> ids;
-        for (auto& element : attr_associated_elements)
-          ids.push_back(element->GetIdAttribute());
-
-        text_alternative = TextFromElements(
-            true, visited, attr_associated_elements, related_objects);
-        if (!ids.IsEmpty())
-          AXObjectCache().UpdateReverseRelations(this, ids);
-        if (!text_alternative.IsNull()) {
-          if (name_sources) {
-            NameSource& source = name_sources->back();
-            source.type = name_from;
-            source.related_objects = *related_objects;
-            source.text = text_alternative;
-            *found_text_alternative = true;
-          } else {
-            *found_text_alternative = true;
-            return text_alternative;
-          }
-        } else if (name_sources) {
-          name_sources->back().invalid = true;
+      // Operate on a copy of |visited| so that if |nameSources| is not null,
+      // the set of visited objects is preserved unmodified for future
+      // calculations.
+      AXObjectSet visited_copy = visited;
+      Vector<String> ids;
+      text_alternative =
+          TextFromAriaLabelledby(visited_copy, related_objects, ids);
+      if (!ids.IsEmpty())
+        AXObjectCache().UpdateReverseRelations(this, ids);
+      if (!text_alternative.IsNull()) {
+        if (name_sources) {
+          NameSource& source = name_sources->back();
+          source.type = name_from;
+          source.related_objects = *related_objects;
+          source.text = text_alternative;
+          *found_text_alternative = true;
+        } else {
+          *found_text_alternative = true;
+          return text_alternative;
         }
+      } else if (name_sources) {
+        name_sources->back().invalid = true;
       }
     }
   }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 6aecf2a..c850c45 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -1081,14 +1081,6 @@
   relation_cache_->UpdateAriaOwns(owner, id_vector, owned_children);
 }
 
-void AXObjectCacheImpl::UpdateAriaOwnsFromAttrAssociatedElements(
-    const AXObject* owner,
-    const HeapVector<Member<Element>>& attr_associated_elements,
-    HeapVector<Member<AXObject>>& owned_children) {
-  relation_cache_->UpdateAriaOwnsFromAttrAssociatedElements(
-      owner, attr_associated_elements, owned_children);
-}
-
 bool AXObjectCacheImpl::MayHaveHTMLLabel(const HTMLElement& elem) {
   // Return false if this type of element will not accept a <label for> label.
   if (!elem.IsLabelable())
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
index f25d4cd..99e3fe4 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -239,16 +239,6 @@
                       const Vector<String>& id_vector,
                       HeapVector<Member<AXObject>>& owned_children);
 
-  // Given an object that has explicitly set elements for aria-owns, update the
-  // internal state to reflect the new set of children owned by this object.
-  // Note that |owned_children| will be the AXObjects corresponding to the
-  // elements in |attr_associated_elements|. These elements are validated -
-  // exist in the DOM, and are a descendant of a shadow including ancestor.
-  void UpdateAriaOwnsFromAttrAssociatedElements(
-      const AXObject* owner,
-      const HeapVector<Member<Element>>& attr_associated_elements,
-      HeapVector<Member<AXObject>>& owned_children);
-
   bool MayHaveHTMLLabel(const HTMLElement& elem);
 
   // Synchronously returns whether or not we currently have permission to
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
index e029471..2787cb8 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -143,20 +143,6 @@
   }
 }
 
-void AXRelationCache::UpdateAriaOwnsFromAttrAssociatedElements(
-    const AXObject* owner,
-    const HeapVector<Member<Element>>& attr_associated_elements,
-    HeapVector<Member<AXObject>>& validated_owned_children_result) {
-  // attr-associated-elements are already validated, so just copy them into the
-  // validated map.
-  for (auto& e : attr_associated_elements)
-    validated_owned_children_result.push_back(GetOrCreate(e));
-
-  // Update the internal mappings of owned children.
-  CompareValidatedChildrenToOwnedChildren(owner,
-                                          validated_owned_children_result);
-}
-
 void AXRelationCache::UpdateAriaOwns(
     const AXObject* owner,
     const Vector<String>& owned_id_vector,
@@ -164,6 +150,7 @@
   // Track reverse relations for future tree updates.
   UpdateReverseRelations(owner, owned_id_vector);
 
+  //
   // Figure out the ids that actually correspond to children that exist
   // and that we can legally own (not cyclical, not already owned, etc.) and
   // update the maps and |validated_owned_children_result| based on that.
@@ -171,25 +158,16 @@
   // Figure out the children that are owned by this object and are in the
   // tree.
   TreeScope& scope = owner->GetNode()->GetTreeScope();
+  Vector<AXID> validated_owned_child_axids;
   for (const String& id_name : owned_id_vector) {
     Element* element = scope.getElementById(AtomicString(id_name));
     AXObject* child = GetOrCreate(element);
-    if (IsValidOwnsRelation(const_cast<AXObject*>(owner), child))
+    if (IsValidOwnsRelation(const_cast<AXObject*>(owner), child)) {
+      validated_owned_child_axids.push_back(child->AXObjectID());
       validated_owned_children_result.push_back(child);
+    }
   }
 
-  // Update the internal validated mapping of owned children.
-  CompareValidatedChildrenToOwnedChildren(owner,
-                                          validated_owned_children_result);
-}
-
-void AXRelationCache::CompareValidatedChildrenToOwnedChildren(
-    const AXObject* owner,
-    HeapVector<Member<AXObject>>& validated_owned_children_result) {
-  Vector<AXID> validated_owned_child_axids;
-  for (auto& child : validated_owned_children_result)
-    validated_owned_child_axids.push_back(child->AXObjectID());
-
   // Compare this to the current list of owned children, and exit early if
   // there are no changes.
   Vector<AXID> current_child_axids =
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
index acfc1819..2dec5cfb 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
@@ -41,16 +41,6 @@
                       const Vector<String>& id_vector,
                       HeapVector<Member<AXObject>>& owned_children);
 
-  // Given an object that has explicitly set elements for aria-owns, update the
-  // internal state to reflect the new set of children owned by this object.
-  // Note that |owned_children| will be the AXObjects corresponding to the
-  // elements in |attr_associated_elements|. These elements are validated -
-  // exist in the DOM, and are a descendant of a shadow including ancestor.
-  void UpdateAriaOwnsFromAttrAssociatedElements(
-      const AXObject* owner,
-      const HeapVector<Member<Element>>& attr_associated_elements,
-      HeapVector<Member<AXObject>>& owned_children);
-
   // Return true if any label ever pointed to the element via the for attribute.
   bool MayHaveHTMLLabelViaForAttribute(const HTMLElement&);
 
@@ -82,12 +72,6 @@
   void MapOwnedChildren(const AXObject* owner, Vector<AXID>);
   void GetReverseRelated(Node*, HeapVector<Member<AXObject>>& sources);
 
-  // Updates |aria_owner_to_children_mapping_| after calling UpdateAriaOwns for
-  // either the content attribute or the attr associated elements.
-  void CompareValidatedChildrenToOwnedChildren(
-      const AXObject* owner,
-      HeapVector<Member<AXObject>>& validated_owned_children_result);
-
   WeakPersistent<AXObjectCacheImpl> object_cache_;
 
   // Map from the AXID of the owner to the AXIDs of the children.
diff --git a/third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.cc b/third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.cc
index e51a7fe..0999dae 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.h"
-#include "third_party/blink/renderer/core/dom/qualified_name.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
 
 namespace blink {
@@ -74,16 +73,6 @@
 
  private:
   AXObjectAttribute attribute_;
-  QualifiedName GetAttributeQualifiedName() {
-    if (attribute_ == AXObjectAttribute::kAriaActiveDescendant)
-      return html_names::kAriaActivedescendantAttr;
-    if (attribute_ == AXObjectAttribute::kAriaDetails)
-      return html_names::kAriaDetailsAttr;
-    if (attribute_ == AXObjectAttribute::kAriaErrorMessage)
-      return html_names::kAriaErrormessageAttr;
-    NOTREACHED();
-    return g_null_name;
-  }
 
   void Run(const AXObject& obj,
            AXSparseAttributeClient& attribute_map,
@@ -94,8 +83,7 @@
     auto* element = DynamicTo<Element>(obj.GetNode());
     if (!element)
       return;
-    const QualifiedName& q_name = GetAttributeQualifiedName();
-    Element* target = element->GetElementAttribute(q_name);
+    Element* target = element->GetTreeScope().getElementById(value);
     if (!target)
       return;
     AXObject* ax_target = obj.AXObjectCache().GetOrCreate(target);
@@ -112,37 +100,36 @@
  private:
   AXObjectVectorAttribute attribute_;
 
-  QualifiedName GetAttributeQualifiedName() {
-    if (attribute_ == AXObjectVectorAttribute::kAriaControls)
-      return html_names::kAriaControlsAttr;
-    if (attribute_ == AXObjectVectorAttribute::kAriaFlowTo)
-      return html_names::kAriaFlowtoAttr;
-    NOTREACHED();
-    return g_null_name;
-  }
-
   void Run(const AXObject& obj,
            AXSparseAttributeClient& attribute_map,
            const AtomicString& value) override {
-    Element* element = DynamicTo<Element>(obj.GetNode());
-    if (!element)
+    Node* node = obj.GetNode();
+    if (!node || !node->IsElementNode())
       return;
 
-    bool is_null = false;
-    HeapVector<Member<Element>> attr_associated_elements =
-        element->GetElementArrayAttribute(GetAttributeQualifiedName(), is_null);
-    if (is_null)
+    String attribute_value = value.GetString();
+    if (attribute_value.IsEmpty())
       return;
+
+    Vector<String> ids;
+    attribute_value.Split(' ', ids);
+    if (ids.IsEmpty())
+      return;
+
     HeapVector<Member<AXObject>> objects;
-    for (const auto& reflected_element : attr_associated_elements) {
-      AXObject* ax_element = obj.AXObjectCache().GetOrCreate(reflected_element);
-      if (!ax_element)
-        continue;
-      if (AXObject* parent = ax_element->ParentObject())
-        parent->UpdateChildrenIfNecessary();
-      if (!ax_element->AccessibilityIsIgnored())
-        objects.push_back(ax_element);
+    TreeScope& scope = node->GetTreeScope();
+    for (const auto& id : ids) {
+      if (Element* id_element = scope.getElementById(AtomicString(id))) {
+        AXObject* ax_id_element = obj.AXObjectCache().GetOrCreate(id_element);
+        if (!ax_id_element)
+          continue;
+        if (AXObject* parent = ax_id_element->ParentObject())
+          parent->UpdateChildrenIfNecessary();
+        if (!ax_id_element->AccessibilityIsIgnored())
+          objects.push_back(ax_id_element);
+      }
     }
+
     attribute_map.AddObjectVectorAttribute(attribute_, objects);
   }
 };
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
index 22bada4b..d8b6c82f 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
@@ -235,7 +235,8 @@
   if (!(video_frame->format() == media::PIXEL_FORMAT_I420 ||
         video_frame->format() == media::PIXEL_FORMAT_ARGB ||
         video_frame->format() == media::PIXEL_FORMAT_I420A ||
-        video_frame->format() == media::PIXEL_FORMAT_NV12)) {
+        video_frame->format() == media::PIXEL_FORMAT_NV12 ||
+        video_frame->format() == media::PIXEL_FORMAT_XRGB)) {
     NOTREACHED() << media::VideoPixelFormatToString(video_frame->format());
     return;
   }
@@ -299,8 +300,13 @@
   } else {
     // Accelerated decoders produce ARGB/ABGR texture-backed frames (see
     // https://crbug.com/585242), fetch them using a PaintCanvasVideoRenderer.
+    // Additionally, Macintosh accelerated decoders can produce XRGB content
+    // and are treated the same way.
+    //
+    // TODO(crbug/1023390): Add browsertest for these.
     DCHECK(video_frame->HasTextures());
-    DCHECK_EQ(media::PIXEL_FORMAT_ARGB, video_frame->format());
+    DCHECK(video_frame->format() == media::PIXEL_FORMAT_ARGB ||
+           video_frame->format() == media::PIXEL_FORMAT_XRGB);
 
     const gfx::Size& old_visible_size = video_frame->visible_rect().size();
     gfx::Size new_visible_size = old_visible_size;
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
index d6cfb2f..addc9453 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <algorithm>
+#include <array>
 #include <limits>
 #include <string>
 #include <utility>
@@ -23,6 +24,7 @@
 #include "media/base/audio_fifo.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/channel_layout.h"
+#include "media/base/limits.h"
 #include "media/webrtc/helpers.h"
 #include "media/webrtc/webrtc_switches.h"
 #include "third_party/blink/public/platform/platform.h"
@@ -31,7 +33,6 @@
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
 #include "third_party/webrtc/api/audio/echo_canceller3_config.h"
 #include "third_party/webrtc/api/audio/echo_canceller3_config_json.h"
 #include "third_party/webrtc/api/audio/echo_canceller3_factory.h"
@@ -428,21 +429,16 @@
             std::numeric_limits<base::subtle::Atomic32>::max());
   base::subtle::Release_Store(&render_delay_ms_, audio_delay_milliseconds);
 
-  // Limit the number of channels to two (stereo) now when multi-channel audio
-  // sources are supported. We still want to prevent the AEC from "seeing" the
-  // full signal.
-  // TODO(crbug.com/982276): process all channels when multi-channel AEC is
-  // supported.
-  int channels = std::min(2, audio_bus->channels());
-
-  Vector<const float*> channel_ptrs(channels);
-  for (int i = 0; i < channels; ++i)
-    channel_ptrs[i] = audio_bus->channel(i);
+  DCHECK_LE(audio_bus->channels(), media::limits::kMaxChannels);
+  std::array<const float*, media::limits::kMaxChannels> input_ptrs;
+  for (int i = 0; i < audio_bus->channels(); ++i)
+    input_ptrs[i] = audio_bus->channel(i);
 
   // TODO(ajm): Should AnalyzeReverseStream() account for the
   // |audio_delay_milliseconds|?
   const int apm_error = audio_processing_->AnalyzeReverseStream(
-      channel_ptrs.data(), webrtc::StreamConfig(sample_rate, channels));
+      input_ptrs.data(),
+      webrtc::StreamConfig(sample_rate, audio_bus->channels()));
   if (apm_error != webrtc::AudioProcessing::kNoError &&
       apm_playout_error_code_log_count_ < 10) {
     LOG(ERROR) << "MSAP::OnPlayoutData: AnalyzeReverseStream error="
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc
index 15d3b352..add66a2 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc
@@ -47,8 +47,6 @@
 // The number of packers used for testing.
 const int kNumberOfPacketsForTest = 100;
 
-const int kMaxNumberOfPlayoutDataChannels = 2;
-
 void ReadDataFromSpeechFile(char* data, int length) {
   base::FilePath file;
   CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &file));
@@ -91,14 +89,15 @@
     std::unique_ptr<media::AudioBus> data_bus =
         media::AudioBus::Create(params.channels(), params.frames_per_buffer());
 
-    // |data_bus_playout| is used if the number of capture channels is larger
-    // that max allowed playout channels. |data_bus_playout_to_use| points to
-    // the AudioBus to use, either |data_bus| or |data_bus_playout|.
+    // |data_bus_playout| is used if the capture channels include a keyboard
+    // channel. |data_bus_playout_to_use| points to the AudioBus to use, either
+    // |data_bus| or |data_bus_playout|.
     std::unique_ptr<media::AudioBus> data_bus_playout;
     media::AudioBus* data_bus_playout_to_use = data_bus.get();
-    if (params.channels() > kMaxNumberOfPlayoutDataChannels) {
-      data_bus_playout =
-          media::AudioBus::CreateWrapper(kMaxNumberOfPlayoutDataChannels);
+    const bool has_keyboard_mic = params.channel_layout() ==
+                                  media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC;
+    if (has_keyboard_mic) {
+      data_bus_playout = media::AudioBus::CreateWrapper(2);
       data_bus_playout->set_frames(params.frames_per_buffer());
       data_bus_playout_to_use = data_bus_playout.get();
     }
@@ -118,8 +117,8 @@
       webrtc::AudioProcessing* ap = audio_processor->audio_processing_.get();
       const bool is_aec_enabled = ap && ap->GetConfig().echo_canceller.enabled;
       if (is_aec_enabled) {
-        if (params.channels() > kMaxNumberOfPlayoutDataChannels) {
-          for (int i = 0; i < kMaxNumberOfPlayoutDataChannels; ++i) {
+        if (has_keyboard_mic) {
+          for (int i = 0; i < data_bus_playout->channels(); ++i) {
             data_bus_playout->SetChannelData(
                 i, const_cast<float*>(data_bus->channel(i)));
           }
diff --git a/third_party/blink/renderer/modules/xr/type_converters.cc b/third_party/blink/renderer/modules/xr/type_converters.cc
index 7a22a4f..2a5079c 100644
--- a/third_party/blink/renderer/modules/xr/type_converters.cc
+++ b/third_party/blink/renderer/modules/xr/type_converters.cc
@@ -57,6 +57,35 @@
   return result;
 }
 
+blink::TransformationMatrix
+TypeConverter<blink::TransformationMatrix, device::mojom::blink::PosePtr>::
+    Convert(const device::mojom::blink::PosePtr& pose) {
+  DCHECK(pose);
+
+  blink::TransformationMatrix result;
+  blink::TransformationMatrix::DecomposedType decomp = {};
+
+  decomp.perspective_w = 1;
+  decomp.scale_x = 1;
+  decomp.scale_y = 1;
+  decomp.scale_z = 1;
+
+  // TODO(https://crbug.com/929841): Remove negation once the bug is fixed.
+  gfx::Quaternion quat = pose->orientation.inverse();
+  decomp.quaternion_x = quat.x();
+  decomp.quaternion_y = quat.y();
+  decomp.quaternion_z = quat.z();
+  decomp.quaternion_w = quat.w();
+
+  decomp.translate_x = pose->position.X();
+  decomp.translate_y = pose->position.Y();
+  decomp.translate_z = pose->position.Z();
+
+  result.Recompose(decomp);
+
+  return result;
+}
+
 blink::HeapVector<blink::Member<blink::DOMPointReadOnly>>
 TypeConverter<blink::HeapVector<blink::Member<blink::DOMPointReadOnly>>,
               WTF::Vector<device::mojom::blink::XRPlanePointDataPtr>>::
diff --git a/third_party/blink/renderer/modules/xr/type_converters.h b/third_party/blink/renderer/modules/xr/type_converters.h
index 700d7ec..f8050b1 100644
--- a/third_party/blink/renderer/modules/xr/type_converters.h
+++ b/third_party/blink/renderer/modules/xr/type_converters.h
@@ -27,6 +27,13 @@
 };
 
 template <>
+struct TypeConverter<blink::TransformationMatrix,
+                     device::mojom::blink::PosePtr> {
+  static blink::TransformationMatrix Convert(
+      const device::mojom::blink::PosePtr& pose);
+};
+
+template <>
 struct TypeConverter<blink::HeapVector<blink::Member<blink::DOMPointReadOnly>>,
                      WTF::Vector<device::mojom::blink::XRPlanePointDataPtr>> {
   static blink::HeapVector<blink::Member<blink::DOMPointReadOnly>> Convert(
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index 9ad0039..c094840 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -505,19 +505,17 @@
     return ScriptPromise();
   }
 
-  device::mojom::blink::VRPosePtr pose_ptr =
-      device::mojom::blink::VRPose::New();
-
-  pose_ptr->orientation =
+  // TODO(https://crbug.com/929841): Remove negation in quaternion once the bug
+  // is fixed.
+  device::mojom::blink::PosePtr pose_ptr = device::mojom::blink::Pose::New(
       gfx::Quaternion(-decomposed.quaternion_x, -decomposed.quaternion_y,
-                      -decomposed.quaternion_z, decomposed.quaternion_w);
-  pose_ptr->position = blink::FloatPoint3D(
-      decomposed.translate_x, decomposed.translate_y, decomposed.translate_z);
+                      -decomposed.quaternion_z, decomposed.quaternion_w),
+      blink::FloatPoint3D(decomposed.translate_x, decomposed.translate_y,
+                          decomposed.translate_z));
 
   DVLOG(3) << __func__
-           << ": pose_ptr->orientation = " << pose_ptr->orientation->ToString()
-           << ", pose_ptr->position = [" << pose_ptr->position->X() << ", "
-           << pose_ptr->position->Y() << ", " << pose_ptr->position->Z() << "]";
+           << ": pose_ptr->orientation = " << pose_ptr->orientation.ToString()
+           << ", pose_ptr->position = " << pose_ptr->position.ToString();
 
   if (plane) {
     xr_->xrEnvironmentProviderRemote()->CreatePlaneAnchor(
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
index eb83c1a..1bc2794 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
@@ -172,7 +172,8 @@
   // Ends the effect on the top of the state stack if the stack is not empty,
   // and update the bounds of the SaveLayer[Alpha]Op of the effect.
   void EndEffect();
-  void UpdateEffectBounds(const FloatRect&, const TransformPaintPropertyNode&);
+  void UpdateEffectBounds(const base::Optional<FloatRect>&,
+                          const TransformPaintPropertyNode&);
 
   // Starts a clip state by adjusting the transform state, applying
   // |combined_clip_rect| which is combined from one or more consecutive clips,
@@ -243,7 +244,7 @@
     // Records the bounds of the effect which initiated the entry. Note that
     // the effect is not |this->effect| (which is the previous effect), but the
     // |current_effect_| when this entry is the top of the stack.
-    FloatRect bounds;
+    base::Optional<FloatRect> bounds;
   };
   Vector<EffectBoundsInfo> effect_bounds_stack_;
 
@@ -589,22 +590,25 @@
   const ClipPaintPropertyNode* input_clip = current_clip_;
   PushState(StateEntry::kEffect, saved_count);
   effect_bounds_stack_.emplace_back(
-      EffectBoundsInfo{save_layer_id, current_transform_});
+      EffectBoundsInfo{save_layer_id, current_transform_, base::nullopt});
   current_clip_ = input_clip;
   current_effect_ = &effect;
 }
 
 void ConversionContext::UpdateEffectBounds(
-    const FloatRect& bounds,
+    const base::Optional<FloatRect>& bounds,
     const TransformPaintPropertyNode& transform) {
-  if (effect_bounds_stack_.IsEmpty() || bounds.IsEmpty())
+  if (effect_bounds_stack_.IsEmpty() || !bounds)
     return;
 
   auto& effect_bounds_info = effect_bounds_stack_.back();
-  FloatRect mapped_bounds = bounds;
+  FloatRect mapped_bounds = *bounds;
   GeometryMapper::SourceToDestinationRect(
       transform, *effect_bounds_info.transform, mapped_bounds);
-  effect_bounds_info.bounds.Unite(mapped_bounds);
+  if (effect_bounds_info.bounds)
+    effect_bounds_info.bounds->Unite(mapped_bounds);
+  else
+    effect_bounds_info.bounds = mapped_bounds;
 }
 
 void ConversionContext::EndEffect() {
@@ -618,20 +622,20 @@
 
   DCHECK(effect_bounds_stack_.size());
   const auto& bounds_info = effect_bounds_stack_.back();
-  FloatRect bounds = bounds_info.bounds;
-  if (!bounds.IsEmpty()) {
+  base::Optional<FloatRect> bounds = bounds_info.bounds;
+  if (bounds) {
     if (current_effect_->Filter().IsEmpty()) {
-      cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id, bounds);
+      cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id, *bounds);
     } else {
       // The bounds for the SaveLayer[Alpha]Op should be the source bounds
       // before the filter is applied, in the space of the TranslateOp which was
       // emitted before the SaveLayer[Alpha]Op.
-      auto save_layer_bounds = bounds;
+      auto save_layer_bounds = *bounds;
       save_layer_bounds.MoveBy(-current_effect_->FiltersOrigin());
       cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id,
                                      save_layer_bounds);
       // We need to propagate the filtered bounds to the parent.
-      bounds = current_effect_->MapRect(bounds);
+      bounds = current_effect_->MapRect(*bounds);
     }
   }
 
@@ -750,7 +754,17 @@
       cc_list_.EndPaintOfUnpaired(
           chunk_to_layer_mapper_.MapVisualRect(item.VisualRect()));
     }
-    UpdateEffectBounds(FloatRect(chunk.bounds), chunk_state.Transform());
+
+    // Chunk bounds are only important when we are actually drawing. There may
+    // also be cases when we only generate a paint record and do not draw,
+    // for example, to implement an SVG clip. In such cases, we can safely
+    // ignore effect bounds.
+    base::Optional<FloatRect> chunk_bounds;
+    if (cc_list_.GetUsageHint() ==
+        cc::DisplayItemList::kTopLevelDisplayItemList) {
+      chunk_bounds = FloatRect(chunk.bounds);
+    }
+    UpdateEffectBounds(chunk_bounds, chunk_state.Transform());
   }
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc
index e7aaa29..7953f48 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc
@@ -101,7 +101,7 @@
   Vector<cc::PaintOpType> expected_ops_;
 };
 
-#define EXPECT_EFFECT_BOUNDS(x, y, width, height, op_buffer, index)         \
+#define EXPECT_EFFECT_BOUNDS(rect, op_buffer, index)                        \
   do {                                                                      \
     FloatRect bounds;                                                       \
     if (const auto* save_layer_alpha =                                      \
@@ -113,7 +113,7 @@
     } else {                                                                \
       FAIL() << "No SaveLayer[Alpha]Op at " << index;                       \
     }                                                                       \
-    EXPECT_EQ(FloatRect(x, y, width, height), bounds);                      \
+    EXPECT_EQ(rect, bounds);                                                \
   } while (false)
 
 #define EXPECT_TRANSFORM_MATRIX(transform, op_buffer, index)                 \
@@ -183,6 +183,10 @@
   }
 };
 
+const cc::DisplayItemList::UsageHint kUsageHints[] = {
+    cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer,
+    cc::DisplayItemList::kTopLevelDisplayItemList};
+
 TEST_P(PaintChunksToCcLayerTest, EffectGroupingSimple) {
   // This test verifies effects are applied as a group.
   auto e1 = CreateOpacityEffect(e0(), 0.5f);
@@ -190,18 +194,23 @@
   chunks.AddChunk(t0(), c0(), *e1, IntRect(0, 0, 50, 50));
   chunks.AddChunk(t0(), c0(), *e1, IntRect(20, 20, 70, 70));
 
-  sk_sp<PaintRecord> output =
-      PaintChunksToCcLayer::Convert(
-          chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
-          chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
-          ->ReleaseAsRecord();
-  EXPECT_THAT(
-      *output,
-      PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e1>
-                                cc::PaintOpType::DrawRecord,      // <p0/>
-                                cc::PaintOpType::DrawRecord,      // <p1/>
-                                cc::PaintOpType::Restore}));      // </e1>
-  EXPECT_EFFECT_BOUNDS(0, 0, 90, 90, *output, 0);
+  const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                       FloatRect(0, 0, 90, 90)};
+
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    sk_sp<PaintRecord> output =
+        PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(),
+                                      gfx::Vector2dF(), chunks.items,
+                                      kUsageHints[hint])
+            ->ReleaseAsRecord();
+    EXPECT_THAT(
+        *output,
+        PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e1>
+                                  cc::PaintOpType::DrawRecord,      // <p0/>
+                                  cc::PaintOpType::DrawRecord,      // <p1/>
+                                  cc::PaintOpType::Restore}));      // </e1>
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0);
+  }
 }
 
 TEST_P(PaintChunksToCcLayerTest, EffectGroupingNested) {
@@ -213,24 +222,33 @@
   chunks.AddChunk(t0(), c0(), *e2);
   chunks.AddChunk(t0(), c0(), *e3, IntRect(111, 222, 333, 444));
 
-  sk_sp<PaintRecord> output =
-      PaintChunksToCcLayer::Convert(
-          chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
-          chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
-          ->ReleaseAsRecord();
-  EXPECT_THAT(
-      *output,
-      PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e1>
-                                cc::PaintOpType::SaveLayerAlpha,  // <e2>
-                                cc::PaintOpType::DrawRecord,      // <p0/>
-                                cc::PaintOpType::Restore,         // </e2>
-                                cc::PaintOpType::SaveLayerAlpha,  // <e3>
-                                cc::PaintOpType::DrawRecord,      // <p1/>
-                                cc::PaintOpType::Restore,         // </e3>
-                                cc::PaintOpType::Restore}));      // </e1>
-  EXPECT_EFFECT_BOUNDS(0, 0, 444, 666, *output, 0);
-  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 1);
-  EXPECT_EFFECT_BOUNDS(111, 222, 333, 444, *output, 4);
+  const FloatRect kExpectedBounds1[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                        FloatRect(0, 0, 444, 666)};
+  const FloatRect kExpectedBounds2[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                        FloatRect(0, 0, 100, 100)};
+  const FloatRect kExpectedBounds3[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                        FloatRect(111, 222, 333, 444)};
+
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    sk_sp<PaintRecord> output =
+        PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(),
+                                      gfx::Vector2dF(), chunks.items,
+                                      kUsageHints[hint])
+            ->ReleaseAsRecord();
+    EXPECT_THAT(
+        *output,
+        PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e1>
+                                  cc::PaintOpType::SaveLayerAlpha,  // <e2>
+                                  cc::PaintOpType::DrawRecord,      // <p0/>
+                                  cc::PaintOpType::Restore,         // </e2>
+                                  cc::PaintOpType::SaveLayerAlpha,  // <e3>
+                                  cc::PaintOpType::DrawRecord,      // <p1/>
+                                  cc::PaintOpType::Restore,         // </e3>
+                                  cc::PaintOpType::Restore}));      // </e1>
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds1[hint], *output, 0);
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds2[hint], *output, 1);
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds3[hint], *output, 4);
+  }
 }
 
 TEST_P(PaintChunksToCcLayerTest, EffectFilterGroupingNestedWithTransforms) {
@@ -246,40 +264,47 @@
   chunks.AddChunk(*t2, c0(), *e1, IntRect(0, 0, 50, 50));
   chunks.AddChunk(*t1, c0(), *e2, IntRect(20, 20, 70, 70));
 
-  sk_sp<PaintRecord> output =
-      PaintChunksToCcLayer::Convert(
-          chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
-          chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
-          ->ReleaseAsRecord();
-  EXPECT_THAT(
-      *output,
-      PaintRecordMatcher::Make(
-          {cc::PaintOpType::Save, cc::PaintOpType::Concat,     // <t1*t2>
-           cc::PaintOpType::SaveLayerAlpha,                    // <e1>
-           cc::PaintOpType::DrawRecord,                        // <p1/>
-           cc::PaintOpType::Save, cc::PaintOpType::Translate,  // <e2_offset>
-           cc::PaintOpType::SaveLayer,                         // <e2>
-           cc::PaintOpType::Translate,  // <e2_offset^-1/>
-           cc::PaintOpType::Save, cc::PaintOpType::Translate,  // <t2^-1>
-           cc::PaintOpType::DrawRecord,                        // <p2/>
-           cc::PaintOpType::Restore,                           // </t2^-1>
-           cc::PaintOpType::Restore,                           // </e2>
-           cc::PaintOpType::Restore,                           // </e2_offset>
-           cc::PaintOpType::Restore,                           // </e1>
-           cc::PaintOpType::Restore}));                        // </t1*t2>
-  EXPECT_TRANSFORM_MATRIX(t1->Matrix() * t2->SlowMatrix(), *output, 1);
-  // chunk1.bounds + e2(t2^-1(chunk2.bounds))
-  EXPECT_EFFECT_BOUNDS(0, 0, 155, 155, *output, 2);
-  // e2_offset
-  EXPECT_TRANSLATE(60, 60, *output, 5);
-  // t2^-1(chunk2.bounds) - e2_offset
-  EXPECT_EFFECT_BOUNDS(10, 10, 70, 70, *output, 6);
-  // -e2_offset
-  EXPECT_TRANSLATE(-e2->FiltersOrigin().X(), -e2->FiltersOrigin().Y(), *output,
-                   7);
-  // t2^1
-  EXPECT_TRANSLATE(-t2->Translation2D().Width(), -t2->Translation2D().Height(),
-                   *output, 9);
+  const FloatRect kExpectedBounds1[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                        FloatRect(0, 0, 155, 155)};
+  const FloatRect kExpectedBounds2[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                        FloatRect(10, 10, 70, 70)};
+
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    sk_sp<PaintRecord> output =
+        PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(),
+                                      gfx::Vector2dF(), chunks.items,
+                                      kUsageHints[hint])
+            ->ReleaseAsRecord();
+    EXPECT_THAT(
+        *output,
+        PaintRecordMatcher::Make(
+            {cc::PaintOpType::Save, cc::PaintOpType::Concat,     // <t1*t2>
+             cc::PaintOpType::SaveLayerAlpha,                    // <e1>
+             cc::PaintOpType::DrawRecord,                        // <p1/>
+             cc::PaintOpType::Save, cc::PaintOpType::Translate,  // <e2_offset>
+             cc::PaintOpType::SaveLayer,                         // <e2>
+             cc::PaintOpType::Translate,  // <e2_offset^-1/>
+             cc::PaintOpType::Save, cc::PaintOpType::Translate,  // <t2^-1>
+             cc::PaintOpType::DrawRecord,                        // <p2/>
+             cc::PaintOpType::Restore,                           // </t2^-1>
+             cc::PaintOpType::Restore,                           // </e2>
+             cc::PaintOpType::Restore,                           // </e2_offset>
+             cc::PaintOpType::Restore,                           // </e1>
+             cc::PaintOpType::Restore}));                        // </t1*t2>
+    EXPECT_TRANSFORM_MATRIX(t1->Matrix() * t2->SlowMatrix(), *output, 1);
+    // chunk1.bounds + e2(t2^-1(chunk2.bounds))
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds1[hint], *output, 2);
+    // e2_offset
+    EXPECT_TRANSLATE(60, 60, *output, 5);
+    // t2^-1(chunk2.bounds) - e2_offset
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds2[hint], *output, 6);
+    // -e2_offset
+    EXPECT_TRANSLATE(-e2->FiltersOrigin().X(), -e2->FiltersOrigin().Y(),
+                     *output, 7);
+    // t2^1
+    EXPECT_TRANSLATE(-t2->Translation2D().Width(),
+                     -t2->Translation2D().Height(), *output, 9);
+  }
 }
 
 TEST_P(PaintChunksToCcLayerTest, InterleavedClipEffect) {
@@ -301,38 +326,45 @@
   chunks.AddChunk(t0(), *c3, *e1, IntRect(20, 20, 70, 70));
   chunks.AddChunk(t0(), *c4, e0());
 
-  sk_sp<PaintRecord> output =
-      PaintChunksToCcLayer::Convert(
-          chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
-          chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
-          ->ReleaseAsRecord();
-  EXPECT_THAT(*output, PaintRecordMatcher::Make(
-                           {cc::PaintOpType::Save,
-                            cc::PaintOpType::ClipRect,    // <c1+c2>
-                            cc::PaintOpType::DrawRecord,  // <p0/>
-                            cc::PaintOpType::Save,
-                            cc::PaintOpType::ClipRect,        // <c3>
-                            cc::PaintOpType::DrawRecord,      // <p1/>
-                            cc::PaintOpType::Restore,         // </c3>
-                            cc::PaintOpType::SaveLayerAlpha,  // <e1>
-                            cc::PaintOpType::Save,
-                            cc::PaintOpType::ClipRect,        // <c3+c4>
-                            cc::PaintOpType::SaveLayerAlpha,  // <e2>
-                            cc::PaintOpType::DrawRecord,      // <p2/>
-                            cc::PaintOpType::Restore,         // </e2>
-                            cc::PaintOpType::Restore,         // </c3+c4>
-                            cc::PaintOpType::Save,
-                            cc::PaintOpType::ClipRect,    // <c3>
-                            cc::PaintOpType::DrawRecord,  // <p3/>
-                            cc::PaintOpType::Restore,     // </c3>
-                            cc::PaintOpType::Restore,     // </e1>
-                            cc::PaintOpType::Save,
-                            cc::PaintOpType::ClipRect,    // <c3+c4>
-                            cc::PaintOpType::DrawRecord,  // <p4/>
-                            cc::PaintOpType::Restore,     // </c3+c4>
-                            cc::PaintOpType::Restore}));  // </c1+c2>
-  EXPECT_EFFECT_BOUNDS(0, 0, 90, 90, *output, 7);
-  EXPECT_EFFECT_BOUNDS(0, 0, 50, 50, *output, 10);
+  const FloatRect kExpectedBounds1[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                        FloatRect(0, 0, 90, 90)};
+  const FloatRect kExpectedBounds2[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                        FloatRect(0, 0, 50, 50)};
+
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    sk_sp<PaintRecord> output =
+        PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(),
+                                      gfx::Vector2dF(), chunks.items,
+                                      kUsageHints[hint])
+            ->ReleaseAsRecord();
+    EXPECT_THAT(*output, PaintRecordMatcher::Make(
+                             {cc::PaintOpType::Save,
+                              cc::PaintOpType::ClipRect,    // <c1+c2>
+                              cc::PaintOpType::DrawRecord,  // <p0/>
+                              cc::PaintOpType::Save,
+                              cc::PaintOpType::ClipRect,        // <c3>
+                              cc::PaintOpType::DrawRecord,      // <p1/>
+                              cc::PaintOpType::Restore,         // </c3>
+                              cc::PaintOpType::SaveLayerAlpha,  // <e1>
+                              cc::PaintOpType::Save,
+                              cc::PaintOpType::ClipRect,        // <c3+c4>
+                              cc::PaintOpType::SaveLayerAlpha,  // <e2>
+                              cc::PaintOpType::DrawRecord,      // <p2/>
+                              cc::PaintOpType::Restore,         // </e2>
+                              cc::PaintOpType::Restore,         // </c3+c4>
+                              cc::PaintOpType::Save,
+                              cc::PaintOpType::ClipRect,    // <c3>
+                              cc::PaintOpType::DrawRecord,  // <p3/>
+                              cc::PaintOpType::Restore,     // </c3>
+                              cc::PaintOpType::Restore,     // </e1>
+                              cc::PaintOpType::Save,
+                              cc::PaintOpType::ClipRect,    // <c3+c4>
+                              cc::PaintOpType::DrawRecord,  // <p4/>
+                              cc::PaintOpType::Restore,     // </c3+c4>
+                              cc::PaintOpType::Restore}));  // </c1+c2>
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds1[hint], *output, 7);
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds2[hint], *output, 10);
+  }
 }
 
 TEST_P(PaintChunksToCcLayerTest, ClipSpaceInversion) {
@@ -375,24 +407,29 @@
   chunks.AddChunk(t0(), c0(), *e1);
   chunks.AddChunk(*t1, c0(), *e1);
 
-  sk_sp<PaintRecord> output =
-      PaintChunksToCcLayer::Convert(
-          chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
-          chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
-          ->ReleaseAsRecord();
-  EXPECT_THAT(*output,
-              PaintRecordMatcher::Make(
-                  {cc::PaintOpType::Save, cc::PaintOpType::Concat,  // <t1>
-                   cc::PaintOpType::SaveLayerAlpha,                 // <e1>
-                   cc::PaintOpType::Save, cc::PaintOpType::Concat,  // <t1^-1>
-                   cc::PaintOpType::DrawRecord,                     // <p0/>
-                   cc::PaintOpType::Restore,                        // </t1^-1>
-                   cc::PaintOpType::DrawRecord,                     // <p1/>
-                   cc::PaintOpType::Restore,                        // </e1>
-                   cc::PaintOpType::Restore}));                     // </t1>
-  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 2);
-  EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 1);
-  EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 4);
+  const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                       FloatRect(0, 0, 100, 100)};
+
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    sk_sp<PaintRecord> output =
+        PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(),
+                                      gfx::Vector2dF(), chunks.items,
+                                      kUsageHints[hint])
+            ->ReleaseAsRecord();
+    EXPECT_THAT(*output,
+                PaintRecordMatcher::Make(
+                    {cc::PaintOpType::Save, cc::PaintOpType::Concat,  // <t1>
+                     cc::PaintOpType::SaveLayerAlpha,                 // <e1>
+                     cc::PaintOpType::Save, cc::PaintOpType::Concat,  // <t1^-1>
+                     cc::PaintOpType::DrawRecord,                     // <p0/>
+                     cc::PaintOpType::Restore,     // </t1^-1>
+                     cc::PaintOpType::DrawRecord,  // <p1/>
+                     cc::PaintOpType::Restore,     // </e1>
+                     cc::PaintOpType::Restore}));  // </t1>
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 2);
+    EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 1);
+    EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 4);
+  }
 }
 
 TEST_P(PaintChunksToCcLayerTest, FilterEffectSpaceInversion) {
@@ -410,29 +447,33 @@
   TestChunks chunks;
   chunks.AddChunk(t0(), c0(), *e1);
 
-  auto output =
-      PaintChunksToCcLayer::Convert(
-          chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
-          chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
-          ->ReleaseAsRecord();
-  EXPECT_THAT(
-      *output,
-      PaintRecordMatcher::Make(
-          {cc::PaintOpType::Save, cc::PaintOpType::Concat,     // <t1>
-           cc::PaintOpType::Save, cc::PaintOpType::Translate,  // <e1_offset>
-           cc::PaintOpType::SaveLayer,                         // <e1>
-           cc::PaintOpType::Translate,                      // <e1_offset^-1/>
-           cc::PaintOpType::Save, cc::PaintOpType::Concat,  // <t1^-1>
-           cc::PaintOpType::DrawRecord,                     // <p0/>
-           cc::PaintOpType::Restore,                        // </t1^-1>
-           cc::PaintOpType::Restore,                        // </e1>
-           cc::PaintOpType::Restore,                        // </e1_offset>
-           cc::PaintOpType::Restore}));                     // </t1>
-  EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 1);
-  EXPECT_TRANSLATE(66, 88, *output, 3);
-  EXPECT_EFFECT_BOUNDS(-66, -88, 50, 50, *output, 4);
-  EXPECT_TRANSLATE(-66, -88, *output, 5);
-  EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 7);
+  const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                       FloatRect(-66, -88, 50, 50)};
+
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    auto output = PaintChunksToCcLayer::Convert(
+                      chunks.chunks, PropertyTreeState::Root(),
+                      gfx::Vector2dF(), chunks.items, kUsageHints[hint])
+                      ->ReleaseAsRecord();
+    EXPECT_THAT(
+        *output,
+        PaintRecordMatcher::Make(
+            {cc::PaintOpType::Save, cc::PaintOpType::Concat,     // <t1>
+             cc::PaintOpType::Save, cc::PaintOpType::Translate,  // <e1_offset>
+             cc::PaintOpType::SaveLayer,                         // <e1>
+             cc::PaintOpType::Translate,                      // <e1_offset^-1/>
+             cc::PaintOpType::Save, cc::PaintOpType::Concat,  // <t1^-1>
+             cc::PaintOpType::DrawRecord,                     // <p0/>
+             cc::PaintOpType::Restore,                        // </t1^-1>
+             cc::PaintOpType::Restore,                        // </e1>
+             cc::PaintOpType::Restore,                        // </e1_offset>
+             cc::PaintOpType::Restore}));                     // </t1>
+    EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 1);
+    EXPECT_TRANSLATE(66, 88, *output, 3);
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 4);
+    EXPECT_TRANSLATE(-66, -88, *output, 5);
+    EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 7);
+  }
 }
 
 TEST_P(PaintChunksToCcLayerTest, NonRootLayerSimple) {
@@ -482,20 +523,25 @@
   TestChunks chunks;
   chunks.AddChunk(t0(), *c2, *e1);
 
-  sk_sp<PaintRecord> output =
-      PaintChunksToCcLayer::Convert(
-          chunks.chunks, PropertyTreeState(t0(), *c1, e0()), gfx::Vector2dF(),
-          chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
-          ->ReleaseAsRecord();
-  EXPECT_THAT(
-      *output,
-      PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e1>
-                                cc::PaintOpType::Save,
-                                cc::PaintOpType::ClipRect,    // <c2>
-                                cc::PaintOpType::DrawRecord,  // <p0/>
-                                cc::PaintOpType::Restore,     // </c2>
-                                cc::PaintOpType::Restore}));  // </e1>
-  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
+  const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                       FloatRect(0, 0, 100, 100)};
+
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    sk_sp<PaintRecord> output =
+        PaintChunksToCcLayer::Convert(
+            chunks.chunks, PropertyTreeState(t0(), *c1, e0()), gfx::Vector2dF(),
+            chunks.items, kUsageHints[hint])
+            ->ReleaseAsRecord();
+    EXPECT_THAT(
+        *output,
+        PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e1>
+                                  cc::PaintOpType::Save,
+                                  cc::PaintOpType::ClipRect,    // <c2>
+                                  cc::PaintOpType::DrawRecord,  // <p0/>
+                                  cc::PaintOpType::Restore,     // </c2>
+                                  cc::PaintOpType::Restore}));  // </e1>
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0);
+  }
 }
 
 TEST_P(PaintChunksToCcLayerTest,
@@ -507,23 +553,30 @@
   TestChunks chunks;
   chunks.AddChunk(t0(), *c1, *e2);
 
-  sk_sp<PaintRecord> output =
-      PaintChunksToCcLayer::Convert(
-          chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
-          chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
-          ->ReleaseAsRecord();
-  EXPECT_THAT(
-      *output,
-      PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e1>
-                                cc::PaintOpType::SaveLayerAlpha,  // <e2>
-                                cc::PaintOpType::Save,
-                                cc::PaintOpType::ClipRect,    // <c1>
-                                cc::PaintOpType::DrawRecord,  // <p0/>
-                                cc::PaintOpType::Restore,     // </c1>
-                                cc::PaintOpType::Restore,     // </e2>
-                                cc::PaintOpType::Restore}));  // </e1>
-  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
-  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 1);
+  const FloatRect kExpectedBounds1[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                        FloatRect(0, 0, 100, 100)};
+  const FloatRect kExpectedBounds2[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                        FloatRect(0, 0, 100, 100)};
+
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    sk_sp<PaintRecord> output =
+        PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(),
+                                      gfx::Vector2dF(), chunks.items,
+                                      kUsageHints[hint])
+            ->ReleaseAsRecord();
+    EXPECT_THAT(
+        *output,
+        PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e1>
+                                  cc::PaintOpType::SaveLayerAlpha,  // <e2>
+                                  cc::PaintOpType::Save,
+                                  cc::PaintOpType::ClipRect,    // <c1>
+                                  cc::PaintOpType::DrawRecord,  // <p0/>
+                                  cc::PaintOpType::Restore,     // </c1>
+                                  cc::PaintOpType::Restore,     // </e2>
+                                  cc::PaintOpType::Restore}));  // </e1>
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds1[hint], *output, 0);
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds2[hint], *output, 1);
+  }
 }
 
 TEST_P(PaintChunksToCcLayerTest,
@@ -535,20 +588,25 @@
   TestChunks chunks;
   chunks.AddChunk(t0(), *c1, *e2);
 
-  sk_sp<PaintRecord> output =
-      PaintChunksToCcLayer::Convert(
-          chunks.chunks, PropertyTreeState(t0(), c0(), *e1), gfx::Vector2dF(),
-          chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
-          ->ReleaseAsRecord();
-  EXPECT_THAT(
-      *output,
-      PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e2>
-                                cc::PaintOpType::Save,
-                                cc::PaintOpType::ClipRect,    // <c1>
-                                cc::PaintOpType::DrawRecord,  // <p0/>
-                                cc::PaintOpType::Restore,     // </c1>
-                                cc::PaintOpType::Restore}));  // </e2>
-  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
+  const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                       FloatRect(0, 0, 100, 100)};
+
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    sk_sp<PaintRecord> output =
+        PaintChunksToCcLayer::Convert(
+            chunks.chunks, PropertyTreeState(t0(), c0(), *e1), gfx::Vector2dF(),
+            chunks.items, kUsageHints[hint])
+            ->ReleaseAsRecord();
+    EXPECT_THAT(
+        *output,
+        PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e2>
+                                  cc::PaintOpType::Save,
+                                  cc::PaintOpType::ClipRect,    // <c1>
+                                  cc::PaintOpType::DrawRecord,  // <p0/>
+                                  cc::PaintOpType::Restore,     // </c1>
+                                  cc::PaintOpType::Restore}));  // </e2>
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0);
+  }
 }
 
 TEST_P(PaintChunksToCcLayerTest,
@@ -560,17 +618,22 @@
   TestChunks chunks;
   chunks.AddChunk(t0(), *c1, *e2);
 
-  sk_sp<PaintRecord> output =
-      PaintChunksToCcLayer::Convert(
-          chunks.chunks, PropertyTreeState(t0(), *c1, *e1), gfx::Vector2dF(),
-          chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
-          ->ReleaseAsRecord();
-  EXPECT_THAT(
-      *output,
-      PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e2>
-                                cc::PaintOpType::DrawRecord,      // <p0/>
-                                cc::PaintOpType::Restore}));      // </e2>
-  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
+  const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                       FloatRect(0, 0, 100, 100)};
+
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    sk_sp<PaintRecord> output =
+        PaintChunksToCcLayer::Convert(
+            chunks.chunks, PropertyTreeState(t0(), *c1, *e1), gfx::Vector2dF(),
+            chunks.items, kUsageHints[hint])
+            ->ReleaseAsRecord();
+    EXPECT_THAT(
+        *output,
+        PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha,  // <e2>
+                                  cc::PaintOpType::DrawRecord,      // <p0/>
+                                  cc::PaintOpType::Restore}));      // </e2>
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0);
+  }
 }
 
 TEST_P(PaintChunksToCcLayerTest, VisualRect) {
@@ -688,17 +751,22 @@
   chunks.AddChunk(nullptr, t0(), c0(), e0());
   chunks.AddChunk(nullptr, t0(), c0(), *e1);
 
-  sk_sp<PaintRecord> output =
-      PaintChunksToCcLayer::Convert(
-          chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
-          chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
-          ->ReleaseAsRecord();
+  const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                       FloatRect(0, 0, 100, 100)};
 
-  EXPECT_THAT(*output, PaintRecordMatcher::Make({
-                           cc::PaintOpType::SaveLayerAlpha,  // <e1>
-                           cc::PaintOpType::Restore,         // </e1>
-                       }));
-  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    sk_sp<PaintRecord> output =
+        PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(),
+                                      gfx::Vector2dF(), chunks.items,
+                                      kUsageHints[hint])
+            ->ReleaseAsRecord();
+
+    EXPECT_THAT(*output, PaintRecordMatcher::Make({
+                             cc::PaintOpType::SaveLayerAlpha,  // <e1>
+                             cc::PaintOpType::Restore,         // </e1>
+                         }));
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0);
+  }
 }
 
 TEST_P(PaintChunksToCcLayerTest, CombineClips) {
@@ -1313,5 +1381,28 @@
                        }));
 }
 
+// https://crbug.com/918240
+TEST_P(PaintChunksToCcLayerTest, EmptyChunkRectDoesntTurnToUnsetOne) {
+  CompositorFilterOperations filter;
+  filter.AppendBlurFilter(5);
+  auto e1 = CreateFilterEffect(e0(), t0(), &c0(), filter, FloatPoint(0, 0));
+  TestChunks chunks;
+  chunks.AddChunk(nullptr, t0(), c0(), *e1, {0, 0, 0, 0});
+
+  const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect),
+                                       FloatRect(0, 0, 0, 0)};
+
+  for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) {
+    auto output = PaintChunksToCcLayer::Convert(
+                      chunks.chunks, PropertyTreeState::Root(),
+                      gfx::Vector2dF(), chunks.items, kUsageHints[hint])
+                      ->ReleaseAsRecord();
+    EXPECT_THAT(*output,
+                PaintRecordMatcher::Make({cc::PaintOpType::SaveLayer,   // <e1>
+                                          cc::PaintOpType::Restore}));  // </e1>
+    EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0);
+  }
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md b/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md
index ac60de4..13329bb3 100644
--- a/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md
+++ b/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md
@@ -1,16 +1,15 @@
 # Blink GC API reference
 
-This is a through document for Oilpan API usage.
-If you want to learn the API usage quickly, look at
+This document describes the usage of Oilpan -- Blink's garbage collector.
+If you just want to get an overview of the API you can have a look at
 [this tutorial](https://docs.google.com/presentation/d/1XPu03ymz8W295mCftEC9KshH9Icxfq81YwIJQzQrvxo/edit#slide=id.p).
-If you're just interested in wrapper tracing,
-see [Wrapper Tracing Reference](../bindings/TraceWrapperReference.md).
+If you're interested in wrapper tracing, see [Wrapper Tracing Reference](../bindings/TraceWrapperReference.md).
 
 [TOC]
 
 ## Header file
 
-Unless otherwise noted, any of the primitives explained in this page requires the following `#include` statement:
+Unless otherwise noted, any of the primitives explained on this page require the following `#include` statement:
 
 ```c++
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -20,8 +19,7 @@
 
 ### GarbageCollected
 
-A class that wants the lifetime management of its instances to be managed by Blink GC (Oilpan), it must inherit from
-`GarbageCollected<YourClass>`.
+A class that wants the lifetime management of its instances to be managed by Oilpan, it must inherit from `GarbageCollected<T>`.
 
 ```c++
 class YourClass : public GarbageCollected<YourClass> {
@@ -29,91 +27,70 @@
 };
 ```
 
-Instances of such classes are said to be *on Oilpan heap*, or *on heap* for short, while instances of other classes
-are called *off heap*. In the rest of this document, the terms "on heap" or "on-heap objects" are used to mean the
-objects on Oilpan heap instead of on normal (default) dynamic allocator's heap space.
+Instances of such classes are said to be *on Oilpan heap*, or *on heap* for short, while instances of other classes are called *off heap*.
+In the rest of this document, the terms *on heap* or *on-heap objects* are used to mean the objects on Oilpan heap instead of on normal (default) dynamic allocator's heap space.
 
-You can create an instance of your class normally through `new`, while you may not free the object with `delete`,
-as the Blink GC system is responsible for deallocating the object once it determines the object is unreachable.
+You can create an instance of your class through `MakeGarbageCollected<T>`, while you may not free the object with `delete`, as Oilpan is responsible for deallocating the object once it determines the object is unreachable.
 
 You may not allocate an on-heap object on stack.
 
 Your class may need to have a tracing method. See [Tracing](#Tracing) for details.
 
-Your class will be automatically finalized as long as it is non-trivially destructible. Non-final classes are
-required to have a virtual destructor.
-
+Your class will be automatically finalized as long as it is non-trivially destructible.
+Non-final classes are required to have a virtual destructor.
 Note that finalization is done at an arbitrary time after the object becomes unreachable.
+Any destructor executed within the finalization period *must not* touch any other on-heap object because destructors can be executed in any order.
 
-Any destructor executed within the finalization period must not touch any other on-heap object, because destructors
-can be executed in any order.
-
-`GarbageCollected<T>` or any class deriving from `GarbageCollected<T>`, directly or indirectly, must be the first
-element in its base class list (called "leftmost derivation rule"). This rule is needed to assure each on-heap object
-has its own canonical address.
+`GarbageCollected<T>` or any class deriving from `GarbageCollected<T>`, directly or indirectly, must be the first element in its base class list (called "leftmost derivation rule").
+This rule is needed to assure each on-heap object has its own canonical address.
 
 ```c++
-class A : public GarbageCollected<A>, public P { // OK, GarbageCollected<A> is leftmost.
+class A : public GarbageCollected<A>, public P {
+  // OK: GarbageCollected<A> is leftmost.
 };
 
-class B : public A, public Q { // OK, A is leftmost.
+class B : public A, public Q {
+  // OK: A is leftmost.
 };
 
-// class C : public R, public A { // BAD, A must be the first base class.
+// class C : public R, public A {
+//   BAD: A must be the first base class.
 // };
 ```
 
-If a non-leftmost base class needs to retain an on-heap object, that base class needs to inherit from
-[GarbageCollectedMixin](#GarbageCollectedMixin). It's generally recommended to make *any* non-leftmost base class
-inherit from `GarbageCollectedMixin`, because it's dangerous to save a pointer to a non-leftmost
-non-`GarbageCollectedMixin` subclass of an on-heap object.
-
-```c++
-void someFunction(P*);
-
-class A : public GarbageCollected<A>, public P {
-public:
-  void someMemberFunction()
-  {
-    someFunction(this); // DANGEROUS, a raw pointer to an on-heap object.
-  }
-};
-```
+If a non-leftmost base class needs to retain an on-heap object, that base class needs to inherit from [GarbageCollectedMixin](#GarbageCollectedMixin). It's generally recommended to make *any* non-leftmost base class inherit from `GarbageCollectedMixin` because it's dangerous to save a pointer to a non-leftmost non-`GarbageCollectedMixin` subclass of an on-heap object.
 
 ### GarbageCollectedMixin
 
-A non-leftmost base class of a garbage-collected class may derive from `GarbageCollectedMixin`. If a direct child
-class of `GarbageCollected<T>` has a non-leftmost base class deriving from `GarbageCollectedMixin`, the
-garbage-collected class must declare the `USING_GARBAGE_COLLECTED_MIXIN(ClassName)` macro in its class declaration.
+A non-leftmost base class of a garbage-collected class should derive from `GarbageCollectedMixin`.
+If a child class of `GarbageCollected<T>` has a non-leftmost base class deriving from `GarbageCollectedMixin`, the garbage-collected class must declare the `USING_GARBAGE_COLLECTED_MIXIN(ClassName)` macro in its class declaration.
 
-A class deriving from `GarbageCollectedMixin` can be treated similarly as garbage-collected classes. Specifically, it
-can have `Member<T>`s and `WeakMember<T>`s, and a tracing method. A pointer to such a class must be retained in the
-same smart pointer wrappers as a pointer to a garbage-collected class, such as `Member<T>` or `Persistent<T>`.
+A class deriving from `GarbageCollectedMixin` can be treated similarly as garbage-collected classes.
+Specifically, it can have `Member<T>`s and `WeakMember<T>`s, and a tracing method.
+A pointer to such a class must be retained in the same smart pointer wrappers as a pointer to a garbage-collected class, such as `Member<T>` or `Persistent<T>`.
 The tracing method of a garbage-collected class, if any, must contain a delegating call for each mixin base class.
 
 ```c++
 class P : public GarbageCollectedMixin {
  public:
-  // OK, needs to trace q_.
+  // OK: Needs to trace q_.
   virtual void Trace(Visitor* visitor) { visitor->Trace(q_); }
  private:
-  // OK, allowed to have Member<T>.
+  // OK: Allowed to have Member<T>.
   Member<Q> q_;
 };
 
-class A : public GarbageCollected<A>, public P {
+class A final : public GarbageCollected<A>, public P {
   USING_GARBAGE_COLLECTED_MIXIN(A);
  public:
   // Delegating call for P is needed.
-  virtual void Trace(Visitor* visitor) { ...; P::Trace(visitor); }
-  ...
+  virtual void Trace(Visitor* visitor) { P::Trace(visitor); }
 };
 ```
 
-Internally, `GarbageCollectedMixin` defines pure virtual functions, and `USING_GARBAGE_COLLECTED_MIXIN(ClassName)`
-implements these virtual functions. Therefore, you cannot instantiate a class that is a descendant of
-`GarbageCollectedMixin` but not a descendant of `GarbageCollected<T>`. Two or more base classes inheritng from
-`GarbageCollectedMixin` can be resolved with a single `USING_GARBAGE_COLLECTED_MIXIN(ClassName)` declaration.
+Internally, `GarbageCollectedMixin` defines pure virtual functions, and `USING_GARBAGE_COLLECTED_MIXIN(ClassName)` implements these virtual functions.
+Therefore, you cannot instantiate a class that is a descendant of `GarbageCollectedMixin` but not a descendant of `GarbageCollected<T>`.
+Two or more base classes inheritng from `GarbageCollectedMixin` can be resolved with a single `USING_GARBAGE_COLLECTED_MIXIN(ClassName)` declaration.
 
 ```c++
 class P : public GarbageCollectedMixin { };
@@ -121,17 +98,18 @@
 class R : public Q { };
 
 class A : public GarbageCollected<A>, public P, public R {
-  USING_GARBAGE_COLLECTED_MIXIN(A); // OK, resolving pure virtual functions of P and R.
+  USING_GARBAGE_COLLECTED_MIXIN(A);
+  // OK: Resolving pure virtual functions of P and R.
 };
 
 class B : public GarbageCollected<B>, public P {
-  USING_GARBAGE_COLLECTED_MIXIN(B); // OK, different garbage-collected classes may inherit from the same mixin (P).
+  USING_GARBAGE_COLLECTED_MIXIN(B);
+  // OK: Different garbage-collected classes may inherit from the same mixin (P).
 };
 
-void someFunction()
-{
-  MakeGarbageCollected<A>(); // OK, A can be instantiated.
-  // MakeGarbageCollected<R>(); // BAD, R has pure virtual functions.
+void foo() {
+  MakeGarbageCollected<A>();    // OK: A can be instantiated.
+  // MakeGarbageCollected<R>(); // BAD: R has pure virtual functions.
 }
 ```
 
@@ -146,8 +124,7 @@
 
 ### USING_PRE_FINALIZER
 
-`USING_PRE_FINALIZER(ClassName, FunctionName)` in a class declaration declares the class has a *pre-finalizer* of name
-`FunctionName`.
+`USING_PRE_FINALIZER(ClassName, FunctionName)` in a class declaration declares the class has a *pre-finalizer* of name `FunctionName`.
 A pre-finalizer must have the function signature `void()` but can have any name.
 
 A pre-finalizer is a user-defined member function of a garbage-collected class that is called when the object is going to be reclaimed.
@@ -205,12 +182,9 @@
 
 ### STACK_ALLOCATED
 
-Class level annotation that should be used if the object is only stack allocated; it disallows use
-of `operator new`. Any garbage-collected objects should be kept as `Member<T>` references, but you do not
-need to define a `Trace()` method as they are on the stack, and automatically traced and kept alive should
-a conservative GC be required.
+Class level annotation that should be used if the object is only stack allocated; it disallows use of `operator new`. Any garbage-collected objects should be kept as `Member<T>` references, but you do not need to define a `Trace()` method as they are on the stack, and automatically traced and kept alive should a conservative GC be required.
 
-Classes with this annotation do not need a `Trace()` method, and should not inherit a garbage collected class.
+Classes with this annotation do not need a `Trace()` method and must not inherit a garbage collected class.
 
 ### DISALLOW_NEW
 
@@ -222,7 +196,6 @@
 Classes with this annotation need a `Trace()` method, but should not inherit a garbage collected class.
 
 
-
 ## Handles
 
 Class templates in this section are smart pointers, each carrying a pointer to an on-heap object (think of `scoped_refptr<T>`
@@ -235,8 +208,7 @@
 On-stack references to on-heap objects must be raw pointers.
 
 ```c++
-void someFunction()
-{
+void someFunction() {
   SomeGarbageCollectedClass* object = MakeGarbageCollected<SomeGarbageCollectedClass>(); // OK, retained by a pointer.
   ...
 }
@@ -319,16 +291,15 @@
 
 ## Tracing
 
-A garbage-collected class may need to have *a tracing method*, which lists up all the on-heap objects it has. The
-tracing method is called when the garbage collector needs to determine (1) all the on-heap objects referred from a
-live object, and (2) all the weak handles that may be filled with `nullptr` later. These are done in the "marking"
-phase of the mark-and-sweep GC.
+A garbage-collected class may be required to have *a tracing method*, which lists up all the on-heap objects it has.
+The tracing method is called when the garbage collector needs to determine (1) all the on-heap objects referred from a
+live object, and (2) all the weak handles that may be filled with `nullptr` later.
 
 The basic form of tracing is illustrated below:
 
 ```c++
 // In a header file:
-class SomeGarbageCollectedClass
+class SomeGarbageCollectedClass final
     : public GarbageCollected<SomeGarbageCollectedClass> {
  public:
   void Trace(Visitor*);
@@ -343,37 +314,31 @@
 }
 ```
 
-Specifically, if your class needs a tracing method, you need to declare and
-define a `Trace(Visitor*)` method. This method is normally declared in the
-header file and defined once in the implementation file, but there are
-variations. Another common variation is to declare a virtual `Trace()` for base
-classes that will be subclassed.
+Specifically, if your class needs a tracing method, you need to declare and define a `Trace(Visitor*)` method.
+This method is normally declared in the header file and defined once in the implementation file, but there are variations.
+Another common variation is to declare a virtual `Trace()` for base classes that will be subclassed.
 
 The function implementation must contain:
-
-*   For each on-heap object `object` in your class, a tracing call: `visitor->Trace(object_);`.
-*   If your class has one or more weak references (`WeakMember<T>`), you have the option of
-    registering a *weak callback* for the object. See details below for how.
-*   For each base class of your class `BaseClass` that is a descendant of `GarbageCollected<T>` or
-    `GarbageCollectedMixin`, a delegation call to base class: `BaseClass::Trace(visitor);`"
-
+- For each on-heap object `object` in your class, a tracing call: `visitor->Trace(object);`.
+- For each base class of your class `BaseClass` that is a descendant of `GarbageCollected<T>` or `GarbageCollectedMixin`, a delegation call to base class: `BaseClass::Trace(visitor)`.
 It is recommended that the delegation call, if any, is put at the end of a tracing method.
+- See [Advanced weak handling](#Advanced%20Weak%20Handling) for implementing non-trivial weakness.
 
 The following example shows more involved usage:
 
 ```c++
 class A : public GarbageCollected<A> {
  public:
-  virtual void Trace(Visitor*) { } // Nothing to trace here.
+  virtual void Trace(Visitor*) {} // Nothing to trace here.
 };
 
 class B : public A {
   // Nothing to trace here; exempted from having a tracing method.
 };
 
-class C : public B {
+class C final : public B {
  public:
-  void Trace(Visitor*) override;
+  void Trace(Visitor*) final;
 
  private:
   Member<X> x_;
@@ -389,110 +354,124 @@
 }
 ```
 
-Given that the class `C` above contained a `WeakMember<Y>` field, you could alternatively
-register a *weak callback* in the trace method, and have it be invoked after the marking
-phase:
-
-```c++
-
-void C::ClearWeakMembers(Visitor* visitor)
-{
-  if (ThreadHeap::isHeapObjectAlive(y_))
-    return;
-
-  // |y_| is not referred to by anyone else, clear the weak
-  // reference along with updating state / clearing any other
-  // resources at the same time. None of those operations are
-  // allowed to perform heap allocations:
-  y_->detach();
-
-  // Note: if the weak callback merely clears the weak reference,
-  // it is much simpler to just |trace| the field rather than
-  // install a custom weak callback.
-  y_ = nullptr;
-}
-
-void C::Trace(Visitor* visitor) {
-  visitor->template registerWeakMembers<C, &C::ClearWeakMembers>(this);
-  visitor->Trace(x_);
-  visitor->Trace(z_); // Heap collection does, too.
-  B::Trace(visitor); // Delegate to the parent. In this case it's empty, but this is required.
-}
-```
-
-Please notice that if the object (of type `C`) is also not reachable, its `Trace` method
-will not be invoked and any follow-on weak processing will not be done. Hence, if the
-object must always perform some operation when the weak reference is cleared, that
-needs to (also) happen during finalization.
-
-Weak callbacks have so far seen little use in Blink, but a mechanism that's available.
-
 ## Heap collections
 
-Heap collections are WTF collection types that support `Member<T>`, `WeakMember<T>`(see [below](#Weak collections)), and garbage collected objects as its elements.
+Oilpan, like any other managed runtime library, provides basic support for collections that integrate its managed types `Member<T>` and `WeakMember<T>`.
+Do not use heap collection with persistent types (e.g. HeapVector<Persistent<T>>).
 
-Here is the complete list:
+Collections compared to other libraries used in Blink:
 
-- WTF::Vector → blink::HeapVector
-- WTF::Deque → blink::HeapDeque
-- WTF::HashMap → blink::HeapHashMap
-- WTF::HashSet → blink::HeapHashSet
-- WTF::LinkedHashSet → blink::HeapLinkedHashSet
-- WTF::ListHashSet → blink::HeapListHashSet
-- WTF::HashCountedSet → blink::HeapHashCountedSet
+| stdlib             | WTF                 | Oilpan                    |
+| ------------------ | ------------------- | ------------------------- |
+| std::vector        | WTF::Vector         | blink::HeapVector         |
+| std::deque         | WTF::Deque          | blink::HeapDeque          |
+| std::unordered_map | WTF::HashMap        | blink::HeapHashMap        |
+| std::unordered_set | WTF::HashSet        | blink::HeapHashSet        |
+| -                  | WTF::LinkedHashSet  | blink::HeapLinkedHashSet  |
+| -                  | WTF::ListHashSet    | blink::HeapListHashSet    |
+| -                  | WTF::HashCountedSet | blink::HeapHashCountedSet |
 
-These heap collections work mostly the same way as their WTF collection counterparts but there are some things to keep in mind.
-
-Heap collections are special in that the types themselves do not inherit from GarbageCollected (hence they are not allocated on the Oilpan heap) but they still *need to be traced* from the trace method (because we need to trace the backing store which is on the Oilpan heap).
+These heap collections work mostly the same way as their stdlib or WTF collection counterparts but there are some things to keep in mind.
+Heap collections do not inherit from `GarbageCollected` but are nonetheless allocated on-heap and thus must be properly traced from a `Trace` method.
 
 ```c++
-class MyGarbageCollectedClass : public GarbageCollected<MyGarbageCollectedClass> {
+class A final : public GarbageCollected<A> {
  public:
-  void Trace(Visitor* visitor) { visitor->Trace(list_); }
+  void Trace(Visitor* visitor) { visitor->Trace(vec_); }
  private:
-  HeapVector<Member<AnotherGarbageCollectedClass>> list_;
+  HeapVector<Member<B>> vec_;
 };
 ```
 
-When you want to add a heap collection as a member of a non-garbage-collected class (on the main thread), please use a Persistent to reference it.
+Like any other object, they may be referred to from a non-garbage-collected class using `Persistent`.
 
 ```c++
-class MyNotGarbageCollectedClass {
+class NonGCed final {
  private:
-  Persistent<HeapVector<Member<MyGarbageCollectedClass>>> list_;
+  Persistent<HeapVector<Member<B>>> vec_;
 };
 ```
 
-On non-main threads these persistent heap collections have been disabled to simplify the thread termination sequence. Please wrap the heap collections in a `Persistent` instead.
+## Advanced weak handling
 
-Please be very cautious if you want to use a heap collection from multiple threads. Reference to heap collections may be passed to another thread using CrossThreadPersistents, but *you may not modify the collection from the non-owner thread*. This is because modifications to collections may trigger backing store reallocations, and Oilpan's per thread heap requires that modifications to a heap happen on its owner thread.
+In addition to basic weak handling using `WeakMember<T>` Oilpan also supports:
+- Weak collections
+- Custom weak callbacks
 
 ### Weak collections
 
-You can put `WeakMember<T>` in heap collections except for `HeapVector` and `HeapDeque` which we do not support.
+Like regular weakness, collections support weakness by putting references in `WeakMember<T>`.
 
-During an Oilpan GC, the weak members that refernce a collected object will be removed from its heap collection, meaning the size of the collection will shrink and you do not have to check for null weak members when iterating through the collection.
+In sequence containers such as `HeapVector` the `WeakMember<T>` references are just cleared without adding any additional handling.
+
+In associative containers such as `HeapHashMap` or `HeapHashSet` Oilpan distinguishes between *pure weakness* and *mixed weakness*:
+- Pure weakness: All entries in such containers are wrapped in `WeakMember<T>`.
+  Examples are `HeapHashSet<WeakMember<T>>` and `HeapHashMap<WeakMember<T>, WeakMember<U>>`.
+- Mixed weakness: Only some entries in such containers are wrapped in `WeakMember<T>`.
+  This can only happen in `HeapHashMap`.
+  Examples are `HeapHashMap<WeakMember<T>, Member<U>>, HeapHashMap<Member<T>, WeakMember<U>>, and HeapHashMap<WeakMember<T>, int>.
+  Note that in the last example the type `int` is traced even though it does not support tracing.
+
+The semantics then are as follows:
+- Pure weakness: Oilpan will automatically remove the entries from the container if any of its declared `WeakMember<T>` fields points to a dead object.
+- Mixed weakness: Oilpan applies ephemeron semantics meaning that the strong parts of an entry are only treated as strong if the `WeakMember<T>` fields point to a live object.
+
+### Custom weak callbacks
+
+In case very specific weakness semantics are required Oilpan allows adding custom weakness callbacks through its tracing method.
+
+There exist two helper methods on `blink::Visitor` to add such callbacks:
+- `RegisterWeakCallback`: Used to add custom weak callbacks of the form `void(void*, const blink::WeakCallbackInfo&)`.
+- `RegisterWeakCallbackMethod`: Helper for adding an instance method.
+
+Note that any custom weak callbacks should not be used to clear `WeakMember<T>` fields as such fields are automatically handled by Oilpan.
+Instead, users should wrap their managed fields in `UntracedMember<T>` indicating that Oilpan is ignoring those fields.
+
+The following example shows how this can be used:
+
+```c++
+
+class W final : public GarbageCollected<W> {
+ public:
+  virtual void Trace(Visitor*);
+ private:
+  void ProcessCustomWeakness(const WeakCallbackInfo&);
+
+  UntracedMember<C> other_;
+};
+
+void W::Trace(Visitor* visitor) {
+  visitor->template RegisterCustomWeakMethod<W, &W::ProcessCustomWeakness>(this);
+}
+
+void W::ProcessCustomWeakness(const WeakCallbackInfo& info) {
+  if (info.IsHeapObjectAlive(other_)) {
+    // Do something with other_.
+  }
+  other_ = nullptr;
+}
+```
+
+Note that the custom weakness callback in this example is only executed if `W` is alive and properly traced.
+If `W` itself dies than the callback will not be executed.
+Operations that must always happen should instead go into destructors or pre-finalizers.
 
 ## Traits helpers
 
-At times, one may be working on code that needs to deal with both "regular" types and classes managed by the Blink GC. The following helpers can aid in writing code that needs to use different wrappers and containers based on whether a type is managed by Oilpan.
+At times, one may be working on code that needs to deal with both, off heap and on heap, objects.
+The following helpers can aid in writing code that needs to use different wrappers and containers based on whether a type is managed by Oilpan.
 
 ### AddMemberIfNeeded<T>
 
-Given a type `T`, defines a type alias that is either `Member<T>` or `T` depending on whether `T` is a type managed by the Blink GC.
+Given a type `T`, defines a type alias that is either `Member<T>` or `T` depending on whether `T` is a type managed by Oilpan.
 
 ```c++
-class MyGarbageCollectedClass : public GarbageCollected<MyGarbageCollectedClass> {
-  // ...
-};
+class A final : public GarbageCollected<A> {};
+class B final {};
 
-class MyNotGarbageCollectedClass {
-  // ...
-};
-
-AddMemberIfNeeded<MyNotGarbageCollectedClass> v1;  // MyNotGarbageCollectedClass v1;
-AddMemberIfNeeded<int32_t> v2;                     // int32_t v2;
-AddMemberIfNeeded<MyGarbageCollectedClass> v3;     // Member<MyGarbageCollectedClass> v3;
+AddMemberIfNeeded<B> v1;       // B v1;
+AddMemberIfNeeded<int32_t> v2; // int32_t v2;
+AddMemberIfNeeded<A> v3;       // Member<A> v3;
 ```
 
 ### VectorOf<T>
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index a758b1e..841ba4f9 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -56,6 +56,10 @@
 class IncrementalMarkingScopeBase;
 }  // namespace incremental_marking_test
 
+namespace weakness_marking_test {
+class EphemeronCallbacksCounter;
+}  // namespace weakness_marking_test
+
 class AddressCache;
 class ConcurrentMarkingVisitor;
 class ThreadHeapStatsCollector;
@@ -446,6 +450,7 @@
   template <typename T>
   friend class Member;
   friend class ThreadState;
+  friend class weakness_marking_test::EphemeronCallbacksCounter;
 };
 
 template <typename T>
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.cc b/third_party/blink/renderer/platform/heap/marking_visitor.cc
index 65540b0..8ed5a88 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -101,10 +101,8 @@
     return;
   RegisterWeakCallback(weak_callback, weak_callback_parameter);
 
-  if (weak_desc.callback) {
-    weak_table_worklist_.Push(
-        {weak_desc.base_object_payload, weak_desc.callback});
-  }
+  if (weak_desc.callback)
+    weak_table_worklist_.Push(weak_desc);
 }
 
 bool MarkingVisitorCommon::VisitEphemeronKeyValuePair(
diff --git a/third_party/blink/renderer/platform/heap/trace_traits.h b/third_party/blink/renderer/platform/heap/trace_traits.h
index 23782d35..583aa7f7 100644
--- a/third_party/blink/renderer/platform/heap/trace_traits.h
+++ b/third_party/blink/renderer/platform/heap/trace_traits.h
@@ -241,8 +241,12 @@
 
     template <typename KeyType,
               typename ValueType,
-              bool ephemeron_semantics =
-                  WTF::IsWeak<KeyType>::value != WTF::IsWeak<ValueType>::value>
+              bool ephemeron_semantics = (WTF::IsWeak<KeyType>::value &&
+                                          !WTF::IsWeak<ValueType>::value &&
+                                          WTF::IsTraceable<ValueType>::value) ||
+                                         (WTF::IsWeak<ValueType>::value &&
+                                          !WTF::IsWeak<KeyType>::value &&
+                                          WTF::IsTraceable<KeyType>::value)>
     struct GetWeakTraceDescriptorKVPImpl {
       static TraceDescriptor GetWeakTraceDescriptor(void* backing) {
         return {backing, nullptr};
diff --git a/third_party/blink/renderer/platform/heap/weakness_marking_test.cc b/third_party/blink/renderer/platform/heap/weakness_marking_test.cc
index f493a3ef..2a8869b 100644
--- a/third_party/blink/renderer/platform/heap/weakness_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/weakness_marking_test.cc
@@ -147,4 +147,57 @@
   EXPECT_EQ(1u, map->size());
 }
 
+namespace weakness_marking_test {
+
+class EphemeronCallbacksCounter
+    : public GarbageCollected<EphemeronCallbacksCounter> {
+ public:
+  EphemeronCallbacksCounter(size_t* count_holder)
+      : count_holder_(count_holder) {}
+
+  void Trace(Visitor* visitor) {
+    visitor->RegisterWeakCallbackMethod<EphemeronCallbacksCounter,
+                                        &EphemeronCallbacksCounter::Callback>(
+        this);
+  }
+
+  void Callback(const WeakCallbackInfo& info) {
+    *count_holder_ = ThreadState::Current()->Heap().ephemeron_callbacks_.size();
+  }
+
+ private:
+  size_t* count_holder_;
+};
+
+TEST_F(WeaknessMarkingTest, UntracableEphemeronIsNotRegsitered) {
+  size_t ephemeron_count;
+  Persistent<EphemeronCallbacksCounter> ephemeron_callbacks_counter =
+      MakeGarbageCollected<EphemeronCallbacksCounter>(&ephemeron_count);
+  TestSupportingGC::PreciselyCollectGarbage();
+  size_t old_ephemeron_count = ephemeron_count;
+  using Map = HeapHashMap<WeakMember<IntegerObject>, int>;
+  Persistent<Map> map = MakeGarbageCollected<Map>();
+  map->insert(MakeGarbageCollected<IntegerObject>(1), 2);
+  TestSupportingGC::PreciselyCollectGarbage();
+  // Ephemeron value is not traceable, thus the map shouldn't be treated as an
+  // ephemeron.
+  EXPECT_EQ(old_ephemeron_count, ephemeron_count);
+}
+
+TEST_F(WeaknessMarkingTest, TracableEphemeronIsRegsitered) {
+  size_t ephemeron_count;
+  Persistent<EphemeronCallbacksCounter> ephemeron_callbacks_counter =
+      MakeGarbageCollected<EphemeronCallbacksCounter>(&ephemeron_count);
+  TestSupportingGC::PreciselyCollectGarbage();
+  size_t old_ephemeron_count = ephemeron_count;
+  using Map = HeapHashMap<WeakMember<IntegerObject>, Member<IntegerObject>>;
+  Persistent<Map> map = MakeGarbageCollected<Map>();
+  map->insert(MakeGarbageCollected<IntegerObject>(1),
+              MakeGarbageCollected<IntegerObject>(2));
+  TestSupportingGC::PreciselyCollectGarbage();
+  EXPECT_NE(old_ephemeron_count, ephemeron_count);
+}
+
+}  // namespace weakness_marking_test
+
 }  // namespace blink
diff --git a/third_party/blink/tools/blinkpy/common/net/web_test_results.py b/third_party/blink/tools/blinkpy/common/net/web_test_results.py
index d525917..c454174 100644
--- a/third_party/blink/tools/blinkpy/common/net/web_test_results.py
+++ b/third_party/blink/tools/blinkpy/common/net/web_test_results.py
@@ -40,23 +40,21 @@
 
     def suffixes_for_test_result(self):
         suffixes = set()
-        # add extensions for mismatch failures
-        if self.has_mismatch_with_baseline():
-            if 'expected_text' in self._result_dict['artifacts']:
-                suffixes.add('txt')
-            if 'expected_image' in self._result_dict['artifacts']:
-                suffixes.add('png')
-            if 'expected_audio' in self._result_dict['artifacts']:
-                suffixes.add('wav')
-
-        # add extensions for missing baseline file types
-        if self.is_missing_baseline():
-            if 'actual_text' in self._result_dict['artifacts']:
-                suffixes.add('txt')
-            if 'actual_image' in self._result_dict['artifacts']:
-                suffixes.add('png')
-            if 'actual_audio' in self._result_dict['artifacts']:
-                suffixes.add('wav')
+        artifact_names = self._result_dict.get('artifacts', {}).keys()
+        # Add extensions for mismatches.
+        if 'actual_text' in artifact_names:
+            suffixes.add('txt')
+        if 'actual_image' in artifact_names:
+            suffixes.add('png')
+        if 'actual_audio' in artifact_names:
+            suffixes.add('wav')
+        # Add extensions for missing baselines.
+        if self.is_missing_text():
+            suffixes.add('txt')
+        if self.is_missing_image():
+            suffixes.add('png')
+        if self.is_missing_audio():
+            suffixes.add('wav')
         return suffixes
 
     def result_dict(self):
@@ -92,13 +90,16 @@
     def last_retry_result(self):
         return self.actual_results().split()[-1]
 
-    def has_mismatch_with_baseline(self):
-        """returns true when a test failed because there was a mismatch
-        between a tests actual output and baseline"""
+    def has_non_reftest_mismatch(self):
+        """Returns true if a test without reference failed due to mismatch.
+
+        This happens when the actual output of a non-reftest does not match the
+        baseline, including an implicit all-PASS testharness baseline (i.e. a
+        previously all-PASS testharness test starts to fail)."""
         actual_results = self.actual_results().split(' ')
         artifact_names = self._result_dict.get('artifacts', {}).keys()
         return ('FAIL' in actual_results and
-                any(artifact_name.startswith('expected')
+                any(artifact_name.startswith('actual')
                     for artifact_name in artifact_names) and
                 'reference_file_mismatch' not in artifact_names and
                 'reference_file_match' not in artifact_names)
diff --git a/third_party/blink/tools/blinkpy/common/net/web_test_results_unittest.py b/third_party/blink/tools/blinkpy/common/net/web_test_results_unittest.py
index 40a2666..6020153 100644
--- a/third_party/blink/tools/blinkpy/common/net/web_test_results_unittest.py
+++ b/third_party/blink/tools/blinkpy/common/net/web_test_results_unittest.py
@@ -37,40 +37,66 @@
     "tests": {
         "fast": {
             "dom": {
-                "prototype-inheritance.html": {
+                "many-mismatches.html": {
                     "expected": "PASS",
-                    "actual": "TEXT",
+                    "actual": "FAIL",
+                    "artifacts": {
+                        "actual_text": ["fast/dom/many-mismatches-actual.txt"],
+                        "expected_text": ["fast/dom/many-mismatches-expected.txt"],
+                        "actual_image": ["fast/dom/many-mismatches-actual.png"],
+                        "expected_image": ["fast/dom/many-mismatches-expected.png"]
+                    },
                     "is_unexpected": true
                 },
-                "prototype-banana.html": {
+                "mismatch-implicit-baseline.html": {
+                    "expected": "PASS",
+                    "actual": "FAIL",
+                    "artifacts": {
+                        "actual_text": ["fast/dom/mismatch-implicit-baseline-actual.txt"]
+                    },
+                    "is_unexpected": true
+                },
+                "reference-mismatch.html": {
+                    "expected": "PASS",
+                    "actual": "FAIL",
+                    "artifacts": {
+                        "actual_image": ["fast/dom/reference-mismatch-actual.png"],
+                        "expected_image": ["fast/dom/reference-mismatch-expected.png"],
+                        "reference_file_mismatch": ["reference-mismatch-ref.html"]
+                    },
+                    "is_unexpected": true
+                },
+                "unexpected-pass.html": {
                     "expected": "FAIL",
                     "actual": "PASS",
                     "is_unexpected": true
                 },
-                "prototype-taco.html": {
+                "unexpected-flaky.html": {
                     "expected": "PASS",
-                    "actual": "PASS TEXT",
+                    "actual": "PASS FAIL",
                     "is_unexpected": true
                 },
-                "prototype-chocolate.html": {
+                "expected-flaky.html": {
+                    "expected": "PASS FAIL",
+                    "actual": "PASS FAIL"
+                },
+                "expected-fail.html": {
                     "expected": "FAIL",
-                    "actual": "IMAGE+TEXT"
+                    "actual": "FAIL"
                 },
-                "prototype-strawberry.html": {
+                "missing-text.html": {
                     "expected": "PASS",
-                    "actual": "IMAGE PASS",
-                    "is_unexpected": true
-                },
-                "prototype-crashy.html": {
-                    "expected": "PASS",
-                    "actual": "CRASH",
-                    "is_unexpected": true
-                },
-                "prototype-newtest.html": {
-                    "expected": "PASS",
-                    "actual": "MISSING",
+                    "actual": "FAIL",
+                    "artifacts": {
+                        "actual_text": ["fast/dom/missing-text-actual.txt"]
+                    },
                     "is_unexpected": true,
                     "is_missing_text": true
+                },
+                "prototype-slow.html": {
+                    "expected": "SLOW",
+                    "actual": "FAIL",
+                    "is_unexpected": true
                 }
             }
         },
@@ -78,7 +104,7 @@
             "dynamic-updates": {
                 "SVGFEDropShadowElement-dom-stdDeviation-attr.html": {
                     "expected": "PASS",
-                    "actual": "IMAGE",
+                    "actual": "FAIL",
                     "has_stderr": true,
                     "is_unexpected": true
                 }
@@ -86,17 +112,16 @@
         }
     },
     "skipped": 450,
-    "num_regressions": 15,
+    "num_regressions": 6,
     "layout_tests_dir": "/b/build/slave/Webkit_Mac10_5/build/src/third_party/blink/web_tests",
     "version": 3,
-    "num_passes": 77,
-    "fixable": 1220,
-    "num_flaky": 0,
+    "num_passes": 1,
+    "num_flaky": 1,
     "chromium_revision": "1234",
     "builder_name": "mock_builder_name"
 });"""
 
-    def test_results_from_string(self):
+    def test_empty_results_from_string(self):
         self.assertIsNone(WebTestResults.results_from_string(None))
         self.assertIsNone(WebTestResults.results_from_string(''))
 
@@ -109,48 +134,49 @@
     def test_chromium_revision(self):
         self.assertEqual(WebTestResults.results_from_string(self.example_full_results_json).chromium_revision(), 1234)
 
-    def test_actual_results(self):
-        results = WebTestResults.results_from_string(self.example_full_results_json)
-        self.assertEqual(results.result_for_test('fast/dom/prototype-banana.html').actual_results(), 'PASS')
-        self.assertEqual(results.result_for_test('fast/dom/prototype-taco.html').actual_results(), 'PASS TEXT')
-        self.assertFalse(results.result_for_test('nonexistant.html'))
-
     def test_didnt_run_as_expected_results(self):
         results = WebTestResults.results_from_string(self.example_full_results_json)
         self.assertEqual(
             [r.test_name() for r in results.didnt_run_as_expected_results()],
             [
-                'fast/dom/prototype-banana.html',
-                'fast/dom/prototype-crashy.html',
-                'fast/dom/prototype-inheritance.html',
-                'fast/dom/prototype-newtest.html',
-                'fast/dom/prototype-strawberry.html',
-                'fast/dom/prototype-taco.html',
+                'fast/dom/many-mismatches.html',
+                'fast/dom/mismatch-implicit-baseline.html',
+                'fast/dom/missing-text.html',
+                'fast/dom/prototype-slow.html',
+                'fast/dom/reference-mismatch.html',
+                'fast/dom/unexpected-flaky.html',
+                'fast/dom/unexpected-pass.html',
                 'svg/dynamic-updates/SVGFEDropShadowElement-dom-stdDeviation-attr.html',
             ])
 
-    def test_didnt_run_as_expected_slow_test(self):
-        results = WebTestResults({
-            'tests': {
-                'fast': {
-                    'dom': {
-                        'prototype-fast.html': {
-                            'expected': 'PASS',
-                            'actual': 'TEXT',
-                            'is_unexpected': True,
-                        },
-                        'prototype-slow.html': {
-                            'expected': 'SLOW',
-                            'actual': 'TEXT',
-                            'is_unexpected': True,
-                        }
-                    }
-                }
-            }
-        })
-        self.assertEqual(
-            [r.test_name() for r in results.didnt_run_as_expected_results()],
-            [
-                'fast/dom/prototype-fast.html',
-                'fast/dom/prototype-slow.html',
-            ])
+    def test_result_for_test_non_existent(self):
+        results = WebTestResults.results_from_string(self.example_full_results_json)
+        self.assertFalse(results.result_for_test('nonexistent.html'))
+
+    # The following are tests for a single WebTestResult.
+
+    def test_actual_results(self):
+        results = WebTestResults.results_from_string(self.example_full_results_json)
+        self.assertEqual(results.result_for_test('fast/dom/unexpected-pass.html').actual_results(), 'PASS')
+        self.assertEqual(results.result_for_test('fast/dom/unexpected-flaky.html').actual_results(), 'PASS FAIL')
+
+    def test_expected_results(self):
+        results = WebTestResults.results_from_string(self.example_full_results_json)
+        self.assertEqual(results.result_for_test('fast/dom/many-mismatches.html').expected_results(), 'PASS')
+        self.assertEqual(results.result_for_test('fast/dom/expected-flaky.html').expected_results(), 'PASS FAIL')
+
+    def test_has_non_reftest_mismatch(self):
+        results = WebTestResults.results_from_string(self.example_full_results_json)
+        self.assertTrue(results.result_for_test('fast/dom/many-mismatches.html').has_non_reftest_mismatch())
+        self.assertTrue(results.result_for_test('fast/dom/mismatch-implicit-baseline.html').has_non_reftest_mismatch())
+        self.assertFalse(results.result_for_test('fast/dom/reference-mismatch.html').has_non_reftest_mismatch())
+
+    def test_is_missing_baseline(self):
+        results = WebTestResults.results_from_string(self.example_full_results_json)
+        self.assertTrue(results.result_for_test('fast/dom/missing-text.html').is_missing_baseline())
+        self.assertFalse(results.result_for_test('fast/dom/many-mismatches.html').is_missing_baseline())
+
+    def test_suffixes_for_test_result(self):
+        results = WebTestResults.results_from_string(self.example_full_results_json)
+        self.assertSetEqual(results.result_for_test('fast/dom/many-mismatches.html').suffixes_for_test_result(), {'txt', 'png'})
+        self.assertSetEqual(results.result_for_test('fast/dom/missing-text.html').suffixes_for_test_result(), {'txt'})
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py
index 5b664cd..13ffccd 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py
@@ -327,7 +327,7 @@
         unexpected_results = web_test_results.didnt_run_as_expected_results()
         tests = sorted(
             r.test_name() for r in unexpected_results
-            if r.is_missing_baseline() or r.has_mismatch_with_baseline())
+            if r.is_missing_baseline() or r.has_non_reftest_mismatch())
 
         new_failures = self._fetch_tests_with_new_failures(build)
         if new_failures is None:
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 9bee405..db4ed09 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -83,7 +83,7 @@
 crbug.com/420008 virtual/threaded/http/tests/devtools/tracing/ [ Slow ]
 crbug.com/902685 http/tests/devtools/isolated-code-cache/ [ Slow ]
 crbug.com/902685 virtual/site-isolated-code-cache/http/tests/devtools/isolated-code-cache/ [ Slow ]
-crbug.com/902685 virtual/not-site-per-process/http/tests/devtools/isolated-code-cache/ [ Slow ]
+crbug.com/902685 virtual/not-split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/ [ Slow ]
 crbug.com/902685 http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Slow ]
 crbug.com/902685 virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Slow ]
 crbug.com/902685 virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/ [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 9611b2d..87ebd7777 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3451,7 +3451,11 @@
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-010.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-009.html [ Failure ]
 crbug.com/626703 external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
+crbug.com/981970 external/wpt/fetch/http-cache/split-cache.tentative.html [ Skip ]
 crbug.com/626703 virtual/omt-worker-fetch/external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
+crbug.com/981970 virtual/omt-worker-fetch/external/wpt/fetch/http-cache/split-cache.tentative.html [ Skip ]
+crbug.com/626703 virtual/not-split-http-cache/external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
+crbug.com/626703 virtual/split-http-cache/external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-fonts/font-feature-settings-descriptor-01.html [ Failure ]
 crbug.com/626703 [ Win10 ] external/wpt/fetch/api/redirect/redirect-count.any.worker.html [ Timeout ]
 crbug.com/626703 [ Win10 ] external/wpt/fetch/api/redirect/redirect-count.any.html [ Timeout ]
@@ -5850,10 +5854,9 @@
 crbug.com/1019613 http/tests/devtools/sources/debugger/debug-inlined-scripts.js [ Pass Failure ]
 
 # eval+Trusted Types failures while the feature is implemented.
-crbug.com/940927 external/wpt/trusted-types/trusted-types-eval-reporting-report-only.tentative.https.html [ Timeout ]
-crbug.com/940927 external/wpt/trusted-types/trusted-types-eval-reporting.tentative.https.html [ Failure ]
-crbug.com/940927 external/wpt/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.tentative.https.html [ Timeout ]
-crbug.com/940927 external/wpt/trusted-types/eval-with-permissive-csp.tentative.html [ Failure ]
+external/wpt/trusted-types/eval-csp-no-tt.tentative.html [ Failure ]
+external/wpt/trusted-types/eval-no-csp-no-tt.tentative.html [ Failure ]
+external/wpt/trusted-types/eval-no-csp-no-tt-default-policy.tentative.html [ Failure ]
 
 # Sheriff 2019-10-31
 crbug.com/1020036 [ Debug ] external/wpt/lifecycle/freeze.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 6ffce242..ed0394d 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -434,6 +434,12 @@
              "--disable-site-isolation-trials"]
   },
   {
+    "prefix": "not-split-http-cache-not-site-per-process",
+    "bases": ["http/tests/devtools/isolated-code-cache"],
+    "args": ["--disable-features=SplitCacheByNetworkIsolationKey",
+             "--disable-site-isolation-trials"]
+  },
+  {
     "prefix": "not-site-per-process",
     "bases": ["external/wpt/FileAPI/url/multi-global-origin-serialization.sub.html",
               "external/wpt/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html",
@@ -449,7 +455,6 @@
               "external/wpt/wasm/serialization/module/window-domain-success.sub.html",
               "external/wpt/wasm/serialization/module/window-similar-but-cross-origin-success.sub.html",
               "http/tests/devtools/debugger/fetch-breakpoints.js",
-              "http/tests/devtools/isolated-code-cache",
               "http/tests/devtools/oopif/oopif-storage.js",
               "http/tests/dom/EventListener-incumbent-global-1.html",
               "http/tests/dom/EventListener-incumbent-global-2.html",
@@ -629,5 +634,15 @@
     "prefix": "hdr",
     "bases": [],
     "args": ["--force-color-profile=scrgb-linear"]
+  },
+  {
+    "prefix": "split-http-cache",
+    "bases": ["external/wpt/fetch/http-cache"],
+    "args": ["--enable-features=SplitCacheByNetworkIsolationKey"]
+  },
+  {
+    "prefix": "not-split-http-cache",
+    "bases": ["external/wpt/fetch/http-cache"],
+    "args": ["--disable-features=SplitCacheByNetworkIsolationKey"]
   }
 ]
diff --git a/third_party/blink/web_tests/WebDriverExpectations b/third_party/blink/web_tests/WebDriverExpectations
index 5807deb..13b207c1 100644
--- a/third_party/blink/web_tests/WebDriverExpectations
+++ b/third_party/blink/web_tests/WebDriverExpectations
@@ -77,6 +77,7 @@
 crbug.com/626703 [ Mac ] external/wpt/webdriver/tests/interface.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/webdriver/tests/interface.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/iframe.py>>test_source_origin[cross_origin] [ Failure ]
+crbug.com/1023255 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/iframe.py>>test_source_origin[same_origin] [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_screenshot/iframe.py>>test_source_origin[cross_origin] [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/switch_to_frame/cross_origin.py>>test_nested_cross_origin_iframe [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/switch_to_frame/cross_origin.py>>test_cross_origin_iframe [ Failure ]
diff --git a/third_party/blink/web_tests/accessibility/aria-owns-dynamic-changes.html b/third_party/blink/web_tests/accessibility/aria-owns-dynamic-changes.html
index 123c306a..73db0a8 100644
--- a/third_party/blink/web_tests/accessibility/aria-owns-dynamic-changes.html
+++ b/third_party/blink/web_tests/accessibility/aria-owns-dynamic-changes.html
@@ -17,85 +17,32 @@
 
     futureParent.parentElement.appendChild(child);
     assert_equals(axFutureParent.childrenCount, 1, "after appending child");
-    assert_array_equals(futureParent.ariaOwnsElements, [child]);
 
     child.id = "";
     assert_equals(axFutureParent.childrenCount, 0, "after clearing id");
-    assert_equals(futureParent.ariaOwnsElements, null);
 
     child.id = "future_child";
     assert_equals(axFutureParent.childrenCount, 1, "after setting id");
-    assert_array_equals(futureParent.ariaOwnsElements, [child]);
 
     futureParent.setAttribute("aria-owns", "");
     assert_equals(axFutureParent.childrenCount, 0, "after clearing aria-owns");
-    assert_equals(futureParent.ariaOwnsElements, null);
 
     futureParent.setAttribute("aria-owns", "future_child");
     assert_equals(axFutureParent.childrenCount, 1, "after setting aria-oens");
-    assert_array_equals(futureParent.ariaOwnsElements, [child]);
 
     child.style.visibility = 'hidden';
     assert_equals(axFutureParent.childrenCount, 1, "after setting hidden");
     assert_true(axFutureParent.childAtIndex(0).isIgnored);
-    assert_array_equals(futureParent.ariaOwnsElements, [child]);
 
     child.style.visibility = 'visible';
     assert_equals(axFutureParent.childrenCount, 1, "after setting visible");
-    assert_array_equals(futureParent.ariaOwnsElements, [child]);
 
     child.remove();
     assert_equals(axFutureParent.childrenCount, 0);
-    assert_equals(futureParent.ariaOwnsElements, null);
 }, "Aria-owns is dynamically recomputed.");
 </script>
 
 <div class="container">
-  <div id="explicitParent"></div>
-</div>
-
-<script>
-test(function(t) {
-  const axParent = accessibilityController.accessibleElementById("explicitParent");
-  const child = document.createElement("li");
-
-  // Check element reflection before inserting into the DOM.
-  explicitParent.ariaOwnsElements = [child];
-
-  assert_array_equals(explicitParent.ariaOwnsElements, [], "Element is not reflected as was not in valid scope on insertion.");
-  assert_equals(explicitParent.getAttribute("aria-owns"), "", "Content attribute should be set to empty string due to invalid element.");
-  assert_equals(axParent.childrenCount, 0, "Accessible node should have no children.");
-
-
-  // Add the element into the DOM, and set the IDL attribute.
-  explicitParent.parentElement.appendChild(child);
-  explicitParent.ariaOwnsElements = [child];
-
-  assert_array_equals(explicitParent.ariaOwnsElements, [child], "Element reference is reflected, despite the element having no id");
-  assert_equals(explicitParent.getAttribute("aria-owns"), "", "Content attribute is empty string, as element has no id");
-  assert_equals(axParent.childrenCount, 1, "If explicitly set, even without an ID the node should be in the accessibility tree.");
-
-  child.id = "futureExplicitElement";
-  const axChild = accessibilityController.accessibleElementById("futureExplicitElement");
-
-  assert_array_equals(explicitParent.ariaOwnsElements, [child], "Element has the attr associated element now.");
-  assert_equals(explicitParent.getAttribute("aria-owns"), "", "Content attribute reflects an empty string because the id was missing at the time of setting.");
-  assert_equals(axParent.childrenCount, 1, "After inserting child into DOM and explicitly setting it.")
-  assert_equals(axParent.childAtIndex(0), axChild);
-
-  // Remove the explicitly set attr by removing the content attribute.
-  explicitParent.removeAttribute("aria-owns");
-  assert_equals(explicitParent.ariaOwnsElements, null, "Reflected elements should be null since content attribute was removed.");
-  assert_equals(axParent.childrenCount, 0, "Accessible node should have no children since content attribute was removed.");
-
-  // Set the content attribute.
-  explicitParent.setAttribute("aria-owns", "futureExplicitElement");
-  assert_equals(axParent.childrenCount, 1, "Accessible node should be updated to have child since content attribute was set");
-  assert_equals(axParent.childAtIndex(0), axChild);
-}, "Explicitly set elements are reflected in the AXTree");
-</script>
-
-<div class="container">
   <div id="source1" aria-owns="target1"></div>
   <div id="target"></div>
 </div>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/oversized-images-reporting.html b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/oversized-images-reporting.html
index baa866d0..ef365e06 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/oversized-images-reporting.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/oversized-images-reporting.html
@@ -3,6 +3,7 @@
     <head>
         <script src='/resources/testharness.js'></script>
         <script src='/resources/testharnessreport.js'></script>
+        <script src='../resources/feature-policy-report-json.js'></script>
     </head>
     <body>
         <img src="./oversized.jpg" alt="oversized image" width="50" height="50">
@@ -19,6 +20,7 @@
                     assert_equals(rbody.sourceFile, document.getElementsByTagName('img')[0].src);
                     assert_equals(rbody.lineNumber, null);
                     assert_equals(rbody.columnNumber, null);
+                    check_report_json(reports[0]);
                 }),
                 {types: ['feature-policy-violation'], buffered: true}
             ).observe();
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/payment-reporting.https.html b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/payment-reporting.https.html
index 6655210..80d0b0f 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/payment-reporting.https.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/payment-reporting.https.html
@@ -3,6 +3,7 @@
   <head>
     <script src='/resources/testharness.js'></script>
     <script src='/resources/testharnessreport.js'></script>
+    <script src='../resources/feature-policy-report-json.js'></script>
   </head>
   <body>
     <script>
@@ -17,6 +18,7 @@
   assert_equals(typeof report.body.lineNumber, "number");
   assert_equals(typeof report.body.columnNumber, "number");
   assert_equals(report.body.disposition, "enforce");
+  check_report_json(report);
 };
 
 new ReportingObserver(t.step_func_done(check_report_format),
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html
index d92a685..9e526b90 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html
@@ -3,6 +3,7 @@
   <head>
     <script src='/resources/testharness.js'></script>
     <script src='/resources/testharnessreport.js'></script>
+    <script src='../resources/feature-policy-report-json.js'></script>
   </head>
   <body>
     <script>
@@ -17,6 +18,7 @@
   assert_equals(typeof report.body.lineNumber, "number");
   assert_equals(typeof report.body.columnNumber, "number");
   assert_equals(report.body.disposition, "enforce");
+  check_report_json(report);
 };
 
 new ReportingObserver(t.step_func_done(check_report_format),
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-report-json.js b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-report-json.js
new file mode 100644
index 0000000..08a0ecad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-report-json.js
@@ -0,0 +1,20 @@
+/**
+ * @fileoverview functions for ensuring feature policy report is serializable
+ */
+
+const check_report_json = (report) => {
+  // Ensures toJSON method exists on report.
+  assert_equals(typeof report.toJSON, "function");
+  const report_json = report.toJSON();
+  // Ensures toJSON() call is successful.
+  assert_equals(report.type, report_json.type);
+  assert_equals(report.url, report_json.url);
+  assert_equals(report.body.featureId, report_json.body.featureId);
+  assert_equals(report.body.disposition, report_json.body.disposition);
+  assert_equals(report.body.sourceFile, report_json.body.sourceFile);
+  assert_equals(report.body.lineNumber, report_json.body.lineNumber);
+  assert_equals(report.body.columnNumber, report_json.body.columnNumber);
+  // Ensures JSON.stringify() serializes the report correctly.
+  assert_false(JSON.stringify(report) === "{}");
+  assert_equals(JSON.stringify(report), JSON.stringify(report_json));
+}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/fetch/http-cache/http-cache.js b/third_party/blink/web_tests/external/wpt/fetch/http-cache/http-cache.js
index 3f4a2e7e..ce49ad8 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/http-cache/http-cache.js
+++ b/third_party/blink/web_tests/external/wpt/fetch/http-cache/http-cache.js
@@ -42,6 +42,12 @@
   return function () {
     var uuid = token()
     var requests = expandTemplates(test)
+    var fetchFunctions = makeFetchFunctions(requests, uuid)
+    return runTest(fetchFunctions, requests, uuid)
+  }
+}
+
+function makeFetchFunctions(requests, uuid) {
     var fetchFunctions = []
     for (let i = 0; i < requests.length; ++i) {
       fetchFunctions.push({
@@ -62,6 +68,10 @@
         pauseAfter: 'pause_after' in requests[i]
       })
     }
+    return fetchFunctions
+}
+
+function runTest(fetchFunctions, requests, uuid) {
     var idx = 0
     function runNextStep () {
       if (fetchFunctions.length) {
@@ -86,7 +96,6 @@
         checkRequests(requests, testState)
         return Promise.resolve()
       })
-  }
 }
 
 function expandTemplates (test) {
@@ -226,10 +235,14 @@
 
 function makeTestUrl (uuid, config) {
   var arg = ''
+  var base_url = ''
+  if ('base_url' in config) {
+    base_url = config.base_url
+  }
   if ('query_arg' in config) {
     arg = `&target=${config.query_arg}`
   }
-  return `resources/http-cache.py?dispatch=test&uuid=${uuid}${arg}`
+  return `${base_url}resources/http-cache.py?dispatch=test&uuid=${uuid}${arg}`
 }
 
 function getServerState (uuid) {
diff --git a/third_party/blink/web_tests/external/wpt/fetch/http-cache/resources/http-cache.py b/third_party/blink/web_tests/external/wpt/fetch/http-cache/resources/http-cache.py
index e64fe6df..351b2eb3 100755
--- a/third_party/blink/web_tests/external/wpt/fetch/http-cache/resources/http-cache.py
+++ b/third_party/blink/web_tests/external/wpt/fetch/http-cache/resources/http-cache.py
@@ -13,6 +13,9 @@
 def main(request, response):
     dispatch = request.GET.first("dispatch", None)
     uuid = request.GET.first("uuid", None)
+
+    if request.method == "OPTIONS":
+        return handle_preflight(uuid, request, response)
     if not uuid:
         response.status = (404, "Not Found")
         response.headers.set("Content-Type", "text/plain")
@@ -25,6 +28,14 @@
     response.headers.set("Content-Type", "text/plain")
     return "Fallthrough"
 
+def handle_preflight(uuid, request, response):
+    response.status = (200, "OK")
+    response.headers.set("Access-Control-Allow-Origin", "*")
+    response.headers.set("Access-Control-Allow-Methods", "GET")
+    response.headers.set("Access-Control-Allow-Headers", "*")
+    response.headers.set("Access-Control-Max-Age", "86400")
+    return "Preflight request"
+
 def handle_state(uuid, request, response):
     response.headers.set("Content-Type", "text/plain")
     return json.dumps(request.server.stash.take(uuid))
diff --git a/third_party/blink/web_tests/external/wpt/fetch/http-cache/resources/split-origin-popup.html b/third_party/blink/web_tests/external/wpt/fetch/http-cache/resources/split-origin-popup.html
new file mode 100644
index 0000000..d94ac7f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/http-cache/resources/split-origin-popup.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>HTTP Cache - helper</title>
+  <meta name="help" href="https://fetch.spec.whatwg.org/#request">
+  <meta name="timeout" content="normal">
+  <script src="/resources/testharness.js"></script>
+  <script src="../http-cache.js"></script>
+</head>
+<body>
+<script>
+   window.addEventListener("message", function listener(event) {
+      window.removeEventListener("message", listener)
+
+      var fetchFunction = makeFetchFunctions(event.data.requests, event.data.uuid)[event.data.index]
+      fetchFunction.code(event.data.index).then(
+         function(response) {
+            event.source.postMessage("success", event.origin)
+         },
+         function(response) {
+            event.source.postMessage("success", event.origin)
+         }
+      )
+   })
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/http-cache/split-cache.tentative.html b/third_party/blink/web_tests/external/wpt/fetch/http-cache/split-cache.tentative.html
new file mode 100644
index 0000000..533dd6a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/http-cache/split-cache.tentative.html
@@ -0,0 +1,118 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>HTTP Cache - Partioning by top-level origin</title>
+  <meta name="help" href="https://fetch.spec.whatwg.org/#request">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/common/utils.js"></script>
+  <script src="/common/get-host-info.sub.js"></script>
+  <script src="http-cache.js"></script>
+</head>
+<body>
+<script>
+const host = get_host_info();
+
+// We run this entire test twice: first with a same-origin then with a cross-origin popup
+function performFullTest(is_same_origin_test) {
+  const POPUP_HTTP_ORIGIN = is_same_origin_test ? host.HTTP_ORIGIN : host.HTTP_REMOTE_ORIGIN
+  const LOCAL_HTTP_ORIGIN = host.HTTP_ORIGIN
+
+  const popupBaseURL = POPUP_HTTP_ORIGIN + window.location.pathname.replace(/\/[^\/]*$/, '/') ;
+  const localBaseURL = LOCAL_HTTP_ORIGIN + window.location.pathname.replace(/\/[^\/]*$/, '/') ;
+
+  var test = {
+    name: "HTTP Cache is partitioned by top-frame origin",
+    requests: [
+      {
+        response_headers: [
+              ["Expires", (30 * 24 * 60 * 60)]
+            ],
+        base_url: localBaseURL
+      },
+      {
+        base_url: localBaseURL
+      },
+      {
+        request_headers: [
+          ["Cache-Control", "no-cache"]
+        ],
+        // If the popup's request was a cache hit, we would only expect 2
+        // requests to the server. If it was a cache miss, we would expect 3.
+        expected_response_headers: [
+          ["server-request-count", is_same_origin_test ? "2" : "3"]
+        ],
+        base_url: localBaseURL
+      }
+    ]
+  }
+
+  var uuid = token()
+  var local_requests = expandTemplates(test)
+  var fetchFns = makeFetchFunctions(local_requests, uuid)
+
+  var popup_requests = expandTemplates(test)
+
+  // Request the resource with a long cache expiry
+  function local_fetch() {
+    return fetchFns[0].code(0)
+  }
+
+  function popup_fetch() {
+    return new Promise(function(resolve, reject) {
+      var win = window.open(popupBaseURL + "resources/split-origin-popup.html")
+
+      // Post a message to intisearchte the popup's request and give the necessary
+      // information. Posted researchtedly to account for dropped messages as the
+      // popup is loading.
+      function postMessage(event) {
+        var payload = {
+          index: 1,
+          requests: popup_requests,
+          uuid: uuid
+        }
+        win.postMessage(payload, POPUP_HTTP_ORIGIN)
+      }
+      var messagePoster = setInterval(postMessage, 100)
+
+      // Listen for the result
+      function messageListener(event) {
+        if (event.origin !== POPUP_HTTP_ORIGIN) {
+          reject("Unknown error")
+        } else if (event.data === "success") {
+          resolve()
+        } else if (event.data === "error") {
+          reject("Error in popup")
+        } else {
+          return; // Ignore testharness.js internal messages
+        }
+        window.removeEventListener("message", messageListener)
+        clearInterval(messagePoster)
+        win.close()
+      }
+      window.addEventListener("message", messageListener)
+    })
+  }
+
+  function local_fetch2() {
+    return fetchFns[2].code(2)
+  }
+
+  // Final checks.
+  function check_server_info() {
+    return getServerState(uuid)
+      .then(function (testState) {
+        checkRequests(local_requests, testState)
+        return Promise.resolve()
+      })
+  }
+
+  promise_test(() => local_fetch().then(popup_fetch).then(local_fetch2).then(check_server_info))
+}
+
+performFullTest(true);
+performFullTest(false);
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/reporting/generateTestReport.html b/third_party/blink/web_tests/external/wpt/reporting/generateTestReport.html
index e3c273568..f47ea45c 100644
--- a/third_party/blink/web_tests/external/wpt/reporting/generateTestReport.html
+++ b/third_party/blink/web_tests/external/wpt/reporting/generateTestReport.html
@@ -16,6 +16,13 @@
         assert_equals(reports[0].type, "test");
         assert_true(reports[0].url.endsWith("reporting/generateTestReport.html"));
         assert_equals(reports[0].body.message, "Test message.");
+        // Ensure that the toJSON() call of the report are valid.
+        const reportJSON = reports[0].toJSON();
+        assert_equals(reports[0].type, reportJSON.type);
+        assert_equals(reports[0].url, reportJSON.url);
+        assert_equals(reports[0].body.message, reportJSON.body.message);
+        // Ensure that report can be successfully JSON serialized.
+        assert_equals(JSON.stringify(reports[0]), JSON.stringify(reportJSON));
       });
       test.done();
     });
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/block-eval.tentative.html b/third_party/blink/web_tests/external/wpt/trusted-types/block-eval.tentative.html
index f7d71325..cd4f582 100644
--- a/third_party/blink/web_tests/external/wpt/trusted-types/block-eval.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/block-eval.tentative.html
@@ -21,7 +21,9 @@
 
   test(t => {
     let a = 0;
-    eval(p.createScript('a="Hello transformed string"'));
+    assert_throws(new EvalError(), _ => {
+      eval(p.createScript('a="Hello transformed string"'));
+    });
     assert_equals(a, 0);
   }, "eval with TrustedScript throws (script-src blocks).");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/eval-csp-no-tt.tentative.html b/third_party/blink/web_tests/external/wpt/trusted-types/eval-csp-no-tt.tentative.html
new file mode 100644
index 0000000..e8ed577
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/eval-csp-no-tt.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script nonce="abc" src="/resources/testharness.js"></script>
+  <script nonce="abc" src="/resources/testharnessreport.js"></script>
+  <script nonce="abc" src="support/helper.sub.js"></script>
+  <meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-eval' 'nonce-abc'">
+</head>
+<body>
+<script nonce="abc">
+  const p = trustedTypes.createPolicy("p", {createScript: s => s});
+
+  test(t => {
+    assert_equals(eval(p.createScript("1+1")), 2);
+  }, "eval of TrustedScript works.");
+
+  test(t => {
+    assert_equals(eval('1+1'), 2);
+  }, "eval of string works.");
+
+  test(t => {
+    assert_equals(eval(42), 42);
+    assert_object_equals(eval({}), {});
+    assert_equals(eval(null), null);
+    assert_equals(eval(undefined), undefined);
+   }, "eval of !TrustedScript and !string works.");
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/eval-csp-tt-default-policy.tentative.html b/third_party/blink/web_tests/external/wpt/trusted-types/eval-csp-tt-default-policy.tentative.html
new file mode 100644
index 0000000..8f1926d9c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/eval-csp-tt-default-policy.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script nonce="abc" src="/resources/testharness.js"></script>
+  <script nonce="abc" src="/resources/testharnessreport.js"></script>
+  <script nonce="abc" src="support/helper.sub.js"></script>
+  <meta http-equiv="Content-Security-Policy" content="trusted-types *">
+</head>
+<body>
+<script>
+  trustedTypes.createPolicy("default", {createScript: s => s + 4});
+  const p = trustedTypes.createPolicy("p", {createScript: s => s});
+
+  test(t => {
+    assert_equals(eval(p.createScript('1+1')), 2);
+  }, "eval of TrustedScript works.");
+
+  test(t => {
+    assert_equals(eval('1+1'), 15);
+  }, "eval of string works.");
+
+  test(t => {
+    assert_equals(eval(42), 42);
+    assert_object_equals(eval({}), {});
+    assert_equals(eval(null), null);
+    assert_equals(eval(undefined), undefined);
+   }, "eval of !TrustedScript and !string works.");
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/eval-csp-tt-no-default-policy.tentative.html b/third_party/blink/web_tests/external/wpt/trusted-types/eval-csp-tt-no-default-policy.tentative.html
new file mode 100644
index 0000000..dc976d6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/eval-csp-tt-no-default-policy.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script nonce="abc" src="/resources/testharness.js"></script>
+  <script nonce="abc" src="/resources/testharnessreport.js"></script>
+  <script nonce="abc" src="support/helper.sub.js"></script>
+  <meta http-equiv="Content-Security-Policy" content="trusted-types *">
+</head>
+<body>
+<script>
+  const p = trustedTypes.createPolicy("p", {createScript: s => s});
+
+  test(t => {
+    assert_equals(eval(p.createScript('1+1')), 2);
+  }, "eval of TrustedScript works.");
+
+  test(t => {
+    assert_throws(new EvalError(), _ => eval('1+1'));
+  }, "eval of string fails.");
+
+  test(t => {
+    assert_equals(eval(42), 42);
+    assert_object_equals(eval({}), {});
+    assert_equals(eval(null), null);
+    assert_equals(eval(undefined), undefined);
+   }, "eval of !TrustedScript and !string works.");
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/eval-no-csp-no-tt-default-policy.tentative.html b/third_party/blink/web_tests/external/wpt/trusted-types/eval-no-csp-no-tt-default-policy.tentative.html
new file mode 100644
index 0000000..84c224e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/eval-no-csp-no-tt-default-policy.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script nonce="abc" src="/resources/testharness.js"></script>
+  <script nonce="abc" src="/resources/testharnessreport.js"></script>
+  <script nonce="abc" src="support/helper.sub.js"></script>
+  <!-- No CSP header. -->
+</head>
+<body>
+<script>
+  trustedTypes.createPolicy("default", {createScript: s => s + 4});
+  const p = trustedTypes.createPolicy("p", {createScript: s => s});
+
+  test(t => {
+    assert_equals(eval(p.createScript('1+1')), 2);
+  }, "eval of TrustedScript works.");
+
+  test(t => {
+    assert_equals(eval('1+1'), 2);
+  }, "eval of string works and does not call a default policy.");
+
+  test(t => {
+    assert_equals(eval(42), 42);
+    assert_object_equals(eval({}), {});
+    assert_equals(eval(null), null);
+    assert_equals(eval(undefined), undefined);
+   }, "eval of !TrustedScript and !string works.");
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/eval-no-csp-no-tt.tentative.html b/third_party/blink/web_tests/external/wpt/trusted-types/eval-no-csp-no-tt.tentative.html
new file mode 100644
index 0000000..45086a1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/eval-no-csp-no-tt.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script nonce="abc" src="/resources/testharness.js"></script>
+  <script nonce="abc" src="/resources/testharnessreport.js"></script>
+  <script nonce="abc" src="support/helper.sub.js"></script>
+  <!-- No CSP header. -->
+</head>
+<body>
+<script nonce="abc">
+  const p = trustedTypes.createPolicy("p", {createScript: s => s});
+
+  test(t => {
+    assert_equals(eval(p.createScript('1+1')), 2);
+  }, "eval of TrustedScript works.");
+
+  test(t => {
+    assert_equals(eval('1+1'), 2);
+  }, "eval of string works.");
+
+  test(t => {
+    assert_equals(eval(42), 42);
+    assert_object_equals(eval({}), {});
+    assert_equals(eval(null), null);
+    assert_equals(eval(undefined), undefined);
+   }, "eval of !TrustedScript and !string works.");
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.tentative.https.html b/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.tentative.https.html
index e15ecfa..7902df1 100644
--- a/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.tentative.https.html
@@ -88,8 +88,7 @@
     let p = Promise.resolve()
         .then(promise_violation("script-src"))
         .then(promise_flush());
-    expect_throws(_ => eval('script_run_beacon="should not run"'));
-    eval(scriptyPolicy.createScript('script_run_beacon="i ran"'));
+    expect_throws(_ => eval(scriptyPolicy.createScript('script_run_beacon="i ran"')));
     flush();
     assert_not_equals(script_run_beacon, 'i ran'); // Code did not run.
     return p;
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html b/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html
index 70afb97..10a951fa 100644
--- a/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html
@@ -180,6 +180,18 @@
   }, "Trusted Type violation report: sample for script innerText assignment");
 
   promise_test(t => {
+    let p = Promise.resolve()
+        .then(promise_violation("trusted-types one"))
+        .then(expect_blocked_uri("trusted-types-sink"))
+        .then(expect_sample("eval"))
+        .then(expect_sample("2+2"))
+        .then(promise_flush());
+    expect_throws(_ => eval("2+2"));
+    flush();
+    return p;
+  }, "Trusted Type violation report: sample for eval");
+
+  promise_test(t => {
     // We expect the sample string to always contain the name, and at least the
     // start of the value, but it should not be excessively long.
     let p = Promise.resolve()
diff --git a/third_party/blink/web_tests/external/wpt_automation/README b/third_party/blink/web_tests/external/wpt_automation/README
new file mode 100644
index 0000000..9cc4875
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt_automation/README
@@ -0,0 +1,14 @@
+This folder is for Chrome automation specific only. If your manual test
+can be automated using test_driver APIs exposed in wpt documented in
+https://web-platform-tests.org/writing-tests/testdriver.html
+use those APIs and drop the "manual" suffix from the name of the test.
+
+If your test can only be automated with Chrome only testing APIs
+such as eventSender then leave the "manual" in the name of the test
+and add a mirror automation script in this directory. You need to also
+make sure your test subdirectory is whitelisted under
+resources/testharnessreport.js.
+
+If you add an automation script here please also file an issue to capture
+the follow up work with the owners in that space to make that testing API
+cross browser.
diff --git a/third_party/blink/web_tests/http/tests/reporting-observer/csp.php b/third_party/blink/web_tests/http/tests/reporting-observer/csp.php
index 0df72aa75..0df988f 100644
--- a/third_party/blink/web_tests/http/tests/reporting-observer/csp.php
+++ b/third_party/blink/web_tests/http/tests/reporting-observer/csp.php
@@ -30,6 +30,24 @@
       assert_equals(reports[0].body.statusCode, 200);
       assert_equals(reports[0].body.lineNumber, null);
       assert_equals(reports[0].body.columnNumber, null);
+      // Ensure the toJSON call is successful.
+      const reportJSON = reports[0].toJSON();
+      assert_equals(reportJSON.type, reports[0].type);
+      assert_equals(reportJSON.url, reports[0].url);
+      assert_equals(typeof reportJSON.body, "object");
+      assert_equals(reportJSON.body.documentURL, reports[0].body.documentURL);
+      assert_equals(reportJSON.body.referrer, reports[0].body.referrer);
+      assert_equals(reportJSON.body.blockedURL, reports[0].body.blockedURL);
+      assert_equals(reportJSON.body.effectiveDirective, reports[0].body.effectiveDirective);
+      assert_equals(reportJSON.body.originalPolicy, reports[0].body.originalPolicy);
+      assert_equals(reportJSON.body.sourceFile, reports[0].body.sourceFile);
+      assert_equals(reportJSON.body.sample, reports[0].body.sample);
+      assert_equals(reportJSON.body.disposition, reports[0].body.disposition);
+      assert_equals(reportJSON.body.statusCode, reports[0].body.statusCode);
+      assert_equals(reportJSON.body.lineNumber, reports[0].body.lineNumber);
+      assert_equals(reportJSON.body.columnNumber, reports[0].body.columnNumber);
+      // Ensure that report can be successfully JSON serialized.
+      assert_equals(JSON.stringify(reports[0]), JSON.stringify(reportJSON));
     });
 
     test.done();
diff --git a/third_party/blink/web_tests/platform/linux/svg/as-background-image/svg-as-background-6-expected.png b/third_party/blink/web_tests/platform/linux/svg/as-background-image/svg-as-background-6-expected.png
index d2f3d56..a37fcde9 100644
--- a/third_party/blink/web_tests/platform/linux/svg/as-background-image/svg-as-background-6-expected.png
+++ b/third_party/blink/web_tests/platform/linux/svg/as-background-image/svg-as-background-6-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index 8fdea784..9037c9c 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index 0dd125d..319f53ae 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/as-background-image/svg-as-background-6-expected.png b/third_party/blink/web_tests/platform/mac/svg/as-background-image/svg-as-background-6-expected.png
index 28f15b9..349a5a9 100644
--- a/third_party/blink/web_tests/platform/mac/svg/as-background-image/svg-as-background-6-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/as-background-image/svg-as-background-6-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/svg/as-background-image/svg-as-background-6-expected.png b/third_party/blink/web_tests/platform/win/svg/as-background-image/svg-as-background-6-expected.png
index 4ae0f19..1e957a4 100644
--- a/third_party/blink/web_tests/platform/win/svg/as-background-image/svg-as-background-6-expected.png
+++ b/third_party/blink/web_tests/platform/win/svg/as-background-image/svg-as-background-6-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index f052a38..945f017 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index f327d1d..db615d8 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/reporting-observer/intervention.html b/third_party/blink/web_tests/reporting-observer/intervention.html
index 8e12d888..043d3ed 100644
--- a/third_party/blink/web_tests/reporting-observer/intervention.html
+++ b/third_party/blink/web_tests/reporting-observer/intervention.html
@@ -26,6 +26,20 @@
       assert_equals(typeof reports[0].body.lineNumber, "number");
       assert_equals(typeof reports[0].body.columnNumber, "number");
       assert_equals(typeof reports[0].body.message, "string");
+
+      // Ensure the toJSON call is successful.
+      const reportJSON = reports[0].toJSON();
+      assert_equals(reportJSON.type, reports[0].type);
+      assert_equals(reportJSON.url, reports[0].url);
+      assert_equals(typeof reportJSON.body, "object");
+      assert_equals(reportJSON.body.id, reports[0].body.id);
+      assert_equals(reportJSON.body.message, reports[0].body.message);
+      assert_equals(reportJSON.body.sourceFile, reports[0].body.sourceFile);
+      assert_equals(reportJSON.body.lineNumber, reports[0].body.lineNumber);
+      assert_equals(reportJSON.body.columnNumber, reports[0].body.columnNumber);
+      // Ensure that report can be successfully JSON serialized.
+      assert_false(JSON.stringify(reports[0]) === "{}");
+      assert_equals(JSON.stringify(reports[0]), JSON.stringify(reportJSON));
     });
 
     test.done();
diff --git a/third_party/blink/web_tests/reporting-observer/resources/deprecation.js b/third_party/blink/web_tests/reporting-observer/resources/deprecation.js
index 37a6724..ef2cf94 100644
--- a/third_party/blink/web_tests/reporting-observer/resources/deprecation.js
+++ b/third_party/blink/web_tests/reporting-observer/resources/deprecation.js
@@ -20,6 +20,23 @@
             "reporting-observer/resources/deprecation.js"));
         assert_equals(typeof report.body.lineNumber, "number");
         assert_equals(typeof report.body.columnNumber, "number");
+        // Ensure the toJSON call is successful.
+        const reportJSON = report.toJSON();
+        assert_equals(reportJSON.type, report.type);
+        assert_equals(reportJSON.url, report.url);
+        assert_equals(typeof reportJSON.body, "object");
+        assert_equals(reportJSON.body.id, report.body.id);
+        assert_equals(reportJSON.body.message, report.body.message);
+        assert_equals(reportJSON.body.sourceFile, report.body.sourceFile);
+        assert_equals(reportJSON.body.lineNumber, report.body.lineNumber);
+        assert_equals(reportJSON.body.columnNumber, report.body.columnNumber);
+        assert_equals(
+          JSON.stringify(reportJSON.body.anticipatedRemoval),
+          JSON.stringify(report.body.anticipatedRemoval)
+        );
+        // Ensure that report can be successfully JSON serialized.
+        assert_false(JSON.stringify(report) === "{}");
+        assert_equals(JSON.stringify(report), JSON.stringify(reportJSON));
       }
       assert_not_equals(reports[0].body.id, reports[1].body.id);
     });
diff --git a/third_party/blink/web_tests/virtual/not-site-per-process/README.md b/third_party/blink/web_tests/virtual/not-site-per-process/README.md
index 7da7f6c6..af1c076 100644
--- a/third_party/blink/web_tests/virtual/not-site-per-process/README.md
+++ b/third_party/blink/web_tests/virtual/not-site-per-process/README.md
@@ -15,11 +15,14 @@
 Tests under `virtual/not-site-per-process` are run with
 `--disable-site-isolation-trials` cmdline flag which turns off site
 isolation.  This is needed to preserve test coverage provided by around
-60 tests that fail when run with site isolation. `isolated-code-cache` tests are
-also run with `--disable-features=SplitCacheByNetworkIsolationKey` which turns
-off HTTP cache partitioning. This is needed as a test expects cross-origin
-resources to be cached. Equivalent tests with the feature enabled can be found
-under `virtual/split-http-cache-not-site-per-process`.
+60 tests that fail when run with site isolation.
+
+Instead of including `http/tests/devtools/isolated-code-cache` tests here, we
+split into two virtual test suites
+`virtual/not-split-http-cache-not-site-per-process` and
+`virtual/split-http-cache-not-site-per-process`, which disable and enable HTTP
+cache partitioning, respectively. This split is needed as a test checks whether
+cross-origin resources were cached.
 
 When modifying the list of files that behave differently with and without
 OOPIFs, please consider modifying all the locations below:
diff --git a/third_party/blink/web_tests/virtual/not-split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt b/third_party/blink/web_tests/virtual/not-split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt
new file mode 100644
index 0000000..6ccfa870
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/not-split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt
@@ -0,0 +1,7 @@
+# This suite runs the tests in http/tests/devtools/isolated-code-cache with
+# --disable-features=SplitCacheByNetworkIsolationKey
+# --disable-site-isolation-trials
+# This disables the feature which partitions the HTTP cache by network isolation
+# key for improved security as well as site isolation. See the
+# virtual/not-site-per-process README.md for more detail.
+# Tracking bug for split cache: crbug.com/910708.
diff --git a/third_party/blink/web_tests/virtual/not-split-http-cache/README.md b/third_party/blink/web_tests/virtual/not-split-http-cache/README.md
new file mode 100644
index 0000000..8d0eb1a
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/not-split-http-cache/README.md
@@ -0,0 +1,5 @@
+# virtual/not-split-http-cache
+
+This directory is for tests when the HTTP cache is *not* partitioned by
+top-frame origin. Tests under `virtual/not-split-http-cache` are run with
+`--disable-features=SplitCacheByNetworkIsolationKey`.
diff --git a/third_party/blink/web_tests/virtual/not-split-http-cache/external/wpt/fetch/http-cache/split-cache.tentative-expected.txt b/third_party/blink/web_tests/virtual/not-split-http-cache/external/wpt/fetch/http-cache/split-cache.tentative-expected.txt
new file mode 100644
index 0000000..9b7cf7f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/not-split-http-cache/external/wpt/fetch/http-cache/split-cache.tentative-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+PASS HTTP Cache - Partioning by top-level origin
+FAIL HTTP Cache - Partioning by top-level origin 1 assert_equals: Response 3 header server-request-count is "2", not "3" expected "3" but got "2"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt b/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt
index 58f5e0e..690a50d 100644
--- a/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt
+++ b/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt
@@ -2,5 +2,6 @@
 # --enable-features=SplitCacheByNetworkIsolationKey
 # --disable-site-isolation-trials
 # This feature partitions the HTTP cache by network isolation key for improved
-# security. Tracking bug: crbug.com/910708. This test disables site isolation,
-# which would cause similar results.
+# security. This test also disables site isolation, which would cause similar
+# results. See the virtual/not-site-per-process README.md for more detail.
+# Tracking bug for split cache: crbug.com/910708.
diff --git a/third_party/blink/web_tests/virtual/split-http-cache/README.md b/third_party/blink/web_tests/virtual/split-http-cache/README.md
new file mode 100644
index 0000000..6c83f46
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/split-http-cache/README.md
@@ -0,0 +1,5 @@
+# virtual/split-http-cache
+
+This directory is for tests when the HTTP cache is partitioned by
+top-frame origin. Tests under `virtual/split-http-cache` are run with
+`--enable-features=SplitCacheByNetworkIsolationKey`.
diff --git a/third_party/blink/web_tests/virtual/split-http-cache/external/wpt/fetch/http-cache/split-cache.tentative-expected.txt b/third_party/blink/web_tests/virtual/split-http-cache/external/wpt/fetch/http-cache/split-cache.tentative-expected.txt
new file mode 100644
index 0000000..7ccbc81
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/split-http-cache/external/wpt/fetch/http-cache/split-cache.tentative-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+PASS HTTP Cache - Partioning by top-level origin
+PASS HTTP Cache - Partioning by top-level origin 1
+Harness: the test ran to completion.
+
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
index 6f1fb6a..f9dae6a 100644
--- a/third_party/harfbuzz-ng/README.chromium
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -1,9 +1,9 @@
 Name: harfbuzz-ng
 Short Name: harfbuzz-ng
 URL: http://harfbuzz.org
-Version: 2.6.4-5
-Date: 20191104
-Revision: 7cde68f10cdf2c3ff77c1d9077475c0fc034c75c
+Version: 2.6.4-9
+Date: 20191111
+Revision: 64a45be5198f6e22c91454bda7bd9a9294552dff
 Security Critical: yes
 License: MIT
 License File: src/COPYING
diff --git a/tools/clang/scripts/package.py b/tools/clang/scripts/package.py
index a7ddea7..51683fbd 100755
--- a/tools/clang/scripts/package.py
+++ b/tools/clang/scripts/package.py
@@ -238,6 +238,10 @@
       # Profile runtime (used by profiler and code coverage).
       'lib/clang/$V/lib/darwin/libclang_rt.profile_iossim.a',
       'lib/clang/$V/lib/darwin/libclang_rt.profile_osx.a',
+
+      # UndefinedBehaviorSanitizer runtime.
+      'lib/clang/$V/lib/darwin/libclang_rt.ubsan_iossim_dynamic.dylib',
+      'lib/clang/$V/lib/darwin/libclang_rt.ubsan_osx_dynamic.dylib',
     ])
   elif sys.platform.startswith('linux'):
     want.extend([
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index ad4d99e..21fbb74 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -41,7 +41,7 @@
 # Reverting problematic clang rolls is safe, though.
 CLANG_REVISION = '64a362e7216a43e3ad44e50a89265e72aeb14294'
 CLANG_SVN_REVISION = '373424'
-CLANG_SUB_REVISION = 1
+CLANG_SUB_REVISION = 2
 
 PACKAGE_VERSION = '%s-%s-%s' % (CLANG_SVN_REVISION, CLANG_REVISION[:8],
                                 CLANG_SUB_REVISION)
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index a063b4a..eae79a8 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -601,7 +601,7 @@
     else:
       raise MBErr('unrecognized platform string "%s"' % self.platform)
 
-    return [('pool', 'Chrome'),
+    return [('pool', 'chromium.tests'),
             ('cpu', 'x86-64'),
             os_dim]
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f313c20..29966eb 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -14299,6 +14299,26 @@
   <int value="6" label="Disconnected"/>
 </enum>
 
+<enum name="DiskCacheError">
+  <summary>Error codes defined in net/disk_cache/errors.h</summary>
+  <int value="0" label="OK"/>
+  <int value="1" label="Init failed"/>
+  <int value="2" label="Invalid tail (corrupt list)"/>
+  <int value="3" label="Invalid head (corrupt list)"/>
+  <int value="4" label="Invalid prev (corrupt list link)"/>
+  <int value="5" label="Invalid next (corrupt list link)"/>
+  <int value="6" label="Invalid entry (beyond dirty)"/>
+  <int value="7" label="Invalid address (single entry)"/>
+  <int value="8" label="Invalid links (single entry)"/>
+  <int value="9" label="Number of entries mistmatch"/>
+  <int value="10" label="Read failure (single entry)"/>
+  <int value="11" label="Previous crash (Destructor not called)"/>
+  <int value="12" label="Storage error (Unable to open / create main files)"/>
+  <int value="13" label="Invalid mask (corrupt header)"/>
+  <int value="14" label="Cache cleared (user request)"/>
+  <int value="15" label="Cache created (from empty files)"/>
+</enum>
+
 <enum name="DisplayLinkInstallationStatus">
   <int value="0" label="DisplayLink not installed"/>
   <int value="1" label="7.1 or earlier"/>
@@ -18688,6 +18708,7 @@
   <int value="642" label="DnsOverHttpsTemplates"/>
   <int value="643" label="GloballyScopeHTTPAuthCacheEnabled"/>
   <int value="644" label="WebComponentsV0Enabled"/>
+  <int value="645" label="ClickToCallEnabled"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">
@@ -56782,6 +56803,12 @@
   <int value="1" label="Failure"/>
 </enum>
 
+<enum name="ShortReportReason">
+  <int value="1" label="No create time"/>
+  <int value="2" label="Not full"/>
+  <int value="3" label="Not full and no create time"/>
+</enum>
+
 <enum name="ShouldAllowOpenURLFailureReason">
   <summary>
     Specifies the reason why the web-accessible resource check in
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index d286b1d..abf6645 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -20714,6 +20714,24 @@
   </summary>
 </histogram>
 
+<histogram name="Chrome.CommandLineFlagCount" units="switches"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of command line switches that were present at the start of a
+    chrome session.
+  </summary>
+</histogram>
+
+<histogram name="Chrome.CommandLineUncommonFlagCount" units="switches"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of command line switches that were present at the start of a
+    chrome session, excluding App Mode and User Data Directory switches.
+  </summary>
+</histogram>
+
 <histogram name="Chrome.MessageLoopProblem" enum="MessageLoopProblems"
     expires_after="M81">
   <owner>gab@chromium.org</owner>
@@ -32252,6 +32270,109 @@
   </summary>
 </histogram>
 
+<histogram name="DiskCache.0.AsyncIOTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time for an async IO operation to complete. This covers content Writes
+    and Reads, measured from the cache thread.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.AsyncReadDispatchTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The synchronous portion of an async read.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.AsyncWriteDispatchTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The synchronous portion of an async write.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.AverageOpenEntries2" units="entries">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The average number of open disk cache entries at any given time.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.BufferBytes" units="KB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The total size of all the internal buffers.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.ByteIORate" units="KB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of KB accessed from the cache in a 30 seconds interval.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.CreateTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent creating a new entry on the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.DeletedAge" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The age of the last entry on the queue of deleted entries.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.DeleteData" units="bytes">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The size of the data portion of a given entry by the time it is deleted.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.DeletedRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The percentage of the deleted entries.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.DeleteFailed" units="files">
+  <owner>rvargas@chromium.org</owner>
+  <summary>Number of external files that we were unable to delete.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.DeleteHeader" units="bytes">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The size of the HTML headers of a given entry by the time it is deleted.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.Entries" units="entries">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The number of entries currently stored on the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.EntriesFull" units="entries">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries currently stored on the cache, after it is full.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.EntryAccessRate" units="entries">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries accessed by the cache in a 30 seconds interval.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.EntrySize" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The average size of an entry. It is only measured after the cache is full,
+    so evictions are taking place.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.Error" enum="DiskCacheError">
+  <owner>rvargas@chromium.org</owner>
+  <summary>Critical error ids.</summary>
+</histogram>
+
 <histogram name="DiskCache.0.FilesAge" units="hours" expires_after="M77">
   <obsolete>
     Removed 2019-07-05
@@ -32260,6 +32381,535 @@
   <summary>The age of the cache's files (wall time).</summary>
 </histogram>
 
+<histogram name="DiskCache.0.FillupAge" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The age of the cache (in hours) by the time we reach the size limit.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.FillupTime" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of hours required to fill up the cache, as measured by the cache
+    being running.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.FirstByteIORate" units="KB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The average number of KB accessed from the cache in a 30 seconds interval,
+    by the time we reach the cache size limit.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.FirstEntryAccessRate" units="entries">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The average number of entries accessed by the cache in a 30 seconds
+    interval, by the time we reach the cache size limit.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.FirstEntrySize" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The average size of an entry by the time we reach the cache size limit.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.FirstHighUseRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The percentage of the entries that are highly reused, by the time we fill up
+    the cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.FirstHitRatio" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The hit ratio by the time we fill up the cache. Of course, we started with
+    an empty cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.FirstLargeEntriesRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The percentage of the cache used by entries of more than 512 KB, by the time
+    we reach the cache size limit.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.FirstLowUseRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The percentage of the entries that have been reused a few times, by the time
+    we fill up the cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.FirstNoUseRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The percentage of the entries that have never been reused, by the time we
+    fill up the cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.FirstResurrectRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The percentage of re-created deleted entries versus entries that we really
+    don't know about.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.GetRankings" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent reading the LRU-related portion of an entry.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.HighUseAge" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The age of the last entry on the queue of highly used entries.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.HighUseRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The percentage of the entries that are highly reused.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.HitRatio" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The current hit ratio. It is only measured after the cache is full, so
+    evictions are taking place, and data from the fill-up period is not
+    considered.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.HitRatioBySize2" units="MB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The hit ratio for each size. To calculate the hit ratio of caches of a
+    certain size, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.0.Size2.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.HitRatioByTotalTime" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The hit ratio for each total time. To calculate the hit ratio of caches of a
+    certain age, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.0.TotalTime.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.HitRatioByUseTime" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The hit ratio for each use time. To calculate the hit ratio of caches of a
+    certain age, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.0.UseTime.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.IndexLoad" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    Percentage of the index table that is currently used (the cache is full).
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.KeySize" units="bytes">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The size of each key.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.LargeEntriesRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    Percentage of the cache used by entries of more than 512 KB. It is only
+    measured after the cache is full, so evictions are taking place.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.LoadTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time to load the main entry data from disk, with a &quot;loaded&quot;
+    system.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.LowUseAge" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The age of the last entry on the queue of entries reused a few times.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.LowUseRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The percentage of the entries that have been reused a few times.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.MaxOpenEntries2" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The maximum number of simultaneously open disk cache entries.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.MaxSize" units="MB">
+  <obsolete>
+    Deprecated. See MaxSize2.
+  </obsolete>
+  <owner>rvargas@chromium.org</owner>
+  <summary>The maximum size of the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.MaxSize2" units="MB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The maximum size of the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.NoUseAge" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The age of the next entry to be evicted that has never been reused.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.NoUseRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The percentage of the entries that have never been reused.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.NumberOfReferences" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The number of open entry references at any given moment.</summary>
+  <details>
+    Closely related to AverageOpenEntries, but this one is not the average per
+    client but instead a direct histogram updated every 30 secs.
+  </details>
+</histogram>
+
+<histogram name="DiskCache.0.OpenTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent opening an entry already on the cache (cache hit).
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.PendingIO" units="operations">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The number of pending IO operations (Async IO).</summary>
+</histogram>
+
+<histogram name="DiskCache.0.ReadTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent reading from an entry.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.ResurrectRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The percentage of re-created deleted entries versus entries that we really
+    don't know about.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.ShortReport" enum="ShortReportReason">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The reason for not sending a full report.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.Size" units="MB">
+  <obsolete>
+    Deprecated. See Size2
+  </obsolete>
+  <owner>rvargas@chromium.org</owner>
+  <summary>The current size of the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.Size2" units="MB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The current size of the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.SparseReadTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent reading from an sparse entry.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.SparseWriteTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent writing to an sparse entry.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalClearTimeV1" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent deleting the cache. (Clear browsing data).</summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalClearTimeV2" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent deleting the cache. (Clear browsing data).</summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalDoomCache" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of times that this cache has been deleted by the user.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalDoomRecentEntries" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of times that the user has deleted new entries from this cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalEvictionsGaJs" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The number of times that ga.js was evicted.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalFatalErrors" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of fatal errors detected for this cache (so we delete
+    everything).
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalIOTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The total time it takes to perform a payload IO operation (AKA, directed to
+    an entry). This is measured from the IO thread.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalTime" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>Number of hours that the cache has been used.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalTimeNotFull" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of hours that the cache has been in use, for users that still
+    have available space.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalTrimDeletedTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent removing deleted entries from the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalTrimTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent removing old entries from the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalTrimTimeV1" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent removing old entries from the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.TotalTrimTimeV2" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent evicting entries from the cache (moving them to the deleted
+    list).
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.TrimAge" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time that an entry lives in the cache without being accessed until it is
+    finally purged.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.TrimDeletedItems" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries that are deleted on a single iteration.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.TrimItemsV1" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries that are evicted on a single iteration.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.TrimItemsV2" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries that are evicted (moved to the deleted list) on a
+    single iteration.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.TrimRate" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The number of entries that are evicted per hour of use.</summary>
+</histogram>
+
+<histogram name="DiskCache.0.UpdateRank" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent moving an entry to the front of the LRU list.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.UsedSpace" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The percentage of the allowed disk space that we are currently using.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.UseTime" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    Number of hours that the cache has been used since last week.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.0.WriteTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent writing to an entry.</summary>
+</histogram>
+
+<histogram name="DiskCache.2.AsyncIOTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time for an async IO operation to complete. This covers content Writes
+    and Reads, measured from the cache thread. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.AsyncReadDispatchTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The synchronous portion of an async read. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.AsyncWriteDispatchTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The synchronous portion of an async write. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.AverageOpenEntries2" units="entries">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The average number of open disk cache entries at any given time.
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.BufferBytes" units="KB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The total size of all the internal buffers. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.ByteIORate" units="KB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of KB accessed from the cache in a 30 seconds interval.
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.CreateTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent creating a new entry on the cache. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.DeleteData" units="bytes">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The size of the data portion of a given entry by the time it is deleted.
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.DeleteFailed" units="files">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    Number of external files that we were unable to delete. Media-specific
+    cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.DeleteHeader" units="bytes">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The size of the HTML headers of a given entry by the time it is deleted.
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.Entries" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries currently stored on the cache. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.EntriesFull" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries currently stored on the cache, after it is full.
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.EntryAccessRate" units="entries">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries accessed by the cache in a 30 seconds interval.
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.EntrySize" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The average size of an entry. It is only measured after the cache is full,
+    so evictions are taking place. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.Error" enum="DiskCacheError">
+  <owner>rvargas@chromium.org</owner>
+  <summary>Critical error ids. Media-specific cache.</summary>
+</histogram>
+
 <histogram name="DiskCache.2.FilesAge" units="hours">
   <obsolete>
     Removed 2019-07-05
@@ -32270,6 +32920,441 @@
   </summary>
 </histogram>
 
+<histogram name="DiskCache.2.FillupAge" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The age of the cache (in hours) by the time we reach the size limit.
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.FillupTime" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of hours required to fill up the cache, as measured by the cache
+    being running. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.FirstByteIORate" units="KB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The average number of KB accessed from the cache in a 30 seconds interval,
+    by the time we reach the cache size limit. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.FirstEntryAccessRate" units="entries">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The average number of entries accessed by the cache in a 30 seconds
+    interval, by the time we reach the cache size limit. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.FirstEntrySize" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The average size of an entry by the time we reach the cache size limit.
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.FirstHitRatio" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The hit ratio by the time we fill up the cache. Of course, we started with
+    an empty cache. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.FirstLargeEntriesRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The percentage of the cache used by entries of more than 512 KB, by the time
+    we reach the cache size limit. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.GetRankings" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent reading the LRU-related portion of an entry. Media-specific
+    cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.HitRatio" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The current hit ratio. It is only measured after the cache is full, so
+    evictions are taking place, and data from the fill-up period is not
+    considered. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.HitRatioBySize2" units="MB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The hit ratio for each size. To calculate the hit ratio of caches of a
+    certain size, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.2.Size2. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.HitRatioByTotalTime" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The hit ratio for each total time. To calculate the hit ratio of caches of a
+    certain age, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.2.TotalTime. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.HitRatioByUseTime" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The hit ratio for each use time. To calculate the hit ratio of caches of a
+    certain age, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.2.UseTime. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.IndexLoad" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    Percentage of the index table that is currently used (the cache is full).
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.KeySize" units="bytes">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The size of each key. Media-specific cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.2.LargeEntriesRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    Percentage of the cache used by entries of more than 512 KB. It is only
+    measured after the cache is full, so evictions are taking place.
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.LoadTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time to load the main entry data from disk, with a &quot;loaded&quot;
+    system. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.MaxOpenEntries2" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The maximum number of simultaneously open disk cache entries. Media-specific
+    cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.MaxSize" units="MB">
+  <obsolete>
+    Deprecated. See MaxSize2.
+  </obsolete>
+  <owner>rvargas@chromium.org</owner>
+  <summary>The maximum size of the cache. Media-specific cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.2.MaxSize2" units="MB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The maximum size of the cache. Media-specific cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.2.NumberOfReferences" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of open entry references at any given moment. Closely related to
+    AverageOpenEntries, but this one is not the average per client but instead a
+    direct histogram updated every 30 secs. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.OpenTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent opening an entry already on the cache (cache hit).
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.PendingIO" units="operations">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of pending IO operations (Async IO). Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.ReadTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent reading from an entry. Media-specific cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.2.ResurrectRatio" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The percentage of re-created deleted entries versus entries that we really
+    don't know about. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.ShortReport" enum="ShortReportReason">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The reason for not sending a full report. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.Size" units="MB">
+  <obsolete>
+    Deprecated. See Size2.
+  </obsolete>
+  <owner>rvargas@chromium.org</owner>
+  <summary>The current size of the cache. Media-specific cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.2.Size2" units="MB">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The current size of the cache. Media-specific cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.2.TotalClearTimeV1" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent deleting the cache. (Clear browsing data). Media-specific
+    cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TotalClearTimeV2" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent deleting the cache. (Clear browsing data). Media-specific
+    cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TotalIOTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The total time it takes to perform a payload IO operation (AKA, directed to
+    an entry). This is measured from the IO thread. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TotalTime" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    Number of hours that the cache has been used. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TotalTrimDeletedTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent removing deleted entries from the cache. Media-specific
+    cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TotalTrimTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent removing old entries from the cache. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TotalTrimTimeV1" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent removing old entries from the cache. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TotalTrimTimeV2" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent evicting entries from the cache (moving them to the deleted
+    list). Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TrimAge" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time that an entry lives in the cache without being accessed until it is
+    finally purged. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TrimDeletedItems" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries that are deleted on a single iteration. Media-specific
+    cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TrimItemsV1" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries that are evicted on a single iteration. Media-specific
+    cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TrimItemsV2" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries that are evicted (moved to the deleted list) on a
+    single iteration. Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.TrimRate" units="?">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The number of entries that are evicted per hour of use. Media-specific
+    cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.UpdateRank" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The time spent moving an entry to the front of the LRU list. Media-specific
+    cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.UsedSpace" units="%">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    The percentage of the allowed disk space that we are currently using.
+    Media-specific cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.UseTime" units="hours">
+  <owner>rvargas@chromium.org</owner>
+  <summary>
+    Number of hours that the cache has been used since last week. Media-specific
+    cache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.2.WriteTime" units="ms">
+  <owner>rvargas@chromium.org</owner>
+  <summary>The time spent writing to an entry. Media-specific cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.AsyncIOTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time for an async IO operation to complete. This covers content Writes
+    and Reads, measured from the cache thread. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.AsyncReadDispatchTime" units="ms"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The synchronous portion of an async read. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.AsyncWriteDispatchTime" units="ms"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The synchronous portion of an async write. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.AverageOpenEntries2" units="entries"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The average number of open disk cache entries at any given time. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.BufferBytes" units="KB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The total size of all the internal buffers. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.ByteIORate" units="KB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of KB accessed from the cache in a 30 seconds interval. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.CreateTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The time spent creating a new entry on the cache. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.DeleteData" units="bytes" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The size of the data portion of a given entry by the time it is deleted.
+    AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.DeleteFailed" units="files" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    Number of external files that we were unable to delete. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.Entries" units="entries" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries currently stored on the cache. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.EntriesFull" units="entries" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries currently stored on the cache, after it is full.
+    AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.EntryAccessRate" units="entries"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries accessed by the cache in a 30 seconds interval.
+    AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.EntrySize" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The average size of an entry. It is only measured after the cache is full,
+    so evictions are taking place. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.Error" enum="DiskCacheError" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>Critical error ids. AppCache.</summary>
+</histogram>
+
 <histogram name="DiskCache.3.FilesAge" units="hours" expires_after="2018-08-30">
   <obsolete>
     Removed 2019-07-05
@@ -32278,6 +33363,404 @@
   <summary>The age of the cache's files (wall time). AppCache.</summary>
 </histogram>
 
+<histogram name="DiskCache.3.FillupAge" units="hours" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The age of the cache (in hours) by the time we reach the size limit.
+    AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.FillupTime" units="hours" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of hours required to fill up the cache, as measured by the cache
+    being running. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.FirstByteIORate" units="KB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The average number of KB accessed from the cache in a 30 seconds interval,
+    by the time we reach the cache size limit. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.FirstEntryAccessRate" units="entries"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The average number of entries accessed by the cache in a 30 seconds
+    interval, by the time we reach the cache size limit. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.FirstEntrySize" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The average size of an entry by the time we reach the cache size limit.
+    AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.FirstHitRatio" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The hit ratio by the time we fill up the cache. Of course, we started with
+    an empty cache. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.FirstLargeEntriesRatio" units="%"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The percentage of the cache used by entries of more than 512 KB, by the time
+    we reach the cache size limit. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.GetRankings" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time spent reading the LRU-related portion of an entry. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.HitRatio" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The current hit ratio. It is only measured after the cache is full, so
+    evictions are taking place, and data from the fill-up period is not
+    considered. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.HitRatioBySize2" units="MB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The hit ratio for each size. To calculate the hit ratio of caches of a
+    certain size, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.3.Size2. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.HitRatioByTotalTime" units="hours"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The hit ratio for each total time. To calculate the hit ratio of caches of a
+    certain age, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.3.TotalTime. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.HitRatioByUseTime" units="hours"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The hit ratio for each use time. To calculate the hit ratio of caches of a
+    certain age, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.3.UseTime. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.IndexLoad" units="%" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    Percentage of the index table that is currently used (the cache is full).
+    AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.KeySize" units="bytes" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The size of each key. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.LargeEntriesRatio" units="%" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    Percentage of the cache used by entries of more than 512 KB. It is only
+    measured after the cache is full, so evictions are taking place. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.LoadTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time to load the main entry data from disk, with a &quot;loaded&quot;
+    system. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.MaxOpenEntries2" units="entries"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The maximum number of simultaneously open disk cache entries. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.MaxSize" units="MB">
+  <obsolete>
+    Deprecated. See MaxSize2.
+  </obsolete>
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The maximum size of the cache. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.MaxSize2" units="MB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The maximum size of the cache. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.OpenTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time spent opening an entry already on the cache (cache hit). AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.PendingIO" units="operations" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The number of pending IO operations (Async IO). AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.ReadTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The time spent reading from an entry. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.ResurrectRatio" units="%" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The percentage of re-created deleted entries versus entries that we really
+    don't know about. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.ShortReport" enum="ShortReportReason"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The reason for not sending a full report. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.Size" units="MB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The current size of the cache. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.TotalIOTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The total time it takes to perform a payload IO operation (AKA, directed to
+    an entry). This is measured from the IO thread. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.TotalTime" units="hours" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>Number of hours that the cache has been used. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.3.TotalTrimDeletedTime" units="ms"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time spent removing deleted entries from the cache. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.TotalTrimTimeV1" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time spent removing old entries from the cache. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.TotalTrimTimeV2" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time spent evicting entries from the cache (moving them to the deleted
+    list). AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.TrimAge" units="hours" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time that an entry lives in the cache without being accessed until it is
+    finally purged. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.TrimDeletedItems" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries that are deleted on a single iteration. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.TrimItemsV1" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries that are evicted on a single iteration. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.TrimItemsV2" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries that are evicted (moved to the deleted list) on a
+    single iteration. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.TrimRate" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries that are evicted per hour of use. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.UpdateRank" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time spent moving an entry to the front of the LRU list. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.UsedSpace" units="%" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The percentage of the allowed disk space that we are currently using.
+    AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.UseTime" units="hours" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    Number of hours that the cache has been used since last week. AppCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.3.WriteTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The time spent writing to an entry. AppCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.4.AsyncIOTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time for an async IO operation to complete. This covers content Writes
+    and Reads, measured from the cache thread. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.AsyncReadDispatchTime" units="ms"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The synchronous portion of an async read. ShaderCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.4.AsyncWriteDispatchTime" units="ms"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The synchronous portion of an async write. ShaderCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.4.AverageOpenEntries2" units="entries"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The average number of open disk cache entries at any given time.
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.BufferBytes" units="KB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The total size of all the internal buffers. ShaderCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.4.ByteIORate" units="KB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of KB accessed from the cache in a 30 seconds interval.
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.CreateTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time spent creating a new entry on the cache. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.DeleteData" units="bytes" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The size of the data portion of a given entry by the time it is deleted.
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.DeleteFailed" units="files" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    Number of external files that we were unable to delete. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.DeleteHeader" units="bytes" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The size of the HTML headers of a given entry by the time it is deleted.
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.Entries" units="entries" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries currently stored on the cache. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.EntriesFull" units="entries" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries currently stored on the cache, after it is full.
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.EntryAccessRate" units="entries"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries accessed by the cache in a 30 seconds interval.
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.EntrySize" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The average size of an entry. It is only measured after the cache is full,
+    so evictions are taking place. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.Error" enum="DiskCacheError" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>Critical error ids. ShaderCache.</summary>
+</histogram>
+
 <histogram name="DiskCache.4.FilesAge" units="hours" expires_after="2018-08-30">
   <obsolete>
     Removed 2019-07-05
@@ -32286,6 +33769,353 @@
   <summary>The age of the cache's files (wall time). ShaderCache.</summary>
 </histogram>
 
+<histogram name="DiskCache.4.FillupAge" units="hours" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The age of the cache (in hours) by the time we reach the size limit.
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.FillupTime" units="hours" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of hours required to fill up the cache, as measured by the cache
+    being running. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.FirstByteIORate" units="KB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The average number of KB accessed from the cache in a 30 seconds interval,
+    by the time we reach the cache size limit. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.FirstEntryAccessRate" units="entries"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The average number of entries accessed by the cache in a 30 seconds
+    interval, by the time we reach the cache size limit. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.FirstEntrySize" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The average size of an entry by the time we reach the cache size limit.
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.FirstHitRatio" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The hit ratio by the time we fill up the cache. Of course, we started with
+    an empty cache. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.FirstLargeEntriesRatio" units="%"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The percentage of the cache used by entries of more than 512 KB, by the time
+    we reach the cache size limit. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.GetRankings" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time spent reading the LRU-related portion of an entry. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.HitRatio" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The current hit ratio. It is only measured after the cache is full, so
+    evictions are taking place, and data from the fill-up period is not
+    considered. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.HitRatioBySize2" units="MB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The hit ratio for each size. To calculate the hit ratio of caches of a
+    certain size, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.4.Size2. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.HitRatioByTotalTime" units="hours"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The hit ratio for each total time. To calculate the hit ratio of caches of a
+    certain age, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.4.TotalTime. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.HitRatioByUseTime" units="hours"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The hit ratio for each use time. To calculate the hit ratio of caches of a
+    certain age, take the ratio of a bin in this histogram to the same bin in
+    DiskCache.4.UseTime. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.IndexLoad" units="%" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    Percentage of the index table that is currently used (the cache is full).
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.KeySize" units="bytes" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The size of each key. ShaderCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.4.LargeEntriesRatio" units="%" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    Percentage of the cache used by entries of more than 512 KB. It is only
+    measured after the cache is full, so evictions are taking place.
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.LoadTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time to load the main entry data from disk, with a &quot;loaded&quot;
+    system. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.MaxOpenEntries2" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The maximum number of simultaneously open disk cache entries. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.MaxSize2" units="MB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The maximum size of the cache. ShaderCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.4.OpenTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time spent opening an entry already on the cache (cache hit).
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.PendingIO" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of pending IO operations (Async IO). ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.ReadTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The time spent reading from an entry. ShaderCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.4.ShortReport" enum="ShortReportReason"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The reason for not sending a full report. ShaderCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.4.Size" units="MB" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The current size of the cache. ShaderCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.4.TotalIOTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The total time it takes to perform a payload IO operation (AKA, directed to
+    an entry). This is measured from the IO thread. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.TotalTime" units="hours" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>Number of hours that the cache has been used. ShaderCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.4.TotalTrimTimeV1" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time spent removing old entries from the cache. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.TrimAge" units="hours" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time that an entry lives in the cache without being accessed until it is
+    finally purged. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.TrimItemsV1" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries that are evicted on a single iteration. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.TrimRate" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The number of entries that are evicted per hour of use. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.UpdateRank" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time spent moving an entry to the front of the LRU list. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.UsedSpace" units="%" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The percentage of the allowed disk space that we are currently using.
+    ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.UseTime" units="hours" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    Number of hours that the cache has been used since last week. ShaderCache.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.4.WriteTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The time spent writing to an entry. ShaderCache.</summary>
+</histogram>
+
+<histogram name="DiskCache.BlockLoad_0" units="%" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The percentage of use for blocks of type 0 (links).</summary>
+</histogram>
+
+<histogram name="DiskCache.BlockLoad_1" units="%" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The percentage of use for blocks of type 1 (256 bytes).</summary>
+</histogram>
+
+<histogram name="DiskCache.BlockLoad_2" units="%" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The percentage of use for blocks of type 2 (1024 bytes).</summary>
+</histogram>
+
+<histogram name="DiskCache.BlockLoad_3" units="%" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The percentage of use for blocks of type 3 (4096 bytes).</summary>
+</histogram>
+
+<histogram name="DiskCache.Blocks_0" units="blocks" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The number of used blocks of type 0 (links).</summary>
+</histogram>
+
+<histogram name="DiskCache.Blocks_1" units="blocks" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The number of used blocks of type 1 (256 bytes).</summary>
+</histogram>
+
+<histogram name="DiskCache.Blocks_2" units="blocks" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The number of used blocks of type 2 (1024 bytes).</summary>
+</histogram>
+
+<histogram name="DiskCache.Blocks_3" units="blocks" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The number of used blocks of type 3 (4096 bytes).</summary>
+</histogram>
+
+<histogram name="DiskCache.DeleteFailed2" units="files" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>Number of block-files that we were unable to delete.</summary>
+</histogram>
+
+<histogram name="DiskCache.Entries" units="entries">
+  <obsolete>
+    Deprecated.
+  </obsolete>
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The number of entries currently stored on the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.Experiment" units="group" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The experiment group for this user.</summary>
+</histogram>
+
+<histogram name="DiskCache.HitRatio" units="?">
+  <obsolete>
+    Deprecated.
+  </obsolete>
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The current hit ratio. It is only measured after the cache is full, so
+    evictions are taking place, and data from the fill-up period is not
+    considered.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.LargeEntriesRatio" units="%">
+  <obsolete>
+    Deprecated.
+  </obsolete>
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    Percentage of the cache used by entries of more than 512 KB. It is only
+    measured after the cache is full, so evictions are taking place.
+  </summary>
+</histogram>
+
+<histogram name="DiskCache.MaxSize" units="MB">
+  <obsolete>
+    Deprecated.
+  </obsolete>
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The maximum size of the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.Size" units="MB">
+  <obsolete>
+    Deprecated.
+  </obsolete>
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>The current size of the cache.</summary>
+</histogram>
+
+<histogram name="DiskCache.SizeStats" units="?">
+  <owner>dmikurube@chromium.org</owner>
+  <summary>The size distribution of data stored on the cache.</summary>
+</histogram>
+
 <histogram name="DiskCache.SizeStats2" units="KB" expires_after="M77">
   <obsolete>
     Removed 2019-07-05
@@ -32305,6 +34135,17 @@
   </summary>
 </histogram>
 
+<histogram name="DiskCache.TrimAge" units="hours">
+  <obsolete>
+    Deprecated.
+  </obsolete>
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    The time that an entry lives in the cache without being accessed until it is
+    finally purged.
+  </summary>
+</histogram>
+
 <histogram name="DisplayManager.InternalDisplayZoomPercentage" units="%">
   <owner>malaykeshav@chromium.org</owner>
   <summary>
@@ -53926,6 +55767,15 @@
   </summary>
 </histogram>
 
+<histogram name="Histogram.PendingProcessNotResponding" units="processes"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    When metrics services (UMA) ran an update, the number of child processes
+    that did not respond, providing histogram updates, before the timeout.
+  </summary>
+</histogram>
+
 <histogram name="Histogram.PermanentNameChanged" enum="HistogramNameHash">
   <owner>asvitkine@chromium.org</owner>
   <owner>bcwhite@chromium.org</owner>
@@ -53935,6 +55785,16 @@
   </summary>
 </histogram>
 
+<histogram name="Histogram.ReceivedProcessGroupCount" enum="BooleanEnabled"
+    expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    When metrics services (UMA) ran an update, the number of times the process
+    groups (renderer processes and plugin/gpu processes) did not respond,
+    providing histogram updates, before the timeout.
+  </summary>
+</histogram>
+
 <histogram name="Histogram.TooManyBuckets.1000" enum="HistogramNameHash"
     expires_after="M78">
   <owner>asvitkine@chromium.org</owner>
@@ -54308,6 +56168,11 @@
   </summary>
 </histogram>
 
+<histogram name="History.GetFavIconFromDB" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>Time to check the thumbnail database for an url's favicon.</summary>
+</histogram>
+
 <histogram name="History.HistoryPageView" enum="HistoryPageView"
     expires_after="2020-03-01">
   <owner>calamity@chromium.org</owner>
@@ -54336,6 +56201,11 @@
   </summary>
 </histogram>
 
+<histogram name="History.InitTime" units="ms" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>TBD</summary>
+</histogram>
+
 <histogram name="History.InMemoryDBItemCount" units="units"
     expires_after="2020-02-16">
   <owner>sky@chromium.org</owner>
@@ -55258,6 +57128,15 @@
   <summary>The time it takes to open a hyphenation dictionary file.</summary>
 </histogram>
 
+<histogram name="Image.ResampleMS" units="?" expires_after="M79">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <summary>
+    Time it takes to do high-quality image resampling for images that are
+    resized. Non-resized images and ones our heuristic decides can be done
+    &quot;badly&quot; are not counted.
+  </summary>
+</histogram>
+
 <histogram name="ImageAnnotationService.AccessibilityV1.CacheHit"
     enum="BooleanCacheHit" expires_after="2020-03-31">
   <owner>amoylan@chromium.org</owner>
@@ -168303,6 +170182,17 @@
       name="Memory.BackgroundTask.OfflinePrefetch.Browser.SharedMemoryFootprint"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="CacheDeletedEntries" separator="_">
+  <suffix name="11" label="Out of the experiment"/>
+  <suffix name="12" label="Control"/>
+  <suffix name="13" label="Extended deleted list (2x)"/>
+  <affected-histogram name="DiskCache.0.DeletedRatio"/>
+  <affected-histogram name="DiskCache.0.HitRatio"/>
+  <affected-histogram name="DiskCache.0.ResurrectRatio"/>
+  <affected-histogram name="DiskCache.2.HitRatio"/>
+  <affected-histogram name="DiskCache.3.HitRatio"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="CachedImageFetcherClients" separator=".">
   <obsolete>
     Renamed to ImageFetcherClients on 04/2019.
@@ -170778,6 +172668,89 @@
   <affected-histogram name="Discarding.ReloadsPer10Minutes"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="DiskCacheExperiment1" separator="_">
+  <suffix name="1" label="Experiment group 1 (70% clients)"/>
+  <suffix name="2" label="Experiment group 2 (10% clients)"/>
+  <suffix name="3" label="Experiment group 3 (10% clients)"/>
+  <suffix name="4" label="Experiment group 4 (10% clients)"/>
+  <affected-histogram name="DiskCache.0.Entries"/>
+  <affected-histogram name="DiskCache.0.MaxSize"/>
+  <affected-histogram name="DiskCache.0.Size"/>
+  <affected-histogram name="DiskCache.0.TrimAge"/>
+  <affected-histogram name="DiskCache.2.Entries"/>
+  <affected-histogram name="DiskCache.2.MaxSize"/>
+  <affected-histogram name="DiskCache.2.Size"/>
+  <affected-histogram name="DiskCache.2.TrimAge"/>
+  <affected-histogram name="DiskCache.3.Entries"/>
+  <affected-histogram name="DiskCache.3.MaxSize"/>
+  <affected-histogram name="DiskCache.3.Size"/>
+  <affected-histogram name="DiskCache.3.TrimAge"/>
+  <affected-histogram name="DiskCache.Entries"/>
+  <affected-histogram name="DiskCache.MaxSize"/>
+  <affected-histogram name="DiskCache.Size"/>
+  <affected-histogram name="DiskCache.TrimAge"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="DiskCacheExperiment2" separator="_">
+  <suffix name="5" label="Experiment group 5"/>
+  <suffix name="6" label="Experiment group 6"/>
+  <suffix name="7" label="Experiment group 7"/>
+  <suffix name="8" label="Experiment group 8"/>
+  <suffix name="9" label="Experiment group 9"/>
+  <suffix name="10" label="Experiment group 10"/>
+  <affected-histogram name="DiskCache.0.EntrySize"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="DiskCacheExperiment3" separator="_">
+  <suffix name="8" label="Experiment group 8"/>
+  <affected-histogram name="DiskCache.0.FirstHighUseRatio"/>
+  <affected-histogram name="DiskCache.0.FirstLowUseRatio"/>
+  <affected-histogram name="DiskCache.0.FirstNoUseRatio"/>
+  <affected-histogram name="DiskCache.0.FirstResurrectRatio"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="DiskCacheExperiment4" separator="_">
+  <suffix name="7" label="Experiment group 7"/>
+  <suffix name="8" label="Experiment group 8"/>
+  <affected-histogram name="DiskCache.0.DeletedAge"/>
+  <affected-histogram name="DiskCache.0.DeletedRatio"/>
+  <affected-histogram name="DiskCache.0.HighUseAge"/>
+  <affected-histogram name="DiskCache.0.HighUseRatio"/>
+  <affected-histogram name="DiskCache.0.LowUseAge"/>
+  <affected-histogram name="DiskCache.0.LowUseRatio"/>
+  <affected-histogram name="DiskCache.0.NoUseAge"/>
+  <affected-histogram name="DiskCache.0.NoUseRatio"/>
+  <affected-histogram name="DiskCache.0.ResurrectRatio"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="DiskCacheExperiment5" separator="_">
+  <suffix name="1" label="Experiment group 1"/>
+  <suffix name="2" label="Experiment group 2"/>
+  <suffix name="3" label="Experiment group 3"/>
+  <suffix name="4" label="Experiment group 4"/>
+  <suffix name="5" label="Experiment group 5"/>
+  <suffix name="6" label="Experiment group 6"/>
+  <suffix name="7" label="Experiment group 7"/>
+  <suffix name="8" label="Experiment group 8"/>
+  <suffix name="9" label="Experiment group 9"/>
+  <suffix name="10" label="Experiment group 10"/>
+  <affected-histogram name="DiskCache.0.HitRatio"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="DiskCacheExperiment5" separator="_">
+  <suffix name="1" label="Experiment group 1"/>
+  <suffix name="2" label="Experiment group 2"/>
+  <suffix name="3" label="Experiment group 3"/>
+  <suffix name="4" label="Experiment group 4"/>
+  <affected-histogram name="DiskCache.0.LargeEntriesRatio"/>
+  <affected-histogram name="DiskCache.2.HitRatio"/>
+  <affected-histogram name="DiskCache.2.LargeEntriesRatio"/>
+  <affected-histogram name="DiskCache.3.HitRatio"/>
+  <affected-histogram name="DiskCache.3.LargeEntriesRatio"/>
+  <affected-histogram name="DiskCache.HitRatio"/>
+  <affected-histogram name="DiskCache.LargeEntriesRatio"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="DiskUsagePerUserCount" separator=".">
   <suffix name="1User" label="Only 1 user exists on device."/>
   <suffix name="2Users" label="2 users exist on device."/>
diff --git a/tools/perf/contrib/cluster_telemetry/screenshot_unittest.py b/tools/perf/contrib/cluster_telemetry/screenshot_unittest.py
index 0be5157..e0e6c020 100644
--- a/tools/perf/contrib/cluster_telemetry/screenshot_unittest.py
+++ b/tools/perf/contrib/cluster_telemetry/screenshot_unittest.py
@@ -11,7 +11,7 @@
 
 
 class ScreenshotUnitTest(legacy_page_test_case.LegacyPageTestCase):
-  @decorators.Enabled('linux')
+  @decorators.Disabled('linux')
   def testScreenshot(self):
     # Screenshots for Cluster Telemetry purposes currently only supported on
     # Linux platform.
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index e39c20c..f1ef714 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -302,6 +302,7 @@
 crbug.com/1009838 [ mac ] system_health.common_desktop/browse:tools:maps:2019 [ Skip ]
 crbug.com/1008001 [ win ] system_health.common_desktop/browse:tools:sheets:2019 [ Skip ]
 crbug.com/1017244 [ desktop ] system_health.common_desktop/browse_accessibility:media:youtube [ Skip ]
+crbug.com/1023366 [ desktop ] system_health.common_desktop/browse:media:youtube:2019 [ Skip ]
 
 # Benchmark: system_health.common_mobile
 crbug.com/1013317 [ android ] system_health.common_mobile/load:news:nytimes [ Skip ]
@@ -414,6 +415,7 @@
 crbug.com/1008028 [ desktop ] v8.browsing_desktop/browse:news:hackernews:2018 [ Skip ]
 crbug.com/1009838 [ mac ] v8.browsing_desktop/browse:tools:maps:2019 [ Skip ]
 crbug.com/1008001 [ win ] v8.browsing_desktop/browse:tools:sheets:2019 [ Skip ]
+crbug.com/1023366 [ desktop ] v8.browsing_desktop/browse:media:youtube:2019 [ Skip ]
 
 # Benchmark v8.browsing_desktop-future
 crbug.com/788796 [ linux ] v8.browsing_desktop-future/browse:media:imgur [ Skip ]
@@ -423,6 +425,7 @@
 crbug.com/958507 [ desktop ] v8.browsing_desktop-future/browse:media:imgur [ Skip ]
 crbug.com/1008028 [ desktop ] v8.browsing_desktop-future/browse:news:hackernews:2018 [ Skip ]
 crbug.com/1008001 [ win ] v8.browsing_desktop-future/browse:tools:sheets:2019 [ Skip ]
+crbug.com/1023366 [ desktop ] v8.browsing_desktop-future/browse:media:youtube:2019 [ Skip ]
 
 # Benchmark: v8.browsing_mobile
 crbug.com/958034 [ android-go android-webview ] v8.browsing_mobile/* [ Skip ]
diff --git a/tools/run-swarmed.py b/tools/run-swarmed.py
index 0c87ff6..b321e6b 100755
--- a/tools/run-swarmed.py
+++ b/tools/run-swarmed.py
@@ -134,7 +134,7 @@
                       help='Number of copies to spawn.')
   parser.add_argument('--device-os', default='M',
                       help='Run tests on the given version of Android.')
-  parser.add_argument('--pool', default='Chrome',
+  parser.add_argument('--pool', default='chromium.tests',
                       help='Use the given swarming pool.')
   parser.add_argument('--results', '-r', default='results',
                       help='Directory in which to store results.')
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 5b6ece5..8a1feb43 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -222,6 +222,7 @@
  <item id="ranker_url_fetcher" hash_code="95682324" type="0" content_hash_code="45958626" os_list="linux,windows" file_path="components/assist_ranker/ranker_url_fetcher.cc"/>
  <item id="rappor_report" hash_code="44606780" type="0" content_hash_code="111287826" os_list="linux,windows" file_path="components/rappor/log_uploader.cc"/>
  <item id="refresh_token_annotation_request" hash_code="7433837" type="1" second_id="29188932" deprecated="2018-01-17" content_hash_code="137103383" file_path=""/>
+ <item id="remote_copy_message_handler" hash_code="80255301" type="0" content_hash_code="117673331" os_list="linux,windows" file_path="chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.cc"/>
  <item id="remote_suggestions_provider" hash_code="49544361" type="0" content_hash_code="126329742" os_list="linux,windows" file_path="components/ntp_snippets/remote/cached_image_fetcher.cc"/>
  <item id="render_view_context_menu" hash_code="25844439" type="0" content_hash_code="69471170" os_list="linux,windows" file_path="chrome/browser/renderer_context_menu/render_view_context_menu.cc"/>
  <item id="renderer_initiated_download" hash_code="116443055" type="0" content_hash_code="37846436" os_list="linux,windows" file_path="content/browser/frame_host/render_frame_host_impl.cc"/>
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index 385f0d74..cfb3cf8 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -1638,10 +1638,12 @@
     int offset,
     AXTextBoundaryDirection direction,
     ax::mojom::TextAffinity affinity) const {
-  base::Optional<int> boundary_offset =
-      GetDelegate()->FindTextBoundary(boundary, offset, direction, affinity);
-  if (boundary_offset.has_value())
-    return *boundary_offset;
+  if (boundary != AXTextBoundary::kSentenceStart) {
+    base::Optional<int> boundary_offset =
+        GetDelegate()->FindTextBoundary(boundary, offset, direction, affinity);
+    if (boundary_offset.has_value())
+      return *boundary_offset;
+  }
 
   std::vector<int32_t> unused_line_start_offsets;
   return static_cast<int>(
diff --git a/ui/base/clipboard/test/test_clipboard.cc b/ui/base/clipboard/test/test_clipboard.cc
index e87c196..d3fdc3b 100644
--- a/ui/base/clipboard/test/test_clipboard.cc
+++ b/ui/base/clipboard/test/test_clipboard.cc
@@ -175,7 +175,7 @@
   if (IsSupportedClipboardBuffer(ClipboardBuffer::kSelection))
     GetStore(ClipboardBuffer::kSelection)
         .data[ClipboardFormatType::GetPlainTextType()] = text;
-  ui::ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
+  ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
 }
 
 void TestClipboard::WriteHTML(const char* markup_data,
@@ -217,6 +217,7 @@
     NOTREACHED() << "Unable to convert bitmap for clipboard";
     return;
   }
+  ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
 }
 
 void TestClipboard::WriteData(const ClipboardFormatType& format,
diff --git a/ui/base/x/x11_display_util.cc b/ui/base/x/x11_display_util.cc
index ff33e44..477de9ef 100644
--- a/ui/base/x/x11_display_util.cc
+++ b/ui/base/x/x11_display_util.cc
@@ -13,9 +13,13 @@
 #include "base/logging.h"
 #include "ui/base/x/x11_util.h"
 #include "ui/display/util/display_util.h"
-#include "ui/display/util/x11/edid_parser_x11.h"
+#include "ui/display/util/edid_parser.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/geometry/matrix3_f.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector3d_f.h"
 #include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/x11_atom_cache.h"
 
 namespace ui {
 
@@ -149,6 +153,55 @@
   return visual->bits_per_rgb;
 }
 
+bool IsRandRAvailable() {
+  int randr_version_major = 0;
+  int randr_version_minor = 0;
+  static bool is_randr_available = XRRQueryVersion(
+      gfx::GetXDisplay(), &randr_version_major, &randr_version_minor);
+  return is_randr_available;
+}
+
+// Get the EDID data from the |output| and stores to |edid|.
+void GetEDIDProperty(XID output, std::vector<uint8_t>* edid) {
+  if (!IsRandRAvailable())
+    return;
+
+  Display* display = gfx::GetXDisplay();
+
+  Atom edid_property = gfx::GetAtom(RR_PROPERTY_RANDR_EDID);
+
+  bool has_edid_property = false;
+  int num_properties = 0;
+  gfx::XScopedPtr<Atom[]> properties(
+      XRRListOutputProperties(display, output, &num_properties));
+  for (int i = 0; i < num_properties; ++i) {
+    if (properties[i] == edid_property) {
+      has_edid_property = true;
+      break;
+    }
+  }
+  if (!has_edid_property)
+    return;
+
+  Atom actual_type;
+  int actual_format;
+  unsigned long bytes_after;
+  unsigned long nitems = 0;
+  unsigned char* prop = nullptr;
+  XRRGetOutputProperty(display, output, edid_property,
+                       0,                // offset
+                       128,              // length
+                       false,            // _delete
+                       false,            // pending
+                       AnyPropertyType,  // req_type
+                       &actual_type, &actual_format, &nitems, &bytes_after,
+                       &prop);
+  DCHECK_EQ(XA_INTEGER, actual_type);
+  DCHECK_EQ(8, actual_format);
+  edid->assign(prop, prop + nitems);
+  XFree(prop);
+}
+
 }  // namespace
 
 int GetXrandrVersion(XDisplay* xdisplay) {
@@ -243,13 +296,14 @@
                       gfx::XObjectDeleter<XRRCrtcInfo, void, XRRFreeCrtcInfo>>
           crtc(XRRGetCrtcInfo(xdisplay, resources.get(), output_info->crtc));
 
-      int64_t display_id = -1;
-      if (!display::EDIDParserX11(output_id).GetDisplayId(
-              static_cast<uint8_t>(i), &display_id)) {
-        // It isn't ideal, but if we can't parse the EDID data, fall back on the
-        // display number.
+      std::vector<uint8_t> edid_bytes;
+      GetEDIDProperty(output_id, &edid_bytes);
+      display::EdidParser edid_parser(edid_bytes);
+      int64_t display_id = edid_parser.GetDisplayId(output_id);
+      // It isn't ideal, but if we can't parse the EDID data, fall back on the
+      // display number.
+      if (!display_id)
         display_id = i;
-      }
 
       gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height);
       display::Display display(display_id, crtc_bounds);
@@ -286,7 +340,16 @@
         gfx::ICCProfile icc_profile = ui::GetICCProfileForMonitor(
             monitor_iter == output_to_monitor.end() ? 0 : monitor_iter->second);
         icc_profile.HistogramDisplay(display.id());
-        display.set_color_space(icc_profile.GetPrimariesOnlyColorSpace());
+        gfx::ColorSpace color_space = icc_profile.GetPrimariesOnlyColorSpace();
+
+        // Most folks do not have an ICC profile set up, but we still want to
+        // detect if a display has a wide color gamut so that HDR videos can be
+        // enabled.  Only do this if |bits_per_component| > 8 or else SDR
+        // screens may have washed out colors.
+        if (bits_per_component > 8 && !color_space.IsValid())
+          color_space = display::GetColorSpaceFromEdid(edid_parser);
+
+        display.set_color_space(color_space);
       }
 
       display.set_color_depth(depth);
diff --git a/ui/display/util/BUILD.gn b/ui/display/util/BUILD.gn
index 622996c..cbaa00b 100644
--- a/ui/display/util/BUILD.gn
+++ b/ui/display/util/BUILD.gn
@@ -29,17 +29,6 @@
     "//ui/gfx/geometry",
   ]
 
-  if (use_x11 || ozone_platform_x11) {
-    sources += [
-      "x11/edid_parser_x11.cc",
-      "x11/edid_parser_x11.h",
-    ]
-    configs += [
-      "//build/config/linux:x11",
-      "//build/config/linux:xrandr",
-    ]
-    deps += [ "//ui/gfx/x" ]
-  }
   if (is_chromeos) {
     deps += [ "//ui/display/types" ]
   } else if (is_mac) {
diff --git a/ui/display/util/display_util.cc b/ui/display/util/display_util.cc
index f68e408b..e960432 100644
--- a/ui/display/util/display_util.cc
+++ b/ui/display/util/display_util.cc
@@ -7,7 +7,9 @@
 #include <stddef.h>
 
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/stl_util.h"
+#include "ui/display/util/edid_parser.h"
 
 namespace display {
 
@@ -17,12 +19,19 @@
 // See crbug.com/136533. The first element maintains the minimum
 // size required to be valid size.
 const int kInvalidDisplaySizeList[][2] = {
-  {40, 30},
-  {50, 40},
-  {160, 90},
-  {160, 100},
+    {40, 30},
+    {50, 40},
+    {160, 90},
+    {160, 100},
 };
 
+// Used in the GetColorSpaceFromEdid function to collect data on whether the
+// color space extracted from an EDID blob passed the sanity checks.
+void EmitEdidColorSpaceChecksOutcomeUma(EdidColorSpaceChecksOutcome outcome) {
+  base::UmaHistogramEnumeration("DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+                                outcome);
+}
+
 }  // namespace
 
 bool IsDisplaySizeBlackListed(const gfx::Size& physical_size) {
@@ -50,4 +59,84 @@
           (static_cast<int64_t>(product_code_hash) << 8) | output_index);
 }
 
+gfx::ColorSpace GetColorSpaceFromEdid(const display::EdidParser& edid_parser) {
+  const SkColorSpacePrimaries primaries = edid_parser.primaries();
+
+  // Sanity check: primaries should verify By <= Ry <= Gy, Bx <= Rx and Gx <=
+  // Rx, to guarantee that the R, G and B colors are each in the correct region.
+  if (!(primaries.fBX <= primaries.fRX && primaries.fGX <= primaries.fRX &&
+        primaries.fBY <= primaries.fRY && primaries.fRY <= primaries.fGY)) {
+    EmitEdidColorSpaceChecksOutcomeUma(
+        EdidColorSpaceChecksOutcome::kErrorBadCoordinates);
+    return gfx::ColorSpace();
+  }
+
+  // Sanity check: the area spawned by the primaries' triangle is too small,
+  // i.e. less than half the surface of the triangle spawned by sRGB/BT.709.
+  constexpr double kBT709PrimariesArea = 0.0954;
+  const float primaries_area_twice =
+      (primaries.fRX * primaries.fGY) + (primaries.fBX * primaries.fRY) +
+      (primaries.fGX * primaries.fBY) - (primaries.fBX * primaries.fGY) -
+      (primaries.fGX * primaries.fRY) - (primaries.fRX * primaries.fBY);
+  if (primaries_area_twice < kBT709PrimariesArea) {
+    EmitEdidColorSpaceChecksOutcomeUma(
+        EdidColorSpaceChecksOutcome::kErrorPrimariesAreaTooSmall);
+    return gfx::ColorSpace();
+  }
+
+  // Sanity check: https://crbug.com/809909, the blue primary coordinates should
+  // not be too far left/upwards of the expected location (namely [0.15, 0.06]
+  // for sRGB/ BT.709/ Adobe RGB/ DCI-P3, and [0.131, 0.046] for BT.2020).
+  constexpr float kExpectedBluePrimaryX = 0.15f;
+  constexpr float kBluePrimaryXDelta = 0.02f;
+  constexpr float kExpectedBluePrimaryY = 0.06f;
+  constexpr float kBluePrimaryYDelta = 0.031f;
+  const bool is_blue_primary_broken =
+      (std::abs(primaries.fBX - kExpectedBluePrimaryX) > kBluePrimaryXDelta) ||
+      (std::abs(primaries.fBY - kExpectedBluePrimaryY) > kBluePrimaryYDelta);
+  if (is_blue_primary_broken) {
+    EmitEdidColorSpaceChecksOutcomeUma(
+        EdidColorSpaceChecksOutcome::kErrorBluePrimaryIsBroken);
+    return gfx::ColorSpace();
+  }
+
+  skcms_Matrix3x3 color_space_as_matrix;
+  if (!primaries.toXYZD50(&color_space_as_matrix)) {
+    EmitEdidColorSpaceChecksOutcomeUma(
+        EdidColorSpaceChecksOutcome::kErrorCannotExtractToXYZD50);
+    return gfx::ColorSpace();
+  }
+
+  const double gamma = edid_parser.gamma();
+  if (gamma < 1.0) {
+    EmitEdidColorSpaceChecksOutcomeUma(
+        EdidColorSpaceChecksOutcome::kErrorBadGamma);
+    return gfx::ColorSpace();
+  }
+  EmitEdidColorSpaceChecksOutcomeUma(EdidColorSpaceChecksOutcome::kSuccess);
+
+  gfx::ColorSpace::TransferID transfer_id =
+      gfx::ColorSpace::TransferID::INVALID;
+  if (base::Contains(edid_parser.supported_color_primary_ids(),
+                     gfx::ColorSpace::PrimaryID::BT2020)) {
+    if (base::Contains(edid_parser.supported_color_transfer_ids(),
+                       gfx::ColorSpace::TransferID::SMPTEST2084)) {
+      transfer_id = gfx::ColorSpace::TransferID::SMPTEST2084;
+    } else if (base::Contains(edid_parser.supported_color_transfer_ids(),
+                              gfx::ColorSpace::TransferID::ARIB_STD_B67)) {
+      transfer_id = gfx::ColorSpace::TransferID::ARIB_STD_B67;
+    }
+  } else if (gamma == 2.2f) {
+    transfer_id = gfx::ColorSpace::TransferID::GAMMA22;
+  } else if (gamma == 2.4f) {
+    transfer_id = gfx::ColorSpace::TransferID::GAMMA24;
+  }
+
+  if (transfer_id != gfx::ColorSpace::TransferID::INVALID)
+    return gfx::ColorSpace::CreateCustom(color_space_as_matrix, transfer_id);
+
+  skcms_TransferFunction transfer = {gamma, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
+  return gfx::ColorSpace::CreateCustom(color_space_as_matrix, transfer);
+}
+
 }  // namespace display
diff --git a/ui/display/util/display_util.h b/ui/display/util/display_util.h
index 6617e52..8bf61ea5 100644
--- a/ui/display/util/display_util.h
+++ b/ui/display/util/display_util.h
@@ -8,13 +8,28 @@
 #include <stdint.h>
 
 #include "ui/display/util/display_util_export.h"
+#include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace display {
 
+class EdidParser;
+
 // 1 inch in mm.
 constexpr float kInchInMm = 25.4f;
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class EdidColorSpaceChecksOutcome {
+  kSuccess = 0,
+  kErrorBadCoordinates = 1,
+  kErrorPrimariesAreaTooSmall = 2,
+  kErrorBluePrimaryIsBroken = 3,
+  kErrorCannotExtractToXYZD50 = 4,
+  kErrorBadGamma = 5,
+  kMaxValue = kErrorBadGamma
+};
+
 // Returns true if a given size is in the list of bogus sizes in mm that should
 // be ignored.
 DISPLAY_UTIL_EXPORT bool IsDisplaySizeBlackListed(
@@ -31,6 +46,11 @@
                                               uint32_t product_code_hash,
                                               uint8_t output_index);
 
+// Uses |edid_parser| to extract a gfx::ColorSpace which will be IsValid() if
+// both gamma and the color primaries were correctly found.
+DISPLAY_UTIL_EXPORT gfx::ColorSpace GetColorSpaceFromEdid(
+    const display::EdidParser& edid_parser);
+
 }  // namespace display
 
 #endif  // UI_DISPLAY_UTIL_DISPLAY_UTIL_H_
diff --git a/ui/display/util/display_util_unittest.cc b/ui/display/util/display_util_unittest.cc
index 11fabe2..60b8c6b 100644
--- a/ui/display/util/display_util_unittest.cc
+++ b/ui/display/util/display_util_unittest.cc
@@ -4,10 +4,121 @@
 
 #include "ui/display/util/display_util.h"
 
+#include "base/test/metrics/histogram_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/util/edid_parser.h"
 
 namespace display {
 
+namespace {
+
+// HP z32x monitor.
+const unsigned char kHPz32x[] =
+    "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x75\x32\x01\x01\x01\x01"
+    "\x1B\x1B\x01\x04\xB5\x46\x27\x78\x3A\x8D\x15\xAC\x51\x32\xB8\x26"
+    "\x0B\x50\x54\x21\x08\x00\xD1\xC0\xA9\xC0\x81\xC0\xD1\x00\xB3\x00"
+    "\x95\x00\xA9\x40\x81\x80\x4D\xD0\x00\xA0\xF0\x70\x3E\x80\x30\x20"
+    "\x35\x00\xB9\x88\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x18\x3C\x1E"
+    "\x87\x3C\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48"
+    "\x50\x20\x5A\x33\x32\x78\x0A\x20\x20\x20\x20\x20\x00\x00\x00\xFF"
+    "\x00\x43\x4E\x43\x37\x32\x37\x30\x4D\x57\x30\x0A\x20\x20\x01\x46"
+    "\x02\x03\x18\xF1\x4B\x10\x1F\x04\x13\x03\x12\x02\x11\x01\x05\x14"
+    "\x23\x09\x07\x07\x83\x01\x00\x00\xA3\x66\x00\xA0\xF0\x70\x1F\x80"
+    "\x30\x20\x35\x00\xB9\x88\x21\x00\x00\x1A\x56\x5E\x00\xA0\xA0\xA0"
+    "\x29\x50\x30\x20\x35\x00\xB9\x88\x21\x00\x00\x1A\xEF\x51\x00\xA0"
+    "\xF0\x70\x19\x80\x30\x20\x35\x00\xB9\x88\x21\x00\x00\x1A\xE2\x68"
+    "\x00\xA0\xA0\x40\x2E\x60\x20\x30\x63\x00\xB9\x88\x21\x00\x00\x1C"
+    "\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20\x36\x00\xB9\x88\x21\x00"
+    "\x00\x1A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3E";
+
+// Chromebook Samus internal display.
+const unsigned char kSamus[] =
+    "\x00\xff\xff\xff\xff\xff\xff\x00\x30\xe4\x2e\x04\x00\x00\x00\x00"
+    "\x00\x18\x01\x04\xa5\x1b\x12\x96\x02\x4f\xd5\xa2\x59\x52\x93\x26"
+    "\x17\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+    "\x01\x01\x01\x01\x01\x01\x6d\x6f\x00\x9e\xa0\xa4\x31\x60\x30\x20"
+    "\x3a\x00\x10\xb5\x10\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00"
+    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\x00\x4c"
+    "\x47\x20\x44\x69\x73\x70\x6c\x61\x79\x0a\x20\x20\x00\x00\x00\xfe"
+    "\x00\x4c\x50\x31\x32\x39\x51\x45\x32\x2d\x53\x50\x41\x31\x00\x6c";
+
+// Chromebook Eve internal display.
+const unsigned char kEve[] =
+    "\x00\xff\xff\xff\xff\xff\xff\x00\x4d\x10\x8a\x14\x00\x00\x00\x00"
+    "\x16\x1b\x01\x04\xa5\x1a\x11\x78\x06\xde\x50\xa3\x54\x4c\x99\x26"
+    "\x0f\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+    "\x01\x01\x01\x01\x01\x01\xbb\x62\x60\xa0\x90\x40\x2e\x60\x30\x20"
+    "\x3a\x00\x03\xad\x10\x00\x00\x18\x00\x00\x00\x10\x00\x00\x00\x00"
+    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00"
+    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfc"
+    "\x00\x4c\x51\x31\x32\x33\x50\x31\x4a\x58\x33\x32\x0a\x20\x00\xb6";
+
+// Invalid EDID: too short to contain chromaticity nor gamma information.
+const unsigned char kInvalidEdid[] =
+    "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x76\x26\x01\x01\x01\x01"
+    "\x02\x12\x01\x03\x80\x34\x21";
+
+// Partially valid EDID: gamma information is marked as non existent.
+const unsigned char kEdidWithNoGamma[] =
+    "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x76\x26\x01\x01\x01\x01"
+    "\x02\x12\x01\x03\x80\x34\x21\xFF\xEE\xEF\x95\xA3\x54\x4C\x9B\x26"
+    "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x81\x80\x81\x99\x71\x00\xA9\x00";
+
+// A Samsung monitor that supports HDR metadata.
+constexpr unsigned char kHDR[] =
+    "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xf6\x0d\x00\x0e\x00\x01"
+    "\x01\x1b\x01\x03\x80\x5f\x36\x78\x0a\x23\xad\xa4\x54\x4d\x99\x26"
+    "\x0f\x47\x4a\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
+    "\xa9\xc0\xb3\x00\x01\x01\x04\x74\x00\x30\xf2\x70\x5a\x80\xb0\x58"
+    "\x8a\x00\x50\x1d\x74\x00\x00\x1e\x02\x3a\x80\x18\x71\x38\x2d\x40"
+    "\x58\x2c\x45\x00\x50\x1d\x74\x00\x00\x1e\x00\x00\x00\xfd\x00\x18"
+    "\x4b\x0f\x51\x1e\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc"
+    "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x5a"
+    "\x02\x03\x4f\xf0\x53\x5f\x10\x1f\x04\x13\x05\x14\x20\x21\x22\x5d"
+    "\x5e\x62\x63\x64\x07\x16\x03\x12\x2c\x09\x07\x07\x15\x07\x50\x3d"
+    "\x04\xc0\x57\x07\x00\x83\x01\x00\x00\xe2\x00\x0f\xe3\x05\x83\x01"
+    "\x6e\x03\x0c\x00\x30\x00\xb8\x3c\x20\x00\x80\x01\x02\x03\x04\xe3"
+    "\x06\x0d\x01\xe5\x0e\x60\x61\x65\x66\xe5\x01\x8b\x84\x90\x01\x01"
+    "\x1d\x80\xd0\x72\x1c\x16\x20\x10\x2c\x25\x80\x50\x1d\x74\x00\x00"
+    "\x9e\x66\x21\x56\xaa\x51\x00\x1e\x30\x46\x8f\x33\x00\x50\x1d\x74"
+    "\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbd";
+
+// EDID collected in the wild: valid but with primaries in the wrong order.
+const unsigned char kSST210[] =
+    "\xff\x00\xff\xff\xff\xff\x00\xff\x2d\x4c\x42\x52\x32\x31\x50\x43"
+    "\x0c\x2b\x03\x01\x33\x80\xa2\x20\x56\x2a\x9c\x4e\x50\x5b\x26\x95"
+    "\x50\x23\xbf\x59\x80\xef\x80\x81\x59\x61\x59\x45\x59\x31\x40\x31"
+    "\x01\x01\x01\x01\x01\x01\x32\x8c\xa0\x40\xb0\x60\x40\x19\x32\x1e"
+    "\x00\x13\x44\x06\x00\x21\x1e\x00\x00\x00\xfd\x00\x38\x00\x1e\x55"
+    "\x0f\x51\x0a\x00\x20\x20\x20\x20\x20\x20\x00\x00\xfc\x00\x32\x00"
+    "\x30\x31\x20\x54\x69\x44\x69\x67\x61\x74\x0a\x6c\x00\x00\xff\x00"
+    "\x48\x00\x4b\x34\x41\x54\x30\x30\x32\x38\x0a\x38\x20\x20\xf8\x00";
+
+// EDID of |kSST210| with the order of the primaries corrected. Still invalid
+// because the triangle of primaries is too small.
+const unsigned char kSST210Corrected[] =
+    "\xff\x00\xff\xff\xff\xff\x00\xff\x2d\x4c\x42\x52\x32\x31\x50\x43"
+    "\x0c\x2b\x03\x01\x33\x80\xa2\x20\x56\x2a\x9c\x95\x50\x4e\x50\x5b"
+    "\x26\x23\xbf\x59\x80\xef\x80\x81\x59\x61\x59\x45\x59\x31\x40\x31"
+    "\x01\x01\x01\x01\x01\x01\x32\x8c\xa0\x40\xb0\x60\x40\x19\x32\x1e"
+    "\x00\x13\x44\x06\x00\x21\x1e\x00\x00\x00\xfd\x00\x38\x00\x1e\x55"
+    "\x0f\x51\x0a\x00\x20\x20\x20\x20\x20\x20\x00\x00\xfc\x00\x32\x00"
+    "\x30\x31\x20\x54\x69\x44\x69\x67\x61\x74\x0a\x6c\x00\x00\xff\x00"
+    "\x48\x00\x4b\x34\x41\x54\x30\x30\x32\x38\x0a\x38\x20\x20\xf8\x00";
+
+// This EDID produces blue primary coordinates too far off the expected point,
+// which would paint blue colors as purple. See https://crbug.com/809909.
+const unsigned char kBrokenBluePrimaries[] =
+    "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x83\x4d\x83\x00\x00\x00\x00"
+    "\x00\x19\x01\x04\x95\x1d\x10\x78\x0a\xee\x25\xa3\x54\x4c\x99\x29"
+    "\x26\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+    "\x01\x01\x01\x01\x01\x01\xd2\x37\x80\xa2\x70\x38\x40\x40\x30\x20"
+    "\x25\x00\x25\xa5\x10\x00\x00\x1a\xa6\x2c\x80\xa2\x70\x38\x40\x40"
+    "\x30\x20\x25\x00\x25\xa5\x10\x00\x00\x1a\x00\x00\x00\xfe\x00\x56"
+    "\x59\x54\x39\x36\x80\x31\x33\x33\x48\x4c\x0a\x20\x00\x00\x00\x00";
+
+}  // namespace
+
 TEST(DisplayUtilTest, TestBlackListedDisplay) {
   EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(10, 10)));
   EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(40, 30)));
@@ -20,4 +131,182 @@
   EXPECT_FALSE(IsDisplaySizeBlackListed(gfx::Size(272, 181)));
 }
 
+TEST(DisplayUtilTest, GetColorSpaceFromEdid) {
+  base::HistogramTester histogram_tester;
+
+  // Test with HP z32x monitor.
+  constexpr SkColorSpacePrimaries expected_hpz32x_primaries = {
+      .fRX = 0.673828f,
+      .fRY = 0.316406f,
+      .fGX = 0.198242f,
+      .fGY = 0.719727f,
+      .fBX = 0.148438f,
+      .fBY = 0.043945f,
+      .fWX = 0.313477f,
+      .fWY = 0.329102f};
+  skcms_Matrix3x3 expected_hpz32x_toXYZ50_matrix;
+  expected_hpz32x_primaries.toXYZD50(&expected_hpz32x_toXYZ50_matrix);
+  const std::vector<uint8_t> hpz32x_edid(kHPz32x,
+                                         kHPz32x + base::size(kHPz32x) - 1);
+  const gfx::ColorSpace expected_hpz32x_color_space =
+      gfx::ColorSpace::CreateCustom(
+          expected_hpz32x_toXYZ50_matrix,
+          skcms_TransferFunction({2.2, 1, 0, 0, 0, 0, 0}));
+  EXPECT_EQ(expected_hpz32x_color_space.ToString(),
+            GetColorSpaceFromEdid(display::EdidParser(hpz32x_edid)).ToString());
+  histogram_tester.ExpectBucketCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+      static_cast<base::HistogramBase::Sample>(
+          EdidColorSpaceChecksOutcome::kSuccess),
+      1);
+
+  // Test with Chromebook Samus internal display.
+  constexpr SkColorSpacePrimaries expected_samus_primaries = {.fRX = 0.633789f,
+                                                              .fRY = 0.347656f,
+                                                              .fGX = 0.323242f,
+                                                              .fGY = 0.577148f,
+                                                              .fBX = 0.151367f,
+                                                              .fBY = 0.090820f,
+                                                              .fWX = 0.313477f,
+                                                              .fWY = 0.329102f};
+  skcms_Matrix3x3 expected_samus_toXYZ50_matrix;
+  expected_samus_primaries.toXYZD50(&expected_samus_toXYZ50_matrix);
+  const std::vector<uint8_t> samus_edid(kSamus,
+                                        kSamus + base::size(kSamus) - 1);
+  const gfx::ColorSpace expected_samus_color_space =
+      gfx::ColorSpace::CreateCustom(
+          expected_samus_toXYZ50_matrix,
+          skcms_TransferFunction({2.5, 1, 0, 0, 0, 0, 0}));
+  EXPECT_EQ(expected_samus_color_space.ToString(),
+            GetColorSpaceFromEdid(display::EdidParser(samus_edid)).ToString());
+  histogram_tester.ExpectBucketCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+      static_cast<base::HistogramBase::Sample>(
+          EdidColorSpaceChecksOutcome::kSuccess),
+      2);
+
+  // Test with Chromebook Eve internal display.
+  constexpr SkColorSpacePrimaries expected_eve_primaries = {.fRX = 0.639648f,
+                                                            .fRY = 0.329102f,
+                                                            .fGX = 0.299805f,
+                                                            .fGY = 0.599609f,
+                                                            .fBX = 0.149414f,
+                                                            .fBY = 0.059570f,
+                                                            .fWX = 0.312500f,
+                                                            .fWY = 0.328125f};
+  skcms_Matrix3x3 expected_eve_toXYZ50_matrix;
+  expected_eve_primaries.toXYZD50(&expected_eve_toXYZ50_matrix);
+  const std::vector<uint8_t> eve_edid(kEve, kEve + base::size(kEve) - 1);
+  const gfx::ColorSpace expected_eve_color_space =
+      gfx::ColorSpace::CreateCustom(
+          expected_eve_toXYZ50_matrix,
+          skcms_TransferFunction({2.2, 1, 0, 0, 0, 0, 0}));
+  EXPECT_EQ(expected_eve_color_space.ToString(),
+            GetColorSpaceFromEdid(display::EdidParser(eve_edid)).ToString());
+  histogram_tester.ExpectBucketCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+      static_cast<base::HistogramBase::Sample>(
+          EdidColorSpaceChecksOutcome::kSuccess),
+      3);
+
+  // Test with a display that supports HDR.
+  constexpr SkColorSpacePrimaries expected_hdr_primaries = {.fRX = 0.640625f,
+                                                            .fRY = 0.330078f,
+                                                            .fGX = 0.300781f,
+                                                            .fGY = 0.600586f,
+                                                            .fBX = 0.150391f,
+                                                            .fBY = 0.060547f,
+                                                            .fWX = 0.280273f,
+                                                            .fWY = 0.290039f};
+  skcms_Matrix3x3 expected_hdr_toXYZ50_matrix;
+  expected_hdr_primaries.toXYZD50(&expected_hdr_toXYZ50_matrix);
+  const std::vector<uint8_t> hdr_edid(kHDR, kHDR + base::size(kHDR) - 1);
+  const gfx::ColorSpace expected_hdr_color_space =
+      gfx::ColorSpace::CreateCustom(expected_hdr_toXYZ50_matrix,
+                                    gfx::ColorSpace::TransferID::SMPTEST2084);
+  EXPECT_TRUE(expected_hdr_color_space.IsHDR());
+  EXPECT_EQ(expected_hdr_color_space.ToString(),
+            GetColorSpaceFromEdid(display::EdidParser(hdr_edid)).ToString());
+  histogram_tester.ExpectBucketCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+      static_cast<base::HistogramBase::Sample>(
+          EdidColorSpaceChecksOutcome::kSuccess),
+      4);
+
+  // Test with gamma marked as non-existent.
+  const std::vector<uint8_t> no_gamma_edid(
+      kEdidWithNoGamma, kEdidWithNoGamma + base::size(kEdidWithNoGamma) - 1);
+  const gfx::ColorSpace no_gamma_color_space =
+      GetColorSpaceFromEdid(display::EdidParser(no_gamma_edid));
+  EXPECT_FALSE(no_gamma_color_space.IsValid());
+  histogram_tester.ExpectBucketCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+      static_cast<base::HistogramBase::Sample>(
+          EdidColorSpaceChecksOutcome::kErrorBadGamma),
+      1);
+  histogram_tester.ExpectTotalCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome", 5);
+}
+
+TEST(DisplayUtilTest, GetInvalidColorSpaceFromEdid) {
+  base::HistogramTester histogram_tester;
+  const std::vector<uint8_t> empty_edid;
+  EXPECT_EQ(gfx::ColorSpace(),
+            GetColorSpaceFromEdid(display::EdidParser(empty_edid)));
+  histogram_tester.ExpectBucketCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+      static_cast<base::HistogramBase::Sample>(
+          EdidColorSpaceChecksOutcome::kErrorPrimariesAreaTooSmall),
+      1);
+
+  const std::vector<uint8_t> invalid_edid(
+      kInvalidEdid, kInvalidEdid + base::size(kInvalidEdid) - 1);
+  const gfx::ColorSpace invalid_color_space =
+      GetColorSpaceFromEdid(display::EdidParser(invalid_edid));
+  EXPECT_FALSE(invalid_color_space.IsValid());
+  histogram_tester.ExpectBucketCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+      static_cast<base::HistogramBase::Sample>(
+          EdidColorSpaceChecksOutcome::kErrorPrimariesAreaTooSmall),
+      2);
+
+  const std::vector<uint8_t> sst210_edid(kSST210,
+                                         kSST210 + base::size(kSST210) - 1);
+  const gfx::ColorSpace sst210_color_space =
+      GetColorSpaceFromEdid(display::EdidParser(sst210_edid));
+  EXPECT_FALSE(sst210_color_space.IsValid()) << sst210_color_space.ToString();
+  histogram_tester.ExpectBucketCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+      static_cast<base::HistogramBase::Sample>(
+          EdidColorSpaceChecksOutcome::kErrorBadCoordinates),
+      1);
+
+  const std::vector<uint8_t> sst210_edid_2(
+      kSST210Corrected, kSST210Corrected + base::size(kSST210Corrected) - 1);
+  const gfx::ColorSpace sst210_color_space_2 =
+      GetColorSpaceFromEdid(display::EdidParser(sst210_edid_2));
+  EXPECT_FALSE(sst210_color_space_2.IsValid())
+      << sst210_color_space_2.ToString();
+  histogram_tester.ExpectBucketCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+      static_cast<base::HistogramBase::Sample>(
+          EdidColorSpaceChecksOutcome::kErrorPrimariesAreaTooSmall),
+      3);
+
+  const std::vector<uint8_t> broken_blue_edid(
+      kBrokenBluePrimaries,
+      kBrokenBluePrimaries + base::size(kBrokenBluePrimaries) - 1);
+  const gfx::ColorSpace broken_blue_color_space =
+      GetColorSpaceFromEdid(display::EdidParser(broken_blue_edid));
+  EXPECT_FALSE(broken_blue_color_space.IsValid())
+      << broken_blue_color_space.ToString();
+  histogram_tester.ExpectBucketCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+      static_cast<base::HistogramBase::Sample>(
+          EdidColorSpaceChecksOutcome::kErrorBluePrimaryIsBroken),
+      1);
+  histogram_tester.ExpectTotalCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome", 5);
+}
+
 }  // namespace display
diff --git a/ui/display/util/x11/edid_parser_x11.cc b/ui/display/util/x11/edid_parser_x11.cc
deleted file mode 100644
index 57baee92..0000000
--- a/ui/display/util/x11/edid_parser_x11.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/display/util/x11/edid_parser_x11.h"
-
-#include "base/strings/string_util.h"
-#include "ui/display/util/edid_parser.h"
-#include "ui/gfx/x/x11.h"
-#include "ui/gfx/x/x11_atom_cache.h"
-#include "ui/gfx/x/x11_types.h"
-
-namespace display {
-
-namespace {
-
-bool IsRandRAvailable() {
-  int randr_version_major = 0;
-  int randr_version_minor = 0;
-  static bool is_randr_available = XRRQueryVersion(
-      gfx::GetXDisplay(), &randr_version_major, &randr_version_minor);
-  return is_randr_available;
-}
-
-// Get the EDID data from the |output| and stores to |edid|.
-// Returns true if EDID property is successfully obtained. Otherwise returns
-// false and does not touch |edid|.
-bool GetEDIDProperty(XID output, std::vector<uint8_t>* edid) {
-  if (!IsRandRAvailable())
-    return false;
-
-  Display* display = gfx::GetXDisplay();
-
-  Atom edid_property = gfx::GetAtom(RR_PROPERTY_RANDR_EDID);
-
-  bool has_edid_property = false;
-  int num_properties = 0;
-  gfx::XScopedPtr<Atom[]> properties(
-      XRRListOutputProperties(display, output, &num_properties));
-  for (int i = 0; i < num_properties; ++i) {
-    if (properties[i] == edid_property) {
-      has_edid_property = true;
-      break;
-    }
-  }
-  if (!has_edid_property)
-    return false;
-
-  Atom actual_type;
-  int actual_format;
-  unsigned long bytes_after;
-  unsigned long nitems = 0;
-  unsigned char* prop = nullptr;
-  XRRGetOutputProperty(display,
-                       output,
-                       edid_property,
-                       0,                // offset
-                       128,              // length
-                       false,            // _delete
-                       false,            // pending
-                       AnyPropertyType,  // req_type
-                       &actual_type,
-                       &actual_format,
-                       &nitems,
-                       &bytes_after,
-                       &prop);
-  DCHECK_EQ(XA_INTEGER, actual_type);
-  DCHECK_EQ(8, actual_format);
-  edid->assign(prop, prop + nitems);
-  XFree(prop);
-  return true;
-}
-
-}  // namespace
-
-EDIDParserX11::EDIDParserX11(XID output_id) : output_id_(output_id) {
-  GetEDIDProperty(output_id_, &edid_);
-}
-
-EDIDParserX11::~EDIDParserX11() {}
-
-bool EDIDParserX11::GetDisplayId(uint8_t index, int64_t* out_display_id) const {
-  if (edid_.empty())
-    return false;
-
-  *out_display_id = EdidParser(edid_).GetDisplayId(output_id_);
-  return true;
-}
-
-}  // namespace display
diff --git a/ui/display/util/x11/edid_parser_x11.h b/ui/display/util/x11/edid_parser_x11.h
deleted file mode 100644
index 383d828..0000000
--- a/ui/display/util/x11/edid_parser_x11.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_DISPLAY_UTIL_X11_EDID_PARSER_X11_H_
-#define UI_DISPLAY_UTIL_X11_EDID_PARSER_X11_H_
-
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "ui/display/types/display_constants.h"
-#include "ui/display/util/display_util_export.h"
-#include "ui/display/util/edid_parser.h"
-
-typedef unsigned long XID;
-typedef XID RROutput;
-
-// Xrandr utility functions to help get EDID information.
-
-namespace display {
-
-// Xrandr utility class to help get EDID information.
-class DISPLAY_UTIL_EXPORT EDIDParserX11 {
- public:
-  EDIDParserX11(XID output_id);
-  ~EDIDParserX11();
-
-  // Sets |out_display_id| to the display ID from the EDID of this output.
-  // Returns true if successful, false otherwise.
-  bool GetDisplayId(uint8_t index, int64_t* out_display_id) const;
-
-  XID output_id() const { return output_id_; }
-  const std::vector<uint8_t>& edid() const { return edid_; }
-
- private:
-  const XID output_id_;
-
-  // This will be an empty vector upon failure to get the EDID from the
-  // |output_id_|.
-  std::vector<uint8_t> edid_;
-
-  DISALLOW_COPY_AND_ASSIGN(EDIDParserX11);
-};
-
-}  // namespace display
-
-#endif  // UI_DISPLAY_UTIL_X11_EDID_PARSER_X11_H_
diff --git a/ui/gfx/x/x11.h b/ui/gfx/x/x11.h
index f53865fc..61b4ab6 100644
--- a/ui/gfx/x/x11.h
+++ b/ui/gfx/x/x11.h
@@ -90,6 +90,7 @@
 #undef Bool           // Defined by X11/Xlib.h to int
 #undef RootWindow     // Defined by X11/Xlib.h
 #undef DestroyAll     // Defined by X11/X.h to 0
+#undef Always         // Defined by X11/X.h to 2
 #undef AddToList      // Defined by X11/extensions/XI.h to 0
 #undef COUNT          // Defined by X11/extensions/XI.h to 0
 #undef CREATE         // Defined by X11/extensions/XI.h to 1
diff --git a/ui/gl/gl_visual_picker_glx.cc b/ui/gl/gl_visual_picker_glx.cc
index 7894212..050ced45 100644
--- a/ui/gl/gl_visual_picker_glx.cc
+++ b/ui/gl/gl_visual_picker_glx.cc
@@ -5,6 +5,7 @@
 #include "ui/gl/gl_visual_picker_glx.h"
 
 #include <algorithm>
+#include <bitset>
 #include <cstring>
 #include <numeric>
 #include <vector>
diff --git a/ui/login/oobe.css b/ui/login/oobe.css
index ca88408..c811118 100644
--- a/ui/login/oobe.css
+++ b/ui/login/oobe.css
@@ -200,6 +200,10 @@
   z-index: 1;
 }
 
+html[screen=user-adding] #version-labels {
+  color: white;
+}
+
 #bluetooth-name {
   background: rgba(255,255,255,.17);
   border-radius: 4px;
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index f2fc2ed..4c6bbeb 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -19,6 +19,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/display/types/display_mode.h"
+#include "ui/display/util/display_util.h"
 #include "ui/display/util/edid_parser.h"
 
 namespace ui {
@@ -28,13 +29,6 @@
 static const size_t kDefaultCursorWidth = 64;
 static const size_t kDefaultCursorHeight = 64;
 
-// Used in the GetColorSpaceFromEdid function to collect data on whether the
-// color space extracted from an EDID blob passed the sanity checks.
-void EmitEdidColorSpaceChecksOutcomeUma(EdidColorSpaceChecksOutcome outcome) {
-  base::UmaHistogramEnumeration("DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-                                outcome);
-}
-
 bool IsCrtcInUse(
     uint32_t crtc,
     const std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>&
@@ -482,7 +476,7 @@
     year_of_manufacture = edid_parser.year_of_manufacture();
     has_overscan =
         edid_parser.has_overscan_flag() && edid_parser.overscan_flag();
-    display_color_space = GetColorSpaceFromEdid(edid_parser);
+    display_color_space = display::GetColorSpaceFromEdid(edid_parser);
     base::UmaHistogramBoolean("DrmUtil.CreateDisplaySnapshot.IsHDR",
                               display_color_space.IsHDR());
     bits_per_channel = std::max(edid_parser.bits_per_channel(), 0);
@@ -653,84 +647,4 @@
   return params;
 }
 
-gfx::ColorSpace GetColorSpaceFromEdid(const display::EdidParser& edid_parser) {
-  const SkColorSpacePrimaries primaries = edid_parser.primaries();
-
-  // Sanity check: primaries should verify By <= Ry <= Gy, Bx <= Rx and Gx <=
-  // Rx, to guarantee that the R, G and B colors are each in the correct region.
-  if (!(primaries.fBX <= primaries.fRX && primaries.fGX <= primaries.fRX &&
-        primaries.fBY <= primaries.fRY && primaries.fRY <= primaries.fGY)) {
-    EmitEdidColorSpaceChecksOutcomeUma(
-        EdidColorSpaceChecksOutcome::kErrorBadCoordinates);
-    return gfx::ColorSpace();
-  }
-
-  // Sanity check: the area spawned by the primaries' triangle is too small,
-  // i.e. less than half the surface of the triangle spawned by sRGB/BT.709.
-  constexpr double kBT709PrimariesArea = 0.0954;
-  const float primaries_area_twice =
-      (primaries.fRX * primaries.fGY) + (primaries.fBX * primaries.fRY) +
-      (primaries.fGX * primaries.fBY) - (primaries.fBX * primaries.fGY) -
-      (primaries.fGX * primaries.fRY) - (primaries.fRX * primaries.fBY);
-  if (primaries_area_twice < kBT709PrimariesArea) {
-    EmitEdidColorSpaceChecksOutcomeUma(
-        EdidColorSpaceChecksOutcome::kErrorPrimariesAreaTooSmall);
-    return gfx::ColorSpace();
-  }
-
-  // Sanity check: https://crbug.com/809909, the blue primary coordinates should
-  // not be too far left/upwards of the expected location (namely [0.15, 0.06]
-  // for sRGB/ BT.709/ Adobe RGB/ DCI-P3, and [0.131, 0.046] for BT.2020).
-  constexpr float kExpectedBluePrimaryX = 0.15f;
-  constexpr float kBluePrimaryXDelta = 0.02f;
-  constexpr float kExpectedBluePrimaryY = 0.06f;
-  constexpr float kBluePrimaryYDelta = 0.031f;
-  const bool is_blue_primary_broken =
-      (std::abs(primaries.fBX - kExpectedBluePrimaryX) > kBluePrimaryXDelta) ||
-      (std::abs(primaries.fBY - kExpectedBluePrimaryY) > kBluePrimaryYDelta);
-  if (is_blue_primary_broken) {
-    EmitEdidColorSpaceChecksOutcomeUma(
-        EdidColorSpaceChecksOutcome::kErrorBluePrimaryIsBroken);
-    return gfx::ColorSpace();
-  }
-
-  skcms_Matrix3x3 color_space_as_matrix;
-  if (!primaries.toXYZD50(&color_space_as_matrix)) {
-    EmitEdidColorSpaceChecksOutcomeUma(
-        EdidColorSpaceChecksOutcome::kErrorCannotExtractToXYZD50);
-    return gfx::ColorSpace();
-  }
-
-  const double gamma = edid_parser.gamma();
-  if (gamma < 1.0) {
-    EmitEdidColorSpaceChecksOutcomeUma(
-        EdidColorSpaceChecksOutcome::kErrorBadGamma);
-    return gfx::ColorSpace();
-  }
-  EmitEdidColorSpaceChecksOutcomeUma(EdidColorSpaceChecksOutcome::kSuccess);
-
-  gfx::ColorSpace::TransferID transfer_id =
-      gfx::ColorSpace::TransferID::INVALID;
-  if (base::Contains(edid_parser.supported_color_primary_ids(),
-                     gfx::ColorSpace::PrimaryID::BT2020)) {
-    if (base::Contains(edid_parser.supported_color_transfer_ids(),
-                       gfx::ColorSpace::TransferID::SMPTEST2084)) {
-      transfer_id = gfx::ColorSpace::TransferID::SMPTEST2084;
-    } else if (base::Contains(edid_parser.supported_color_transfer_ids(),
-                              gfx::ColorSpace::TransferID::ARIB_STD_B67)) {
-      transfer_id = gfx::ColorSpace::TransferID::ARIB_STD_B67;
-    }
-  } else if (gamma == 2.2f) {
-    transfer_id = gfx::ColorSpace::TransferID::GAMMA22;
-  } else if (gamma == 2.4f) {
-    transfer_id = gfx::ColorSpace::TransferID::GAMMA24;
-  }
-
-  if (transfer_id != gfx::ColorSpace::TransferID::INVALID)
-    return gfx::ColorSpace::CreateCustom(color_space_as_matrix, transfer_id);
-
-  skcms_TransferFunction transfer = {gamma, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
-  return gfx::ColorSpace::CreateCustom(color_space_as_matrix, transfer);
-}
-
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/common/drm_util.h b/ui/ozone/platform/drm/common/drm_util.h
index c46941ff..1643dcb 100644
--- a/ui/ozone/platform/drm/common/drm_util.h
+++ b/ui/ozone/platform/drm/common/drm_util.h
@@ -22,7 +22,6 @@
 
 namespace display {
 class DisplayMode;
-class EdidParser;
 }  // namespace display
 
 namespace gfx {
@@ -31,18 +30,6 @@
 
 namespace ui {
 
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class EdidColorSpaceChecksOutcome {
-  kSuccess = 0,
-  kErrorBadCoordinates = 1,
-  kErrorPrimariesAreaTooSmall = 2,
-  kErrorBluePrimaryIsBroken = 3,
-  kErrorCannotExtractToXYZD50 = 4,
-  kErrorBadGamma = 5,
-  kMaxValue = kErrorBadGamma
-};
-
 // Representation of the information required to initialize and configure a
 // native display. |index| is the position of the connection and will be
 // used to generate a unique identifier for the display.
@@ -136,10 +123,6 @@
 std::vector<OverlayCheckReturn_Params> CreateParamsFromOverlayStatusList(
     const OverlayStatusList& returns);
 
-// Uses |edid_parser| to extract a gfx::ColorSpace which will be IsValid() if
-// both gamma and the color primaries were correctly found.
-gfx::ColorSpace GetColorSpaceFromEdid(const display::EdidParser& edid_parser);
-
 }  // namespace ui
 
 #endif  // UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_
diff --git a/ui/ozone/platform/drm/common/drm_util_unittest.cc b/ui/ozone/platform/drm/common/drm_util_unittest.cc
index d51eaca..c0e59bb 100644
--- a/ui/ozone/platform/drm/common/drm_util_unittest.cc
+++ b/ui/ozone/platform/drm/common/drm_util_unittest.cc
@@ -10,7 +10,6 @@
 #include <map>
 
 #include "base/stl_util.h"
-#include "base/test/metrics/histogram_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkMatrix44.h"
@@ -21,115 +20,6 @@
 
 namespace ui {
 
-namespace {
-
-// HP z32x monitor.
-const unsigned char kHPz32x[] =
-    "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x75\x32\x01\x01\x01\x01"
-    "\x1B\x1B\x01\x04\xB5\x46\x27\x78\x3A\x8D\x15\xAC\x51\x32\xB8\x26"
-    "\x0B\x50\x54\x21\x08\x00\xD1\xC0\xA9\xC0\x81\xC0\xD1\x00\xB3\x00"
-    "\x95\x00\xA9\x40\x81\x80\x4D\xD0\x00\xA0\xF0\x70\x3E\x80\x30\x20"
-    "\x35\x00\xB9\x88\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x18\x3C\x1E"
-    "\x87\x3C\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48"
-    "\x50\x20\x5A\x33\x32\x78\x0A\x20\x20\x20\x20\x20\x00\x00\x00\xFF"
-    "\x00\x43\x4E\x43\x37\x32\x37\x30\x4D\x57\x30\x0A\x20\x20\x01\x46"
-    "\x02\x03\x18\xF1\x4B\x10\x1F\x04\x13\x03\x12\x02\x11\x01\x05\x14"
-    "\x23\x09\x07\x07\x83\x01\x00\x00\xA3\x66\x00\xA0\xF0\x70\x1F\x80"
-    "\x30\x20\x35\x00\xB9\x88\x21\x00\x00\x1A\x56\x5E\x00\xA0\xA0\xA0"
-    "\x29\x50\x30\x20\x35\x00\xB9\x88\x21\x00\x00\x1A\xEF\x51\x00\xA0"
-    "\xF0\x70\x19\x80\x30\x20\x35\x00\xB9\x88\x21\x00\x00\x1A\xE2\x68"
-    "\x00\xA0\xA0\x40\x2E\x60\x20\x30\x63\x00\xB9\x88\x21\x00\x00\x1C"
-    "\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20\x36\x00\xB9\x88\x21\x00"
-    "\x00\x1A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3E";
-
-// Chromebook Samus internal display.
-const unsigned char kSamus[] =
-    "\x00\xff\xff\xff\xff\xff\xff\x00\x30\xe4\x2e\x04\x00\x00\x00\x00"
-    "\x00\x18\x01\x04\xa5\x1b\x12\x96\x02\x4f\xd5\xa2\x59\x52\x93\x26"
-    "\x17\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
-    "\x01\x01\x01\x01\x01\x01\x6d\x6f\x00\x9e\xa0\xa4\x31\x60\x30\x20"
-    "\x3a\x00\x10\xb5\x10\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00"
-    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\x00\x4c"
-    "\x47\x20\x44\x69\x73\x70\x6c\x61\x79\x0a\x20\x20\x00\x00\x00\xfe"
-    "\x00\x4c\x50\x31\x32\x39\x51\x45\x32\x2d\x53\x50\x41\x31\x00\x6c";
-
-// Chromebook Eve internal display.
-const unsigned char kEve[] =
-    "\x00\xff\xff\xff\xff\xff\xff\x00\x4d\x10\x8a\x14\x00\x00\x00\x00"
-    "\x16\x1b\x01\x04\xa5\x1a\x11\x78\x06\xde\x50\xa3\x54\x4c\x99\x26"
-    "\x0f\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
-    "\x01\x01\x01\x01\x01\x01\xbb\x62\x60\xa0\x90\x40\x2e\x60\x30\x20"
-    "\x3a\x00\x03\xad\x10\x00\x00\x18\x00\x00\x00\x10\x00\x00\x00\x00"
-    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00"
-    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfc"
-    "\x00\x4c\x51\x31\x32\x33\x50\x31\x4a\x58\x33\x32\x0a\x20\x00\xb6";
-
-// A Samsung monitor that supports HDR metadata.
-constexpr unsigned char kHDR[] =
-    "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xf6\x0d\x00\x0e\x00\x01"
-    "\x01\x1b\x01\x03\x80\x5f\x36\x78\x0a\x23\xad\xa4\x54\x4d\x99\x26"
-    "\x0f\x47\x4a\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
-    "\xa9\xc0\xb3\x00\x01\x01\x04\x74\x00\x30\xf2\x70\x5a\x80\xb0\x58"
-    "\x8a\x00\x50\x1d\x74\x00\x00\x1e\x02\x3a\x80\x18\x71\x38\x2d\x40"
-    "\x58\x2c\x45\x00\x50\x1d\x74\x00\x00\x1e\x00\x00\x00\xfd\x00\x18"
-    "\x4b\x0f\x51\x1e\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc"
-    "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x5a"
-    "\x02\x03\x4f\xf0\x53\x5f\x10\x1f\x04\x13\x05\x14\x20\x21\x22\x5d"
-    "\x5e\x62\x63\x64\x07\x16\x03\x12\x2c\x09\x07\x07\x15\x07\x50\x3d"
-    "\x04\xc0\x57\x07\x00\x83\x01\x00\x00\xe2\x00\x0f\xe3\x05\x83\x01"
-    "\x6e\x03\x0c\x00\x30\x00\xb8\x3c\x20\x00\x80\x01\x02\x03\x04\xe3"
-    "\x06\x0d\x01\xe5\x0e\x60\x61\x65\x66\xe5\x01\x8b\x84\x90\x01\x01"
-    "\x1d\x80\xd0\x72\x1c\x16\x20\x10\x2c\x25\x80\x50\x1d\x74\x00\x00"
-    "\x9e\x66\x21\x56\xaa\x51\x00\x1e\x30\x46\x8f\x33\x00\x50\x1d\x74"
-    "\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbd";
-
-// Partially valid EDID: gamma information is marked as non existent.
-const unsigned char kEdidWithNoGamma[] =
-    "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x76\x26\x01\x01\x01\x01"
-    "\x02\x12\x01\x03\x80\x34\x21\xFF\xEE\xEF\x95\xA3\x54\x4C\x9B\x26"
-    "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x81\x80\x81\x99\x71\x00\xA9\x00";
-
-// Invalid EDID: too short to contain chromaticity nor gamma information.
-const unsigned char kInvalidEdid[] =
-    "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x76\x26\x01\x01\x01\x01"
-    "\x02\x12\x01\x03\x80\x34\x21";
-
-// EDID collected in the wild: valid but with primaries in the wrong order.
-const unsigned char kSST210[] =
-    "\xff\x00\xff\xff\xff\xff\x00\xff\x2d\x4c\x42\x52\x32\x31\x50\x43"
-    "\x0c\x2b\x03\x01\x33\x80\xa2\x20\x56\x2a\x9c\x4e\x50\x5b\x26\x95"
-    "\x50\x23\xbf\x59\x80\xef\x80\x81\x59\x61\x59\x45\x59\x31\x40\x31"
-    "\x01\x01\x01\x01\x01\x01\x32\x8c\xa0\x40\xb0\x60\x40\x19\x32\x1e"
-    "\x00\x13\x44\x06\x00\x21\x1e\x00\x00\x00\xfd\x00\x38\x00\x1e\x55"
-    "\x0f\x51\x0a\x00\x20\x20\x20\x20\x20\x20\x00\x00\xfc\x00\x32\x00"
-    "\x30\x31\x20\x54\x69\x44\x69\x67\x61\x74\x0a\x6c\x00\x00\xff\x00"
-    "\x48\x00\x4b\x34\x41\x54\x30\x30\x32\x38\x0a\x38\x20\x20\xf8\x00";
-
-// EDID of |kSST210| with the order of the primaries corrected. Still invalid
-// because the triangle of primaries is too small.
-const unsigned char kSST210Corrected[] =
-    "\xff\x00\xff\xff\xff\xff\x00\xff\x2d\x4c\x42\x52\x32\x31\x50\x43"
-    "\x0c\x2b\x03\x01\x33\x80\xa2\x20\x56\x2a\x9c\x95\x50\x4e\x50\x5b"
-    "\x26\x23\xbf\x59\x80\xef\x80\x81\x59\x61\x59\x45\x59\x31\x40\x31"
-    "\x01\x01\x01\x01\x01\x01\x32\x8c\xa0\x40\xb0\x60\x40\x19\x32\x1e"
-    "\x00\x13\x44\x06\x00\x21\x1e\x00\x00\x00\xfd\x00\x38\x00\x1e\x55"
-    "\x0f\x51\x0a\x00\x20\x20\x20\x20\x20\x20\x00\x00\xfc\x00\x32\x00"
-    "\x30\x31\x20\x54\x69\x44\x69\x67\x61\x74\x0a\x6c\x00\x00\xff\x00"
-    "\x48\x00\x4b\x34\x41\x54\x30\x30\x32\x38\x0a\x38\x20\x20\xf8\x00";
-
-// This EDID produces blue primary coordinates too far off the expected point,
-// which would paint blue colors as purple. See https://crbug.com/809909.
-const unsigned char kBrokenBluePrimaries[] =
-    "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x83\x4d\x83\x00\x00\x00\x00"
-    "\x00\x19\x01\x04\x95\x1d\x10\x78\x0a\xee\x25\xa3\x54\x4c\x99\x29"
-    "\x26\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
-    "\x01\x01\x01\x01\x01\x01\xd2\x37\x80\xa2\x70\x38\x40\x40\x30\x20"
-    "\x25\x00\x25\xa5\x10\x00\x00\x1a\xa6\x2c\x80\xa2\x70\x38\x40\x40"
-    "\x30\x20\x25\x00\x25\xa5\x10\x00\x00\x1a\x00\x00\x00\xfe\x00\x56"
-    "\x59\x54\x39\x36\x80\x31\x33\x33\x48\x4c\x0a\x20\x00\x00\x00\x00";
-
-}  // anonymous namespace
-
 bool operator==(const ui::DisplayMode_Params& a,
                 const ui::DisplayMode_Params& b) {
   return a.size == b.size && a.is_interlaced == b.is_interlaced &&
@@ -332,184 +222,6 @@
   EXPECT_EQ(42, iter->second);
 }
 
-TEST_F(DrmUtilTest, GetColorSpaceFromEdid) {
-  base::HistogramTester histogram_tester;
-
-  // Test with HP z32x monitor.
-  constexpr SkColorSpacePrimaries expected_hpz32x_primaries = {
-      .fRX = 0.673828f,
-      .fRY = 0.316406f,
-      .fGX = 0.198242f,
-      .fGY = 0.719727f,
-      .fBX = 0.148438f,
-      .fBY = 0.043945f,
-      .fWX = 0.313477f,
-      .fWY = 0.329102f};
-  skcms_Matrix3x3 expected_hpz32x_toXYZ50_matrix;
-  expected_hpz32x_primaries.toXYZD50(&expected_hpz32x_toXYZ50_matrix);
-  const std::vector<uint8_t> hpz32x_edid(kHPz32x,
-                                         kHPz32x + base::size(kHPz32x) - 1);
-  const gfx::ColorSpace expected_hpz32x_color_space =
-      gfx::ColorSpace::CreateCustom(
-          expected_hpz32x_toXYZ50_matrix,
-          skcms_TransferFunction({2.2, 1, 0, 0, 0, 0, 0}));
-  EXPECT_EQ(expected_hpz32x_color_space.ToString(),
-            GetColorSpaceFromEdid(display::EdidParser(hpz32x_edid)).ToString());
-  histogram_tester.ExpectBucketCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-      static_cast<base::HistogramBase::Sample>(
-          EdidColorSpaceChecksOutcome::kSuccess),
-      1);
-
-  // Test with Chromebook Samus internal display.
-  constexpr SkColorSpacePrimaries expected_samus_primaries = {.fRX = 0.633789f,
-                                                              .fRY = 0.347656f,
-                                                              .fGX = 0.323242f,
-                                                              .fGY = 0.577148f,
-                                                              .fBX = 0.151367f,
-                                                              .fBY = 0.090820f,
-                                                              .fWX = 0.313477f,
-                                                              .fWY = 0.329102f};
-  skcms_Matrix3x3 expected_samus_toXYZ50_matrix;
-  expected_samus_primaries.toXYZD50(&expected_samus_toXYZ50_matrix);
-  const std::vector<uint8_t> samus_edid(kSamus,
-                                        kSamus + base::size(kSamus) - 1);
-  const gfx::ColorSpace expected_samus_color_space =
-      gfx::ColorSpace::CreateCustom(
-          expected_samus_toXYZ50_matrix,
-          skcms_TransferFunction({2.5, 1, 0, 0, 0, 0, 0}));
-  EXPECT_EQ(expected_samus_color_space.ToString(),
-            GetColorSpaceFromEdid(display::EdidParser(samus_edid)).ToString());
-  histogram_tester.ExpectBucketCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-      static_cast<base::HistogramBase::Sample>(
-          EdidColorSpaceChecksOutcome::kSuccess),
-      2);
-
-  // Test with Chromebook Eve internal display.
-  constexpr SkColorSpacePrimaries expected_eve_primaries = {.fRX = 0.639648f,
-                                                            .fRY = 0.329102f,
-                                                            .fGX = 0.299805f,
-                                                            .fGY = 0.599609f,
-                                                            .fBX = 0.149414f,
-                                                            .fBY = 0.059570f,
-                                                            .fWX = 0.312500f,
-                                                            .fWY = 0.328125f};
-  skcms_Matrix3x3 expected_eve_toXYZ50_matrix;
-  expected_eve_primaries.toXYZD50(&expected_eve_toXYZ50_matrix);
-  const std::vector<uint8_t> eve_edid(kEve, kEve + base::size(kEve) - 1);
-  const gfx::ColorSpace expected_eve_color_space =
-      gfx::ColorSpace::CreateCustom(
-          expected_eve_toXYZ50_matrix,
-          skcms_TransferFunction({2.2, 1, 0, 0, 0, 0, 0}));
-  EXPECT_EQ(expected_eve_color_space.ToString(),
-            GetColorSpaceFromEdid(display::EdidParser(eve_edid)).ToString());
-  histogram_tester.ExpectBucketCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-      static_cast<base::HistogramBase::Sample>(
-          EdidColorSpaceChecksOutcome::kSuccess),
-      3);
-
-  // Test with a display that supports HDR.
-  constexpr SkColorSpacePrimaries expected_hdr_primaries = {.fRX = 0.640625f,
-                                                            .fRY = 0.330078f,
-                                                            .fGX = 0.300781f,
-                                                            .fGY = 0.600586f,
-                                                            .fBX = 0.150391f,
-                                                            .fBY = 0.060547f,
-                                                            .fWX = 0.280273f,
-                                                            .fWY = 0.290039f};
-  skcms_Matrix3x3 expected_hdr_toXYZ50_matrix;
-  expected_hdr_primaries.toXYZD50(&expected_hdr_toXYZ50_matrix);
-  const std::vector<uint8_t> hdr_edid(kHDR, kHDR + base::size(kHDR) - 1);
-  const gfx::ColorSpace expected_hdr_color_space =
-      gfx::ColorSpace::CreateCustom(expected_hdr_toXYZ50_matrix,
-                                    gfx::ColorSpace::TransferID::SMPTEST2084);
-  EXPECT_TRUE(expected_hdr_color_space.IsHDR());
-  EXPECT_EQ(expected_hdr_color_space.ToString(),
-            GetColorSpaceFromEdid(display::EdidParser(hdr_edid)).ToString());
-  histogram_tester.ExpectBucketCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-      static_cast<base::HistogramBase::Sample>(
-          EdidColorSpaceChecksOutcome::kSuccess),
-      4);
-
-  // Test with gamma marked as non-existent.
-  const std::vector<uint8_t> no_gamma_edid(
-      kEdidWithNoGamma, kEdidWithNoGamma + base::size(kEdidWithNoGamma) - 1);
-  const gfx::ColorSpace no_gamma_color_space =
-      GetColorSpaceFromEdid(display::EdidParser(no_gamma_edid));
-  EXPECT_FALSE(no_gamma_color_space.IsValid());
-  histogram_tester.ExpectBucketCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-      static_cast<base::HistogramBase::Sample>(
-          EdidColorSpaceChecksOutcome::kErrorBadGamma),
-      1);
-  histogram_tester.ExpectTotalCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome", 5);
-}
-
-TEST_F(DrmUtilTest, GetInvalidColorSpaceFromEdid) {
-  base::HistogramTester histogram_tester;
-  const std::vector<uint8_t> empty_edid;
-  EXPECT_EQ(gfx::ColorSpace(),
-            GetColorSpaceFromEdid(display::EdidParser(empty_edid)));
-  histogram_tester.ExpectBucketCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-      static_cast<base::HistogramBase::Sample>(
-          EdidColorSpaceChecksOutcome::kErrorPrimariesAreaTooSmall),
-      1);
-
-  const std::vector<uint8_t> invalid_edid(
-      kInvalidEdid, kInvalidEdid + base::size(kInvalidEdid) - 1);
-  const gfx::ColorSpace invalid_color_space =
-      GetColorSpaceFromEdid(display::EdidParser(invalid_edid));
-  EXPECT_FALSE(invalid_color_space.IsValid());
-  histogram_tester.ExpectBucketCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-      static_cast<base::HistogramBase::Sample>(
-          EdidColorSpaceChecksOutcome::kErrorPrimariesAreaTooSmall),
-      2);
-
-  const std::vector<uint8_t> sst210_edid(kSST210,
-                                         kSST210 + base::size(kSST210) - 1);
-  const gfx::ColorSpace sst210_color_space =
-      GetColorSpaceFromEdid(display::EdidParser(sst210_edid));
-  EXPECT_FALSE(sst210_color_space.IsValid()) << sst210_color_space.ToString();
-  histogram_tester.ExpectBucketCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-      static_cast<base::HistogramBase::Sample>(
-          EdidColorSpaceChecksOutcome::kErrorBadCoordinates),
-      1);
-
-  const std::vector<uint8_t> sst210_edid_2(
-      kSST210Corrected, kSST210Corrected + base::size(kSST210Corrected) - 1);
-  const gfx::ColorSpace sst210_color_space_2 =
-      GetColorSpaceFromEdid(display::EdidParser(sst210_edid_2));
-  EXPECT_FALSE(sst210_color_space_2.IsValid())
-      << sst210_color_space_2.ToString();
-  histogram_tester.ExpectBucketCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-      static_cast<base::HistogramBase::Sample>(
-          EdidColorSpaceChecksOutcome::kErrorPrimariesAreaTooSmall),
-      3);
-
-  const std::vector<uint8_t> broken_blue_edid(
-      kBrokenBluePrimaries,
-      kBrokenBluePrimaries + base::size(kBrokenBluePrimaries) - 1);
-  const gfx::ColorSpace broken_blue_color_space =
-      GetColorSpaceFromEdid(display::EdidParser(broken_blue_edid));
-  EXPECT_FALSE(broken_blue_color_space.IsValid())
-      << broken_blue_color_space.ToString();
-  histogram_tester.ExpectBucketCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-      static_cast<base::HistogramBase::Sample>(
-          EdidColorSpaceChecksOutcome::kErrorBluePrimaryIsBroken),
-      1);
-  histogram_tester.ExpectTotalCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome", 5);
-}
-
 TEST_F(DrmUtilTest, TestDisplayModesExtraction) {
   // Initialize a list of display modes.
   constexpr size_t kNumModes = 5;
diff --git a/ui/views/layout/animating_layout_manager.cc b/ui/views/layout/animating_layout_manager.cc
index f83d88f..08bd8546 100644
--- a/ui/views/layout/animating_layout_manager.cc
+++ b/ui/views/layout/animating_layout_manager.cc
@@ -319,7 +319,7 @@
   // TODO(dfried): consider cases where the minimum size might not be just the
   // minimum size of the embedded layout.
   gfx::Size minimum_size = target_layout_manager()->GetMinimumSize(host);
-  if (should_animate_bounds_ && is_animating_)
+  if (should_animate_bounds_)
     minimum_size.SetToMin(current_layout_.host_size);
   return minimum_size;
 }
diff --git a/ui/views/layout/animating_layout_manager_unittest.cc b/ui/views/layout/animating_layout_manager_unittest.cc
index 4077546..1158d422 100644
--- a/ui/views/layout/animating_layout_manager_unittest.cc
+++ b/ui/views/layout/animating_layout_manager_unittest.cc
@@ -12,6 +12,7 @@
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/animation/animation_test_api.h"
+#include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/view.h"
@@ -69,6 +70,24 @@
   ProposedLayout layout_;
 };
 
+// Version of FillLayout that ignores invisible views.
+class SmartFillLayout : public FillLayout {
+ public:
+  gfx::Size GetPreferredSize(const View* host) const override {
+    if (host->children().empty())
+      return gfx::Size();
+
+    gfx::Size preferred_size;
+    for (View* child : host->children()) {
+      if (child->GetVisible())
+        preferred_size.SetToMax(child->GetPreferredSize());
+    }
+    gfx::Rect rect(preferred_size);
+    rect.Inset(-host->GetInsets());
+    return rect.size();
+  }
+};
+
 constexpr gfx::Size kChildViewSize{10, 10};
 
 }  // anonymous namespace
@@ -123,6 +142,17 @@
   const ProposedLayout& layout1() const { return layout1_; }
   const ProposedLayout& layout2() const { return layout2_; }
 
+  // Replaces one of the children of |view| with a blank TestView.
+  // Because child views have e.g. preferred size set by default, in order to
+  // use non-default setup this method should be called.
+  void ReplaceChild(int index) {
+    View* const old_view = children_[index];
+    view_->RemoveChildView(old_view);
+    delete old_view;
+    children_[index] =
+        view_->AddChildViewAt(std::make_unique<TestView>(), index);
+  }
+
   void EnsureLayout(const ProposedLayout& expected) {
     for (size_t i = 0; i < expected.child_layouts.size(); ++i) {
       const auto& expected_child = expected.child_layouts[i];
@@ -1115,6 +1145,131 @@
   EnsureLayout(expected_end);
 }
 
+// Regression test for issues: crbug.com/1021332, crbug.com/1003500
+TEST_F(AnimatingLayoutManagerSteppingTest,
+       FlexLayout_AnimateOutOnDescendentVisbilitySet) {
+  constexpr gfx::Insets kChildMargins(5);
+  layout()->SetShouldAnimateBounds(false);
+  layout()->SetOrientation(LayoutOrientation::kHorizontal);
+  layout()->SetDefaultFadeMode(
+      AnimatingLayoutManager::FadeInOutMode::kScaleFromZero);
+  ReplaceChild(0);
+  child(0)->SetLayoutManager(std::make_unique<SmartFillLayout>());
+  View* const grandchild = child(0)->AddChildView(std::make_unique<View>());
+  grandchild->SetPreferredSize(kChildViewSize);
+  auto* const flex_layout =
+      layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>());
+  flex_layout->SetOrientation(LayoutOrientation::kHorizontal);
+  flex_layout->SetCollapseMargins(true);
+  flex_layout->SetCrossAxisAlignment(LayoutAlignment::kStart);
+  flex_layout->SetDefault(kMarginsKey, kChildMargins);
+  child(1)->SetProperty(kFlexBehaviorKey, FlexSpecification::ForSizeRule(
+                                              MinimumFlexSizeRule::kPreferred,
+                                              MaximumFlexSizeRule::kUnbounded));
+
+  const ProposedLayout expected_start{
+      {50, 20},
+      {{child(0), true, {{5, 5}, kChildViewSize}},
+       {child(1), true, {{20, 5}, kChildViewSize}},
+       {child(2), true, {{35, 5}, kChildViewSize}}}};
+
+  const ProposedLayout expected_end{
+      {50, 20},
+      {{child(0), false},
+       {child(1), true, {5, 5, 25, 10}},
+       {child(2), true, {{35, 5}, kChildViewSize}}}};
+
+  // Set up the initial state of the host view and children.
+  SizeAndLayout();
+  EXPECT_FALSE(layout()->is_animating());
+  EnsureLayout(expected_start);
+
+  grandchild->SetVisible(false);
+
+  view()->Layout();
+  EXPECT_TRUE(layout()->is_animating());
+  EnsureLayout(expected_start);
+
+  animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
+  view()->Layout();
+  EXPECT_TRUE(layout()->is_animating());
+  ProposedLayout expected =
+      ProposedLayoutBetween(0.5, expected_start, expected_end);
+  expected.child_layouts[0].visible = true;
+  expected.child_layouts[0].bounds = expected_start.child_layouts[0].bounds;
+  expected.child_layouts[0].bounds.set_width(
+      expected.child_layouts[1].bounds.x() - 10);
+  EnsureLayout(expected);
+
+  animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
+  view()->Layout();
+  EXPECT_FALSE(layout()->is_animating());
+  EnsureLayout(expected_end);
+}
+
+// Regression test for issues: crbug.com/1021332, crbug.com/1003500
+TEST_F(AnimatingLayoutManagerSteppingTest,
+       FlexLayout_AnimateInOnDescendentVisbilitySet) {
+  constexpr gfx::Insets kChildMargins(5);
+  layout()->SetShouldAnimateBounds(false);
+  layout()->SetOrientation(LayoutOrientation::kHorizontal);
+  layout()->SetDefaultFadeMode(
+      AnimatingLayoutManager::FadeInOutMode::kScaleFromZero);
+  ReplaceChild(0);
+  child(0)->SetLayoutManager(std::make_unique<SmartFillLayout>());
+  View* const grandchild = child(0)->AddChildView(std::make_unique<View>());
+  grandchild->SetPreferredSize(kChildViewSize);
+  grandchild->SetVisible(false);
+  auto* const flex_layout =
+      layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>());
+  flex_layout->SetOrientation(LayoutOrientation::kHorizontal);
+  flex_layout->SetCollapseMargins(true);
+  flex_layout->SetCrossAxisAlignment(LayoutAlignment::kStart);
+  flex_layout->SetDefault(kMarginsKey, kChildMargins);
+  child(1)->SetProperty(kFlexBehaviorKey, FlexSpecification::ForSizeRule(
+                                              MinimumFlexSizeRule::kPreferred,
+                                              MaximumFlexSizeRule::kUnbounded));
+
+  const ProposedLayout expected_start{
+      {50, 20},
+      {{child(0), false},
+       {child(1), true, {5, 5, 25, 10}},
+       {child(2), true, {{35, 5}, kChildViewSize}}}};
+
+  const ProposedLayout expected_end{
+      {50, 20},
+      {{child(0), true, {{5, 5}, kChildViewSize}},
+       {child(1), true, {{20, 5}, kChildViewSize}},
+       {child(2), true, {{35, 5}, kChildViewSize}}}};
+
+  // Set up the initial state of the host view and children.
+  view()->SetSize(expected_end.host_size);
+  EXPECT_FALSE(layout()->is_animating());
+  EnsureLayout(expected_start);
+
+  grandchild->SetVisible(true);
+
+  view()->Layout();
+  EXPECT_TRUE(layout()->is_animating());
+  EnsureLayout(expected_start);
+
+  animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
+  view()->Layout();
+  EXPECT_TRUE(layout()->is_animating());
+  ProposedLayout expected =
+      ProposedLayoutBetween(0.5, expected_start, expected_end);
+  expected.child_layouts[0].visible = true;
+  expected.child_layouts[0].bounds = expected_end.child_layouts[0].bounds;
+  expected.child_layouts[0].bounds.set_width(
+      expected.child_layouts[1].bounds.x() - 10);
+  EnsureLayout(expected);
+
+  animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
+  view()->Layout();
+  EXPECT_FALSE(layout()->is_animating());
+  EnsureLayout(expected_end);
+}
+
 TEST_F(AnimatingLayoutManagerSteppingTest, FlexLayout_FadeInOnAdded) {
   constexpr gfx::Insets kChildMargins(5);
   layout()->SetShouldAnimateBounds(false);
diff --git a/ui/views/layout/layout_manager.cc b/ui/views/layout/layout_manager.cc
index 3408ea77..c159ea4 100644
--- a/ui/views/layout/layout_manager.cc
+++ b/ui/views/layout/layout_manager.cc
@@ -39,7 +39,14 @@
 void LayoutManager::ViewRemoved(View* host, View* view) {
 }
 
-void LayoutManager::ViewVisibilitySet(View* host, View* view, bool visible) {}
+void LayoutManager::ViewVisibilitySet(View* host, View* view, bool visible) {
+  // Changing the visibility of a child view should force a re-layout. There is
+  // more sophisticated logic in LayoutManagerBase but this should be adequate
+  // for most legacy layouts (none of which override this method).
+  // TODO(dfried): Remove this if/when LayoutManager and LayoutManagerBase can
+  // be merged.
+  host->InvalidateLayout();
+}
 
 void LayoutManager::SetViewVisibility(View* view, bool visible) {
   DCHECK_EQ(view->parent()->GetLayoutManager(), this);
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 4169f58..ce25d76 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -429,12 +429,6 @@
 }
 
 void View::SetVisible(bool visible) {
-  if (parent_) {
-    LayoutManager* const layout_manager = parent_->GetLayoutManager();
-    if (layout_manager && layout_manager->view_setting_visibility_on_ != this)
-      layout_manager->ViewVisibilitySet(parent_, this, visible);
-  }
-
   if (visible_ != visible) {
     // If the View is currently visible, schedule paint to refresh parent.
     // TODO(beng): not sure we should be doing this if we have a layer.
@@ -458,6 +452,12 @@
     // Notify all other subscriptions of the change.
     OnPropertyChanged(&visible_, kPropertyEffectsPaint);
   }
+
+  if (parent_) {
+    LayoutManager* const layout_manager = parent_->GetLayoutManager();
+    if (layout_manager && layout_manager->view_setting_visibility_on_ != this)
+      layout_manager->ViewVisibilitySet(parent_, this, visible);
+  }
 }
 
 PropertyChangedSubscription View::AddVisibleChangedCallback(
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
index 18994c6..60edc4b 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
@@ -28,7 +28,7 @@
       }
 
       iron-pages {
-        overflow: auto;
+        overflow: var(--iron-pages-overflow, visible);
         padding: 0 32px;
       }
     </style>
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java
index e9cb3cb..cd24555 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java
@@ -77,6 +77,13 @@
     }
 
     @Override
+    public int getHttpStatusCode() {
+        throwIfNativeDestroyed();
+        return NavigationImplJni.get().getHttpStatusCode(
+                mNativeNavigationImpl, NavigationImpl.this);
+    }
+
+    @Override
     public boolean isSameDocument() {
         throwIfNativeDestroyed();
         return NavigationImplJni.get().isSameDocument(mNativeNavigationImpl, NavigationImpl.this);
@@ -133,6 +140,7 @@
         int getState(long nativeNavigationImpl, NavigationImpl caller);
         String getUri(long nativeNavigationImpl, NavigationImpl caller);
         String[] getRedirectChain(long nativeNavigationImpl, NavigationImpl caller);
+        int getHttpStatusCode(long nativeNavigationImpl, NavigationImpl caller);
         boolean isSameDocument(long nativeNavigationImpl, NavigationImpl caller);
         boolean isErrorPage(long nativeNavigationImpl, NavigationImpl caller);
         int getLoadError(long nativeNavigationImpl, NavigationImpl caller);
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl
index 378a163..7a7a896 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl
@@ -14,9 +14,11 @@
 
   List<String> getRedirectChain() = 2;
 
-  boolean isSameDocument() = 3;
+  int getHttpStatusCode() = 3;
 
-  boolean isErrorPage() = 4;
+  boolean isSameDocument() = 4;
 
-  int getLoadError() = 5;
+  boolean isErrorPage() = 5;
+
+  int getLoadError() = 6;
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java
index 8e56b297..fcfbb25 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java
@@ -9,4 +9,4 @@
  *
  * Whenever any AIDL file is changed, sVersionNumber must be incremented.
  * */
-public final class WebLayerVersion { public static final int sVersionNumber = 12; }
+public final class WebLayerVersion { public static final int sVersionNumber = 13; }
diff --git a/weblayer/browser/navigation_browsertest.cc b/weblayer/browser/navigation_browsertest.cc
index 936e91cf..a58ab77c 100644
--- a/weblayer/browser/navigation_browsertest.cc
+++ b/weblayer/browser/navigation_browsertest.cc
@@ -34,6 +34,7 @@
   bool completed() { return completed_; }
   bool is_error_page() { return is_error_page_; }
   Navigation::LoadError load_error() { return load_error_; }
+  int http_status_code() { return http_status_code_; }
 
  private:
   // NavigationObserver implementation:
@@ -47,6 +48,7 @@
   void Finish(Navigation* navigation) {
     is_error_page_ = navigation->IsErrorPage();
     load_error_ = navigation->GetLoadError();
+    http_status_code_ = navigation->GetHttpStatusCode();
     run_loop_.Quit();
   }
 
@@ -55,6 +57,7 @@
   bool completed_ = false;
   bool is_error_page_ = false;
   Navigation::LoadError load_error_ = Navigation::kNoError;
+  int http_status_code_ = 0;
 };
 
 }  // namespace
@@ -72,6 +75,7 @@
   EXPECT_TRUE(observer.completed());
   EXPECT_FALSE(observer.is_error_page());
   EXPECT_EQ(observer.load_error(), Navigation::kNoError);
+  EXPECT_EQ(observer.http_status_code(), 200);
 }
 
 IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, HttpClientError) {
@@ -85,6 +89,7 @@
   EXPECT_TRUE(observer.completed());
   EXPECT_FALSE(observer.is_error_page());
   EXPECT_EQ(observer.load_error(), Navigation::kHttpClientError);
+  EXPECT_EQ(observer.http_status_code(), 404);
 }
 
 IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, HttpServerError) {
@@ -98,6 +103,7 @@
   EXPECT_TRUE(observer.completed());
   EXPECT_FALSE(observer.is_error_page());
   EXPECT_EQ(observer.load_error(), Navigation::kHttpServerError);
+  EXPECT_EQ(observer.http_status_code(), 500);
 }
 
 IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SSLError) {
diff --git a/weblayer/browser/navigation_impl.cc b/weblayer/browser/navigation_impl.cc
index 8b59b41..e4c0be0 100644
--- a/weblayer/browser/navigation_impl.cc
+++ b/weblayer/browser/navigation_impl.cc
@@ -70,6 +70,11 @@
   return NavigationState::kWaitingResponse;
 }
 
+int NavigationImpl::GetHttpStatusCode() {
+  auto* response_headers = navigation_handle_->GetResponseHeaders();
+  return response_headers ? response_headers->response_code() : 0;
+}
+
 bool NavigationImpl::IsSameDocument() {
   return navigation_handle_->IsSameDocument();
 }
diff --git a/weblayer/browser/navigation_impl.h b/weblayer/browser/navigation_impl.h
index 2aed912..2591c00c 100644
--- a/weblayer/browser/navigation_impl.h
+++ b/weblayer/browser/navigation_impl.h
@@ -37,6 +37,10 @@
   base::android::ScopedJavaLocalRef<jobjectArray> GetRedirectChain(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
+  int GetHttpStatusCode(JNIEnv* env,
+                        const base::android::JavaParamRef<jobject>& obj) {
+    return GetHttpStatusCode();
+  }
   bool IsSameDocument(JNIEnv* env,
                       const base::android::JavaParamRef<jobject>& obj) {
     return IsSameDocument();
@@ -60,6 +64,7 @@
   GURL GetURL() override;
   const std::vector<GURL>& GetRedirectChain() override;
   NavigationState GetState() override;
+  int GetHttpStatusCode() override;
   bool IsSameDocument() override;
   bool IsErrorPage() override;
   LoadError GetLoadError() override;
diff --git a/weblayer/public/java/org/chromium/weblayer/Navigation.java b/weblayer/public/java/org/chromium/weblayer/Navigation.java
index 2897ddd..9fcb744 100644
--- a/weblayer/public/java/org/chromium/weblayer/Navigation.java
+++ b/weblayer/public/java/org/chromium/weblayer/Navigation.java
@@ -67,6 +67,19 @@
     }
 
     /**
+     * Returns the status code of the navigation. Returns 0 if the navigation  hasn't completed yet
+     * or if a response wasn't received.
+     */
+    public int getHttpStatusCode() {
+        ThreadCheck.ensureOnUiThread();
+        try {
+            return mNavigationImpl.getHttpStatusCode();
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
+    /**
      * Whether the navigation happened without changing document. Examples of same document
      * navigations are:
      *  - reference fragment navigations
diff --git a/weblayer/public/navigation.h b/weblayer/public/navigation.h
index 61fac6c..b5b3f5d 100644
--- a/weblayer/public/navigation.h
+++ b/weblayer/public/navigation.h
@@ -37,6 +37,10 @@
 
   virtual NavigationState GetState() = 0;
 
+  // Returns the status code of the navigation. Returns 0 if the navigation
+  // hasn't completed yet or if a response wasn't received.
+  virtual int GetHttpStatusCode() = 0;
+
   // Whether the navigation happened without changing document. Examples of
   // same document navigations are:
   // * reference fragment navigations
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
index 7cf69fe..23bc024 100644
--- a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
@@ -52,12 +52,14 @@
         public static class NavigationCallbackHelper extends CallbackHelper {
             private Uri mUri;
             private boolean mIsSameDocument;
+            private int mHttpStatusCode;
             private List<Uri> mRedirectChain;
             private @LoadError int mLoadError;
 
             public void notifyCalled(Navigation navigation) {
                 mUri = navigation.getUri();
                 mIsSameDocument = navigation.isSameDocument();
+                mHttpStatusCode = navigation.getHttpStatusCode();
                 mRedirectChain = navigation.getRedirectChain();
                 mLoadError = navigation.getLoadError();
                 notifyCalled();
@@ -87,6 +89,10 @@
                 assertEquals(mUri.toString(), uri);
                 assertEquals(mLoadError, loadError);
             }
+
+            public int getHttpStatusCode() {
+                return mHttpStatusCode;
+            }
         }
 
         public static class NavigationCallbackValueRecorder {
@@ -188,6 +194,7 @@
         mCallback.onReadyToCommitCallback.assertCalledWith(curCommittedCount, URL2);
         mCallback.onCompletedCallback.assertCalledWith(curCompletedCount, URL2);
         mCallback.onFirstContentfulPaintCallback.waitForCallback(curOnFirstContentfulPaintCount);
+        assertEquals(mCallback.onCompletedCallback.getHttpStatusCode(), 200);
     }
 
     @Test
@@ -365,6 +372,7 @@
 
         mCallback.onCompletedCallback.assertCalledWith(
                 curCompletedCount, url, LoadError.HTTP_CLIENT_ERROR);
+        assertEquals(mCallback.onCompletedCallback.getHttpStatusCode(), 404);
     }
 
     private void setNavigationCallback(InstrumentationActivity activity) {