diff --git a/.gn b/.gn
index e26556c..7c3afd8 100644
--- a/.gn
+++ b/.gn
@@ -276,7 +276,7 @@
   "//extensions/common/*",
   "//extensions/components/javascript_dialog_extensions_client",
   "//extensions/components/native_app_window",
-  "//extensions/renderer:unit_tests",
+  "//extensions/renderer/*",
   "//extensions/shell/*",
   "//extensions/strings/*",
   "//fuchsia/*",
diff --git a/BUILD.gn b/BUILD.gn
index 2adee9d2..653e1d24 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -278,6 +278,10 @@
     ]
   }
 
+  if (is_win && (target_cpu == "x86" || target_cpu == "x64")) {
+    deps += [ "//chrome/browser/browser_switcher/bho:browser_switcher_dlls" ]
+  }
+
   if (is_win || is_linux) {
     deps += [
       "//tools/traffic_annotation/auditor:traffic_annotation_auditor",
@@ -586,7 +590,6 @@
   if (is_win) {
     deps += [
       "//base:pe_image_test",
-      "//chrome/browser/browser_switcher/bho:browser_switcher_bho",
       "//chrome/chrome_cleaner:chrome_cleaner_unittests",
       "//chrome/chrome_elf:chrome_elf_unittests",
       "//chrome/chrome_elf:dll_hash_main",
diff --git a/DEPS b/DEPS
index b58a6a52..361df66e 100644
--- a/DEPS
+++ b/DEPS
@@ -138,11 +138,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': '698fa78b3cae067ed27ae95f0e90e8493e7cc2f9',
+  'skia_revision': '072e6fc374db4c3b067c88e1742844413091757b',
   # 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': '602da62c858cf96badde864f813b36d4ab6d8dbc',
+  'v8_revision': 'ece78714270f84701fc01bb28653627772bdd1c1',
   # 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.
@@ -150,11 +150,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': '2589cdcc88ec50d257267b674e9c634bfb491ba7',
+  'angle_revision': 'fce1e2d1e279ba78b4d92636b10af55d0a025539',
   # 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': 'b8260a8e915d92116a20c348736d0fd7c36e90a3',
+  'swiftshader_revision': '6dd5f33587130dc35fbe0ecf1df1a6af730f1b70',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -201,7 +201,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': '1750292dc4b69f16531439b71276ee5cc2de5ae2',
+  'catapult_revision': 'b64faff265e813a264b472c7bb62848c84f8f99b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -273,7 +273,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.
-  'dawn_revision': '3e51fada29125bdfb54ecd0fb32d17cba2a52587',
+  'dawn_revision': '17738b6d8c4f18b8a9045a18169cd499d9923647',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -807,7 +807,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '286660f0dde07d2193fe4d3eeb908a300df78f35',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '53cfcd3219c1d711afac88b3ff8d48d595a2d281',
       'condition': 'checkout_linux',
   },
 
@@ -1187,7 +1187,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '0b2e12b08cdcb8be58c992c8702cee90e0420324',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '07b9e93544271ef4ddea3519b73ac6cda236d3d6',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1355,7 +1355,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'da1c65fb53c3925dbd1e7118a322637a00a894b9',
+    Var('webrtc_git') + '/src.git' + '@' + '51db421682d67c9ab78cc029d0aa3b2e94d1f3e0',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1396,7 +1396,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2875b88d0b00b147e86b90e538af9b6df692f031',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7d984193c8c4943a6b631d3807fd9f53ff8de60b',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 3d9f538..1a3aa9f6 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -784,6 +784,7 @@
     "//gin",
     "//media",
     "//media/midi",
+    "//media/mojo:buildflags",
     "//net",
     "//net:extras",
     "//printing",
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS
index 628d5bb..9659f87 100644
--- a/android_webview/browser/DEPS
+++ b/android_webview/browser/DEPS
@@ -49,6 +49,7 @@
   "-ipc/ipc_sync_message_filter.h",
 
   "+media/base/android",
+  "+media/mojo/buildflags.h",
 
   "+components/policy/policy_constants.h",
   "+components/embedder_support/android",
diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h
index 6b33ef37..a2f8f0d 100644
--- a/android_webview/browser/aw_browser_context.h
+++ b/android_webview/browser/aw_browser_context.h
@@ -152,6 +152,8 @@
   // Constructs HttpAuthDynamicParams based on |user_pref_service_|.
   network::mojom::HttpAuthDynamicParamsPtr CreateHttpAuthDynamicParams();
 
+  PrefService* GetPrefService() const { return user_pref_service_.get(); }
+
  private:
   void OnAuthPrefsChanged();
 
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 7369a5e..a8a1de8 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -56,6 +56,7 @@
 #include "base/task/post_task.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/cdm/browser/cdm_message_filter_android.h"
+#include "components/cdm/browser/media_drm_storage_impl.h"
 #include "components/content_capture/browser/content_capture_receiver_manager.h"
 #include "components/crash/content/browser/crash_handler_host_linux.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
@@ -91,6 +92,7 @@
 #include "content/public/common/url_loader_throttle.h"
 #include "content/public/common/user_agent.h"
 #include "content/public/common/web_preferences.h"
+#include "media/mojo/buildflags.h"
 #include "net/android/network_library.h"
 #include "net/http/http_util.h"
 #include "net/log/net_log.h"
@@ -230,6 +232,45 @@
       std::move(cookie_manager_info));
 }
 
+#if BUILDFLAG(ENABLE_MOJO_CDM)
+void CreateOriginId(cdm::MediaDrmStorageImpl::OriginIdObtainedCB callback) {
+  std::move(callback).Run(true, base::UnguessableToken::Create());
+}
+
+void AllowEmptyOriginIdCB(base::OnceCallback<void(bool)> callback) {
+  // Since CreateOriginId() always returns a non-empty origin ID, we don't need
+  // to allow empty origin ID.
+  std::move(callback).Run(false);
+}
+
+void CreateMediaDrmStorage(content::RenderFrameHost* render_frame_host,
+                           ::media::mojom::MediaDrmStorageRequest request) {
+  DCHECK(render_frame_host);
+
+  if (render_frame_host->GetLastCommittedOrigin().opaque()) {
+    DVLOG(1) << __func__ << ": Unique origin.";
+    return;
+  }
+
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(render_frame_host);
+  DCHECK(web_contents) << "WebContents not available.";
+
+  auto* aw_browser_context =
+      static_cast<AwBrowserContext*>(web_contents->GetBrowserContext());
+  DCHECK(aw_browser_context) << "AwBrowserContext not available.";
+
+  PrefService* pref_service = aw_browser_context->GetPrefService();
+  DCHECK(pref_service);
+
+  // The object will be deleted on connection error, or when the frame navigates
+  // away.
+  new cdm::MediaDrmStorageImpl(
+      render_frame_host, pref_service, base::BindRepeating(&CreateOriginId),
+      base::BindRepeating(&AllowEmptyOriginIdCB), std::move(request));
+}
+#endif  // BUILDFLAG(ENABLE_MOJO_CDM)
+
 }  // anonymous namespace
 
 std::string GetProduct() {
@@ -873,6 +914,15 @@
   return safe_browsing_url_checker_delegate_.get();
 }
 
+void AwContentBrowserClient::ExposeInterfacesToMediaService(
+    service_manager::BinderRegistry* registry,
+    content::RenderFrameHost* render_frame_host) {
+#if BUILDFLAG(ENABLE_MOJO_CDM)
+  registry->AddInterface(
+      base::BindRepeating(&CreateMediaDrmStorage, render_frame_host));
+#endif
+}
+
 bool AwContentBrowserClient::ShouldOverrideUrlLoading(
     int frame_tree_node_id,
     bool browser_initiated,
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index 64116c9b..d429ed7 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -180,6 +180,9 @@
       service_manager::BinderRegistry* registry,
       blink::AssociatedInterfaceRegistry* associated_registry,
       content::RenderProcessHost* render_process_host) override;
+  void ExposeInterfacesToMediaService(
+      service_manager::BinderRegistry* registry,
+      content::RenderFrameHost* render_frame_host) override;
   std::vector<std::unique_ptr<content::URLLoaderThrottle>>
   CreateURLLoaderThrottles(
       const network::ResourceRequest& request,
diff --git a/android_webview/browser/aw_feature_list_creator.cc b/android_webview/browser/aw_feature_list_creator.cc
index 700a518..596ea5f 100644
--- a/android_webview/browser/aw_feature_list_creator.cc
+++ b/android_webview/browser/aw_feature_list_creator.cc
@@ -25,6 +25,7 @@
 #include "base/time/time.h"
 #include "cc/base/switches.h"
 #include "components/autofill/core/common/autofill_prefs.h"
+#include "components/cdm/browser/media_drm_storage_impl.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_service.h"
 #include "components/policy/core/browser/configuration_policy_pref_store.h"
@@ -39,6 +40,7 @@
 #include "components/variations/pref_names.h"
 #include "components/variations/service/safe_seed_manager.h"
 #include "components/variations/service/variations_service.h"
+#include "media/mojo/buildflags.h"
 #include "services/preferences/tracked/segregated_pref_store.h"
 
 namespace android_webview {
@@ -48,6 +50,9 @@
 // These prefs go in the JsonPrefStore, and will persist across runs. Other
 // prefs go in the InMemoryPrefStore, and will be lost when the process ends.
 const char* const kPersistentPrefsWhitelist[] = {
+    // Persisted to avoid having to provision MediaDrm every time the
+    // application tries to play protected content after restart.
+    cdm::prefs::kMediaDrmStorage,
     // Randomly-generated GUID which pseudonymously identifies uploaded metrics.
     metrics::prefs::kMetricsClientID,
     // Random seed values for variation's entropy providers, used to assign
@@ -92,6 +97,10 @@
   variations::VariationsService::RegisterPrefs(pref_registry.get());
   safe_browsing::RegisterProfilePrefs(pref_registry.get());
 
+#if BUILDFLAG(ENABLE_MOJO_CDM)
+  cdm::MediaDrmStorageImpl::RegisterProfilePrefs(pref_registry.get());
+#endif
+
   PrefServiceFactory pref_service_factory;
 
   std::set<std::string> persistent_prefs;
diff --git a/ash/app_list/views/search_result_page_view.cc b/ash/app_list/views/search_result_page_view.cc
index 71b51bf..5445f54 100644
--- a/ash/app_list/views/search_result_page_view.cc
+++ b/ash/app_list/views/search_result_page_view.cc
@@ -181,7 +181,7 @@
   // Leaves a placeholder area for the search box and the separator below it.
   scroller->SetBorder(views::CreateEmptyBorder(
       gfx::Insets(kSearchBoxHeight + kSeparatorThickness, 0, 0, 0)));
-  scroller->set_draw_overflow_indicator(false);
+  scroller->SetDrawOverflowIndicator(false);
   scroller->SetContents(base::WrapUnique(contents_view_));
   // Setting clip height is necessary to make ScrollView take into account its
   // contents' size. Using zeroes doesn't prevent it from scrolling and sizing
diff --git a/ash/assistant/ui/base/assistant_scroll_view.cc b/ash/assistant/ui/base/assistant_scroll_view.cc
index d7e0c86..9f65819 100644
--- a/ash/assistant/ui/base/assistant_scroll_view.cc
+++ b/ash/assistant/ui/base/assistant_scroll_view.cc
@@ -80,7 +80,7 @@
 
 void AssistantScrollView::InitLayout() {
   SetBackgroundColor(SK_ColorTRANSPARENT);
-  set_draw_overflow_indicator(false);
+  SetDrawOverflowIndicator(false);
 
   // Content view.
   auto content_view = std::make_unique<ContentView>();
diff --git a/ash/components/shortcut_viewer/views/keyboard_shortcut_view.cc b/ash/components/shortcut_viewer/views/keyboard_shortcut_view.cc
index a0015b0..c1eacd9 100644
--- a/ash/components/shortcut_viewer/views/keyboard_shortcut_view.cc
+++ b/ash/components/shortcut_viewer/views/keyboard_shortcut_view.cc
@@ -113,7 +113,7 @@
 ShortcutsListScrollView* CreateScrollView(
     std::unique_ptr<views::View> content_view) {
   ShortcutsListScrollView* const scroller = new ShortcutsListScrollView();
-  scroller->set_draw_overflow_indicator(false);
+  scroller->SetDrawOverflowIndicator(false);
   scroller->ClipHeightTo(0, 0);
   scroller->SetContents(std::move(content_view));
   scroller->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
diff --git a/ash/login/ui/login_menu_view.cc b/ash/login/ui/login_menu_view.cc
index 864b04f..4f0f3b5 100644
--- a/ash/login/ui/login_menu_view.cc
+++ b/ash/login/ui/login_menu_view.cc
@@ -136,7 +136,7 @@
 
   scroller_ = new views::ScrollView();
   scroller_->SetBackgroundColor(SK_ColorTRANSPARENT);
-  scroller_->set_draw_overflow_indicator(false);
+  scroller_->SetDrawOverflowIndicator(false);
   scroller_->ClipHeightTo(kMenuItemHeightDp, kMenuItemHeightDp * 5);
   AddChildView(scroller_);
 
diff --git a/ash/login/ui/scrollable_users_list_view.cc b/ash/login/ui/scrollable_users_list_view.cc
index a7fddacf..a3c31f5 100644
--- a/ash/login/ui/scrollable_users_list_view.cc
+++ b/ash/login/ui/scrollable_users_list_view.cc
@@ -332,7 +332,7 @@
   ensure_min_height->AddChildView(user_view_host_);
   SetContents(std::move(ensure_min_height));
   SetBackgroundColor(SK_ColorTRANSPARENT);
-  set_draw_overflow_indicator(false);
+  SetDrawOverflowIndicator(false);
 
   SetVerticalScrollBar(new UsersListScrollBar(false));
   SetHorizontalScrollBar(new UsersListScrollBar(true));
diff --git a/ash/multi_user/multi_user_window_manager_impl.cc b/ash/multi_user/multi_user_window_manager_impl.cc
index 0219b321..f43ce74 100644
--- a/ash/multi_user/multi_user_window_manager_impl.cc
+++ b/ash/multi_user/multi_user_window_manager_impl.cc
@@ -12,7 +12,6 @@
 #include "ash/public/cpp/multi_user_window_manager_delegate.h"
 #include "ash/public/cpp/multi_user_window_manager_observer.h"
 #include "ash/public/cpp/shell_window_ids.h"
-#include "ash/public/cpp/wallpaper_user_info.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/wm/desks/desks_util.h"
@@ -60,26 +59,6 @@
   return false;
 }
 
-WallpaperUserInfo WallpaperUserInfoForAccount(const AccountId& account_id) {
-  DCHECK(account_id.is_valid());
-  WallpaperUserInfo wallpaper_user_info;
-  SessionControllerImpl* session_controller =
-      Shell::Get()->session_controller();
-  for (const std::unique_ptr<UserSession>& user_session :
-       session_controller->GetUserSessions()) {
-    if (user_session->user_info.account_id == account_id) {
-      wallpaper_user_info.account_id = account_id;
-      wallpaper_user_info.type = user_session->user_info.type;
-      wallpaper_user_info.is_ephemeral = user_session->user_info.is_ephemeral;
-      wallpaper_user_info.has_gaia_account =
-          user_session->user_info.has_gaia_account;
-      return wallpaper_user_info;
-    }
-  }
-  NOTREACHED();
-  return wallpaper_user_info;
-}
-
 }  // namespace
 
 // A class to temporarily change the animation properties for a window.
@@ -306,8 +285,7 @@
   // animation only to be reshown again by the destructor of the old animation.
   animation_.reset();
   animation_ = std::make_unique<UserSwitchAnimator>(
-      this, WallpaperUserInfoForAccount(current_account_id_),
-      GetAdjustedAnimationTime(kUserFadeTime));
+      this, current_account_id_, GetAdjustedAnimationTime(kUserFadeTime));
 
   // Call RequestCaptureState here instead of having MediaClient observe
   // ActiveUserChanged because it must happen after
diff --git a/ash/multi_user/user_switch_animator.cc b/ash/multi_user/user_switch_animator.cc
index 5392aa1..e12c831 100644
--- a/ash/multi_user/user_switch_animator.cc
+++ b/ash/multi_user/user_switch_animator.cc
@@ -80,13 +80,11 @@
 
 }  // namespace
 
-UserSwitchAnimator::UserSwitchAnimator(
-    MultiUserWindowManagerImpl* owner,
-    const WallpaperUserInfo& wallpaper_user_info,
-    base::TimeDelta animation_speed)
+UserSwitchAnimator::UserSwitchAnimator(MultiUserWindowManagerImpl* owner,
+                                       const AccountId& new_account_id,
+                                       base::TimeDelta animation_speed)
     : owner_(owner),
-      wallpaper_user_info_(wallpaper_user_info),
-      new_account_id_(wallpaper_user_info_.account_id),
+      new_account_id_(new_account_id),
       animation_speed_(animation_speed),
       animation_step_(ANIMATION_STEP_HIDE_OLD_USER),
       screen_cover_(GetScreenCover(NULL)),
@@ -171,7 +169,7 @@
     wallpaper_controller->SetAnimationDuration(
         duration > kMinimalAnimationTime ? duration : kMinimalAnimationTime);
     if (screen_cover_ != NEW_USER_COVERS_SCREEN) {
-      wallpaper_controller->ShowUserWallpaper(wallpaper_user_info_);
+      wallpaper_controller->ShowUserWallpaper(new_account_id_);
       wallpaper_user_id_for_test_ =
           (NO_USER_COVERS_SCREEN == screen_cover_ ? "->" : "") +
           new_account_id_.Serialize();
@@ -180,7 +178,7 @@
     // Revert the wallpaper cross dissolve animation duration back to the
     // default.
     if (screen_cover_ == NEW_USER_COVERS_SCREEN)
-      wallpaper_controller->ShowUserWallpaper(wallpaper_user_info_);
+      wallpaper_controller->ShowUserWallpaper(new_account_id_);
 
     // Coming here the wallpaper user id is the final result. No matter how we
     // got here.
diff --git a/ash/multi_user/user_switch_animator.h b/ash/multi_user/user_switch_animator.h
index 79a2414..ec6617e0f 100644
--- a/ash/multi_user/user_switch_animator.h
+++ b/ash/multi_user/user_switch_animator.h
@@ -10,7 +10,6 @@
 #include <string>
 
 #include "ash/ash_export.h"
-#include "ash/public/cpp/wallpaper_user_info.h"
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -36,10 +35,10 @@
     ANIMATION_STEP_ENDED           // The animation has ended.
   };
 
-  // Creates a UserSwitchAnimator to animate between the current user and
-  // |user_info|.
+  // Creates a UserSwitchAnimator to animate between the current user and the
+  // user associated with |new_account_id|.
   UserSwitchAnimator(MultiUserWindowManagerImpl* owner,
-                     const WallpaperUserInfo& user_info,
+                     const AccountId& new_account_id,
                      base::TimeDelta animation_speed);
   ~UserSwitchAnimator();
 
@@ -101,10 +100,6 @@
   // The owning window manager.
   MultiUserWindowManagerImpl* owner_;
 
-  // Contains the wallpaper configuration for the user switching to. This is
-  // passed to the WallpaperController at the right time.
-  WallpaperUserInfo wallpaper_user_info_;
-
   // The new user to set.
   AccountId new_account_id_;
 
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index eca09158..1b9158e7 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -186,7 +186,6 @@
     "wallpaper_controller_observer.h",
     "wallpaper_info.h",
     "wallpaper_types.h",
-    "wallpaper_user_info.h",
     "window_animation_types.h",
     "window_pin_type.cc",
     "window_pin_type.h",
diff --git a/ash/public/cpp/wallpaper_controller.h b/ash/public/cpp/wallpaper_controller.h
index a3bf172..7fd25a7 100644
--- a/ash/public/cpp/wallpaper_controller.h
+++ b/ash/public/cpp/wallpaper_controller.h
@@ -14,6 +14,8 @@
 #include "base/files/file_path.h"
 #include "base/time/time.h"
 
+class AccountId;
+
 namespace gfx {
 class ImageSkia;
 }
@@ -22,7 +24,6 @@
 
 class WallpaperControllerObserver;
 class WallpaperControllerClient;
-struct WallpaperUserInfo;
 
 // Used by Chrome to set the wallpaper displayed by ash.
 class ASH_PUBLIC_EXPORT WallpaperController {
@@ -44,15 +45,15 @@
 
   // Sets wallpaper from a local file and updates the saved wallpaper info for
   // the user.
-  // |user_info|: The user's information related to wallpaper.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
+  // |account_id|: The user's account id.
+  // |wallpaper_files_id|: The file id for |account_id|.
   // |file_name|: The name of the wallpaper file.
   // |layout|: The layout of the wallpaper, used for wallpaper resizing.
   // |image|: The wallpaper image.
   // |preview_mode|: If true, show the wallpaper immediately but doesn't change
   //                 the user wallpaper info until |ConfirmPreviewWallpaper| is
   //                 called.
-  virtual void SetCustomWallpaper(const WallpaperUserInfo& user_info,
+  virtual void SetCustomWallpaper(const AccountId& account_id,
                                   const std::string& wallpaper_files_id,
                                   const std::string& file_name,
                                   WallpaperLayout layout,
@@ -63,7 +64,7 @@
   // corresponding to |url| already exists in local file system (i.e.
   // |SetOnlineWallpaperFromData| was called earlier with the same |url|),
   // returns true and sets wallpaper for the user, otherwise returns false.
-  // |user_info|: The user's information related to wallpaper.
+  // |account_id|: The user's account id.
   // |url|: The wallpaper url.
   // |layout|: The layout of the wallpaper, used for wallpaper resizing.
   // |preview_mode|: If true, show the wallpaper immediately but doesn't change
@@ -72,7 +73,7 @@
   // Responds with true if the wallpaper file exists in local file system.
   using SetOnlineWallpaperIfExistsCallback = base::OnceCallback<void(bool)>;
   virtual void SetOnlineWallpaperIfExists(
-      const WallpaperUserInfo& user_info,
+      const AccountId& account_id,
       const std::string& url,
       WallpaperLayout layout,
       bool preview_mode,
@@ -82,7 +83,7 @@
   // to local file system. After this, |SetOnlineWallpaperIfExists| will return
   // true for the same |url|, so that there's no need to provide |image_data|
   // when the same wallpaper needs to be set again or for another user.
-  // |user_info|: The user's information related to wallpaper.
+  // |account_id|: The user's account id.
   // |url|: The wallpaper url.
   // |layout|: The layout of the wallpaper, used for wallpaper resizing.
   // |preview_mode|: If true, show the wallpaper immediately but doesn't change
@@ -92,7 +93,7 @@
   // error etc.).
   using SetOnlineWallpaperFromDataCallback = base::OnceCallback<void(bool)>;
   virtual void SetOnlineWallpaperFromData(
-      const WallpaperUserInfo& user_info,
+      const AccountId& account_id,
       const std::string& image_data,
       const std::string& url,
       WallpaperLayout layout,
@@ -101,10 +102,11 @@
 
   // Sets the user's wallpaper to be the default wallpaper. Note: different user
   // types may have different default wallpapers.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
+  // |account_id|: The user's account id.
+  // |wallpaper_files_id|: The file id for |account_id|.
   // |show_wallpaper|: If false, don't show the new wallpaper now but only
   //                   update cache.
-  virtual void SetDefaultWallpaper(const WallpaperUserInfo& user_info,
+  virtual void SetDefaultWallpaper(const AccountId& account_id,
                                    const std::string& wallpaper_files_id,
                                    bool show_wallpaper) = 0;
 
@@ -124,10 +126,10 @@
   // wallpaper immediately, otherwise, the policy wallpaper will be shown the
   // next time |ShowUserWallpaper| is called. Note: it is different from device
   // policy.
-  // |user_info|: The user's information related to wallpaper.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
+  // |account_id|: The user's account id.
+  // |wallpaper_files_id|: The file id for |account_id|.
   // |data|: The data used to decode the image.
-  virtual void SetPolicyWallpaper(const WallpaperUserInfo& user_info,
+  virtual void SetPolicyWallpaper(const AccountId& account_id,
                                   const std::string& wallpaper_files_id,
                                   const std::string& data) = 0;
 
@@ -140,15 +142,15 @@
 
   // Sets wallpaper from a third-party app (as opposed to the Chrome OS
   // wallpaper picker).
-  // |user_info|: The user's information related to wallpaper.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
+  // |account_id|: The user's account id.
+  // |wallpaper_files_id|: The file id for |account_id|.
   // |file_name|: The name of the wallpaper file.
   // |layout|: The layout of the wallpaper, used for wallpaper resizing.
   // |image|: The wallpaper image.
   // Returns if the wallpaper is allowed to be shown on screen. It's false if:
   // 1) the user is not permitted to change wallpaper, or
   // 2) updating the on-screen wallpaper is not allowed at the given moment.
-  virtual bool SetThirdPartyWallpaper(const WallpaperUserInfo& user_info,
+  virtual bool SetThirdPartyWallpaper(const AccountId& account_id,
                                       const std::string& wallpaper_files_id,
                                       const std::string& file_name,
                                       WallpaperLayout layout,
@@ -164,9 +166,9 @@
 
   // Updates the layout for the user's custom wallpaper and reloads the
   // wallpaper with the new layout.
-  // |user_info|: The user's information related to wallpaper.
+  // |account_id|: The user's account id.
   // |layout|: The new layout of the wallpaper.
-  virtual void UpdateCustomWallpaperLayout(const WallpaperUserInfo& user_info,
+  virtual void UpdateCustomWallpaperLayout(const AccountId& account_id,
                                            WallpaperLayout layout) = 0;
 
   // Shows the user's wallpaper, which is determined in the following order:
@@ -175,7 +177,7 @@
   // 3) Use the wallpaper set by the user (either by |SetOnlineWallpaper| or
   //    |SetCustomWallpaper|), if any.
   // 4) Use the default wallpaper of this user.
-  virtual void ShowUserWallpaper(const WallpaperUserInfo& user_info) = 0;
+  virtual void ShowUserWallpaper(const AccountId& account_id) = 0;
 
   // Used by the gaia-signin UI. Signin wallpaper is considered either as the
   // device policy wallpaper or the default wallpaper.
@@ -199,8 +201,9 @@
   virtual void RemoveAlwaysOnTopWallpaper() = 0;
 
   // Removes all of the user's saved wallpapers and related info.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
-  virtual void RemoveUserWallpaper(const WallpaperUserInfo& user_info,
+  // |account_id|: The user's account id.
+  // |wallpaper_files_id|: The file id for |account_id|.
+  virtual void RemoveUserWallpaper(const AccountId& account_id,
                                    const std::string& wallpaper_files_id) = 0;
 
   // Removes all of the user's saved wallpapers and related info if the
@@ -208,9 +211,9 @@
   // wallpaper to be the default. If the user has logged in, show the default
   // wallpaper immediately, otherwise, the default wallpaper will be shown the
   // next time |ShowUserWallpaper| is called.
-  // |user_info|: The user's information related to wallpaper.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
-  virtual void RemovePolicyWallpaper(const WallpaperUserInfo& user_info,
+  // |account_id|: The user's account id.
+  // |wallpaper_files_id|: The file id for |account_id|.
+  virtual void RemovePolicyWallpaper(const AccountId& account_id,
                                      const std::string& wallpaper_files_id) = 0;
 
   // Returns the urls of the wallpapers that exist in local file system (i.e.
diff --git a/ash/public/cpp/wallpaper_user_info.h b/ash/public/cpp/wallpaper_user_info.h
deleted file mode 100644
index 254d4964..0000000
--- a/ash/public/cpp/wallpaper_user_info.h
+++ /dev/null
@@ -1,36 +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 ASH_PUBLIC_CPP_WALLPAPER_USER_INFO_H_
-#define ASH_PUBLIC_CPP_WALLPAPER_USER_INFO_H_
-
-#include "ash/public/cpp/ash_public_export.h"
-#include "components/account_id/account_id.h"
-#include "components/user_manager/user_type.h"
-
-namespace ash {
-
-// User info needed to set wallpapers. Clients must specify the user because
-// it's not always the same as the active user, e.g., when showing wallpapers
-// for different user pods at login screen, or setting wallpapers selectively
-// for primary user and active user during a multi-profile session.
-struct ASH_PUBLIC_EXPORT WallpaperUserInfo {
-  // The user's account id.
-  AccountId account_id = EmptyAccountId();
-
-  // The user type.
-  user_manager::UserType type = user_manager::USER_TYPE_REGULAR;
-
-  // True if the user's non-cryptohome data (wallpaper, avatar etc.) is
-  // ephemeral. See |UserManager::IsCurrentUserNonCryptohomeDataEphemeral| for
-  // more details.
-  bool is_ephemeral = false;
-
-  // True if the user has gaia account.
-  bool has_gaia_account = false;
-};
-
-}  // namespace ash
-
-#endif  // ASH_PUBLIC_CPP_WALLPAPER_USER_INFO_H_
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index cb2323f26..c55d2f94 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -182,6 +182,7 @@
 
 void ShellTestApi::EnableTabletModeWindowManager(bool enable) {
   AccelerometerReader::GetInstance()->DisableForTest();
+  TabletModeController::SetForceNoScreenshotForTest();
   shell_->tablet_mode_controller()->EnableTabletModeWindowManager(enable);
 }
 
diff --git a/ash/system/message_center/notifier_settings_view.cc b/ash/system/message_center/notifier_settings_view.cc
index 6223e9c0b..baab9df 100644
--- a/ash/system/message_center/notifier_settings_view.cc
+++ b/ash/system/message_center/notifier_settings_view.cc
@@ -489,7 +489,7 @@
   scroller->SetBackgroundColor(SK_ColorTRANSPARENT);
   scroller->SetVerticalScrollBar(new views::OverlayScrollBar(false));
   scroller->SetHorizontalScrollBar(new views::OverlayScrollBar(true));
-  scroller->set_draw_overflow_indicator(false);
+  scroller->SetDrawOverflowIndicator(false);
   scroller_ = AddChildView(std::move(scroller));
 
   no_notifiers_view_ = AddChildView(std::make_unique<EmptyNotifierView>());
diff --git a/ash/system/message_center/unified_message_center_view.cc b/ash/system/message_center/unified_message_center_view.cc
index 103083b..f7174d1 100644
--- a/ash/system/message_center/unified_message_center_view.cc
+++ b/ash/system/message_center/unified_message_center_view.cc
@@ -326,7 +326,7 @@
       std::make_unique<ScrollerContentsView>(message_list_view_, this));
   scroller_->SetBackgroundColor(SK_ColorTRANSPARENT);
   scroller_->SetVerticalScrollBar(scroll_bar_);
-  scroller_->set_draw_overflow_indicator(false);
+  scroller_->SetDrawOverflowIndicator(false);
   AddChildView(scroller_);
 }
 
diff --git a/ash/system/tray/tray_detailed_view.cc b/ash/system/tray/tray_detailed_view.cc
index cbced271..6ebfcca 100644
--- a/ash/system/tray/tray_detailed_view.cc
+++ b/ash/system/tray/tray_detailed_view.cc
@@ -312,8 +312,7 @@
   DCHECK(!scroller_);
   auto scroll_content = std::make_unique<ScrollContentsView>(delegate_);
   scroller_ = new views::ScrollView;
-  scroller_->set_draw_overflow_indicator(
-      delegate_->IsOverflowIndicatorEnabled());
+  scroller_->SetDrawOverflowIndicator(delegate_->IsOverflowIndicatorEnabled());
   scroll_content_ = scroller_->SetContents(std::move(scroll_content));
   // TODO(varkha): Make the sticky rows work with EnableViewPortLayer().
   scroller_->SetBackgroundColor(
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index d606c44..c73806b 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -373,25 +373,21 @@
   GetSessionControllerClient()->CreatePredefinedUserSessions(n);
 }
 
-void AshTestBase::SimulateUserLogin(const std::string& user_email) {
-  TestSessionControllerClient* const session_controller_client =
-      GetSessionControllerClient();
-  session_controller_client->AddUserSession(user_email);
-  session_controller_client->SwitchActiveUser(
-      AccountId::FromUserEmail(user_email));
-  session_controller_client->SetSessionState(SessionState::ACTIVE);
+void AshTestBase::SimulateUserLogin(const std::string& user_email,
+                                    user_manager::UserType user_type) {
+  TestSessionControllerClient* session = GetSessionControllerClient();
+  session->AddUserSession(user_email, user_type);
+  session->SwitchActiveUser(AccountId::FromUserEmail(user_email));
+  session->SetSessionState(SessionState::ACTIVE);
 }
 
 void AshTestBase::SimulateNewUserFirstLogin(const std::string& user_email) {
-  TestSessionControllerClient* const session_controller_client =
-      GetSessionControllerClient();
-  session_controller_client->AddUserSession(
+  TestSessionControllerClient* session = GetSessionControllerClient();
+  session->AddUserSession(
       user_email, user_manager::USER_TYPE_REGULAR, true /* enable_settings */,
       true /* provide_pref_service */, true /* is_new_profile */);
-  session_controller_client->SwitchActiveUser(
-      AccountId::FromUserEmail(user_email));
-  session_controller_client->SetSessionState(
-      session_manager::SessionState::ACTIVE);
+  session->SwitchActiveUser(AccountId::FromUserEmail(user_email));
+  session->SetSessionState(session_manager::SessionState::ACTIVE);
 }
 
 void AshTestBase::SimulateGuestLogin() {
diff --git a/ash/test/ash_test_base.h b/ash/test/ash_test_base.h
index 1a9a676..571d4268 100644
--- a/ash/test/ash_test_base.h
+++ b/ash/test/ash_test_base.h
@@ -205,7 +205,9 @@
 
   // Simulates a user sign-in. It creates a new user session, adds it to
   // existing user sessions and makes it the active user session.
-  void SimulateUserLogin(const std::string& user_email);
+  void SimulateUserLogin(
+      const std::string& user_email,
+      user_manager::UserType user_type = user_manager::USER_TYPE_REGULAR);
 
   // Simular to SimulateUserLogin but for a newly created user first ever login.
   void SimulateNewUserFirstLogin(const std::string& user_email);
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index 64ca13e..a183b64 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -30,6 +30,7 @@
 #include "ash/test_shell_delegate.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/strings/string_split.h"
@@ -186,6 +187,8 @@
 
   // Ensure tests have a wallpaper as placeholder.
   shell->wallpaper_controller()->CreateEmptyWallpaperForTesting();
+
+  TabletModeController::SetForceNoScreenshotForTest();
 }
 
 void AshTestHelper::TearDown() {
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index 030f26f..897d918 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -46,6 +46,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "components/user_manager/user_type.h"
 #include "services/data_decoder/public/cpp/decode_image.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
@@ -439,6 +440,24 @@
   return url_list;
 }
 
+// Returns true if the user's wallpaper is to be treated as ephemeral.
+bool IsEphemeralUser(const AccountId& id) {
+  for (const auto& s : Shell::Get()->session_controller()->GetUserSessions()) {
+    if (s->user_info.account_id == id)
+      return s->user_info.is_ephemeral;
+  }
+  return false;
+}
+
+// Returns the type of the user with the specified |id| or USER_TYPE_REGULAR.
+user_manager::UserType GetUserType(const AccountId& id) {
+  for (const auto& s : Shell::Get()->session_controller()->GetUserSessions()) {
+    if (s->user_info.account_id == id)
+      return s->user_info.type;
+  }
+  return user_manager::USER_TYPE_REGULAR;
+}
+
 }  // namespace
 
 const char WallpaperControllerImpl::kSmallWallpaperSubDir[] = "small";
@@ -523,7 +542,6 @@
 // static
 void WallpaperControllerImpl::SetWallpaperFromPath(
     const AccountId& account_id,
-    const user_manager::UserType& user_type,
     const WallpaperInfo& info,
     const base::FilePath& wallpaper_path,
     bool show_wallpaper,
@@ -544,12 +562,12 @@
     reply_task_runner->PostTask(
         FROM_HERE,
         base::BindOnce(&WallpaperControllerImpl::SetDefaultWallpaperImpl,
-                       weak_ptr, account_id, user_type, show_wallpaper));
+                       weak_ptr, account_id, show_wallpaper));
   } else {
     reply_task_runner->PostTask(
-        FROM_HERE, base::BindOnce(&WallpaperControllerImpl::StartDecodeFromPath,
-                                  weak_ptr, account_id, user_type, valid_path,
-                                  info, show_wallpaper));
+        FROM_HERE,
+        base::BindOnce(&WallpaperControllerImpl::StartDecodeFromPath, weak_ptr,
+                       account_id, valid_path, info, show_wallpaper));
   }
 }
 
@@ -667,11 +685,10 @@
   ++wallpaper_count_for_testing_;
 }
 
-bool WallpaperControllerImpl::IsPolicyControlled(const AccountId& account_id,
-                                                 bool is_ephemeral) const {
+bool WallpaperControllerImpl::IsPolicyControlled(
+    const AccountId& account_id) const {
   WallpaperInfo info;
-  return GetUserWallpaperInfo(account_id, &info, is_ephemeral) &&
-         info.type == POLICY;
+  return GetUserWallpaperInfo(account_id, &info) && info.type == POLICY;
 }
 
 void WallpaperControllerImpl::UpdateWallpaperBlur(bool blur) {
@@ -710,9 +727,8 @@
 }
 
 bool WallpaperControllerImpl::SetUserWallpaperInfo(const AccountId& account_id,
-                                                   const WallpaperInfo& info,
-                                                   bool is_ephemeral) {
-  if (is_ephemeral) {
+                                                   const WallpaperInfo& info) {
+  if (IsEphemeralUser(account_id)) {
     ephemeral_users_wallpaper_info_[account_id] = info;
     return true;
   }
@@ -721,7 +737,7 @@
     return false;
 
   WallpaperInfo old_info;
-  if (GetUserWallpaperInfo(account_id, &old_info, is_ephemeral)) {
+  if (GetUserWallpaperInfo(account_id, &old_info)) {
     // Remove the color cache of the previous wallpaper if it exists.
     DictionaryPrefUpdate wallpaper_colors_update(local_state_,
                                                  prefs::kWallpaperColors);
@@ -744,9 +760,8 @@
 }
 
 bool WallpaperControllerImpl::GetUserWallpaperInfo(const AccountId& account_id,
-                                                   WallpaperInfo* info,
-                                                   bool is_ephemeral) const {
-  if (is_ephemeral) {
+                                                   WallpaperInfo* info) const {
+  if (IsEphemeralUser(account_id)) {
     // Ephemeral users do not save anything to local state. Return true if the
     // info can be found in the map, otherwise return false.
     auto it = ephemeral_users_wallpaper_info_.find(account_id);
@@ -826,14 +841,13 @@
 
 void WallpaperControllerImpl::StartDecodeFromPath(
     const AccountId& account_id,
-    const user_manager::UserType& user_type,
     const base::FilePath& wallpaper_path,
     const WallpaperInfo& info,
     bool show_wallpaper) {
   ReadAndDecodeWallpaper(
       base::BindOnce(&WallpaperControllerImpl::OnWallpaperDecoded,
-                     weak_factory_.GetWeakPtr(), account_id, user_type,
-                     wallpaper_path, info, show_wallpaper),
+                     weak_factory_.GetWeakPtr(), account_id, wallpaper_path,
+                     info, show_wallpaper),
       sequenced_task_runner_, wallpaper_path);
 }
 
@@ -855,22 +869,22 @@
 }
 
 void WallpaperControllerImpl::SetCustomWallpaper(
-    const WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id,
     const std::string& file_name,
     WallpaperLayout layout,
     const gfx::ImageSkia& image,
     bool preview_mode) {
   DCHECK(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
-  if (!CanSetUserWallpaper(user_info.account_id, user_info.is_ephemeral))
+  if (!CanSetUserWallpaper(account_id))
     return;
 
-  const bool is_active_user = IsActiveUser(user_info.account_id);
+  const bool is_active_user = IsActiveUser(account_id);
   if (preview_mode) {
     DCHECK(is_active_user);
     confirm_preview_wallpaper_callback_ =
         base::BindOnce(&WallpaperControllerImpl::SaveAndSetWallpaper,
-                       weak_factory_.GetWeakPtr(), user_info,
+                       weak_factory_.GetWeakPtr(), account_id,
                        wallpaper_files_id, file_name, CUSTOMIZED, layout,
                        /*show_wallpaper=*/false, image);
     reload_preview_wallpaper_callback_ =
@@ -882,22 +896,21 @@
     // Show the preview wallpaper.
     reload_preview_wallpaper_callback_.Run();
   } else {
-    SaveAndSetWallpaper(user_info, wallpaper_files_id, file_name, CUSTOMIZED,
+    SaveAndSetWallpaper(account_id, wallpaper_files_id, file_name, CUSTOMIZED,
                         layout, /*show_wallpaper=*/is_active_user, image);
   }
 }
 
 void WallpaperControllerImpl::SetOnlineWallpaperIfExists(
-    const WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& url,
     WallpaperLayout layout,
     bool preview_mode,
     SetOnlineWallpaperIfExistsCallback callback) {
   DCHECK(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
-  DCHECK(CanSetUserWallpaper(user_info.account_id, user_info.is_ephemeral));
+  DCHECK(CanSetUserWallpaper(account_id));
 
-  const OnlineWallpaperParams params = {
-      user_info.account_id, user_info.is_ephemeral, url, layout, preview_mode};
+  const OnlineWallpaperParams params = {account_id, url, layout, preview_mode};
   base::PostTaskAndReplyWithResult(
       sequenced_task_runner_.get(), FROM_HERE,
       base::BindOnce(&GetExistingOnlineWallpaperPath, url),
@@ -906,20 +919,19 @@
 }
 
 void WallpaperControllerImpl::SetOnlineWallpaperFromData(
-    const WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& image_data,
     const std::string& url,
     WallpaperLayout layout,
     bool preview_mode,
     SetOnlineWallpaperFromDataCallback callback) {
   if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted() ||
-      !CanSetUserWallpaper(user_info.account_id, user_info.is_ephemeral)) {
+      !CanSetUserWallpaper(account_id)) {
     std::move(callback).Run(/*success=*/false);
     return;
   }
 
-  const OnlineWallpaperParams params = {
-      user_info.account_id, user_info.is_ephemeral, url, layout, preview_mode};
+  const OnlineWallpaperParams params = {account_id, url, layout, preview_mode};
   LoadedCallback decoded_callback =
       base::BindOnce(&WallpaperControllerImpl::OnOnlineWallpaperDecoded,
                      weak_factory_.GetWeakPtr(), params, /*save_file=*/true,
@@ -938,22 +950,19 @@
 }
 
 void WallpaperControllerImpl::SetDefaultWallpaper(
-    const WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id,
     bool show_wallpaper) {
-  if (!CanSetUserWallpaper(user_info.account_id, user_info.is_ephemeral))
+  if (!CanSetUserWallpaper(account_id))
     return;
 
-  RemoveUserWallpaper(user_info, wallpaper_files_id);
-  if (!InitializeUserWallpaperInfo(user_info.account_id,
-                                   user_info.is_ephemeral)) {
+  RemoveUserWallpaper(account_id, wallpaper_files_id);
+  if (!InitializeUserWallpaperInfo(account_id)) {
     LOG(ERROR) << "Initializing user wallpaper info fails. This should never "
                   "happen except in tests.";
   }
-  if (show_wallpaper) {
-    SetDefaultWallpaperImpl(user_info.account_id, user_info.type,
-                            /*show_wallpaper=*/true);
-  }
+  if (show_wallpaper)
+    SetDefaultWallpaperImpl(account_id, /*show_wallpaper=*/true);
 }
 
 void WallpaperControllerImpl::SetCustomizedDefaultWallpaperPaths(
@@ -969,12 +978,11 @@
 
   // Customized default wallpapers are subject to the same restrictions as other
   // default wallpapers, e.g. they should not be set during guest sessions.
-  SetDefaultWallpaperImpl(EmptyAccountId(), user_manager::USER_TYPE_REGULAR,
-                          show_wallpaper);
+  SetDefaultWallpaperImpl(EmptyAccountId(), show_wallpaper);
 }
 
 void WallpaperControllerImpl::SetPolicyWallpaper(
-    const WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id,
     const std::string& data) {
   // There is no visible wallpaper in kiosk mode.
@@ -986,7 +994,7 @@
       Shell::Get()->session_controller()->IsActiveUserSessionStarted();
   LoadedCallback callback = base::BindOnce(
       &WallpaperControllerImpl::SaveAndSetWallpaper, weak_factory_.GetWeakPtr(),
-      user_info, wallpaper_files_id, kPolicyWallpaperFile, POLICY,
+      account_id, wallpaper_files_id, kPolicyWallpaperFile, POLICY,
       WALLPAPER_LAYOUT_CENTER_CROPPED, show_wallpaper);
 
   if (bypass_decode_for_testing_) {
@@ -1018,17 +1026,16 @@
 }
 
 bool WallpaperControllerImpl::SetThirdPartyWallpaper(
-    const WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id,
     const std::string& file_name,
     WallpaperLayout layout,
     const gfx::ImageSkia& image) {
-  bool allowed_to_set_wallpaper =
-      CanSetUserWallpaper(user_info.account_id, user_info.is_ephemeral);
-  bool allowed_to_show_wallpaper = IsActiveUser(user_info.account_id);
+  bool allowed_to_set_wallpaper = CanSetUserWallpaper(account_id);
+  bool allowed_to_show_wallpaper = IsActiveUser(account_id);
 
   if (allowed_to_set_wallpaper) {
-    SaveAndSetWallpaper(user_info, wallpaper_files_id, file_name, CUSTOMIZED,
+    SaveAndSetWallpaper(account_id, wallpaper_files_id, file_name, CUSTOMIZED,
                         layout, allowed_to_show_wallpaper, image);
   }
   return allowed_to_set_wallpaper && allowed_to_show_wallpaper;
@@ -1058,38 +1065,32 @@
 }
 
 void WallpaperControllerImpl::UpdateCustomWallpaperLayout(
-    const WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     WallpaperLayout layout) {
   // This method has a very specific use case: the user should be active and
   // have a custom wallpaper.
-  const UserSession* const session = GetActiveUserSession();
-  if (!session || session->user_info.account_id != user_info.account_id)
+  if (!IsActiveUser(account_id))
     return;
 
   WallpaperInfo info;
-  if (!GetUserWallpaperInfo(user_info.account_id, &info,
-                            user_info.is_ephemeral) ||
-      info.type != CUSTOMIZED) {
+  if (!GetUserWallpaperInfo(account_id, &info) || info.type != CUSTOMIZED)
     return;
-  }
   if (info.layout == layout)
     return;
 
   info.layout = layout;
-  if (!SetUserWallpaperInfo(user_info.account_id, info,
-                            user_info.is_ephemeral)) {
+  if (!SetUserWallpaperInfo(account_id, info)) {
     LOG(ERROR) << "Setting user wallpaper info fails. This should never happen "
                   "except in tests.";
   }
-  ShowUserWallpaper(user_info);
+  ShowUserWallpaper(account_id);
 }
 
-void WallpaperControllerImpl::ShowUserWallpaper(
-    const WallpaperUserInfo& user_info) {
-  current_user_ = user_info;
-
-  if (current_user_.type == user_manager::USER_TYPE_KIOSK_APP ||
-      current_user_.type == user_manager::USER_TYPE_ARC_KIOSK_APP) {
+void WallpaperControllerImpl::ShowUserWallpaper(const AccountId& account_id) {
+  current_user_ = account_id;
+  const user_manager::UserType user_type = GetUserType(account_id);
+  if (user_type == user_manager::USER_TYPE_KIOSK_APP ||
+      user_type == user_manager::USER_TYPE_ARC_KIOSK_APP) {
     return;
   }
 
@@ -1098,13 +1099,11 @@
     return;
   }
 
-  const AccountId account_id = current_user_.account_id;
-  const bool is_ephemeral = current_user_.is_ephemeral;
   WallpaperInfo info;
-  if (!GetUserWallpaperInfo(account_id, &info, is_ephemeral)) {
-    if (!InitializeUserWallpaperInfo(account_id, is_ephemeral))
+  if (!GetUserWallpaperInfo(account_id, &info)) {
+    if (!InitializeUserWallpaperInfo(account_id))
       return;
-    GetUserWallpaperInfo(account_id, &info, is_ephemeral);
+    GetUserWallpaperInfo(account_id, &info);
   }
 
   // For ephemeral users, the cache is the only place to access their wallpaper
@@ -1122,15 +1121,13 @@
   }
 
   if (info.type == DEFAULT) {
-    SetDefaultWallpaperImpl(account_id, current_user_.type,
-                            /*show_wallpaper=*/true);
+    SetDefaultWallpaperImpl(account_id, /*show_wallpaper=*/true);
     return;
   }
 
   if (info.type != CUSTOMIZED && info.type != POLICY && info.type != DEVICE) {
     // Load wallpaper according to WallpaperInfo.
-    SetWallpaperFromInfo(account_id, current_user_.type, info,
-                         /*show_wallpaper=*/true);
+    SetWallpaperFromInfo(account_id, info, /*show_wallpaper=*/true);
     return;
   }
 
@@ -1161,20 +1158,18 @@
 
   sequenced_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&SetWallpaperFromPath, account_id, current_user_.type,
-                     info, wallpaper_path, /*show_wallpaper=*/true,
+      base::BindOnce(&SetWallpaperFromPath, account_id, info, wallpaper_path,
+                     /*show_wallpaper=*/true,
                      base::ThreadTaskRunnerHandle::Get(),
                      weak_factory_.GetWeakPtr()));
 }
 
 void WallpaperControllerImpl::ShowSigninWallpaper() {
-  current_user_ = WallpaperUserInfo();
-  if (ShouldSetDevicePolicyWallpaper()) {
+  current_user_ = EmptyAccountId();
+  if (ShouldSetDevicePolicyWallpaper())
     SetDevicePolicyWallpaper();
-  } else {
-    SetDefaultWallpaperImpl(EmptyAccountId(), user_manager::USER_TYPE_REGULAR,
-                            /*show_wallpaper=*/true);
-  }
+  else
+    SetDefaultWallpaperImpl(EmptyAccountId(), /*show_wallpaper=*/true);
 }
 
 void WallpaperControllerImpl::ShowOneShotWallpaper(
@@ -1209,16 +1204,16 @@
 }
 
 void WallpaperControllerImpl::RemoveUserWallpaper(
-    const WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id) {
-  RemoveUserWallpaperInfo(user_info.account_id, user_info.is_ephemeral);
-  RemoveUserWallpaperImpl(user_info.account_id, wallpaper_files_id);
+  RemoveUserWallpaperInfo(account_id);
+  RemoveUserWallpaperImpl(account_id, wallpaper_files_id);
 }
 
 void WallpaperControllerImpl::RemovePolicyWallpaper(
-    const WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id) {
-  if (!IsPolicyControlled(user_info.account_id, user_info.is_ephemeral))
+  if (!IsPolicyControlled(account_id))
     return;
 
   // Updates the screen only when the user has logged in.
@@ -1226,8 +1221,8 @@
       Shell::Get()->session_controller()->IsActiveUserSessionStarted();
   // Removes the wallpaper info so that the user is no longer policy controlled,
   // otherwise setting default wallpaper is not allowed.
-  RemoveUserWallpaperInfo(user_info.account_id, user_info.is_ephemeral);
-  SetDefaultWallpaper(user_info, wallpaper_files_id, show_wallpaper);
+  RemoveUserWallpaperInfo(account_id);
+  SetDefaultWallpaper(account_id, wallpaper_files_id, show_wallpaper);
 }
 
 void WallpaperControllerImpl::GetOfflineWallpaperList(
@@ -1291,16 +1286,14 @@
   const UserSession* const active_user_session = GetActiveUserSession();
   if (!active_user_session)
     return false;
-  return IsPolicyControlled(active_user_session->user_info.account_id,
-                            active_user_session->user_info.is_ephemeral);
+  return IsPolicyControlled(active_user_session->user_info.account_id);
 }
 
 WallpaperInfo WallpaperControllerImpl::GetActiveUserWallpaperInfo() {
   WallpaperInfo info;
   const UserSession* const active_user_session = GetActiveUserSession();
   if (!active_user_session ||
-      !GetUserWallpaperInfo(active_user_session->user_info.account_id, &info,
-                            active_user_session->user_info.is_ephemeral)) {
+      !GetUserWallpaperInfo(active_user_session->user_info.account_id, &info)) {
     info.location = std::string();
     info.layout = NUM_WALLPAPER_LAYOUT;
   }
@@ -1418,8 +1411,7 @@
 }
 
 void WallpaperControllerImpl::ShowDefaultWallpaperForTesting() {
-  SetDefaultWallpaperImpl(EmptyAccountId(), user_manager::USER_TYPE_REGULAR,
-                          /*show_wallpaper=*/true);
+  SetDefaultWallpaperImpl(EmptyAccountId(), /*show_wallpaper=*/true);
 }
 
 void WallpaperControllerImpl::CreateEmptyWallpaperForTesting() {
@@ -1499,15 +1491,14 @@
 }
 
 void WallpaperControllerImpl::RemoveUserWallpaperInfo(
-    const AccountId& account_id,
-    bool is_ephemeral) {
+    const AccountId& account_id) {
   if (wallpaper_cache_map_.find(account_id) != wallpaper_cache_map_.end())
     wallpaper_cache_map_.erase(account_id);
 
   if (!local_state_)
     return;
   WallpaperInfo info;
-  GetUserWallpaperInfo(account_id, &info, is_ephemeral);
+  GetUserWallpaperInfo(account_id, &info);
   DictionaryPrefUpdate prefs_wallpapers_info_update(local_state_,
                                                     prefs::kUserWallpaperInfo);
   prefs_wallpapers_info_update->RemoveWithoutPathExpansion(
@@ -1547,7 +1538,6 @@
 
 void WallpaperControllerImpl::SetDefaultWallpaperImpl(
     const AccountId& account_id,
-    const user_manager::UserType& user_type,
     bool show_wallpaper) {
   // There is no visible wallpaper in kiosk mode.
   if (IsInKioskMode())
@@ -1561,6 +1551,7 @@
       use_small ? WALLPAPER_LAYOUT_CENTER : WALLPAPER_LAYOUT_CENTER_CROPPED;
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   base::FilePath file_path;
+  const user_manager::UserType user_type = GetUserType(account_id);
 
   // The wallpaper is determined in the following order:
   // Guest wallpaper, child wallpaper, customized default wallpaper, and regular
@@ -1602,13 +1593,13 @@
   }
 }
 
-bool WallpaperControllerImpl::CanSetUserWallpaper(const AccountId& account_id,
-                                                  bool is_ephemeral) const {
+bool WallpaperControllerImpl::CanSetUserWallpaper(
+    const AccountId& account_id) const {
   // There is no visible wallpaper in kiosk mode.
   if (IsInKioskMode())
     return false;
   // Don't allow user wallpapers while policy is in effect.
-  if (IsPolicyControlled(account_id, is_ephemeral))
+  if (IsPolicyControlled(account_id))
     return false;
   return true;
 }
@@ -1646,11 +1637,10 @@
 }
 
 bool WallpaperControllerImpl::InitializeUserWallpaperInfo(
-    const AccountId& account_id,
-    bool is_ephemeral) {
+    const AccountId& account_id) {
   const WallpaperInfo info = {std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
                               DEFAULT, base::Time::Now().LocalMidnight()};
-  return SetUserWallpaperInfo(account_id, info, is_ephemeral);
+  return SetUserWallpaperInfo(account_id, info);
 }
 
 void WallpaperControllerImpl::SetOnlineWallpaperFromPath(
@@ -1714,8 +1704,7 @@
     bool show_wallpaper) {
   WallpaperInfo wallpaper_info = {params.url, params.layout, ONLINE,
                                   base::Time::Now().LocalMidnight()};
-  if (!SetUserWallpaperInfo(params.account_id, wallpaper_info,
-                            params.is_ephemeral)) {
+  if (!SetUserWallpaperInfo(params.account_id, wallpaper_info)) {
     LOG(ERROR) << "Setting user wallpaper info fails. This should never happen "
                   "except in tests.";
   }
@@ -1730,7 +1719,6 @@
 
 void WallpaperControllerImpl::SetWallpaperFromInfo(
     const AccountId& account_id,
-    const user_manager::UserType& user_type,
     const WallpaperInfo& info,
     bool show_wallpaper) {
   if (info.type != ONLINE && info.type != DEFAULT) {
@@ -1738,7 +1726,7 @@
     // unexpected cases, revert to default wallpaper to fail safely. See
     // crosbug.com/38429.
     LOG(ERROR) << "Wallpaper reverts to default unexpected.";
-    SetDefaultWallpaperImpl(account_id, user_type, show_wallpaper);
+    SetDefaultWallpaperImpl(account_id, show_wallpaper);
     return;
   }
 
@@ -1748,7 +1736,7 @@
     // were created directly in local state (for testing). Ignore such
     // errors i.e. allow such type of debug configurations on the desktop.
     LOG(WARNING) << "User wallpaper info is empty: " << account_id.Serialize();
-    SetDefaultWallpaperImpl(account_id, user_type, show_wallpaper);
+    SetDefaultWallpaperImpl(account_id, show_wallpaper);
     return;
   }
 
@@ -1766,8 +1754,8 @@
 
     ReadAndDecodeWallpaper(
         base::BindOnce(&WallpaperControllerImpl::OnWallpaperDecoded,
-                       weak_factory_.GetWeakPtr(), account_id, user_type,
-                       wallpaper_path, info, show_wallpaper),
+                       weak_factory_.GetWeakPtr(), account_id, wallpaper_path,
+                       info, show_wallpaper),
         sequenced_task_runner_, wallpaper_path);
   } else {
     // Default wallpapers are migrated from M21 user profiles. A code
@@ -1780,8 +1768,8 @@
 
     ReadAndDecodeWallpaper(
         base::BindOnce(&WallpaperControllerImpl::OnWallpaperDecoded,
-                       weak_factory_.GetWeakPtr(), account_id, user_type,
-                       wallpaper_path, info, show_wallpaper),
+                       weak_factory_.GetWeakPtr(), account_id, wallpaper_path,
+                       info, show_wallpaper),
         sequenced_task_runner_, wallpaper_path);
   }
 }
@@ -1810,7 +1798,7 @@
 }
 
 void WallpaperControllerImpl::SaveAndSetWallpaper(
-    const WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id,
     const std::string& file_name,
     WallpaperType type,
@@ -1831,8 +1819,7 @@
   // appropriate wallpaper resolution.
   WallpaperInfo info = {relative_path, layout, type,
                         base::Time::Now().LocalMidnight()};
-  if (!SetUserWallpaperInfo(user_info.account_id, info,
-                            user_info.is_ephemeral)) {
+  if (!SetUserWallpaperInfo(account_id, info)) {
     LOG(ERROR) << "Setting user wallpaper info fails. This should never happen "
                   "except in tests.";
   }
@@ -1842,9 +1829,9 @@
                              wallpaper_files_id, file_name);
 
   const bool should_save_to_disk =
-      !user_info.is_ephemeral ||
+      !IsEphemeralUser(account_id) ||
       (type == POLICY &&
-       user_info.type == user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+       GetUserType(account_id) == user_manager::USER_TYPE_PUBLIC_ACCOUNT);
 
   if (should_save_to_disk) {
     image.EnsureRepsForSupportedScales();
@@ -1865,13 +1852,12 @@
                        /*always_on_top=*/false);
   }
 
-  wallpaper_cache_map_[user_info.account_id] =
+  wallpaper_cache_map_[account_id] =
       CustomWallpaperElement(wallpaper_path, image);
 }
 
 void WallpaperControllerImpl::OnWallpaperDecoded(
     const AccountId& account_id,
-    const user_manager::UserType& user_type,
     const base::FilePath& path,
     const WallpaperInfo& info,
     bool show_wallpaper,
@@ -1880,7 +1866,7 @@
   if (image.isNull()) {
     LOG(ERROR) << "Failed to decode user wallpaper at " << path.value()
                << " Falls back to default wallpaper. ";
-    SetDefaultWallpaperImpl(account_id, user_type, show_wallpaper);
+    SetDefaultWallpaperImpl(account_id, show_wallpaper);
     return;
   }
 
@@ -1900,7 +1886,7 @@
     reload_always_on_top_wallpaper_callback_.Run();
   else if (reload_preview_wallpaper_callback_)
     reload_preview_wallpaper_callback_.Run();
-  else if (current_user_.account_id.is_valid())
+  else if (current_user_.is_valid())
     ShowUserWallpaper(current_user_);
   else
     ShowSigninWallpaper();
@@ -2071,8 +2057,7 @@
   if (image.isNull()) {
     // If device policy wallpaper failed decoding, fall back to the default
     // wallpaper.
-    SetDefaultWallpaperImpl(EmptyAccountId(), user_manager::USER_TYPE_REGULAR,
-                            /*show_wallpaper=*/true);
+    SetDefaultWallpaperImpl(EmptyAccountId(), /*show_wallpaper=*/true);
   } else {
     WallpaperInfo info(device_policy_wallpaper_path_.value(),
                        WALLPAPER_LAYOUT_CENTER_CROPPED, DEVICE,
diff --git a/ash/wallpaper/wallpaper_controller_impl.h b/ash/wallpaper/wallpaper_controller_impl.h
index e9dfdd6..1c361b9 100644
--- a/ash/wallpaper/wallpaper_controller_impl.h
+++ b/ash/wallpaper/wallpaper_controller_impl.h
@@ -16,7 +16,6 @@
 #include "ash/public/cpp/wallpaper_controller.h"
 #include "ash/public/cpp/wallpaper_info.h"
 #include "ash/public/cpp/wallpaper_types.h"
-#include "ash/public/cpp/wallpaper_user_info.h"
 #include "ash/session/session_observer.h"
 #include "ash/shell_observer.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_color_calculator_observer.h"
@@ -26,7 +25,6 @@
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
 #include "base/timer/timer.h"
-#include "components/user_manager/user_type.h"
 #include "ui/compositor/compositor_lock.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -107,7 +105,6 @@
   // wallpaper immediately. Must run on wallpaper sequenced worker thread.
   static void SetWallpaperFromPath(
       const AccountId& account_id,
-      const user_manager::UserType& user_type,
       const WallpaperInfo& info,
       const base::FilePath& wallpaper_path,
       bool show_wallpaper,
@@ -159,7 +156,7 @@
 
   // Returns whether a wallpaper policy is enforced for |account_id| (not
   // including device policy).
-  bool IsPolicyControlled(const AccountId& account_id, bool is_ephemeral) const;
+  bool IsPolicyControlled(const AccountId& account_id) const;
 
   // Update the blurred state of the current wallpaper. Applies blur if |blur|
   // is true and blur is allowed by the controller, otherwise any existing blur
@@ -178,18 +175,16 @@
   // |HasShownAnyWallpaper| if there's need to distinguish.
   bool IsWallpaperBlurred() const;
 
-  // Sets wallpaper info for |account_id| and saves it to local state if
-  // |is_ephemeral| is false. Returns false if it fails (which happens if local
+  // Sets wallpaper info for |account_id| and saves it to local state if the
+  // user is not ephemeral. Returns false if it fails (which happens if local
   // state is not available).
   bool SetUserWallpaperInfo(const AccountId& account_id,
-                            const WallpaperInfo& info,
-                            bool is_ephemeral);
+                            const WallpaperInfo& info);
 
-  // Gets wallpaper info of |account_id| from local state, or memory if
-  // |is_ephemeral| is true. Returns false if wallpaper info is not found.
+  // Gets wallpaper info of |account_id| from local state, or memory if the user
+  // is ephemeral. Returns false if wallpaper info is not found.
   bool GetUserWallpaperInfo(const AccountId& account_id,
-                            WallpaperInfo* info,
-                            bool is_ephemeral) const;
+                            WallpaperInfo* info) const;
 
   // Gets encoded wallpaper from cache. Returns true if success.
   bool GetWallpaperFromCache(const AccountId& account_id,
@@ -205,7 +200,6 @@
 
   // A wrapper of |ReadAndDecodeWallpaper| used in |SetWallpaperFromPath|.
   void StartDecodeFromPath(const AccountId& account_id,
-                           const user_manager::UserType& user_type,
                            const base::FilePath& wallpaper_path,
                            const WallpaperInfo& info,
                            bool show_wallpaper);
@@ -216,53 +210,53 @@
             const base::FilePath& wallpapers,
             const base::FilePath& custom_wallpapers,
             const base::FilePath& device_policy_wallpaper) override;
-  void SetCustomWallpaper(const WallpaperUserInfo& user_info,
+  void SetCustomWallpaper(const AccountId& account_id,
                           const std::string& wallpaper_files_id,
                           const std::string& file_name,
                           WallpaperLayout layout,
                           const gfx::ImageSkia& image,
                           bool preview_mode) override;
   void SetOnlineWallpaperIfExists(
-      const WallpaperUserInfo& user_info,
+      const AccountId& account_id,
       const std::string& url,
       WallpaperLayout layout,
       bool preview_mode,
       SetOnlineWallpaperIfExistsCallback callback) override;
   void SetOnlineWallpaperFromData(
-      const WallpaperUserInfo& user_info,
+      const AccountId& account_id,
       const std::string& image_data,
       const std::string& url,
       WallpaperLayout layout,
       bool preview_mode,
       SetOnlineWallpaperFromDataCallback callback) override;
-  void SetDefaultWallpaper(const WallpaperUserInfo& user_info,
+  void SetDefaultWallpaper(const AccountId& account_id,
                            const std::string& wallpaper_files_id,
                            bool show_wallpaper) override;
   void SetCustomizedDefaultWallpaperPaths(
       const base::FilePath& customized_default_small_path,
       const base::FilePath& customized_default_large_path) override;
-  void SetPolicyWallpaper(const WallpaperUserInfo& user_info,
+  void SetPolicyWallpaper(const AccountId& account_id,
                           const std::string& wallpaper_files_id,
                           const std::string& data) override;
   void SetDevicePolicyWallpaperPath(
       const base::FilePath& device_policy_wallpaper_path) override;
-  bool SetThirdPartyWallpaper(const WallpaperUserInfo& user_info,
+  bool SetThirdPartyWallpaper(const AccountId& account_id,
                               const std::string& wallpaper_files_id,
                               const std::string& file_name,
                               WallpaperLayout layout,
                               const gfx::ImageSkia& image) override;
   void ConfirmPreviewWallpaper() override;
   void CancelPreviewWallpaper() override;
-  void UpdateCustomWallpaperLayout(const WallpaperUserInfo& user_info,
+  void UpdateCustomWallpaperLayout(const AccountId& account_id,
                                    WallpaperLayout layout) override;
-  void ShowUserWallpaper(const WallpaperUserInfo& user_info) override;
+  void ShowUserWallpaper(const AccountId& account_id) override;
   void ShowSigninWallpaper() override;
   void ShowOneShotWallpaper(const gfx::ImageSkia& image) override;
   void ShowAlwaysOnTopWallpaper(const base::FilePath& image_path) override;
   void RemoveAlwaysOnTopWallpaper() override;
-  void RemoveUserWallpaper(const WallpaperUserInfo& user_info,
+  void RemoveUserWallpaper(const AccountId& account_id,
                            const std::string& wallpaper_files_id) override;
-  void RemovePolicyWallpaper(const WallpaperUserInfo& user_info,
+  void RemovePolicyWallpaper(const AccountId& account_id,
                              const std::string& wallpaper_files_id) override;
   void GetOfflineWallpaperList(
       GetOfflineWallpaperListCallback callback) override;
@@ -336,7 +330,6 @@
 
   struct OnlineWallpaperParams {
     AccountId account_id;
-    bool is_ephemeral;
     std::string url;
     WallpaperLayout layout;
     bool preview_mode;
@@ -356,7 +349,7 @@
   int GetWallpaperContainerId(bool locked);
 
   // Removes |account_id|'s wallpaper info and color cache if it exists.
-  void RemoveUserWallpaperInfo(const AccountId& account_id, bool is_ephemeral);
+  void RemoveUserWallpaperInfo(const AccountId& account_id);
 
   // Implementation of |RemoveUserWallpaper|, which deletes |account_id|'s
   // custom wallpapers and directories.
@@ -365,16 +358,13 @@
 
   // Implementation of |SetDefaultWallpaper|. Sets wallpaper to default if
   // |show_wallpaper| is true. Otherwise just save the defaut wallpaper to
-  // cache. |user_type| is the type of the user initiating the wallpaper
-  // request; may be different from the active user.
+  // cache.
   void SetDefaultWallpaperImpl(const AccountId& account_id,
-                               const user_manager::UserType& user_type,
                                bool show_wallpaper);
 
   // When kiosk app is running or policy is enforced, setting a user wallpaper
   // is not allowed.
-  bool CanSetUserWallpaper(const AccountId& account_id,
-                           bool is_ephemeral) const;
+  bool CanSetUserWallpaper(const AccountId& account_id) const;
 
   // Returns true if the specified wallpaper is already stored in
   // |current_wallpaper_|. If |compare_layouts| is false, layout is ignored.
@@ -390,9 +380,8 @@
       const base::FilePath& file_path);
 
   // Initializes wallpaper info for the user to default and saves it to local
-  // state if |is_ephemeral| is false. Returns false if initialization fails.
-  bool InitializeUserWallpaperInfo(const AccountId& account_id,
-                                   bool is_ephemeral);
+  // state the user is not ephemeral. Returns false if initialization fails.
+  bool InitializeUserWallpaperInfo(const AccountId& account_id);
 
   // Used as the callback of checking ONLINE wallpaper existence in
   // |SetOnlineWallpaperIfExists|. Initiates reading and decoding the wallpaper
@@ -418,7 +407,6 @@
   // Decodes |account_id|'s wallpaper. Shows the decoded wallpaper if
   // |show_wallpaper| is true.
   void SetWallpaperFromInfo(const AccountId& account_id,
-                            const user_manager::UserType& user_type,
                             const WallpaperInfo& info,
                             bool show_wallpaper);
 
@@ -430,11 +418,11 @@
                                  bool show_wallpaper,
                                  const gfx::ImageSkia& image);
 
-  // Saves |image| to disk if |user_info->is_ephemeral| is false, or if it is a
+  // Saves |image| to disk if the user's data is not ephemeral, or if it is a
   // policy wallpaper for public accounts. Shows the wallpaper immediately if
   // |show_wallpaper| is true, otherwise only sets the wallpaper info and
   // updates the cache.
-  void SaveAndSetWallpaper(const WallpaperUserInfo& user_info,
+  void SaveAndSetWallpaper(const AccountId& account_id,
                            const std::string& wallpaper_files_id,
                            const std::string& file_name,
                            WallpaperType type,
@@ -447,7 +435,6 @@
   // types should use this.) Shows the wallpaper immediately if |show_wallpaper|
   // is true. Otherwise, only updates the cache.
   void OnWallpaperDecoded(const AccountId& account_id,
-                          const user_manager::UserType& user_type,
                           const base::FilePath& path,
                           const WallpaperInfo& info,
                           bool show_wallpaper,
@@ -553,11 +540,11 @@
   const std::vector<color_utils::ColorProfile> color_profiles_;
 
   // The wallpaper info for ephemeral users, which is not stored to local state.
-  // See |WallpaperUserInfo::is_ephemeral| for details.
+  // See |UserInfo::is_ephemeral| for details.
   std::map<AccountId, WallpaperInfo> ephemeral_users_wallpaper_info_;
 
-  // Cached user info of the current user.
-  WallpaperUserInfo current_user_;
+  // Account id of the current user.
+  AccountId current_user_;
 
   // Cached wallpapers of users.
   CustomWallpaperMap wallpaper_cache_map_;
diff --git a/ash/wallpaper/wallpaper_controller_test_api.cc b/ash/wallpaper/wallpaper_controller_test_api.cc
index f0dc2de..2e85428 100644
--- a/ash/wallpaper/wallpaper_controller_test_api.cc
+++ b/ash/wallpaper/wallpaper_controller_test_api.cc
@@ -47,8 +47,7 @@
       base::BindOnce(&WallpaperControllerImpl::SetWallpaperFromInfo,
                      controller_->weak_factory_.GetWeakPtr(),
                      AccountId::FromUserEmail("user@test.com"),
-                     user_manager::USER_TYPE_REGULAR, kTestWallpaperInfo,
-                     /*show_wallpaper=*/true);
+                     kTestWallpaperInfo, /*show_wallpaper=*/true);
   controller_->reload_preview_wallpaper_callback_ = base::BindRepeating(
       &WallpaperControllerImpl::ShowWallpaperImage,
       controller_->weak_factory_.GetWeakPtr(),
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 76e0620..713bbf7 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -32,6 +32,7 @@
 #include "base/test/bind_test_util.h"
 #include "base/time/time_override.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "components/user_manager/user_names.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/aura/window.h"
@@ -375,23 +376,6 @@
                          base::Time::Now().LocalMidnight());
   }
 
-  // Creates a |WallpaperUserInfo| struct for |account_id| and optional |type|.
-  // Additionally, clear the wallpaper count and the decoding request list.
-  // This may be called multiple times for the same |account_id|.
-  WallpaperUserInfo InitializeUser(
-      const AccountId& account_id,
-      user_manager::UserType type = user_manager::USER_TYPE_REGULAR) {
-    WallpaperUserInfo wallpaper_user_info;
-    wallpaper_user_info.account_id = account_id;
-    wallpaper_user_info.type = type;
-    wallpaper_user_info.is_ephemeral = false;
-    wallpaper_user_info.has_gaia_account = true;
-    ClearWallpaperCount();
-    ClearDecodeFilePaths();
-
-    return wallpaper_user_info;
-  }
-
   // Saves images with different resolution to corresponding paths and saves
   // wallpaper info to local state, so that subsequent calls of |ShowWallpaper|
   // can retrieve the images and info.
@@ -419,8 +403,7 @@
     // Saves wallpaper info to local state for user.
     WallpaperInfo info = {relative_path, WALLPAPER_LAYOUT_CENTER_CROPPED,
                           CUSTOMIZED, base::Time::Now().LocalMidnight()};
-    ASSERT_TRUE(controller_->SetUserWallpaperInfo(account_id, info,
-                                                  false /*is_ephemeral=*/));
+    ASSERT_TRUE(controller_->SetUserWallpaperInfo(account_id, info));
   }
 
   // Simulates setting a custom wallpaper by directly setting the wallpaper
@@ -429,8 +412,7 @@
     ASSERT_TRUE(controller_->SetUserWallpaperInfo(
         account_id,
         WallpaperInfo("dummy_file_location", WALLPAPER_LAYOUT_CENTER,
-                      CUSTOMIZED, base::Time::Now().LocalMidnight()),
-        false /*is_ephemeral=*/));
+                      CUSTOMIZED, base::Time::Now().LocalMidnight())));
   }
 
   // Initializes default wallpaper paths "*default_*file" and writes JPEG
@@ -499,7 +481,7 @@
       bool preview_mode,
       WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback callback) {
     const WallpaperControllerImpl::OnlineWallpaperParams params = {
-        account_id, false /*is_ephemeral=*/, url, layout, preview_mode};
+        account_id, url, layout, preview_mode};
     controller_->OnOnlineWallpaperDecoded(params, save_file,
                                           std::move(callback), image);
   }
@@ -902,16 +884,16 @@
 
   // Set a custom wallpaper for |kUser1|. Verify the wallpaper is set
   // successfully and wallpaper info is updated.
-  controller_->SetCustomWallpaper(InitializeUser(account_id_1),
-                                  wallpaper_files_id_1, file_name_1, layout,
-                                  image, false /*preview_mode=*/);
+  ClearWallpaperCount();
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_1,
+                                  file_name_1, layout, image,
+                                  false /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
   EXPECT_EQ(controller_->GetWallpaperType(), CUSTOMIZED);
   WallpaperInfo wallpaper_info;
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo expected_wallpaper_info(
       base::FilePath(wallpaper_files_id_1).Append(file_name_1).value(), layout,
       CUSTOMIZED, base::Time::Now().LocalMidnight());
@@ -923,19 +905,20 @@
   SimulateUserLogin(kUser2);
   const SkColor custom_wallpaper_color = SK_ColorCYAN;
   image = CreateImage(640, 480, custom_wallpaper_color);
-  controller_->SetCustomWallpaper(InitializeUser(account_id_1),
-                                  wallpaper_files_id_1, file_name_1, layout,
-                                  image, false /*preview_mode=*/);
+  ClearWallpaperCount();
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_1,
+                                  file_name_1, layout, image,
+                                  false /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   EXPECT_EQ(wallpaper_info, expected_wallpaper_info);
 
   // Verify the updated wallpaper is shown after |kUser1| becomes active again.
   SimulateUserLogin(kUser1);
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(custom_wallpaper_color, GetWallpaperColor());
@@ -959,8 +942,9 @@
   // Verify that the attempt to set an online wallpaper without providing image
   // data fails.
   run_loop.reset(new base::RunLoop());
+  ClearWallpaperCount();
   controller_->SetOnlineWallpaperIfExists(
-      InitializeUser(account_id_1), kDummyUrl, layout, false /*preview_mode=*/,
+      account_id_1, kDummyUrl, layout, false /*preview_mode=*/,
       base::BindLambdaForTesting([&run_loop](bool file_exists) {
         EXPECT_FALSE(file_exists);
         run_loop->Quit();
@@ -970,17 +954,17 @@
 
   // Set an online wallpaper with image data. Verify that the wallpaper is set
   // successfully.
+  ClearWallpaperCount();
   controller_->SetOnlineWallpaperFromData(
-      InitializeUser(account_id_1), std::string() /*image_data=*/, kDummyUrl,
-      layout, false /*preview_mode=*/,
+      account_id_1, std::string() /*image_data=*/, kDummyUrl, layout,
+      false /*preview_mode=*/,
       WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), ONLINE);
   // Verify that the user wallpaper info is updated.
   WallpaperInfo wallpaper_info;
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo expected_wallpaper_info(kDummyUrl, layout, ONLINE,
                                         base::Time::Now().LocalMidnight());
   EXPECT_EQ(wallpaper_info, expected_wallpaper_info);
@@ -988,8 +972,9 @@
   // Change the on-screen wallpaper to a different one. (Otherwise the
   // subsequent calls will be no-op since we intentionally prevent reloading the
   // same wallpaper.)
+  ClearWallpaperCount();
   controller_->SetCustomWallpaper(
-      InitializeUser(account_id_1), wallpaper_files_id_1, file_name_1, layout,
+      account_id_1, wallpaper_files_id_1, file_name_1, layout,
       CreateImage(640, 480, kWallpaperColor), false /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
@@ -998,9 +983,10 @@
   // Attempt to set an online wallpaper without providing the image data. Verify
   // it succeeds this time because |SetOnlineWallpaperFromData| has saved the
   // file.
+  ClearWallpaperCount();
   run_loop.reset(new base::RunLoop());
   controller_->SetOnlineWallpaperIfExists(
-      InitializeUser(account_id_1), kDummyUrl, layout, false /*preview_mode=*/,
+      account_id_1, kDummyUrl, layout, false /*preview_mode=*/,
       base::BindLambdaForTesting([&run_loop](bool file_exists) {
         EXPECT_TRUE(file_exists);
         run_loop->Quit();
@@ -1024,14 +1010,14 @@
   // the on-screen wallpaper doesn't change since |kUser1| is not active, but
   // wallpaper info is updated properly.
   SimulateUserLogin(kUser2);
+  ClearWallpaperCount();
   controller_->SetOnlineWallpaperFromData(
-      InitializeUser(account_id_1), std::string() /*image_data=*/, kDummyUrl2,
-      layout, false /*preview_mode=*/,
+      account_id_1, std::string() /*image_data=*/, kDummyUrl2, layout,
+      false /*preview_mode=*/,
       WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo expected_wallpaper_info_2(kDummyUrl2, layout, ONLINE,
                                           base::Time::Now().LocalMidnight());
   EXPECT_EQ(wallpaper_info, expected_wallpaper_info_2);
@@ -1044,37 +1030,36 @@
 
   // The user starts with no wallpaper info and is not controlled by policy.
   WallpaperInfo wallpaper_info;
-  EXPECT_FALSE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                 false /*is_ephemeral=*/));
   EXPECT_FALSE(
-      controller_->IsPolicyControlled(account_id_1, false /*is_ephemeral=*/));
+      controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
+  EXPECT_FALSE(controller_->IsPolicyControlled(account_id_1));
   // A default wallpaper is shown for the user.
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), DEFAULT);
 
   // Set a policy wallpaper. Verify that the user becomes policy controlled and
   // the wallpaper info is updated.
-  controller_->SetPolicyWallpaper(InitializeUser(account_id_1),
-                                  wallpaper_files_id_1,
+  ClearWallpaperCount();
+  controller_->SetPolicyWallpaper(account_id_1, wallpaper_files_id_1,
                                   std::string() /*data=*/);
   RunAllTasksUntilIdle();
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo policy_wallpaper_info(base::FilePath(wallpaper_files_id_1)
                                           .Append("policy-controlled.jpeg")
                                           .value(),
                                       WALLPAPER_LAYOUT_CENTER_CROPPED, POLICY,
                                       base::Time::Now().LocalMidnight());
   EXPECT_EQ(wallpaper_info, policy_wallpaper_info);
-  EXPECT_TRUE(
-      controller_->IsPolicyControlled(account_id_1, false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->IsPolicyControlled(account_id_1));
   // Verify the wallpaper is not updated since the user hasn't logged in.
   EXPECT_EQ(0, GetWallpaperCount());
 
   // Log in the user. Verify the policy wallpaper is now being shown.
   SimulateUserLogin(kUser1);
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), POLICY);
 
@@ -1082,24 +1067,21 @@
   // shown in the login screen.
   ClearWallpaper();
   ClearLogin();
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_EQ(controller_->GetWallpaperType(), POLICY);
-  EXPECT_TRUE(
-      controller_->IsPolicyControlled(account_id_1, false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->IsPolicyControlled(account_id_1));
   // Remove the policy wallpaper. Verify the wallpaper info is reset to default
   // and the user is no longer policy controlled.
-  controller_->RemovePolicyWallpaper(InitializeUser(account_id_1),
-                                     wallpaper_files_id_1);
+  ClearWallpaperCount();
+  controller_->RemovePolicyWallpaper(account_id_1, wallpaper_files_id_1);
   WaitUntilCustomWallpapersDeleted(account_id_1);
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo default_wallpaper_info(std::string(),
                                        WALLPAPER_LAYOUT_CENTER_CROPPED, DEFAULT,
                                        base::Time::Now().LocalMidnight());
   EXPECT_EQ(wallpaper_info, default_wallpaper_info);
-  EXPECT_FALSE(
-      controller_->IsPolicyControlled(account_id_1, false /*is_ephemeral=*/));
+  EXPECT_FALSE(controller_->IsPolicyControlled(account_id_1));
   // Verify the wallpaper is not updated since the user hasn't logged in (to
   // avoid abrupt wallpaper change in login screen).
   EXPECT_EQ(0, GetWallpaperCount());
@@ -1107,7 +1089,8 @@
 
   // Log in the user. Verify the default wallpaper is now being shown.
   SimulateUserLogin(kUser1);
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), DEFAULT);
 }
@@ -1117,8 +1100,8 @@
     EXPECT_EQ(CUSTOMIZED, controller_->GetWallpaperType());
     EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
     WallpaperInfo wallpaper_info;
-    EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                  false /*is_ephemeral=*/));
+    EXPECT_TRUE(
+        controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
     WallpaperInfo expected_wallpaper_info(
         base::FilePath(wallpaper_files_id_1).Append(file_name_1).value(),
         WALLPAPER_LAYOUT_CENTER, CUSTOMIZED, base::Time::Now().LocalMidnight());
@@ -1128,20 +1111,18 @@
   // Set a custom wallpaper. Verify the user is not policy controlled and the
   // wallpaper info is correct.
   SimulateUserLogin(kUser1);
+  ClearWallpaperCount();
   controller_->SetCustomWallpaper(
-      InitializeUser(account_id_1), wallpaper_files_id_1, file_name_1,
-      WALLPAPER_LAYOUT_CENTER, CreateImage(640, 480, kWallpaperColor),
-      false /*preview_mode=*/);
+      account_id_1, wallpaper_files_id_1, file_name_1, WALLPAPER_LAYOUT_CENTER,
+      CreateImage(640, 480, kWallpaperColor), false /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
-  EXPECT_FALSE(
-      controller_->IsPolicyControlled(account_id_1, false /*is_ephemeral=*/));
+  EXPECT_FALSE(controller_->IsPolicyControlled(account_id_1));
   verify_custom_wallpaper_info();
 
   // Verify RemovePolicyWallpaper() is a no-op when the user doesn't have a
   // policy wallpaper.
-  controller_->RemovePolicyWallpaper(InitializeUser(account_id_1),
-                                     wallpaper_files_id_1);
+  controller_->RemovePolicyWallpaper(account_id_1, wallpaper_files_id_1);
   RunAllTasksUntilIdle();
   verify_custom_wallpaper_info();
 }
@@ -1152,20 +1133,20 @@
 
   // Verify the user starts with no wallpaper info.
   WallpaperInfo wallpaper_info;
-  EXPECT_FALSE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                 false /*is_ephemeral=*/));
+  EXPECT_FALSE(
+      controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
 
   // Set a third-party wallpaper for |kUser1|.
   const WallpaperLayout layout = WALLPAPER_LAYOUT_CENTER;
   gfx::ImageSkia third_party_wallpaper = CreateImage(640, 480, kWallpaperColor);
+  ClearWallpaperCount();
   EXPECT_TRUE(controller_->SetThirdPartyWallpaper(
-      InitializeUser(account_id_1), wallpaper_files_id_1, file_name_1, layout,
+      account_id_1, wallpaper_files_id_1, file_name_1, layout,
       third_party_wallpaper));
   // Verify the wallpaper is shown.
   EXPECT_EQ(1, GetWallpaperCount());
   // Verify the user wallpaper info is updated.
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo expected_wallpaper_info(
       base::FilePath(wallpaper_files_id_1).Append(file_name_1).value(), layout,
       CUSTOMIZED, base::Time::Now().LocalMidnight());
@@ -1175,15 +1156,15 @@
   // |kUser1|; the operation should not be allowed, because |kUser1| is not the
   // active user.
   SimulateUserLogin(kUser2);
+  ClearWallpaperCount();
   EXPECT_FALSE(controller_->SetThirdPartyWallpaper(
-      InitializeUser(account_id_1), wallpaper_files_id_2, file_name_2, layout,
+      account_id_1, wallpaper_files_id_2, file_name_2, layout,
       third_party_wallpaper));
   // Verify the wallpaper is not shown.
   EXPECT_EQ(0, GetWallpaperCount());
   // Verify the wallpaper info for |kUser1| is updated, because setting
   // wallpaper is still allowed for non-active users.
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo expected_wallpaper_info_2(
       base::FilePath(wallpaper_files_id_2).Append(file_name_2).value(), layout,
       CUSTOMIZED, base::Time::Now().LocalMidnight());
@@ -1191,28 +1172,25 @@
 
   // Set a policy wallpaper for |kUser2|. Verify that |kUser2| becomes policy
   // controlled.
-  controller_->SetPolicyWallpaper(InitializeUser(account_id_2),
-                                  wallpaper_files_id_2,
+  controller_->SetPolicyWallpaper(account_id_2, wallpaper_files_id_2,
                                   std::string() /*data=*/);
   RunAllTasksUntilIdle();
-  EXPECT_TRUE(
-      controller_->IsPolicyControlled(account_id_2, false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->IsPolicyControlled(account_id_2));
   EXPECT_TRUE(controller_->IsActiveUserWallpaperControlledByPolicy());
 
   // Setting a third-party wallpaper for |kUser2| should not be allowed, because
   // third-party wallpapers cannot be set for policy controlled users.
+  ClearWallpaperCount();
   EXPECT_FALSE(controller_->SetThirdPartyWallpaper(
-      InitializeUser(account_id_2), wallpaper_files_id_1, file_name_1, layout,
+      account_id_2, wallpaper_files_id_1, file_name_1, layout,
       third_party_wallpaper));
   // Verify the wallpaper is not shown.
   EXPECT_EQ(0, GetWallpaperCount());
   // Verify |kUser2| is still policy controlled and has the policy wallpaper
   // info.
-  EXPECT_TRUE(
-      controller_->IsPolicyControlled(account_id_2, false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->IsPolicyControlled(account_id_2));
   EXPECT_TRUE(controller_->IsActiveUserWallpaperControlledByPolicy());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_2, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_2, &wallpaper_info));
   WallpaperInfo policy_wallpaper_info(base::FilePath(wallpaper_files_id_2)
                                           .Append("policy-controlled.jpeg")
                                           .value(),
@@ -1228,8 +1206,7 @@
   // First, simulate setting a user custom wallpaper.
   SimulateSettingCustomWallpaper(account_id_1);
   WallpaperInfo wallpaper_info;
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo default_wallpaper_info(std::string(),
                                        WALLPAPER_LAYOUT_CENTER_CROPPED, DEFAULT,
                                        base::Time::Now().LocalMidnight());
@@ -1240,8 +1217,9 @@
   // file path.
   UpdateDisplay("1600x1200");
   RunAllTasksUntilIdle();
-  controller_->SetDefaultWallpaper(InitializeUser(account_id_1),
-                                   wallpaper_files_id_1,
+  ClearWallpaperCount();
+  ClearDecodeFilePaths();
+  controller_->SetDefaultWallpaper(account_id_1, wallpaper_files_id_1,
                                    true /*show_wallpaper=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
@@ -1250,8 +1228,7 @@
   EXPECT_EQ(default_wallpaper_dir_.GetPath().Append(kDefaultLargeWallpaperName),
             GetDecodeFilePaths()[0]);
 
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   // The user wallpaper info has been reset to the default value.
   EXPECT_EQ(wallpaper_info, default_wallpaper_info);
 
@@ -1261,8 +1238,9 @@
   // file path.
   UpdateDisplay("800x600");
   RunAllTasksUntilIdle();
-  controller_->SetDefaultWallpaper(InitializeUser(account_id_1),
-                                   wallpaper_files_id_1,
+  ClearWallpaperCount();
+  ClearDecodeFilePaths();
+  controller_->SetDefaultWallpaper(account_id_1, wallpaper_files_id_1,
                                    true /*show_wallpaper=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
@@ -1271,8 +1249,7 @@
   EXPECT_EQ(default_wallpaper_dir_.GetPath().Append(kDefaultSmallWallpaperName),
             GetDecodeFilePaths()[0]);
 
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   // The user wallpaper info has been reset to the default value.
   EXPECT_EQ(wallpaper_info, default_wallpaper_info);
 
@@ -1282,8 +1259,9 @@
   // set successfully with the correct file path.
   UpdateDisplay("800x600/r");
   RunAllTasksUntilIdle();
-  controller_->SetDefaultWallpaper(InitializeUser(account_id_1),
-                                   wallpaper_files_id_1,
+  ClearWallpaperCount();
+  ClearDecodeFilePaths();
+  controller_->SetDefaultWallpaper(account_id_1, wallpaper_files_id_1,
                                    true /*show_wallpaper=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
@@ -1292,8 +1270,7 @@
   EXPECT_EQ(default_wallpaper_dir_.GetPath().Append(kDefaultSmallWallpaperName),
             GetDecodeFilePaths()[0]);
 
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   // The user wallpaper info has been reset to the default value.
   EXPECT_EQ(wallpaper_info, default_wallpaper_info);
 }
@@ -1304,16 +1281,16 @@
   const std::string child_email = "child@test.com";
   const AccountId child_account_id = AccountId::FromUserEmail(child_email);
   const std::string child_wallpaper_files_id = GetDummyFileId(child_account_id);
-  SimulateUserLogin(child_email);
+  SimulateUserLogin(child_email, user_manager::USER_TYPE_CHILD);
 
   // Verify the large child wallpaper is set successfully with the correct file
   // path.
   UpdateDisplay("1600x1200");
   RunAllTasksUntilIdle();
-  WallpaperUserInfo wallpaper_user_info = InitializeUser(child_account_id);
-  wallpaper_user_info.type = user_manager::USER_TYPE_CHILD;
-  controller_->SetDefaultWallpaper(
-      wallpaper_user_info, child_wallpaper_files_id, true /*show_wallpaper=*/);
+  ClearWallpaperCount();
+  ClearDecodeFilePaths();
+  controller_->SetDefaultWallpaper(child_account_id, child_wallpaper_files_id,
+                                   true /*show_wallpaper=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), DEFAULT);
@@ -1325,10 +1302,10 @@
   // path.
   UpdateDisplay("800x600");
   RunAllTasksUntilIdle();
-  wallpaper_user_info = InitializeUser(child_account_id);
-  wallpaper_user_info.type = user_manager::USER_TYPE_CHILD;
-  controller_->SetDefaultWallpaper(
-      wallpaper_user_info, child_wallpaper_files_id, true /*show_wallpaper=*/);
+  ClearWallpaperCount();
+  ClearDecodeFilePaths();
+  controller_->SetDefaultWallpaper(child_account_id, child_wallpaper_files_id,
+                                   true /*show_wallpaper=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), DEFAULT);
@@ -1344,29 +1321,29 @@
   SimulateUserLogin(kUser1);
   SimulateSettingCustomWallpaper(account_id_1);
   WallpaperInfo wallpaper_info;
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo default_wallpaper_info(std::string(),
                                        WALLPAPER_LAYOUT_CENTER_CROPPED, DEFAULT,
                                        base::Time::Now().LocalMidnight());
   EXPECT_NE(wallpaper_info.type, default_wallpaper_info.type);
 
   SimulateGuestLogin();
+  const AccountId guest_id =
+      AccountId::FromUserEmail(user_manager::kGuestUserName);
 
   // Verify that during a guest session, |SetDefaultWallpaper| removes the user
   // custom wallpaper info, but a guest specific wallpaper should be set,
   // instead of the regular default wallpaper.
   UpdateDisplay("1600x1200");
   RunAllTasksUntilIdle();
-
-  controller_->SetDefaultWallpaper(
-      InitializeUser(account_id_1, user_manager::USER_TYPE_GUEST),
-      wallpaper_files_id_1, true /*show_wallpaper=*/);
+  ClearWallpaperCount();
+  ClearDecodeFilePaths();
+  controller_->SetDefaultWallpaper(guest_id, wallpaper_files_id_1,
+                                   true /*show_wallpaper=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), DEFAULT);
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(guest_id, &wallpaper_info));
   EXPECT_EQ(wallpaper_info, default_wallpaper_info);
   ASSERT_EQ(1u, GetDecodeFilePaths().size());
   EXPECT_EQ(default_wallpaper_dir_.GetPath().Append(kGuestLargeWallpaperName),
@@ -1374,9 +1351,10 @@
 
   UpdateDisplay("800x600");
   RunAllTasksUntilIdle();
-  controller_->SetDefaultWallpaper(
-      InitializeUser(account_id_1, user_manager::USER_TYPE_GUEST),
-      wallpaper_files_id_1, true /*show_wallpaper=*/);
+  ClearWallpaperCount();
+  ClearDecodeFilePaths();
+  controller_->SetDefaultWallpaper(guest_id, wallpaper_files_id_1,
+                                   true /*show_wallpaper=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), DEFAULT);
@@ -1387,30 +1365,26 @@
 
 TEST_F(WallpaperControllerTest, IgnoreWallpaperRequestInKioskMode) {
   gfx::ImageSkia image = CreateImage(640, 480, kWallpaperColor);
-  const std::string kiosk_app = "kiosk";
-
-  // Simulate kiosk login.
-  TestSessionControllerClient* session = GetSessionControllerClient();
-  session->AddUserSession(kiosk_app, user_manager::USER_TYPE_KIOSK_APP);
-  session->SwitchActiveUser(AccountId::FromUserEmail(kiosk_app));
-  session->SetSessionState(SessionState::ACTIVE);
+  SimulateUserLogin("kiosk", user_manager::USER_TYPE_KIOSK_APP);
 
   // Verify that |SetCustomWallpaper| doesn't set wallpaper in kiosk mode, and
-  // |account_id|'s wallpaper info is not updated.
-  controller_->SetCustomWallpaper(
-      InitializeUser(account_id_1), wallpaper_files_id_1, file_name_1,
-      WALLPAPER_LAYOUT_CENTER, image, false /*preview_mode=*/);
+  // |account_id_1|'s wallpaper info is not updated.
+  ClearWallpaperCount();
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_1,
+                                  file_name_1, WALLPAPER_LAYOUT_CENTER, image,
+                                  false /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
   WallpaperInfo wallpaper_info;
-  EXPECT_FALSE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                 false /*is_ephemeral=*/));
+  EXPECT_FALSE(
+      controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
 
   // Verify that |SetOnlineWallpaperFromData| doesn't set wallpaper in kiosk
-  // mode, and |account_id|'s wallpaper info is not updated.
+  // mode, and |account_id_1|'s wallpaper info is not updated.
   std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
+  ClearWallpaperCount();
   controller_->SetOnlineWallpaperFromData(
-      InitializeUser(account_id_1), std::string() /*image_data=*/, kDummyUrl,
+      account_id_1, std::string() /*image_data=*/, kDummyUrl,
       WALLPAPER_LAYOUT_CENTER, false /*preview_mode=*/,
       base::BindLambdaForTesting([&run_loop](bool success) {
         EXPECT_FALSE(success);
@@ -1418,18 +1392,18 @@
       }));
   run_loop->Run();
   EXPECT_EQ(0, GetWallpaperCount());
-  EXPECT_FALSE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                 false /*is_ephemeral=*/));
+  EXPECT_FALSE(
+      controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
 
   // Verify that |SetDefaultWallpaper| doesn't set wallpaper in kiosk mode, and
-  // |account_id|'s wallpaper info is not updated.
-  controller_->SetDefaultWallpaper(InitializeUser(account_id_1),
-                                   wallpaper_files_id_1,
+  // |account_id_1|'s wallpaper info is not updated.
+  ClearWallpaperCount();
+  controller_->SetDefaultWallpaper(account_id_1, wallpaper_files_id_1,
                                    true /*show_wallpaper=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
-  EXPECT_FALSE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                 false /*is_ephemeral=*/));
+  EXPECT_FALSE(
+      controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
 }
 
 TEST_F(WallpaperControllerTest, IgnoreWallpaperRequestWhenPolicyIsEnforced) {
@@ -1438,23 +1412,21 @@
   SimulateUserLogin(kUser1);
 
   // Set a policy wallpaper for the user. Verify the user is policy controlled.
-  controller_->SetPolicyWallpaper(InitializeUser(account_id_1),
-                                  wallpaper_files_id_1,
+  controller_->SetPolicyWallpaper(account_id_1, wallpaper_files_id_1,
                                   std::string() /*data=*/);
   RunAllTasksUntilIdle();
-  EXPECT_TRUE(
-      controller_->IsPolicyControlled(account_id_1, false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->IsPolicyControlled(account_id_1));
 
   // Verify that |SetCustomWallpaper| doesn't set wallpaper when policy is
   // enforced, and the user wallpaper info is not updated.
-  controller_->SetCustomWallpaper(
-      InitializeUser(account_id_1), wallpaper_files_id_1, file_name_1,
-      WALLPAPER_LAYOUT_CENTER, image, false /*preview_mode=*/);
+  ClearWallpaperCount();
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_1,
+                                  file_name_1, WALLPAPER_LAYOUT_CENTER, image,
+                                  false /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
   WallpaperInfo wallpaper_info;
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo policy_wallpaper_info(base::FilePath(wallpaper_files_id_1)
                                           .Append("policy-controlled.jpeg")
                                           .value(),
@@ -1465,8 +1437,9 @@
   // Verify that |SetOnlineWallpaperFromData| doesn't set wallpaper when policy
   // is enforced, and the user wallpaper info is not updated.
   std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
+  ClearWallpaperCount();
   controller_->SetOnlineWallpaperFromData(
-      InitializeUser(account_id_1), std::string() /*image_data=*/, kDummyUrl,
+      account_id_1, std::string() /*image_data=*/, kDummyUrl,
       WALLPAPER_LAYOUT_CENTER_CROPPED, false /*preview_mode=*/,
       base::BindLambdaForTesting([&run_loop](bool success) {
         EXPECT_FALSE(success);
@@ -1474,19 +1447,17 @@
       }));
   run_loop->Run();
   EXPECT_EQ(0, GetWallpaperCount());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   EXPECT_EQ(wallpaper_info, policy_wallpaper_info);
 
   // Verify that |SetDefaultWallpaper| doesn't set wallpaper when policy is
   // enforced, and the user wallpaper info is not updated.
-  controller_->SetDefaultWallpaper(InitializeUser(account_id_1),
-                                   wallpaper_files_id_1,
+  ClearWallpaperCount();
+  controller_->SetDefaultWallpaper(account_id_1, wallpaper_files_id_1,
                                    true /*show_wallpaper=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   EXPECT_EQ(wallpaper_info, policy_wallpaper_info);
 }
 
@@ -1504,7 +1475,7 @@
 
   // Verify |SetOnlineWallpaperFromData| updates wallpaper cache for |user1|.
   controller_->SetOnlineWallpaperFromData(
-      InitializeUser(account_id_1), std::string() /*image_data=*/, kDummyUrl,
+      account_id_1, std::string() /*image_data=*/, kDummyUrl,
       WALLPAPER_LAYOUT_CENTER, false /*preview_mode=*/,
       WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback());
   RunAllTasksUntilIdle();
@@ -1521,25 +1492,23 @@
   EXPECT_TRUE(controller_->GetPathFromCache(account_id_1, &path));
 
   // Verify |SetDefaultWallpaper| clears wallpaper cache.
-  controller_->SetDefaultWallpaper(InitializeUser(account_id_1),
-                                   wallpaper_files_id_1,
+  controller_->SetDefaultWallpaper(account_id_1, wallpaper_files_id_1,
                                    true /*show_wallpaper=*/);
   EXPECT_FALSE(
       controller_->GetWallpaperFromCache(account_id_1, &cached_wallpaper));
   EXPECT_FALSE(controller_->GetPathFromCache(account_id_1, &path));
 
   // Verify |SetCustomWallpaper| updates wallpaper cache for |user1|.
-  controller_->SetCustomWallpaper(
-      InitializeUser(account_id_1), wallpaper_files_id_1, file_name_1,
-      WALLPAPER_LAYOUT_CENTER, image, false /*preview_mode=*/);
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_1,
+                                  file_name_1, WALLPAPER_LAYOUT_CENTER, image,
+                                  false /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_TRUE(
       controller_->GetWallpaperFromCache(account_id_1, &cached_wallpaper));
   EXPECT_TRUE(controller_->GetPathFromCache(account_id_1, &path));
 
   // Verify |RemoveUserWallpaper| clears wallpaper cache.
-  controller_->RemoveUserWallpaper(InitializeUser(account_id_1),
-                                   wallpaper_files_id_1);
+  controller_->RemoveUserWallpaper(account_id_1, wallpaper_files_id_1);
   EXPECT_FALSE(
       controller_->GetWallpaperFromCache(account_id_1, &cached_wallpaper));
   EXPECT_FALSE(controller_->GetPathFromCache(account_id_1, &path));
@@ -1561,7 +1530,8 @@
       default_wallpaper_dir_.GetPath().Append(kDefaultLargeWallpaperName);
 
   CreateAndSaveWallpapers(account_id_1);
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   // Display is initialized to 800x600. The small resolution custom wallpaper is
   // expected. A second decode request with small resolution default wallpaper
@@ -1646,7 +1616,8 @@
   // Show a user wallpaper.
   UpdateDisplay("800x600");
   RunAllTasksUntilIdle();
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   // Rotating the display should trigger a wallpaper reload.
@@ -1657,7 +1628,7 @@
   // Calling |ShowUserWallpaper| again with the same account id and display
   // size should not trigger wallpaper reload (crbug.com/158383).
   ClearWallpaperCount();
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
 
@@ -1668,9 +1639,8 @@
   wm::GetWindowState(wallpaper_picker_window.get())->Activate();
   ClearWallpaperCount();
   controller_->SetCustomWallpaper(
-      InitializeUser(account_id_1), wallpaper_files_id_1, file_name_1,
-      WALLPAPER_LAYOUT_CENTER, CreateImage(640, 480, kWallpaperColor),
-      true /*preview_mode=*/);
+      account_id_1, wallpaper_files_id_1, file_name_1, WALLPAPER_LAYOUT_CENTER,
+      CreateImage(640, 480, kWallpaperColor), true /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   // Rotating the display should trigger a wallpaper reload.
@@ -1709,15 +1679,15 @@
 
   // Set a custom wallpaper for the user. Verify that it's set successfully
   // and the wallpaper info is updated.
-  controller_->SetCustomWallpaper(InitializeUser(account_id_1),
-                                  wallpaper_files_id_1, file_name_1, layout,
-                                  image, false /*preview_mode=*/);
+  ClearWallpaperCount();
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_1,
+                                  file_name_1, layout, image,
+                                  false /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperLayout(), layout);
   WallpaperInfo wallpaper_info;
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo expected_custom_wallpaper_info(
       base::FilePath(wallpaper_files_id_1).Append(file_name_1).value(), layout,
       CUSTOMIZED, base::Time::Now().LocalMidnight());
@@ -1725,43 +1695,41 @@
 
   // Now change to a different layout. Verify that the layout is updated for
   // both the current wallpaper and the saved wallpaper info.
-  controller_->UpdateCustomWallpaperLayout(InitializeUser(account_id_1),
-                                           new_layout);
+  ClearWallpaperCount();
+  controller_->UpdateCustomWallpaperLayout(account_id_1, new_layout);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperLayout(), new_layout);
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   expected_custom_wallpaper_info.layout = new_layout;
   EXPECT_EQ(wallpaper_info, expected_custom_wallpaper_info);
 
   // Now set an online wallpaper. Verify that it's set successfully and the
   // wallpaper info is updated.
   image = CreateImage(640, 480, kWallpaperColor);
+  ClearWallpaperCount();
   controller_->SetOnlineWallpaperFromData(
-      InitializeUser(account_id_1), std::string() /*image_data=*/, kDummyUrl,
-      layout, false /*preview_mode=*/,
+      account_id_1, std::string() /*image_data=*/, kDummyUrl, layout,
+      false /*preview_mode=*/,
       WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), ONLINE);
   EXPECT_EQ(controller_->GetWallpaperLayout(), layout);
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   WallpaperInfo expected_online_wallpaper_info(
       kDummyUrl, layout, ONLINE, base::Time::Now().LocalMidnight());
   EXPECT_EQ(wallpaper_info, expected_online_wallpaper_info);
 
   // Now change the layout of the online wallpaper. Verify that it's a no-op.
-  controller_->UpdateCustomWallpaperLayout(InitializeUser(account_id_1),
-                                           new_layout);
+  ClearWallpaperCount();
+  controller_->UpdateCustomWallpaperLayout(account_id_1, new_layout);
   RunAllTasksUntilIdle();
   // The wallpaper is not updated.
   EXPECT_EQ(0, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperLayout(), layout);
   // The saved wallpaper info is not updated.
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   EXPECT_EQ(wallpaper_info, expected_online_wallpaper_info);
 }
 
@@ -1786,8 +1754,7 @@
   EXPECT_TRUE(base::PathExists(small_wallpaper_path_2));
 
   // Simulate the removal of |kUser2|.
-  controller_->RemoveUserWallpaper(InitializeUser(account_id_2),
-                                   wallpaper_files_id_2);
+  controller_->RemoveUserWallpaper(account_id_2, wallpaper_files_id_2);
   // Wait until all files under the user's custom wallpaper directory are
   // removed.
   WaitUntilCustomWallpapersDeleted(account_id_2);
@@ -1810,13 +1777,11 @@
 
   // Now login another user and set a default wallpaper for the user.
   SimulateUserLogin(kUser2);
-  controller_->SetDefaultWallpaper(InitializeUser(account_id_2),
-                                   wallpaper_files_id_2,
+  controller_->SetDefaultWallpaper(account_id_2, wallpaper_files_id_2,
                                    true /*show_wallpaper=*/);
 
   // Simulate the removal of |kUser2|.
-  controller_->RemoveUserWallpaper(InitializeUser(account_id_2),
-                                   wallpaper_files_id_2);
+  controller_->RemoveUserWallpaper(account_id_2, wallpaper_files_id_2);
 
   // Verify that the other user's wallpaper is not affected.
   EXPECT_TRUE(base::PathExists(small_wallpaper_path_1));
@@ -1833,8 +1798,7 @@
   EXPECT_FALSE(controller_->IsActiveUserWallpaperControlledByPolicy());
   // Set a policy wallpaper for the active user. Verify that the active user
   // becomes policy controlled.
-  controller_->SetPolicyWallpaper(InitializeUser(account_id_1),
-                                  wallpaper_files_id_1,
+  controller_->SetPolicyWallpaper(account_id_1, wallpaper_files_id_1,
                                   std::string() /*data=*/);
   RunAllTasksUntilIdle();
   EXPECT_TRUE(controller_->IsActiveUserWallpaperControlledByPolicy());
@@ -1955,7 +1919,8 @@
   ClearLogin();
 
   // Show the first wallpaper. Verify that the slower animation should be used.
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_TRUE(controller_->ShouldShowInitialAnimation());
   EXPECT_EQ(1, GetWallpaperCount());
@@ -1963,8 +1928,8 @@
   // Show the second wallpaper. Verify that the slower animation should not be
   // used. (Use a different user type to ensure a different wallpaper is shown,
   // otherwise requests of loading the same wallpaper are ignored.)
-  controller_->ShowUserWallpaper(
-      InitializeUser(AccountId::FromUserEmail("child@test.com")));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(AccountId::FromUserEmail("child@test.com"));
   RunAllTasksUntilIdle();
   EXPECT_FALSE(controller_->ShouldShowInitialAnimation());
   EXPECT_EQ(1, GetWallpaperCount());
@@ -1972,7 +1937,8 @@
   // Log in the user and show the wallpaper. Verify that the slower animation
   // should not be used.
   SimulateUserLogin(kUser1);
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_FALSE(controller_->ShouldShowInitialAnimation());
   EXPECT_EQ(1, GetWallpaperCount());
@@ -1989,21 +1955,23 @@
   ClearLogin();
 
   // Show the first wallpaper.
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_FALSE(controller_->ShouldShowInitialAnimation());
   EXPECT_EQ(1, GetWallpaperCount());
 
   // Show the second wallpaper.
-  controller_->ShowUserWallpaper(
-      InitializeUser(AccountId::FromUserEmail("child@test.com")));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(AccountId::FromUserEmail("child@test.com"));
   RunAllTasksUntilIdle();
   EXPECT_FALSE(controller_->ShouldShowInitialAnimation());
   EXPECT_EQ(1, GetWallpaperCount());
 
   // Log in the user and show the wallpaper.
   SimulateUserLogin(kUser1);
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_FALSE(controller_->ShouldShowInitialAnimation());
   EXPECT_EQ(1, GetWallpaperCount());
@@ -2013,15 +1981,16 @@
   // Verify the user starts with a default wallpaper and the user wallpaper info
   // is initialized with default values.
   SimulateUserLogin(kUser1);
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   WallpaperInfo user_wallpaper_info;
   WallpaperInfo default_wallpaper_info(std::string(),
                                        WALLPAPER_LAYOUT_CENTER_CROPPED, DEFAULT,
                                        base::Time::Now().LocalMidnight());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, default_wallpaper_info);
 
   // Simulate opening the wallpaper picker window.
@@ -2034,15 +2003,16 @@
   const WallpaperLayout layout = WALLPAPER_LAYOUT_CENTER;
   gfx::ImageSkia custom_wallpaper = CreateImage(640, 480, kWallpaperColor);
   EXPECT_NE(kWallpaperColor, GetWallpaperColor());
-  controller_->SetCustomWallpaper(InitializeUser(account_id_1),
-                                  wallpaper_files_id_1, file_name_1, layout,
-                                  custom_wallpaper, true /*preview_mode=*/);
+  ClearWallpaperCount();
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_1,
+                                  file_name_1, layout, custom_wallpaper,
+                                  true /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
   // Verify that the user wallpaper info remains unchanged during the preview.
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, default_wallpaper_info);
 
   // Now confirm the preview wallpaper, verify that there's no wallpaper change
@@ -2057,8 +2027,8 @@
   WallpaperInfo custom_wallpaper_info(
       base::FilePath(wallpaper_files_id_1).Append(file_name_1).value(), layout,
       CUSTOMIZED, base::Time::Now().LocalMidnight());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, custom_wallpaper_info);
 
   // Set an empty online wallpaper for the user, verify it fails.
@@ -2092,8 +2062,8 @@
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(online_wallpaper_color, GetWallpaperColor());
   // Verify that the user wallpaper info remains unchanged during the preview.
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, custom_wallpaper_info);
 
   // Now confirm the preview wallpaper, verify that there's no wallpaper change
@@ -2107,8 +2077,8 @@
   // info.
   WallpaperInfo online_wallpaper_info(kDummyUrl, layout, ONLINE,
                                       base::Time::Now().LocalMidnight());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, online_wallpaper_info);
 }
 
@@ -2116,15 +2086,16 @@
   // Verify the user starts with a default wallpaper and the user wallpaper info
   // is initialized with default values.
   SimulateUserLogin(kUser1);
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   WallpaperInfo user_wallpaper_info;
   WallpaperInfo default_wallpaper_info(std::string(),
                                        WALLPAPER_LAYOUT_CENTER_CROPPED, DEFAULT,
                                        base::Time::Now().LocalMidnight());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, default_wallpaper_info);
 
   // Simulate opening the wallpaper picker window.
@@ -2137,15 +2108,16 @@
   const WallpaperLayout layout = WALLPAPER_LAYOUT_CENTER;
   gfx::ImageSkia custom_wallpaper = CreateImage(640, 480, kWallpaperColor);
   EXPECT_NE(kWallpaperColor, GetWallpaperColor());
-  controller_->SetCustomWallpaper(InitializeUser(account_id_1),
-                                  wallpaper_files_id_1, file_name_1, layout,
-                                  custom_wallpaper, true /*preview_mode=*/);
+  ClearWallpaperCount();
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_1,
+                                  file_name_1, layout, custom_wallpaper,
+                                  true /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
   // Verify that the user wallpaper info remains unchanged during the preview.
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, default_wallpaper_info);
 
   // Now cancel the preview. Verify the wallpaper changes back to the default
@@ -2173,8 +2145,8 @@
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(online_wallpaper_color, GetWallpaperColor());
   // Verify that the user wallpaper info remains unchanged during the preview.
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, default_wallpaper_info);
 
   // Now cancel the preview. Verify the wallpaper changes back to the default
@@ -2192,15 +2164,16 @@
   // Verify the user starts with a default wallpaper and the user wallpaper info
   // is initialized with default values.
   SimulateUserLogin(kUser1);
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   WallpaperInfo user_wallpaper_info;
   WallpaperInfo default_wallpaper_info(std::string(),
                                        WALLPAPER_LAYOUT_CENTER_CROPPED, DEFAULT,
                                        base::Time::Now().LocalMidnight());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, default_wallpaper_info);
 
   // Simulate opening the wallpaper picker window.
@@ -2213,15 +2186,16 @@
   const WallpaperLayout layout = WALLPAPER_LAYOUT_CENTER;
   gfx::ImageSkia custom_wallpaper = CreateImage(640, 480, kWallpaperColor);
   EXPECT_NE(kWallpaperColor, GetWallpaperColor());
-  controller_->SetCustomWallpaper(InitializeUser(account_id_1),
-                                  wallpaper_files_id_1, file_name_1, layout,
-                                  custom_wallpaper, true /*preview_mode=*/);
+  ClearWallpaperCount();
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_1,
+                                  file_name_1, layout, custom_wallpaper,
+                                  true /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
   // Verify that the user wallpaper info remains unchanged during the preview.
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, default_wallpaper_info);
 
   // Now set another custom wallpaper for the user and disable preview (this
@@ -2232,9 +2206,9 @@
   gfx::ImageSkia synced_custom_wallpaper =
       CreateImage(640, 480, synced_custom_wallpaper_color);
   ClearWallpaperCount();
-  controller_->SetCustomWallpaper(
-      InitializeUser(account_id_1), wallpaper_files_id_2, file_name_2, layout,
-      synced_custom_wallpaper, false /*preview_mode=*/);
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_2,
+                                  file_name_2, layout, synced_custom_wallpaper,
+                                  false /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
@@ -2242,8 +2216,8 @@
   WallpaperInfo synced_custom_wallpaper_info(
       base::FilePath(wallpaper_files_id_2).Append(file_name_2).value(), layout,
       CUSTOMIZED, base::Time::Now().LocalMidnight());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, synced_custom_wallpaper_info);
 
   // Now cancel the preview. Verify the synced custom wallpaper is shown instead
@@ -2254,8 +2228,8 @@
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(synced_custom_wallpaper_color, GetWallpaperColor());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, synced_custom_wallpaper_info);
 
   // Repeat the above steps for online wallpapers: set a online wallpaper for
@@ -2273,8 +2247,8 @@
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
   // Verify that the user wallpaper info remains unchanged during the preview.
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, synced_custom_wallpaper_info);
 
   // Now set another online wallpaper for the user and disable preview. Verify
@@ -2293,8 +2267,8 @@
   // However, the user wallpaper info should already be updated to the new info.
   WallpaperInfo synced_online_wallpaper_info(kDummyUrl2, layout, ONLINE,
                                              base::Time::Now().LocalMidnight());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, synced_online_wallpaper_info);
 
   // Now cancel the preview. Verify the synced online wallpaper is shown instead
@@ -2305,8 +2279,8 @@
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(synced_online_wallpaper_color, GetWallpaperColor());
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(
-      account_id_1, &user_wallpaper_info, false /*is_ephemeral=*/));
+  EXPECT_TRUE(
+      controller_->GetUserWallpaperInfo(account_id_1, &user_wallpaper_info));
   EXPECT_EQ(user_wallpaper_info, synced_online_wallpaper_info);
 }
 
@@ -2375,9 +2349,10 @@
   SimulateUserLogin(kUser1);
   // First, set a custom wallpaper for |kUser1|. Verify the wallpaper is shown
   // successfully and the user wallpaper info is updated.
-  controller_->SetCustomWallpaper(InitializeUser(account_id_1),
-                                  wallpaper_files_id_1, file_name_1, layout,
-                                  custom_wallpaper, false /*preview_mode=*/);
+  ClearWallpaperCount();
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_1,
+                                  file_name_1, layout, custom_wallpaper,
+                                  false /*preview_mode=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
@@ -2386,8 +2361,7 @@
       base::FilePath(wallpaper_files_id_1).Append(file_name_1).value(), layout,
       WallpaperType::CUSTOMIZED, base::Time::Now().LocalMidnight());
   WallpaperInfo wallpaper_info;
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   EXPECT_EQ(expected_wallpaper_info, wallpaper_info);
 
   // Show a one-shot wallpaper. Verify it is shown successfully.
@@ -2405,11 +2379,10 @@
 
   // Verify the user wallpaper info is unaffected, and the one-shot wallpaper
   // can be replaced by the user wallpaper.
-  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
-                                                false /*is_ephemeral=*/));
+  EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info));
   EXPECT_EQ(expected_wallpaper_info, wallpaper_info);
   ClearWallpaperCount();
-  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
@@ -2443,13 +2416,17 @@
 // Although ephemeral users' custom wallpapers are not saved to disk, they
 // should be kept within the user session. Test for https://crbug.com/825237.
 TEST_F(WallpaperControllerTest, ShowWallpaperForEphemeralUser) {
-  auto initialize_ephemeral_user = [&](const AccountId& account_id) {
-    WallpaperUserInfo wallpaper_user_info = InitializeUser(account_id);
-    wallpaper_user_info.is_ephemeral = true;
-    return wallpaper_user_info;
-  };
+  // Add an ephemeral user session and simulate login, like SimulateUserLogin.
+  UserSession session;
+  session.session_id = 0;
+  session.user_info.account_id = account_id_1;
+  session.user_info.is_ephemeral = true;
+  Shell::Get()->session_controller()->UpdateUserSession(std::move(session));
+  TestSessionControllerClient* const client = GetSessionControllerClient();
+  client->ProvidePrefServiceForUser(account_id_1);
+  client->SwitchActiveUser(AccountId::FromUserEmail(kUser1));
+  client->SetSessionState(SessionState::ACTIVE);
 
-  SimulateUserLogin(kUser1);
   // The user doesn't have wallpaper cache in the beginning.
   gfx::ImageSkia cached_wallpaper;
   EXPECT_FALSE(
@@ -2457,10 +2434,11 @@
   base::FilePath path;
   EXPECT_FALSE(controller_->GetPathFromCache(account_id_1, &path));
 
-  controller_->SetCustomWallpaper(
-      initialize_ephemeral_user(account_id_1), wallpaper_files_id_1,
-      file_name_1, WALLPAPER_LAYOUT_CENTER,
-      CreateImage(640, 480, kWallpaperColor), /*preview_mode=*/false);
+  ClearWallpaperCount();
+  controller_->SetCustomWallpaper(account_id_1, wallpaper_files_id_1,
+                                  file_name_1, WALLPAPER_LAYOUT_CENTER,
+                                  CreateImage(640, 480, kWallpaperColor),
+                                  /*preview_mode=*/false);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(CUSTOMIZED, controller_->GetWallpaperType());
@@ -2476,7 +2454,8 @@
 
   // Calling |ShowUserWallpaper| will continue showing the custom wallpaper
   // instead of reverting to the default.
-  controller_->ShowUserWallpaper(initialize_ephemeral_user(account_id_1));
+  ClearWallpaperCount();
+  controller_->ShowUserWallpaper(account_id_1);
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
   EXPECT_EQ(CUSTOMIZED, controller_->GetWallpaperType());
diff --git a/ash/wm/overview/caption_container_view.cc b/ash/wm/overview/caption_container_view.cc
index b80dca8d..c4d2d50 100644
--- a/ash/wm/overview/caption_container_view.cc
+++ b/ash/wm/overview/caption_container_view.cc
@@ -264,6 +264,7 @@
   if (preview_view_) {
     gfx::Rect preview_bounds = bounds;
     preview_bounds.Inset(0, kHeaderPreferredHeightDp, 0, 0);
+    preview_bounds.ClampToCenteredSize(preview_view_->CalculatePreferredSize());
     preview_view_->SetBoundsRect(preview_bounds);
   }
 
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index eac1089..a89df4a3 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -29,7 +29,6 @@
 #include "ash/wm/splitview/split_view_constants.h"
 #include "ash/wm/splitview/split_view_utils.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
-#include "ash/wm/window_preview_view.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_transient_descendant_iterator.h"
 #include "ash/wm/wm_event.h"
@@ -781,18 +780,13 @@
   }
 
   transform_window_.UpdateMask(should_show);
-  gfx::Rect shadow_bounds =
-      gfx::ToEnclosedRect(transform_window_.GetTransformedBounds());
+  SetShadowBounds(should_show ? base::make_optional(gfx::ToEnclosedRect(
+                                    transform_window_.GetTransformedBounds()))
+                              : base::nullopt);
   if (transform_window_.IsMinimized()) {
     caption_container_view_->UpdatePreviewRoundedCorners(
         should_show, kOverviewWindowRoundingDp);
-    if (caption_container_view_->preview_view()) {
-      shadow_bounds =
-          caption_container_view_->preview_view()->GetBoundsInScreen();
-    }
   }
-  SetShadowBounds(should_show ? base::make_optional(shadow_bounds)
-                              : base::nullopt);
 }
 
 void OverviewItem::OnStartingAnimationComplete() {
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index f02a8a8..19fe5719 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -11,6 +11,7 @@
 #include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/fps_counter.h"
+#include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/tablet_mode.h"
 #include "ash/public/cpp/tablet_mode_toggle_observer.h"
 #include "ash/root_window_controller.h"
@@ -30,6 +31,9 @@
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
 #include "chromeos/dbus/power/power_manager_client.h"
+#include "components/viz/common/frame_sinks/copy_output_request.h"
+#include "components/viz/common/frame_sinks/copy_output_result.h"
+#include "third_party/khronos/GLES2/gl2.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/compositor/layer_animation_sequence.h"
 #include "ui/display/display.h"
@@ -100,6 +104,9 @@
 constexpr char kTabletModeExitHistogram[] =
     "Ash.TabletMode.AnimationSmoothness.Exit";
 
+// Set to true for unit tests so tablet mode can be changed synchronously.
+bool force_no_screenshot = false;
+
 // The angle between AccelerometerReadings are considered stable if
 // their magnitudes do not differ greatly. This returns false if the deviation
 // between the screen and keyboard accelerometers is too high.
@@ -144,6 +151,26 @@
   return sequence->properties() & ui::LayerAnimationElement::TRANSFORM;
 }
 
+std::unique_ptr<ui::Layer> CreateLayerFromScreenshotResult(
+    std::unique_ptr<viz::CopyOutputResult> copy_result) {
+  DCHECK(!copy_result->IsEmpty());
+  DCHECK_EQ(copy_result->format(), viz::CopyOutputResult::Format::RGBA_TEXTURE);
+
+  const gfx::Size layer_size = copy_result->size();
+  viz::TransferableResource transferable_resource =
+      viz::TransferableResource::MakeGL(
+          copy_result->GetTextureResult()->mailbox, GL_LINEAR, GL_TEXTURE_2D,
+          copy_result->GetTextureResult()->sync_token, layer_size,
+          /*is_overlay_candidate=*/false);
+  std::unique_ptr<viz::SingleReleaseCallback> release_callback =
+      copy_result->TakeTextureOwnership();
+  auto screenshot_layer = std::make_unique<ui::Layer>();
+  screenshot_layer->SetTransferableResource(
+      transferable_resource, std::move(release_callback), layer_size);
+
+  return screenshot_layer;
+}
+
 }  // namespace
 
 // Class which records animation smoothness when entering or exiting tablet
@@ -233,6 +260,11 @@
     observer.OnTabletControllerDestroyed();
 }
 
+// static
+void TabletModeController::SetForceNoScreenshotForTest() {
+  force_no_screenshot = true;
+}
+
 // TODO(jcliang): Hide or remove EnableTabletModeWindowManager
 // (http://crbug.com/620241).
 void TabletModeController::EnableTabletModeWindowManager(bool should_enable) {
@@ -246,38 +278,30 @@
   for (auto* root_window : Shell::Get()->GetAllRootWindows())
     RootWindowController::ForWindow(root_window)->HideContextMenu();
 
+  // Suspend occlusion tracker when entering or exiting tablet mode.
+  SuspendOcclusionTracker();
+  DeleteScreenshot();
+
   if (should_enable) {
     state_ = State::kEnteringTabletMode;
-    // Suspend occlusion tracker when entering tablet mode.
-    SuspendOcclusionTracker();
 
-    tablet_mode_window_manager_.reset(new TabletModeWindowManager());
-    tablet_mode_window_manager_->Init();
-
-    base::RecordAction(base::UserMetricsAction("Touchview_Enabled"));
-    RecordTabletModeUsageInterval(TABLET_MODE_INTERVAL_INACTIVE);
-    for (auto& observer : tablet_mode_observers_)
-      observer.OnTabletModeStarted();
-
-    // In some cases, TabletModeWindowManager::TabletModeWindowManager uses
-    // split view to represent windows that were snapped in desktop mode. If
-    // there is a window snapped on one side but no window snapped on the other
-    // side, then overview mode should be started (to be seen on the side with
-    // no snapped window).
-    const auto state = Shell::Get()->split_view_controller()->state();
-    if (state == SplitViewState::kLeftSnapped ||
-        state == SplitViewState::kRightSnapped) {
-      Shell::Get()->overview_controller()->ToggleOverview();
+    // Take a screenshot if there is a top window that will get animated.
+    // TODO(sammiequon): Handle the case where the top window is not on the
+    // primary display.
+    aura::Window* top_window = TabletModeWindowManager::GetTopWindow();
+    bool top_window_on_primary_display =
+        top_window &&
+        top_window->GetRootWindow() == Shell::GetPrimaryRootWindow();
+    if (!force_no_screenshot && top_window_on_primary_display) {
+      screenshot_set_callback_.Reset(
+          base::BindOnce(&TabletModeController::FinishInitTabletMode,
+                         weak_factory_.GetWeakPtr()));
+      TakeScreenshot(top_window, screenshot_set_callback_.callback());
+    } else {
+      FinishInitTabletMode();
     }
-
-    state_ = State::kInTabletMode;
-    if (toggle_observer_)  // Null at startup and in tests.
-      toggle_observer_->OnTabletModeToggled(true);
-    VLOG(1) << "Enter tablet mode.";
   } else {
     state_ = State::kExitingTabletMode;
-    // Suspend occlusion tracker when exiting tablet mode.
-    SuspendOcclusionTracker();
 
     tablet_mode_window_manager_->SetIgnoreWmEventsForExit();
     for (auto& observer : tablet_mode_observers_)
@@ -338,7 +362,7 @@
 }
 
 void TabletModeController::MaybeObserveBoundsAnimation(aura::Window* window) {
-  StopObservingAnimation(/*record_stats=*/false);
+  StopObservingAnimation(/*record_stats=*/false, /*delete_screenshot=*/false);
 
   if (state_ != State::kEnteringTabletMode &&
       state_ != State::kExitingTabletMode) {
@@ -351,7 +375,8 @@
   observed_layer_->GetAnimator()->AddObserver(this);
 }
 
-void TabletModeController::StopObservingAnimation(bool record_stats) {
+void TabletModeController::StopObservingAnimation(bool record_stats,
+                                                  bool delete_screenshot) {
   StopObserving();
 
   if (observed_layer_)
@@ -363,6 +388,9 @@
   if (record_stats && fps_counter_)
     fps_counter_->LogUma();
   fps_counter_.reset();
+
+  if (delete_screenshot)
+    DeleteScreenshot();
 }
 
 void TabletModeController::SetTabletModeToggleObserver(
@@ -580,7 +608,7 @@
   if (!fps_counter_ || !IsTransformAnimationSequence(sequence))
     return;
 
-  StopObservingAnimation(/*record_stats=*/false);
+  StopObservingAnimation(/*record_stats=*/false, /*delete_screenshot=*/true);
 }
 
 void TabletModeController::OnLayerAnimationEnded(
@@ -588,7 +616,7 @@
   if (!fps_counter_ || !IsTransformAnimationSequence(sequence))
     return;
 
-  StopObservingAnimation(/*record_stats=*/true);
+  StopObservingAnimation(/*record_stats=*/true, /*delete_screenshot=*/true);
 }
 
 void TabletModeController::OnLayerAnimationScheduled(
@@ -605,12 +633,12 @@
   // If another animation is scheduled while the animation we were originally
   // watching is still animating, abort and do not log stats as the stats will
   // not be accurate.
-  StopObservingAnimation(/*record_stats=*/false);
+  StopObservingAnimation(/*record_stats=*/false, /*delete_screenshot=*/true);
 }
 
 void TabletModeController::OnWindowDestroying(aura::Window* window) {
   DCHECK_EQ(observed_window_, window);
-  StopObservingAnimation(/*record_stats=*/false);
+  StopObservingAnimation(/*record_stats=*/false, /*delete_screenshot=*/true);
 }
 
 void TabletModeController::HandleHingeRotation(
@@ -882,4 +910,89 @@
   occlusion_tracker_pauser_.reset();
 }
 
+void TabletModeController::FinishInitTabletMode() {
+  tablet_mode_window_manager_.reset(new TabletModeWindowManager());
+  tablet_mode_window_manager_->Init();
+
+  base::RecordAction(base::UserMetricsAction("Touchview_Enabled"));
+  RecordTabletModeUsageInterval(TABLET_MODE_INTERVAL_INACTIVE);
+  for (auto& observer : tablet_mode_observers_)
+    observer.OnTabletModeStarted();
+
+  // In some cases, TabletModeWindowManager::TabletModeWindowManager uses
+  // split view to represent windows that were snapped in desktop mode. If
+  // there is a window snapped on one side but no window snapped on the other
+  // side, then overview mode should be started (to be seen on the side with
+  // no snapped window).
+  const auto state = Shell::Get()->split_view_controller()->state();
+  if (state == SplitViewState::kLeftSnapped ||
+      state == SplitViewState::kRightSnapped) {
+    Shell::Get()->overview_controller()->ToggleOverview();
+  }
+
+  state_ = State::kInTabletMode;
+  if (toggle_observer_)  // Null at startup and in tests.
+    toggle_observer_->OnTabletModeToggled(true);
+  VLOG(1) << "Enter tablet mode.";
+}
+
+void TabletModeController::DeleteScreenshot() {
+  screenshot_layer_.reset();
+  screenshot_taken_callback_.Cancel();
+  screenshot_set_callback_.Cancel();
+}
+
+void TabletModeController::TakeScreenshot(
+    aura::Window* top_window,
+    base::OnceClosure on_screenshot_taken) {
+  DCHECK(top_window);
+  DCHECK(!top_window->IsRootWindow());
+
+  auto* screenshot_window = top_window->GetRootWindow()->GetChildById(
+      kShellWindowId_ScreenRotationContainer);
+
+  // Pause the compositor and hide the top window before taking a screenshot.
+  // Use opacity zero instead of show/hide to preserve MRU ordering.
+  const auto roots = Shell::GetAllRootWindows();
+  for (auto* root : roots)
+    root->GetHost()->compositor()->SetAllowLocksToExtendTimeout(true);
+  top_window->layer()->SetOpacity(0.f);
+
+  // Request a screenshot.
+  screenshot_taken_callback_.Reset(base::BindOnce(
+      &TabletModeController::OnScreenshotTaken, weak_factory_.GetWeakPtr(),
+      top_window, std::move(on_screenshot_taken)));
+  const gfx::Rect request_bounds(screenshot_window->layer()->size());
+  auto screenshot_request = std::make_unique<viz::CopyOutputRequest>(
+      viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
+      screenshot_taken_callback_.callback());
+  screenshot_request->set_area(request_bounds);
+  screenshot_window->layer()->RequestCopyOfOutput(
+      std::move(screenshot_request));
+
+  top_window->layer()->SetOpacity(1.f);
+  for (auto* root : roots)
+    root->GetHost()->compositor()->SetAllowLocksToExtendTimeout(false);
+}
+
+void TabletModeController::OnScreenshotTaken(
+    aura::Window* top_window,
+    base::OnceClosure on_screenshot_taken,
+    std::unique_ptr<viz::CopyOutputResult> copy_result) {
+  if (!copy_result || copy_result->IsEmpty()) {
+    std::move(on_screenshot_taken).Run();
+    return;
+  }
+
+  // Stack the screenshot under |top_window|, to fully occlude all windows
+  // except |top_window| for the duration of the enter tablet mode animation.
+  screenshot_layer_ = CreateLayerFromScreenshotResult(std::move(copy_result));
+  top_window->parent()->layer()->Add(screenshot_layer_.get());
+  screenshot_layer_->SetBounds(top_window->GetRootWindow()->bounds());
+  top_window->parent()->layer()->StackBelow(screenshot_layer_.get(),
+                                            top_window->layer());
+
+  std::move(on_screenshot_taken).Run();
+}
+
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.h b/ash/wm/tablet_mode/tablet_mode_controller.h
index b7ecfc2..a2d1425 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.h
+++ b/ash/wm/tablet_mode/tablet_mode_controller.h
@@ -50,6 +50,10 @@
 class Widget;
 }
 
+namespace viz {
+class CopyOutputResult;
+}
+
 namespace ash {
 
 class InternalInputDevicesEventBlocker;
@@ -87,6 +91,10 @@
   TabletModeController();
   ~TabletModeController() override;
 
+  // Disables taking screenshots for testing as it makes the initialization flow
+  // async.
+  static void SetForceNoScreenshotForTest();
+
   // TODO(jonross): Merge this with AttemptEnterTabletMode. Currently these are
   // separate for several reasons: there is no internal display when running
   // unittests; the event blocker prevents keyboard input when running ChromeOS
@@ -121,15 +129,15 @@
   // Starts observing |window| for animation changes.
   void MaybeObserveBoundsAnimation(aura::Window* window);
 
+  // Stops observing the window which is being animated from tablet <->
+  // clamshell.
+  void StopObservingAnimation(bool record_stats, bool delete_screenshot);
+
   // TabletMode:
   void SetTabletModeToggleObserver(TabletModeToggleObserver* observer) override;
   bool IsEnabled() const override;
   void SetEnabledForTest(bool enabled) override;
 
-  // Stops observing the window which is being animated from tablet <->
-  // clamshell.
-  void StopObservingAnimation(bool record_stats);
-
   // ShellObserver:
   void OnShellInitialized() override;
 
@@ -271,6 +279,24 @@
   // Resets |occlusion_tracker_pauser_|.
   void ResetPauser();
 
+  // Deletes the enter tablet mode screenshot and associated callbacks.
+  void DeleteScreenshot();
+
+  // Finishes initializing for tablet mode. May be called async if a screenshot
+  // was requested while starting initializing.
+  void FinishInitTabletMode();
+
+  // Takes a screenshot of everything in the rotation container, except for
+  // |top_window|.
+  void TakeScreenshot(aura::Window* top_window,
+                      base::OnceClosure on_screenshot_taken);
+
+  // Called when a screenshot is taken. Creates |screenshot_widget_| which holds
+  // the screenshot results and stacks it under |top_window|.
+  void OnScreenshotTaken(aura::Window* top_window,
+                         base::OnceClosure on_screenshot_taken,
+                         std::unique_ptr<viz::CopyOutputResult> copy_result);
+
   // The maximized window manager (if enabled).
   std::unique_ptr<TabletModeWindowManager> tablet_mode_window_manager_;
 
@@ -373,6 +399,17 @@
 
   std::unique_ptr<TabletModeTransitionFpsCounter> fps_counter_;
 
+  base::CancelableOnceCallback<void(std::unique_ptr<viz::CopyOutputResult>)>
+      screenshot_taken_callback_;
+  base::CancelableOnceClosure screenshot_set_callback_;
+
+  // A layer that is created before an enter tablet mode animations is started,
+  // and destroyed when the animation is ended. It contains a screenshot of
+  // everything in the screen rotation container except the top window. It helps
+  // with animation performance because it fully occludes all windows except the
+  // animating window for the duration of the animation.
+  std::unique_ptr<ui::Layer> screenshot_layer_;
+
   base::ObserverList<TabletModeObserver>::Unchecked tablet_mode_observers_;
 
   base::WeakPtrFactory<TabletModeController> weak_factory_{this};
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.cc b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
index 41d2adf..4595d50 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
@@ -106,7 +106,7 @@
       return;
 
     Shell::Get()->tablet_mode_controller()->StopObservingAnimation(
-        /*record_stats=*/false);
+        /*record_stats=*/false, /*delete_screenshot=*/true);
   }
 
  private:
@@ -117,6 +117,14 @@
 
 TabletModeWindowManager::~TabletModeWindowManager() = default;
 
+// static
+aura::Window* TabletModeWindowManager::GetTopWindow() {
+  MruWindowTracker::WindowList windows =
+      Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kActiveDesk);
+
+  return windows.empty() ? nullptr : windows[0];
+}
+
 void TabletModeWindowManager::Init() {
   // The overview mode needs to be ended before the tablet mode is started. To
   // guarantee the proper order, it will be turned off from here.
@@ -187,13 +195,6 @@
     window_state_map_.erase(it);
 }
 
-aura::Window* TabletModeWindowManager::GetTopWindow() {
-  MruWindowTracker::WindowList windows =
-      Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kActiveDesk);
-
-  return windows.empty() ? nullptr : windows[0];
-}
-
 void TabletModeWindowManager::OnOverviewModeEndingAnimationComplete(
     bool canceled) {
   if (canceled)
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.h b/ash/wm/tablet_mode/tablet_mode_window_manager.h
index 7cbfd3b..780b8bf 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.h
@@ -50,6 +50,8 @@
   // This should only be deleted by the creator (TabletModeController).
   ~TabletModeWindowManager() override;
 
+  static aura::Window* GetTopWindow();
+
   void Init();
 
   // Stops tracking windows and returns them to their clamshell mode state. Work
@@ -72,8 +74,6 @@
   // Called from a window state object when it gets destroyed.
   void WindowStateDestroyed(aura::Window* window);
 
-  aura::Window* GetTopWindow();
-
   // OverviewObserver:
   void OnOverviewModeEndingAnimationComplete(bool canceled) override;
 
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.cc b/ash/wm/tablet_mode/tablet_mode_window_state.cc
index eafa160..6cc5bcc 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.cc
@@ -153,6 +153,12 @@
          window;
 }
 
+// True if |window| is the top window in BuildWindowForCycleList.
+bool IsTopWindow(aura::Window* window) {
+  DCHECK(window);
+  return window == TabletModeWindowManager::GetTopWindow();
+}
+
 }  // namespace
 
 // static
@@ -479,9 +485,4 @@
   }
 }
 
-bool TabletModeWindowState::IsTopWindow(aura::Window* window) {
-  DCHECK(window);
-  return window == creator_->GetTopWindow();
-}
-
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.h b/ash/wm/tablet_mode/tablet_mode_window_state.h
index 4c8e8e88..edbe9b99 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.h
@@ -79,9 +79,6 @@
   // window state. If |animated| is set we animate the change.
   void UpdateBounds(wm::WindowState* window_state, bool animated);
 
-  // True if |window| is the top window in BuildWindowForCycleList.
-  bool IsTopWindow(aura::Window* window);
-
   // The original state object of the window.
   std::unique_ptr<wm::WindowState::State> old_state_;
 
diff --git a/ash/wm/window_resizer.cc b/ash/wm/window_resizer.cc
index c9ea3c8..026d19f 100644
--- a/ash/wm/window_resizer.cc
+++ b/ash/wm/window_resizer.cc
@@ -85,7 +85,10 @@
   DCHECK(window_state_->drag_details());
 }
 
-WindowResizer::~WindowResizer() = default;
+WindowResizer::~WindowResizer() {
+  if (destroyed_)
+    *destroyed_ = true;
+}
 
 // static
 int WindowResizer::GetBoundsChangeForWindowComponent(int component) {
@@ -269,14 +272,20 @@
 void WindowResizer::SetBoundsDuringResize(const gfx::Rect& bounds) {
   aura::Window* window = GetTarget();
   DCHECK(window);
-  const gfx::Rect original_bounds = window->bounds();
-  window->SetBounds(bounds);
+  bool destroyed = false;
+  destroyed_ = &destroyed;
   aura::WindowTracker tracker;
   tracker.Add(window);
+
+  const gfx::Rect original_bounds = window->bounds();
+  window->SetBounds(bounds);
+
   if (tracker.windows().empty())
     return;  // Assume we've been destroyed.
   if (bounds.size() == original_bounds.size())
     return;
+  CHECK(!destroyed);
+  destroyed_ = nullptr;
   recorder_->RequestNext();
 }
 
diff --git a/ash/wm/window_resizer.h b/ash/wm/window_resizer.h
index 35e9899..6545519 100644
--- a/ash/wm/window_resizer.h
+++ b/ash/wm/window_resizer.h
@@ -110,6 +110,8 @@
   // Updates |new_bounds| to adhere to the aspect ratio.
   void CalculateBoundsWithAspectRatio(float aspect_ratio,
                                       gfx::Rect* new_bounds);
+  // Remove once it is confirmed that crbug.com/970911 is fixed.
+  bool* destroyed_ = nullptr;
 
   std::unique_ptr<PresentationTimeRecorder> recorder_;
 
diff --git a/base/task/thread_pool/thread_pool_impl_unittest.cc b/base/task/thread_pool/thread_pool_impl_unittest.cc
index 48f2686..ca72741 100644
--- a/base/task/thread_pool/thread_pool_impl_unittest.cc
+++ b/base/task/thread_pool/thread_pool_impl_unittest.cc
@@ -767,6 +767,12 @@
   // Give a chance for the file watcher to fire before closing the handles.
   PlatformThread::Sleep(TestTimeouts::tiny_timeout());
 
+  // Need to join the ServiceThread before closing the pipes as the pipe
+  // becoming readable is observed on the ServiceThread and TSAN correctly
+  // identifies this as a race even though the above sleep makes it highly
+  // unlikely in practice.
+  TearDown();
+
   EXPECT_EQ(0, IGNORE_EINTR(close(pipes[0])));
   EXPECT_EQ(0, IGNORE_EINTR(close(pipes[1])));
 }
diff --git a/base/values.h b/base/values.h
index 3d034cd6..ef24b19 100644
--- a/base/values.h
+++ b/base/values.h
@@ -167,7 +167,7 @@
   bool is_dict() const { return type() == Type::DICTIONARY; }
   bool is_list() const { return type() == Type::LIST; }
 
-  // These will all CHECK if the type doesn't match.
+  // These will all CHECK that the type matches.
   bool GetBool() const;
   int GetInt() const;
   double GetDouble() const;  // Implicitly converts from int if necessary.
@@ -181,7 +181,7 @@
   // a pointer to the element. Otherwise it returns nullptr.
   // returned. Callers are expected to perform a check against null before using
   // the pointer.
-  // Note: This CHECKs if type() is not Type::DICTIONARY.
+  // Note: This CHECKs that type() is Type::DICTIONARY.
   //
   // Example:
   //   auto* found = FindKey("foo");
@@ -193,7 +193,7 @@
   // different type nullptr is returned.
   // Callers are expected to perform a check against null before using the
   // pointer.
-  // Note: This CHECKs if type() is not Type::DICTIONARY.
+  // Note: This CHECKs that type() is Type::DICTIONARY.
   //
   // Example:
   //   auto* found = FindKey("foo", Type::DOUBLE);
@@ -226,7 +226,7 @@
   // |SetKey| looks up |key| in the underlying dictionary and sets the mapped
   // value to |value|. If |key| could not be found, a new element is inserted.
   // A pointer to the modified item is returned.
-  // Note: This CHECKs if type() is not Type::DICTIONARY.
+  // Note: This CHECKs that type() is Type::DICTIONARY.
   // Note: Prefer Set<Type>Key() for simple values.
   //
   // Example:
@@ -255,7 +255,7 @@
   // failure, e.g. the key does not exist, false is returned and the underlying
   // dictionary is not changed. In case of success, |key| is deleted from the
   // dictionary and the method returns true.
-  // Note: This CHECKs if type() is not Type::DICTIONARY.
+  // Note: This CHECKs that type() is Type::DICTIONARY.
   //
   // Example:
   //   bool success = dict.RemoveKey("foo");
@@ -265,7 +265,7 @@
   // failure, e.g. the key does not exist, nullopt is returned and the
   // underlying dictionary is not changed. In case of success, |key| is deleted
   // from the dictionary and the method returns the extracted Value.
-  // Note: This CHECKs if type() is not Type::DICTIONARY.
+  // Note: This CHECKs that type() is Type::DICTIONARY.
   //
   // Example:
   //   Optional<Value> maybe_value = dict.ExtractKey("foo");
@@ -398,12 +398,12 @@
   // dictionary. These are intended for iteration over all items in the
   // dictionary and are compatible with for-each loops and standard library
   // algorithms.
-  // Note: These CHECK if type() is not Type::DICTIONARY.
+  // Note: These CHECK that type() is Type::DICTIONARY.
   dict_iterator_proxy DictItems();
   const_dict_iterator_proxy DictItems() const;
 
   // Returns the size of the dictionary, and if the dictionary is empty.
-  // Note: These CHECK if type() is not Type::DICTIONARY.
+  // Note: These CHECK that type() is Type::DICTIONARY.
   size_t DictSize() const;
   bool DictEmpty() const;
 
@@ -412,7 +412,7 @@
   // passed in dictionary takes precedence and data already present will be
   // replaced. Values within |dictionary| are deep-copied, so |dictionary| may
   // be freed any time after this call.
-  // Note: This CHECKs if type() or dictionary->type() is not Type::DICTIONARY.
+  // Note: This CHECKs that type() and dictionary->type() is Type::DICTIONARY.
   void MergeDictionary(const Value* dictionary);
 
   // These methods allow the convenient retrieval of the contents of the Value.
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index b6e0785..e3a7493 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -428,12 +428,15 @@
     flags_to_add = []
     test_timeout_scale = None
     if self._test_instance.coverage_directory:
-      coverage_basename = '%s.exec' % ('%s_group' % test[0]['method']
-                                       if isinstance(test, list) else
-                                       test['method'])
+      coverage_basename = '%s.exec' % (
+          '%s_%s_group' % (test[0]['class'], test[0]['method']) if isinstance(
+              test, list) else '%s_%s' % (test['class'], test['method']))
       extras['coverage'] = 'true'
       coverage_directory = os.path.join(
           device.GetExternalStoragePath(), 'chrome', 'test', 'coverage')
+      if not device.PathExists(coverage_directory):
+        device.RunShellCommand(['mkdir', '-p', coverage_directory],
+                               check_return=True)
       coverage_device_file = os.path.join(
           coverage_directory, coverage_basename)
       extras['coverageFile'] = coverage_device_file
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index 6139e6c..966b26e1 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -168,10 +168,6 @@
     // http://crbug.com/927330
     "race:net::(anonymous namespace)::g_network_change_notifier\n"
 
-    // https://crbug.com/965717
-    "race:base::internal::ThreadPoolImplTest_"
-    "FileDescriptorWatcherNoOpsAfterShutdown_Test::TestBody\n"
-
     // https://crbug.com/965722
     "race:content::(anonymous namespace)::CorruptDBRequestHandler\n"
 
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn
index 478a987..f0625d42 100644
--- a/build/toolchain/win/BUILD.gn
+++ b/build/toolchain/win/BUILD.gn
@@ -337,82 +337,27 @@
   }
 }
 
-if (target_cpu == "x86" || target_cpu == "x64") {
-  win_build_host_cpu = target_cpu
-} else {
-  win_build_host_cpu = host_cpu
-}
-
-# x86, arm and arm64 build cpu toolchains for Windows (not WinUWP). Only
-# define when the build cpu is one of these architectures since we don't
-# do any cross compiles when targeting x64-bit (the build does generate
-# some 64-bit stuff from x86/arm/arm64 target builds).
-if (win_build_host_cpu != "x64") {
-  build_cpu_toolchain_data = exec_script("setup_toolchain.py",
-                                         [
-                                           visual_studio_path,
-                                           windows_sdk_path,
-                                           visual_studio_runtime_dirs,
-                                           host_os,
-                                           win_build_host_cpu,
-                                           "environment." + win_build_host_cpu,
-                                         ],
-                                         "scope")
-
-  msvc_toolchain(win_build_host_cpu) {
-    environment = "environment." + win_build_host_cpu
-    cl = "${goma_prefix}\"${build_cpu_toolchain_data.vc_bin_dir}/cl.exe\""
-    if (host_os != "win") {
-      # For win cross build.
-      sys_lib_flags = "${build_cpu_toolchain_data.libpath_flags}"
-    }
-    toolchain_args = {
-      current_os = "win"
-      current_cpu = win_build_host_cpu
-      is_clang = false
-    }
-  }
-
-  msvc_toolchain("win_clang_" + win_build_host_cpu) {
-    environment = "environment." + win_build_host_cpu
-    prefix = rebase_path("$clang_base_path/bin", root_build_dir)
-    cl = "${clang_prefix}$prefix/${clang_cl}"
-    sys_include_flags = "${build_cpu_toolchain_data.include_flags_imsvc}"
-    if (host_os != "win") {
-      # For win cross build.
-      sys_lib_flags = "${build_cpu_toolchain_data.libpath_flags}"
-    }
-
-    toolchain_args = {
-      current_os = "win"
-      current_cpu = win_build_host_cpu
-      is_clang = true
-    }
-  }
-}
-
-# 64-bit toolchains, including x64 and arm64.
-template("win_64bit_toolchains") {
+template("win_toolchains") {
   assert(defined(invoker.toolchain_arch))
   toolchain_arch = invoker.toolchain_arch
 
-  win_64bit_toolchain_data = exec_script("setup_toolchain.py",
-                                         [
-                                           visual_studio_path,
-                                           windows_sdk_path,
-                                           visual_studio_runtime_dirs,
-                                           "win",
-                                           toolchain_arch,
-                                           "environment." + toolchain_arch,
-                                         ],
-                                         "scope")
+  win_toolchain_data = exec_script("setup_toolchain.py",
+                                   [
+                                     visual_studio_path,
+                                     windows_sdk_path,
+                                     visual_studio_runtime_dirs,
+                                     "win",
+                                     toolchain_arch,
+                                     "environment." + toolchain_arch,
+                                   ],
+                                   "scope")
 
   msvc_toolchain(target_name) {
     environment = "environment." + toolchain_arch
-    cl = "${goma_prefix}\"${win_64bit_toolchain_data.vc_bin_dir}/cl.exe\""
+    cl = "${goma_prefix}\"${win_toolchain_data.vc_bin_dir}/cl.exe\""
     if (host_os != "win") {
       # For win cross build
-      sys_lib_flags = "${win_64bit_toolchain_data.libpath_flags}"
+      sys_lib_flags = "${win_toolchain_data.libpath_flags}"
     }
 
     toolchain_args = {
@@ -429,10 +374,10 @@
     environment = "environment." + toolchain_arch
     prefix = rebase_path("$clang_base_path/bin", root_build_dir)
     cl = "${clang_prefix}$prefix/${clang_cl}"
-    sys_include_flags = "${win_64bit_toolchain_data.include_flags_imsvc}"
+    sys_include_flags = "${win_toolchain_data.include_flags_imsvc}"
     if (host_os != "win") {
       # For win cross build
-      sys_lib_flags = "${win_64bit_toolchain_data.libpath_flags}"
+      sys_lib_flags = "${win_toolchain_data.libpath_flags}"
     }
 
     toolchain_args = {
@@ -446,14 +391,22 @@
   }
 }
 
-win_64bit_toolchains("x64") {
-  toolchain_arch = "x64"
+if (target_cpu == "x86" || target_cpu == "x64") {
+  win_toolchains("x86") {
+    toolchain_arch = "x86"
+  }
+  win_toolchains("x64") {
+    toolchain_arch = "x64"
+  }
 }
 
 if (target_cpu == "arm64") {
-  win_64bit_toolchains("arm64") {
+  win_toolchains("arm64") {
     toolchain_arch = "arm64"
   }
+  win_toolchains(host_cpu) {
+    toolchain_arch = host_cpu
+  }
 }
 
 # The nacl_win64 toolchain is nearly identical to the plain x64 toolchain.
@@ -461,7 +414,7 @@
 # The only reason it's a separate toolchain is so that it can force
 # is_component_build to false in the toolchain_args() block, because
 # building nacl64.exe in component style does not work.
-win_64bit_toolchains("nacl_win64") {
+win_toolchains("nacl_win64") {
   toolchain_arch = "x64"
   toolchain_args = {
     is_component_build = false
diff --git a/build/toolchain/win/midl.py b/build/toolchain/win/midl.py
index 618252a..badd287 100644
--- a/build/toolchain/win/midl.py
+++ b/build/toolchain/win/midl.py
@@ -157,7 +157,8 @@
   # Copy checked-in outputs to final location.
   THIS_DIR = os.path.abspath(os.path.dirname(__file__))
   source = os.path.join(THIS_DIR, '..', '..', '..',
-      'third_party', 'win_build_output', outdir.replace('gen/', 'midl/'))
+      'third_party', 'win_build_output',
+      re.sub(r'(^[^/]+/)?gen/', 'midl/', outdir))
   if os.path.isdir(os.path.join(source, os.path.basename(idl))):
     source = os.path.join(source, os.path.basename(idl))
   source = os.path.join(source, arch.split('.')[1])  # Append 'x86' or 'x64'.
diff --git a/build/util/generate_wrapper.py b/build/util/generate_wrapper.py
index 5373e1e..339e15e 100755
--- a/build/util/generate_wrapper.py
+++ b/build/util/generate_wrapper.py
@@ -71,8 +71,10 @@
     def main(raw_args):
       executable_path = ExpandWrappedPath('{executable_path}')
       executable_args = ExpandWrappedPaths({executable_args})
-
-      return subprocess.call([executable_path] + executable_args + raw_args)
+      cmd = [executable_path] + executable_args + raw_args
+      if executable_path.endswith('.py'):
+        cmd = [sys.executable] + cmd
+      return subprocess.call(cmd)
 
 
     if __name__ == '__main__':
@@ -119,7 +121,7 @@
   parser.add_argument(
       '--script-language',
       choices=SCRIPT_TEMPLATES.keys(),
-      help='Language in which the warpper script will be written.')
+      help='Language in which the wrapper script will be written.')
   parser.add_argument(
       'executable_args', nargs='*',
       help='Arguments to wrap into the executable.')
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 228c7a2..69e6754 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -703,6 +703,7 @@
 }
 
 process_version("chrome_version_constants") {
+  process_only = true
   template_file = "java/ChromeVersionConstants.java.version"
   sources = [
     branding_file_path,
@@ -1065,6 +1066,7 @@
 version_resource_dir = "$target_gen_dir/templates/chrome_version_xml/res"
 verson_resource_file = "$version_resource_dir/values/strings.xml"
 process_version("version_xml") {
+  process_only = true
   template_file = "java/version_strings.xml.template"
   sources = [
     "//chrome/VERSION",
@@ -1160,7 +1162,7 @@
 
   # Include appropriate factories for native feature modules if necessary.
   if (enable_vr) {
-    if (modularize_vr_native) {
+    if (use_native_modules && modularize_vr_native) {
       deps += [ "//chrome/browser/android/vr:ui_module_factory" ]
     } else {
       deps += [ "//chrome/browser/android/vr:ui_module_dummy_factory" ]
@@ -1418,7 +1420,7 @@
 
     # Include appropriate factories for native feature modules if necessary.
     if (enable_vr) {
-      if (modularize_vr_native) {
+      if (use_native_modules && modularize_vr_native) {
         deps += [ "//chrome/browser/android/vr:ui_module_factory" ]
       } else {
         deps += [ "//chrome/browser/android/vr:ui_module_dummy_factory" ]
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
index f8713ad..821676d 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
@@ -123,8 +123,8 @@
         // Set children top margins to have a spacing between them.
         int childSpacing = activity.getResources().getDimensionPixelSize(
                 R.dimen.autofill_assistant_bottombar_vertical_spacing);
-        int suggestionsVerticalInset =
-                activity.getResources().getDimensionPixelSize(R.dimen.chip_bg_vertical_inset);
+        int suggestionsVerticalInset = activity.getResources().getDimensionPixelSize(
+                org.chromium.chrome.R.dimen.chip_bg_vertical_inset);
         setChildMarginTop(mDetailsCoordinator.getView(), childSpacing);
         setChildMarginTop(mPaymentRequestCoordinator.getView(), childSpacing);
         setChildMarginTop(mFormCoordinator.getView(), childSpacing);
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantPeekHeightCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantPeekHeightCoordinator.java
index febad9a..2141f70 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantPeekHeightCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantPeekHeightCoordinator.java
@@ -88,8 +88,8 @@
                 R.dimen.autofill_assistant_progress_bar_height);
         mChildrenVerticalSpacing = context.getResources().getDimensionPixelSize(
                 R.dimen.autofill_assistant_bottombar_vertical_spacing);
-        mSuggestionsVerticalInset =
-                context.getResources().getDimensionPixelSize(R.dimen.chip_bg_vertical_inset);
+        mSuggestionsVerticalInset = context.getResources().getDimensionPixelSize(
+                org.chromium.chrome.R.dimen.chip_bg_vertical_inset);
 
         // Show only actions if we are in the peek state and peek mode is HANDLE_HEADER_CAROUSELS.
         bottomSheet.addObserver(new EmptyBottomSheetObserver() {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java
index 8ba2a7c..0a26c60c 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java
@@ -53,15 +53,14 @@
         // add this space above and below each chip, and remove the vertical inset added to all
         // ButtonView's.
         mView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                context.getResources().getDimensionPixelSize(R.dimen.min_touch_target_size)
+                context.getResources().getDimensionPixelSize(
+                        org.chromium.chrome.R.dimen.min_touch_target_size)
                         + 2
                                 * context.getResources().getDimensionPixelSize(
-                                        org.chromium.chrome.autofill_assistant.R.dimen
-                                                .autofill_assistant_bottombar_vertical_spacing)
+                                        R.dimen.autofill_assistant_bottombar_vertical_spacing)
                         - 2
                                 * context.getResources().getDimensionPixelSize(
-                                        org.chromium.chrome.autofill_assistant.R.dimen
-                                                .autofill_assistant_button_bg_vertical_inset)));
+                                        R.dimen.autofill_assistant_button_bg_vertical_inset)));
 
         mView.setAdapter(new RecyclerViewAdapter<>(
                 new SimpleRecyclerViewMcp<>(model.getChipsModel(),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
index 1dded26..e70f695 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -12,6 +12,10 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
 import org.chromium.chrome.browser.ntp.snippets.CategoryInt;
@@ -55,6 +59,13 @@
 
     private final RemoteSuggestionsStatusObserver mRemoteSuggestionsStatusObserver;
 
+    // Used to track if the NTP has loaded or not. This assumes that there's only one
+    // NewTabPageAdapter per NTP. This does not fully tear down even when the main activity is
+    // destroyed. This is actually convenient in mimicking the current behavior of
+    // FeedProcessScopeFactory which comparing to is motivation behind experimenting with this
+    // delay.
+    private static boolean sHasLoadedBefore;
+
     /**
      * Creates the adapter that will manage all the cards to display on the NTP.
      * @param uiDelegate used to interact with the rest of the system.
@@ -77,10 +88,25 @@
         mSections = new SectionList(mUiDelegate, offlinePageBridge);
 
         if (mAboveTheFoldView != null) mRoot.addChildren(new AboveTheFoldItem());
-        mRoot.addChildren(mSections);
-
         mFooter = new Footer();
-        mRoot.addChildren(mFooter);
+
+        int sectionDelay = ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
+                ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS,
+                "artificial_legacy_ntp_delay_ms", 0);
+        Runnable addSectionAndFooter = () -> {
+            mRoot.addChildren(mSections);
+            mRoot.addChildren(mFooter);
+        };
+        if (sectionDelay <= 0 || sHasLoadedBefore) {
+            addSectionAndFooter.run();
+            RecordHistogram.recordBooleanHistogram(
+                    "NewTabPage.ContentSuggestions.ArtificialDelay", false);
+        } else {
+            PostTask.postDelayedTask(TaskTraits.USER_BLOCKING, addSectionAndFooter, sectionDelay);
+            RecordHistogram.recordBooleanHistogram(
+                    "NewTabPage.ContentSuggestions.ArtificialDelay", true);
+        }
+        sHasLoadedBefore = true;
 
         mOfflinePageBridge = offlinePageBridge;
 
@@ -296,6 +322,11 @@
         return mRemoteSuggestionsStatusObserver;
     }
 
+    @VisibleForTesting
+    static void setHasLoadedBeforeForTest(boolean value) {
+        sHasLoadedBefore = value;
+    }
+
     private class RemoteSuggestionsStatusObserver
             extends SuggestionsSource.EmptyObserver implements DestructionObserver {
         public RemoteSuggestionsStatusObserver() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
index 7017e61..ed5f1b06 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -4,7 +4,9 @@
 
 package org.chromium.chrome.browser.ntp.cards;
 
+import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -46,10 +48,13 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+import org.robolectric.shadows.ShadowLooper;
 import org.robolectric.shadows.ShadowResources;
 
 import org.chromium.base.Callback;
 import org.chromium.base.task.test.CustomShadowAsyncTask;
+import org.chromium.base.task.test.ShadowPostTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
@@ -93,7 +98,9 @@
  * the need for {@link CustomShadowAsyncTask}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {CustomShadowAsyncTask.class})
+@Config(manifest = Config.NONE,
+        shadows = {CustomShadowAsyncTask.class, ShadowPostTask.class,
+                NewTabPageAdapterTest.ShadowChromeFeatureList.class})
 @DisableFeatures({ChromeFeatureList.CONTENT_SUGGESTIONS_SCROLL_TO_LOAD,
         ChromeFeatureList.CHROME_DUET, ChromeFeatureList.UNIFIED_CONSENT})
 public class NewTabPageAdapterTest {
@@ -176,6 +183,7 @@
      */
     private static class ItemsMatcher { // TODO(pke): Find better name.
         private final List<String> mExpectedDescriptions = new ArrayList<>();
+        private final List<String> mExpectedNotDescriptions = new ArrayList<>();
         private final List<String> mActualDescriptions = new ArrayList<>();
 
         public ItemsMatcher(RecyclerViewAdapter.Delegate root) {
@@ -188,6 +196,10 @@
             mExpectedDescriptions.add(description);
         }
 
+        private void expectNotDescription(String description) {
+            mExpectedNotDescriptions.add(description);
+        }
+
         public void expectSection(SectionDescriptor descriptor) {
             if (descriptor.mHeader) {
                 expectDescription("HEADER");
@@ -229,8 +241,35 @@
             expectDescription("FOOTER");
         }
 
+        public void expectNotFooter() {
+            expectNotDescription("FOOTER");
+        }
+
         public void finish() {
             assertThat(mActualDescriptions, is(mExpectedDescriptions));
+            for (final String notDescriptions : mExpectedNotDescriptions) {
+                assertThat(mActualDescriptions, not(contains(notDescriptions)));
+            }
+        }
+    }
+
+    @Implements(ChromeFeatureList.class)
+    static class ShadowChromeFeatureList {
+        private static Integer sValue;
+
+        @Resetter
+        public static void reset() {
+            sValue = null;
+        }
+
+        @Implementation
+        public static int getFieldTrialParamByFeatureAsInt(
+                String featureName, String paramName, int defaultValue) {
+            return sValue == null ? defaultValue : sValue;
+        }
+
+        public static void setValue(int value) {
+            sValue = value;
         }
     }
 
@@ -265,6 +304,7 @@
         PrefServiceBridge.setInstanceForTesting(mPrefServiceBridge);
 
         resetUiDelegate();
+        NewTabPageAdapter.setHasLoadedBeforeForTest(false);
         reloadNtp();
     }
 
@@ -276,6 +316,8 @@
                 ChromePreferenceManager.NTP_SIGNIN_PROMO_DISMISSED, false);
         ChromePreferenceManager.getInstance().clearNewTabPageSigninPromoSuppressionPeriodStart();
         PrefServiceBridge.setInstanceForTesting(null);
+        ShadowPostTask.reset();
+        ShadowChromeFeatureList.reset();
     }
 
     /**
@@ -1040,6 +1082,31 @@
         assertItemsFor(sectionWithStatusCard().withProgress());
     }
 
+    @Test
+    @Feature({"Ntp"})
+    public void testArtificialDelay() {
+        ShadowChromeFeatureList.setValue(1000);
+        NewTabPageAdapter.setHasLoadedBeforeForTest(false);
+        reloadNtp();
+
+        ItemsMatcher matcher = new ItemsMatcher(mAdapter.getRootForTesting());
+        matcher.expectAboveTheFoldItem();
+        matcher.expectNotFooter();
+        matcher.finish();
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertItemsFor(sectionWithStatusCard().withProgress());
+    }
+
+    @Test
+    @Feature({"Ntp"})
+    public void testArtificialDelaySecondLoad() {
+        ShadowChromeFeatureList.setValue(1000);
+        reloadNtp();
+        reloadNtp();
+
+        assertItemsFor(sectionWithStatusCard().withProgress());
+    }
+
     /**
      * Robolectric shadow to mock out calls to {@link Resources#getString}.
      */
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index a4c0430e..338daf24d 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -570,7 +570,6 @@
     "//chrome/common:buildflags",
     "//chrome/common:mojo_bindings",
     "//chrome/services/file_util/public/cpp:manifest",
-    "//chrome/services/noop/public/cpp:manifest",
     "//components/services/patch/public/cpp:manifest",
     "//components/services/quarantine",
     "//components/services/quarantine/public/cpp:manifest",
diff --git a/chrome/app/DEPS b/chrome/app/DEPS
index a4aa4e6..13bf73a 100644
--- a/chrome/app/DEPS
+++ b/chrome/app/DEPS
@@ -91,7 +91,6 @@
     "+chrome/services/file_util/public",
     "+chrome/services/isolated_xr_device/manifest.h",
     "+chrome/services/media_gallery_util/public",
-    "+chrome/services/noop/public",
     "+chrome/services/printing/public",
     "+chrome/services/removable_storage_writer/public",
     "+chrome/services/util_win/public",
diff --git a/chrome/app/builtin_service_manifests.cc b/chrome/app/builtin_service_manifests.cc
index 865f68f49..03f0652 100644
--- a/chrome/app/builtin_service_manifests.cc
+++ b/chrome/app/builtin_service_manifests.cc
@@ -10,7 +10,6 @@
 #include "chrome/common/constants.mojom.h"
 #include "chrome/services/ble_scan_parser/public/cpp/manifest.h"
 #include "chrome/services/file_util/public/cpp/manifest.h"
-#include "chrome/services/noop/public/cpp/manifest.h"
 #include "components/services/patch/public/cpp/manifest.h"
 #include "components/services/quarantine/public/cpp/manifest.h"
 #include "components/services/unzip/public/cpp/manifest.h"
@@ -138,7 +137,6 @@
   static base::NoDestructor<std::vector<service_manager::Manifest>> manifests{{
       GetChromeManifest(),
       GetFileUtilManifest(),
-      GetNoopManifest(),
       patch::GetManifest(),
       unzip::GetManifest(),
       proxy_resolver::GetManifest(),
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 0f6f4f0..eaddd82c 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -4975,6 +4975,9 @@
   <message name="IDS_SETTINGS_SECURITY_KEYS_PIN_ERROR_TOO_SHORT" desc="A validation error shown beneath a text box where the user is expected to enter a PIN for a security key. PINs, in this context, are short, often numeric codes that are often used with, for example, ATM cards. Security keys are external devices used to authenticate people.">
     PIN must be at least 4 characters
   </message>
+  <message name="IDS_SETTINGS_SECURITY_KEYS_PIN_ERROR_TOO_SHORT_SMALL" desc="A validation error shown beneath a text box where the user is expected to enter a PIN for a security key. PINs, in this context, are short, often numeric codes that are often used with, for example, ATM cards. Security keys are external devices used to authenticate people. This message has limited horizontal space.">
+    Too short
+  </message>
   <message name="IDS_SETTINGS_SECURITY_KEYS_PIN_ERROR_TOO_LONG" desc="A validation error shown beneath a text box where the user is expected to enter a PIN for a security key. PINs, in this context, are short, often numeric codes that are often used with, for example, ATM cards. Security keys are external devices used to authenticate people.">
     PIN must be at most 63 characters
   </message>
@@ -4999,4 +5002,10 @@
   <message name="IDS_SETTINGS_SECURITY_KEYS_PIN_SOFT_LOCK" desc="A message shown when the user attempts to change the PIN on a security key that has been locked due to too many incorrect PIN attempts. PINs, in this context, are short, often numeric codes that are often used with, for example, ATM cards. Security keys are external devices used to authenticate people.">
     The security key is locked because the wrong PIN was entered too many times. To unlock it, remove and reinsert it.
   </message>
+  <message name="IDS_SETTINGS_SECURITY_KEYS_SHOW_PINS" desc="A tool-tip message that is shown to describe the action of an icon in the dialog where users set or enter PINs for security keys. This message indicates to the user that clicking the icon will cause the PINs to be displayed normally. Since PINs are private information they are shown by default like passwords: with the characters replaced by bullet points. (Security keys are external devices used to authenticate people and PINs are short, often numeric, codes that are commonly used with, for example, ATMs.)">
+    Show PINs
+  </message>
+  <message name="IDS_SETTINGS_SECURITY_KEYS_HIDE_PINS" desc="A tool-tip message that is shown to describe the action of an icon in the dialog where users set or enter PINs for security keys. This message indicates to the user that clicking the icon will cause the PINs to be displayed like passwords: with the characters replaced by bullet points. (Security keys are external devices used to authenticate people and PINs are short, often numeric, codes that are commonly used with, for example, ATMs.)">
+    Hide PINs
+  </message>
 </grit-part>
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 30b1142..cc49225 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -60,7 +60,6 @@
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_BLUETOOTH_PAIRING_TICK" file="cros/bluetooth_pairing_tick.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR_BOOKMARKS_FAVICON" file="common/favicon_bookmarks.png" />
       <if expr="is_macosx">
         <structure type="chrome_scaled_image" name="IDR_BOOKMARK_BAR_FOLDER" file="mac/bookmark_bar_folder.png" />
         <structure type="chrome_scaled_image" name="IDR_BOOKMARK_BAR_FOLDER_MANAGED" file="mac/bookmark_bar_folder_managed.png" />
@@ -72,12 +71,23 @@
         <structure type="chrome_scaled_image" name="IDR_BOOKMARK_BAR_FOLDER" file="win/bookmark_bar_folder.png" />
         <structure type="chrome_scaled_image" name="IDR_BOOKMARK_BAR_FOLDER_MANAGED" file="win/bookmark_bar_folder_managed.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR_BOOKMARK_BAR_APPS_SHORTCUT" file="common/apps_bookmark_bar_icon.png" />
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_BOOKMARK_BAR_APPS_SHORTCUT" file="common/apps_bookmark_bar_icon.png" />
+        <structure type="chrome_scaled_image" name="IDR_BOOKMARKS_FAVICON" file="common/favicon_bookmarks.png" />
+      </if>
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_BUTTON_USER_IMAGE_CHOOSE_FILE" file="cros/choose_file.png" />
         <structure type="chrome_scaled_image" name="IDR_BUTTON_USER_IMAGE_TAKE_PHOTO" file="cros/take_photo.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_BUTTON_MASK" file="common/close_button_mask.png" />
+      <if expr="not is_android">
+      <!-- Note: Tab close buttons are not traditional buttons.  Tab close buttons
+           fill a background with a color from the theme and tile IDR_CLOSE_1 over it.
+           See chrome/browser/ui/views/tabs/tab.cc -->
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_1" file="common/close_1.png" />
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_1_H" file="common/close_1_hover.png" />
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_1_P" file="common/close_1_pressed.png" />
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_BUTTON_MASK" file="common/close_button_mask.png" />
+      </if>
       <if expr="is_win">
         <structure type="chrome_scaled_image" name="IDR_CONFLICT_FAVICON" file="common/favicon_conflicts.png" />
       </if>
@@ -85,8 +95,10 @@
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_DEVICE_DISABLED" file="cros/device_disabled.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR_DOWNLOADS_FAVICON" file="common/favicon_downloads.png" />
-      <structure type="chrome_scaled_image" name="IDR_ERROR_NETWORK_OFFLINE" file="common/error_network_offline.png" />
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_DOWNLOADS_FAVICON" file="common/favicon_downloads.png" />
+        <structure type="chrome_scaled_image" name="IDR_ERROR_NETWORK_OFFLINE" file="common/error_network_offline.png" />
+      </if>
       <if expr="enable_extensions">
         <structure type="chrome_scaled_image" name="IDR_EXTENSIONS_FAVICON" file="common/favicon_extensions.png" />
         <structure type="chrome_scaled_image" name="IDR_EXTENSIONS_RATING_STAR_HALF_LEFT" file="common/extensions_rating_star_half_left.png" />
@@ -123,36 +135,36 @@
       <if expr="_google_chrome">
         <structure type="chrome_scaled_image" name="IDR_GOOGLE_ICON" file="google_chrome/google_icon.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR_HELP_MENU" file="common/help_16.png" />
-      <structure type="chrome_scaled_image" name="IDR_HIDE_PASSWORD_HOVER" file="common/hide_password_hover.png" />
-      <if expr="not is_android and not chromeos">
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_HELP_MENU" file="common/help_16.png" />
+        <structure type="chrome_scaled_image" name="IDR_HIDE_PASSWORD_HOVER" file="common/hide_password_hover.png" />
+        <if expr="not chromeos">
         <!-- User Manager tutorial -->
         <structure type="chrome_scaled_image" name="IDR_ICON_USER_MANAGER_TUTORIAL_YOUR_CHROME" file="common/user_manager_tutorial/your_chrome.png" />
         <structure type="chrome_scaled_image" name="IDR_ICON_USER_MANAGER_TUTORIAL_GUESTS" file="common/user_manager_tutorial/guests.png" />
         <structure type="chrome_scaled_image" name="IDR_ICON_USER_MANAGER_TUTORIAL_FRIENDS" file="common/user_manager_tutorial/family_and_friends.png" />
         <structure type="chrome_scaled_image" name="IDR_ICON_USER_MANAGER_TUTORIAL_COMPLETE" file="common/user_manager_tutorial/complete.png" />
-      </if>
-
-      <if expr="not is_android">
-        <structure type="chrome_scaled_image" name="IDR_TRANSLATE_BUBBLE_ICON" file="common/user_manager_tutorial/translate_bubble_icon.png" />
+        </if>
       </if>
       <structure type="chrome_scaled_image" name="IDR_INPUT_ALERT" file="common/input_alert.png" />
       <structure type="chrome_scaled_image" name="IDR_INPUT_ALERT_MENU" file="common/input_alert_menu.png" />
       <if expr="enable_service_discovery">
         <structure type="chrome_scaled_image" name="IDR_LOCAL_DISCOVERY_CLOUDPRINT_ICON" file="common/cloudprint.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR_SUPERVISED_USER_THEME_FRAME" file="common/supervised_user_theme/theme_frame_supervised.png" />
-      <structure type="chrome_scaled_image" name="IDR_SUPERVISED_USER_THEME_FRAME_INACTIVE" file="common/supervised_user_theme/theme_frame_supervised_inactive.png" />
-      <structure type="chrome_scaled_image" name="IDR_SUPERVISED_USER_THEME_TAB_BACKGROUND" file="common/supervised_user_theme/theme_tab_background_supervised.png" />
-      <structure type="chrome_scaled_image" name="IDR_MAXIMIZE_BUTTON_MASK" file="common/maximize_button_mask.png" />
-      <structure type="chrome_scaled_image" name="IDR_MINIMIZE_BUTTON_MASK" file="common/minimize_button_mask.png" />
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_MANAGEMENT_FAVICON" file="common/favicon_management.png" />
+        <structure type="chrome_scaled_image" name="IDR_MAXIMIZE_BUTTON_MASK" file="common/maximize_button_mask.png" />
+        <structure type="chrome_scaled_image" name="IDR_MINIMIZE_BUTTON_MASK" file="common/minimize_button_mask.png" />
+      </if>
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_DRIVE" file="cros/notification_drive.png" />
         <structure type="chrome_scaled_image" name="IDR_ARC_PLAY_STORE_OPTIN_IN_PROGRESS_NOTIFICATION" file="cros/notification_play_store_optin_in_progress.png" />
         <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_EASYUNLOCK_ENABLED" file="cros/notification_easyunlock_enabled.png" />
       </if>
       <structure type="chrome_scaled_image" name="IDR_PLUGINS_FAVICON" file="common/favicon_extensions.png" />
-      <structure type="chrome_scaled_image" name="IDR_PRERENDER" file="common/prerender_succeed_icon.png" />
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_PRERENDER" file="common/prerender_succeed_icon.png" />
+      </if>
       <if expr="not _google_chrome">
         <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_16" file="chromium/product_logo_16.png" />
         <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_32" file="chromium/product_logo_32.png" />
@@ -160,13 +172,17 @@
         <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_NAME_22" file="chromium/product_logo_name_22.png" />
       </if>
       <if expr="_google_chrome">
-        <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_16" file="google_chrome/product_logo_16.png" />
-        <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_32" file="google_chrome/product_logo_32.png" />
+        <if expr="not is_android">
+          <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_16" file="google_chrome/product_logo_16.png" />
+          <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_32" file="google_chrome/product_logo_32.png" />
+        </if>
         <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_32_BETA" file="google_chrome/product_logo_32_beta.png" />
         <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_32_CANARY" file="google_chrome/product_logo_32_canary.png" />
         <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_32_DEV" file="google_chrome/product_logo_32_dev.png" />
-        <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_WHITE" file="google_chrome/product_logo_white.png" />
-        <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_NAME_22" file="google_chrome/product_logo_name_22.png" />
+        <if expr="not is_android">
+          <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_WHITE" file="google_chrome/product_logo_white.png" />
+          <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_NAME_22" file="google_chrome/product_logo_name_22.png" />
+        </if>
         <structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_ENTERPRISE" file="google_chrome/product_logo_enterprise.png" />
       </if>
 
@@ -229,8 +245,9 @@
         <structure type="chrome_scaled_image" name="IDR_PROFILE_AVATAR_55" file="common/modern_avatars/abstract/avatar_sandwich.png" />
       </if>
       <structure type="chrome_scaled_image" name="IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE" file="common/profile_avatar_placeholder_large.png" />
-      <structure type="chrome_scaled_image" name="IDR_PROFILES_DICE_TURN_ON_SYNC" file="common/turn_on_sync_illustration.png" />
-
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_PROFILES_DICE_TURN_ON_SYNC" file="common/turn_on_sync_illustration.png" />
+      </if>
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_RESET_WARNING" file="cros/reset_warning.png" />
       </if>
@@ -239,24 +256,24 @@
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_SECONDARY_USER_SETTINGS" file="cros/secondary_user_settings.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR_SETTINGS_FAVICON" file="common/favicon_settings.png" />
-      <structure type="chrome_scaled_image" name="IDR_MANAGEMENT_FAVICON" file="common/favicon_management.png" />
-      <structure type="chrome_scaled_image" name="IDR_SHOW_PASSWORD_HOVER" file="common/show_password_hover.png" />
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_SETTINGS_FAVICON" file="common/favicon_settings.png" />
+        <structure type="chrome_scaled_image" name="IDR_SHOW_PASSWORD_HOVER" file="common/show_password_hover.png" />
+      </if>
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_SMB_ICON" file="cros/smb_icon.png" />
         <structure type="chrome_scaled_image" name="IDR_LOGO_AVATAR_CIRCLE_BLUE_COLOR" file="cros/logo_avatar_circle_blue_color.png" />
         <structure type="chrome_scaled_image" name="IDR_LOGO_GOOGLE_COLOR_90" file="cros/logo_google_color_90.png" />
       </if>
+      <if expr="not_is_android">
+        <structure type="chrome_scaled_image" name="IDR_SUPERVISED_USER_THEME_FRAME" file="common/supervised_user_theme/theme_frame_supervised.png" />
+        <structure type="chrome_scaled_image" name="IDR_SUPERVISED_USER_THEME_FRAME_INACTIVE" file="common/supervised_user_theme/theme_frame_supervised_inactive.png" />
+        <structure type="chrome_scaled_image" name="IDR_SUPERVISED_USER_THEME_TAB_BACKGROUND" file="common/supervised_user_theme/theme_tab_background_supervised.png" />
+      </if>
       <if expr="is_macosx">
         <structure type="chrome_scaled_image" name="IDR_SWIPE_BACK" file="mac/back_large.png" />
         <structure type="chrome_scaled_image" name="IDR_SWIPE_FORWARD" file="mac/forward_large.png" />
       </if>
-      <!-- Note: Tab close buttons are not traditional buttons.  Tab close buttons
-           fill a background with a color from the theme and tile IDR_CLOSE_1 over it.
-           See chrome/browser/ui/views/tabs/tab.cc -->
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_1" file="common/close_1.png" />
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_1_H" file="common/close_1_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_1_P" file="common/close_1_pressed.png" />
       <!-- Used by Chrome OS login page. -->
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_TAB_RECORDING_INDICATOR" file="cros/tab_recording_indicator.png" />
@@ -295,17 +312,24 @@
       </if>
       <!-- Instant Extended API toolbar background is common for all platforms. -->
       <structure type="chrome_scaled_image" name="IDR_THEME_WINDOW_CONTROL_BACKGROUND" file="notused.png" />
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_TRANSLATE_BUBBLE_ICON" file="common/user_manager_tutorial/translate_bubble_icon.png" />
+      </if>
       <if expr="not _google_chrome">
-        <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON" file="chromium/webstore_icon.png" />
-        <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_16" file="chromium/webstore_icon_16.png" />
-        <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_24" file="chromium/webstore_icon_24.png" />
-        <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_32" file="chromium/webstore_icon_32.png" />
+        <if expr="not is_android">
+          <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON" file="chromium/webstore_icon.png" />
+          <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_16" file="chromium/webstore_icon_16.png" />
+          <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_24" file="chromium/webstore_icon_24.png" />
+          <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_32" file="chromium/webstore_icon_32.png" />
+        </if>
       </if>
       <if expr="_google_chrome">
-        <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON" file="google_chrome/webstore_icon.png" />
-        <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_16" file="google_chrome/webstore_icon_16.png" />
-        <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_24" file="google_chrome/webstore_icon_24.png" />
-        <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_32" file="google_chrome/webstore_icon_32.png" />
+        <if expr="not is_android">
+          <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON" file="google_chrome/webstore_icon.png" />
+          <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_16" file="google_chrome/webstore_icon_16.png" />
+          <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_24" file="google_chrome/webstore_icon_24.png" />
+          <structure type="chrome_scaled_image" name="IDR_WEBSTORE_ICON_32" file="google_chrome/webstore_icon_32.png" />
+        </if>
       </if>
     </structures>
   </release>
diff --git a/chrome/app/version_assembly/BUILD.gn b/chrome/app/version_assembly/BUILD.gn
index d9151bc..5efaff9 100644
--- a/chrome/app/version_assembly/BUILD.gn
+++ b/chrome/app/version_assembly/BUILD.gn
@@ -10,6 +10,7 @@
 
 # Generates a manifest file with the current build's version information.
 process_version("chrome_exe_version_manifest") {
+  process_only = true
   visibility = [ ":*" ]
   template_file = "chrome_exe_manifest.template"
   sources = [
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index fea6204..9d208e4 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1856,8 +1856,6 @@
     "//chrome/common:channel_info",
     "//chrome/common/net",
     "//chrome/installer/util:with_no_strings",
-    "//chrome/services/noop/public/cpp",
-    "//chrome/services/noop/public/mojom",
     "//components/about_handler",
     "//components/app_modal",
     "//components/assist_ranker",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index f9d1b85..86242ed 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -14,7 +14,6 @@
   "+chrome/services/diagnosticsd/public",
   "+chrome/services/file_util/public",
   "+chrome/services/media_gallery_util/public",
-  "+chrome/services/noop/public",
   "+chrome/services/printing/public",
   "+chrome/services/removable_storage_writer/public",
   "+chrome/services/util_win/public",
diff --git a/chrome/browser/browser_switcher/bho/BUILD.gn b/chrome/browser/browser_switcher/bho/BUILD.gn
index beeeaec..7f66d3f 100644
--- a/chrome/browser/browser_switcher/bho/BUILD.gn
+++ b/chrome/browser/browser_switcher/bho/BUILD.gn
@@ -2,11 +2,24 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/compiler/compiler.gni")
 import("//build/toolchain/win/midl.gni")
 
 assert(is_win)
+assert(target_cpu == "x86" || target_cpu == "x64")
 
 shared_library("browser_switcher_bho") {
+  if (current_cpu == "x64") {
+    # Name the 32-bit and 64-bit output differently, since they'll be copied to
+    # the same directory.
+    output_name = "browser_switcher_bho_64"
+  }
+
+  visibility = [
+    ":browser_switcher_dlls",
+    ":copy_browser_switcher_binaries",
+  ]
+
   # TODO(nicolaso): Reduce binary size as much as possible.
   #
   # TODO(nicolaso): Use ie_bho.def and ie_bho.rc.
@@ -28,6 +41,56 @@
   ]
 }
 
+if (is_clang) {
+  browser_switcher_x64_toolchain = "//build/toolchain/win:win_clang_x64"
+  browser_switcher_x86_toolchain = "//build/toolchain/win:win_clang_x86"
+} else {
+  browser_switcher_x64_toolchain = "//build/toolchain/win:x64"
+  browser_switcher_x86_toolchain = "//build/toolchain/win:x86"
+}
+
+browser_switcher_x64_label =
+    ":browser_switcher_bho($browser_switcher_x64_toolchain)"
+browser_switcher_x86_label =
+    ":browser_switcher_bho($browser_switcher_x86_toolchain)"
+
+copy("copy_browser_switcher_binaries") {
+  # Make sure we have both bitnesses in the root out directory.
+  if (target_cpu == "x86") {
+    cross_build_label = browser_switcher_x64_label
+    cross_build_dll = "browser_switcher_bho_64.dll"
+  } else if (target_cpu == "x64") {
+    cross_build_label = browser_switcher_x86_label
+    cross_build_dll = "browser_switcher_bho.dll"
+  }
+
+  cross_build_out_dir = get_label_info(cross_build_label, "root_out_dir")
+
+  sources = [
+    "$cross_build_out_dir/$cross_build_dll",
+  ]
+  if (symbol_level > 0) {
+    sources += [ "$cross_build_out_dir/$cross_build_dll.pdb" ]
+  }
+
+  outputs = [
+    "$root_out_dir/{{source_file_part}}",
+  ]
+  deps = [
+    browser_switcher_x64_label,
+    browser_switcher_x86_label,
+  ]
+}
+
+group("browser_switcher_dlls") {
+  # Build a DLL for each bitness, and put them in the root out dir.
+  deps = [
+    ":copy_browser_switcher_binaries",
+    browser_switcher_x64_label,
+    browser_switcher_x86_label,
+  ]
+}
+
 midl("ie_bho_idl") {
   sources = [
     "ie_bho_idl.idl",
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index de64cd0..ad62956 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -188,8 +188,6 @@
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/installer/util/google_update_settings.h"
-#include "chrome/services/noop/public/cpp/utils.h"
-#include "chrome/services/noop/public/mojom/noop.mojom.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/autofill/core/common/autofill_switches.h"
 #include "components/browsing_data/core/browsing_data_utils.h"
@@ -4989,8 +4987,6 @@
 
 void ChromeContentBrowserClient::OnNetworkServiceCreated(
     network::mojom::NetworkService* network_service) {
-  chrome::MaybeStartNoopService();
-
   if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
     return;
 
diff --git a/chrome/browser/chromeos/attestation/platform_verification_dialog.cc b/chrome/browser/chromeos/attestation/platform_verification_dialog.cc
index 3d39a6d..c9ee348 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_dialog.cc
+++ b/chrome/browser/chromeos/attestation/platform_verification_dialog.cc
@@ -94,15 +94,15 @@
   chrome::RecordDialogCreation(chrome::DialogIdentifier::PLATFORM_VERIFICATION);
 }
 
-views::View* PlatformVerificationDialog::CreateExtraView() {
+std::unique_ptr<views::View> PlatformVerificationDialog::CreateExtraView() {
   auto learn_more_button = views::CreateVectorImageButton(this);
   views::SetImageFromVectorIcon(learn_more_button.get(),
                                 vector_icons::kHelpOutlineIcon);
   learn_more_button->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_CHROMEOS_ACC_LEARN_MORE));
   learn_more_button->SetFocusForPlatform();
-  learn_more_button_ = learn_more_button.release();
-  return learn_more_button_;
+  learn_more_button_ = learn_more_button.get();
+  return learn_more_button;
 }
 
 bool PlatformVerificationDialog::Cancel() {
diff --git a/chrome/browser/chromeos/attestation/platform_verification_dialog.h b/chrome/browser/chromeos/attestation/platform_verification_dialog.h
index 9fcdc393..d1d00c7e 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_dialog.h
+++ b/chrome/browser/chromeos/attestation/platform_verification_dialog.h
@@ -51,7 +51,7 @@
                              ConsentCallback callback);
 
   // views::DialogDelegate:
-  View* CreateExtraView() override;
+  std::unique_ptr<View> CreateExtraView() override;
   bool Cancel() override;
   bool Accept() override;
   bool Close() override;
diff --git a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc
index 6db4fc8..7509cd14 100644
--- a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc
+++ b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc
@@ -6,8 +6,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/browser/web_applications/system_web_app_manager.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -19,11 +17,6 @@
 
 IN_PROC_BROWSER_TEST_F(ChromeContentBrowserClientChromeOsPartTest,
                        SettingsWindowFontSize) {
-  // Install the Settings App.
-  web_app::WebAppProvider::Get(browser()->profile())
-      ->system_web_app_manager()
-      .InstallSystemAppsForTesting();
-
   const content::WebPreferences kDefaultPrefs;
   const int kDefaultFontSize = kDefaultPrefs.default_font_size;
   const int kDefaultFixedFontSize = kDefaultPrefs.default_fixed_font_size;
diff --git a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
index 33f3b50..5726de1 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
@@ -282,6 +282,8 @@
              IDS_AUDIO_PLAYER_REPEAT_BUTTON_LABEL);
   SET_STRING("AUDIO_PLAYER_OPEN_PLAY_LIST_BUTTON_LABEL",
              IDS_AUDIO_PLAYER_OPEN_PLAY_LIST_BUTTON_LABEL);
+  SET_STRING("AUDIO_PLAYER_ARTWORK_EXPAND_BUTTON_LABEL",
+             IDS_AUDIO_PLAYER_ARTWORK_EXPAND_BUTTON_LABEL);
 }
 
 void AddStringsForCloudImport(base::DictionaryValue* dict) {
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc
index dead4577..f580516 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc
@@ -111,10 +111,10 @@
   return accounts;
 }
 
-std::string DeviceOAuth2TokenServiceDelegate::GetRobotAccountId() const {
-  std::string result;
-  CrosSettings::Get()->GetString(kServiceAccountIdentity, &result);
-  return result;
+CoreAccountId DeviceOAuth2TokenServiceDelegate::GetRobotAccountId() const {
+  std::string account_id;
+  CrosSettings::Get()->GetString(kServiceAccountIdentity, &account_id);
+  return CoreAccountId(account_id);
 }
 
 void DeviceOAuth2TokenServiceDelegate::OnRefreshTokenResponse(
@@ -127,10 +127,11 @@
 void DeviceOAuth2TokenServiceDelegate::OnGetTokenInfoResponse(
     std::unique_ptr<base::DictionaryValue> token_info) {
   std::string gaia_robot_id;
+  // For robot accounts email id is the account id.
   token_info->GetString("email", &gaia_robot_id);
   gaia_oauth_client_.reset();
 
-  CheckRobotAccountId(gaia_robot_id);
+  CheckRobotAccountId(CoreAccountId(gaia_robot_id));
 }
 
 void DeviceOAuth2TokenServiceDelegate::OnOAuthError() {
@@ -148,8 +149,7 @@
   ReportServiceError(GoogleServiceAuthError::CONNECTION_FAILED);
 }
 
-std::string DeviceOAuth2TokenServiceDelegate::GetRefreshToken(
-    const CoreAccountId& account_id) const {
+std::string DeviceOAuth2TokenServiceDelegate::GetRefreshToken() const {
   switch (state_) {
     case STATE_LOADING:
     case STATE_NO_TOKEN:
@@ -180,7 +180,7 @@
     const CoreAccountId& account_id,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     OAuth2AccessTokenConsumer* consumer) {
-  std::string refresh_token = GetRefreshToken(account_id);
+  std::string refresh_token = GetRefreshToken();
   DCHECK(!refresh_token.empty());
   return new OAuth2AccessTokenFetcherImpl(consumer, url_loader_factory,
                                           refresh_token);
@@ -234,7 +234,7 @@
 }
 
 void DeviceOAuth2TokenServiceDelegate::CheckRobotAccountId(
-    const std::string& gaia_robot_id) {
+    const CoreAccountId& gaia_robot_id) {
   // Make sure the value returned by GetRobotAccountId has been validated
   // against current device settings.
   switch (CrosSettings::Get()->PrepareTrustedValues(
@@ -255,7 +255,7 @@
       return;
   }
 
-  std::string policy_robot_id = GetRobotAccountId();
+  CoreAccountId policy_robot_id = GetRobotAccountId();
   if (policy_robot_id == gaia_robot_id) {
     state_ = STATE_TOKEN_VALID;
     ReportServiceError(GoogleServiceAuthError::NONE);
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h b/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h
index c97be65..56e70a5 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h
@@ -48,7 +48,7 @@
                               const StatusCallback& callback);
 
   // Pull the robot account ID from device policy.
-  std::string GetRobotAccountId() const;
+  CoreAccountId GetRobotAccountId() const;
 
   // Implementation of OAuth2TokenServiceDelegate.
   bool RefreshTokenIsAvailable(const CoreAccountId& account_id) const override;
@@ -101,15 +101,15 @@
   // Invoked by CrosSettings when the robot account ID becomes available.
   void OnServiceAccountIdentityChanged();
 
-  // Returns the refresh token for account_id.
-  std::string GetRefreshToken(const CoreAccountId& account_id) const;
+  // Returns the refresh token for the robot account id.
+  std::string GetRefreshToken() const;
 
   // Handles completion of the system salt input.
   void DidGetSystemSalt(const std::string& system_salt);
 
   // Checks whether |gaia_robot_id| matches the expected account ID indicated in
   // device settings.
-  void CheckRobotAccountId(const std::string& gaia_robot_id);
+  void CheckRobotAccountId(const CoreAccountId& gaia_robot_id);
 
   // Encrypts and saves the refresh token. Should only be called when the system
   // salt is available.
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
index ffafa5e..8aa2024 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
@@ -152,7 +152,7 @@
 
     return static_cast<DeviceOAuth2TokenServiceDelegate*>(
                oauth2_service_->GetDelegate())
-        ->GetRefreshToken(oauth2_service_->GetRobotAccountId());
+        ->GetRefreshToken();
   }
 
   // A utility method to return fake URL results, for testing the refresh token
diff --git a/chrome/browser/chromeos/ui/echo_dialog_view.cc b/chrome/browser/chromeos/ui/echo_dialog_view.cc
index 35b55bdd..b1b30b26 100644
--- a/chrome/browser/chromeos/ui/echo_dialog_view.cc
+++ b/chrome/browser/chromeos/ui/echo_dialog_view.cc
@@ -75,15 +75,15 @@
   GetWidget()->Show();
 }
 
-views::View* EchoDialogView::CreateExtraView() {
+std::unique_ptr<views::View> EchoDialogView::CreateExtraView() {
   auto learn_more_button = views::CreateVectorImageButton(this);
   views::SetImageFromVectorIcon(learn_more_button.get(),
                                 vector_icons::kHelpOutlineIcon);
   learn_more_button->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_CHROMEOS_ACC_LEARN_MORE));
   learn_more_button->SetFocusForPlatform();
-  learn_more_button_ = learn_more_button.release();
-  return learn_more_button_;
+  learn_more_button_ = learn_more_button.get();
+  return learn_more_button;
 }
 
 int EchoDialogView::GetDialogButtons() const {
diff --git a/chrome/browser/chromeos/ui/echo_dialog_view.h b/chrome/browser/chromeos/ui/echo_dialog_view.h
index 33f4474e..1cadb812 100644
--- a/chrome/browser/chromeos/ui/echo_dialog_view.h
+++ b/chrome/browser/chromeos/ui/echo_dialog_view.h
@@ -55,7 +55,7 @@
   friend class ExtensionEchoPrivateApiTest;
 
   // views::DialogDelegate overrides.
-  View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   int GetDialogButtons() const override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool Cancel() override;
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index 734a2c2..99e83be 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -54,6 +54,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
+#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
 #include "content/public/test/test_download_http_response.h"
 #include "content/public/test/test_navigation_observer.h"
@@ -349,6 +350,7 @@
         current_browser(),
         extension_->GetResourceURL("empty.html"),
         ui::PAGE_TRANSITION_LINK);
+    EXPECT_TRUE(content::WaitForLoadStop(tab));
     EventRouter::Get(current_browser()->profile())
         ->AddEventListener(downloads::OnCreated::kEventName,
                            tab->GetMainFrame()->GetProcess(), GetExtensionId());
diff --git a/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc b/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc
index fa90ade..24f509a9 100644
--- a/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc
+++ b/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc
@@ -31,6 +31,16 @@
 
 GeneratedPrefsFactory::~GeneratedPrefsFactory() {}
 
+content::BrowserContext* GeneratedPrefsFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  // Use |context| even if it is off-the-record/incognito.
+  return context;
+}
+
+bool GeneratedPrefsFactory::ServiceIsNULLWhileTesting() const {
+  return true;
+}
+
 KeyedService* GeneratedPrefsFactory::BuildServiceInstanceFor(
     content::BrowserContext* profile) const {
   return new GeneratedPrefs(static_cast<Profile*>(profile));
diff --git a/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h b/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h
index e755cf0..10fc892 100644
--- a/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h
+++ b/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h
@@ -29,6 +29,9 @@
   ~GeneratedPrefsFactory() override;
 
   // BrowserContextKeyedServiceFactory implementation.
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+  bool ServiceIsNULLWhileTesting() const override;
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
 
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_api.cc b/chrome/browser/extensions/api/settings_private/settings_private_api.cc
index 4898ddb..821ed53 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_api.cc
+++ b/chrome/browser/extensions/api/settings_private/settings_private_api.cc
@@ -17,10 +17,6 @@
 #include "content/public/common/page_zoom.h"
 #include "extensions/browser/extension_function_registry.h"
 
-namespace {
-  const char kDelegateIsNull[] = "delegate is null";
-}
-
 namespace extensions {
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -37,8 +33,7 @@
 
   SettingsPrivateDelegate* delegate =
       SettingsPrivateDelegateFactory::GetForBrowserContext(browser_context());
-  if (delegate == nullptr)
-    return RespondNow(Error(kDelegateIsNull));
+  DCHECK(delegate);
 
   settings_private::SetPrefResult result =
       delegate->SetPref(parameters->name, parameters->value.get());
@@ -71,11 +66,8 @@
 ExtensionFunction::ResponseAction SettingsPrivateGetAllPrefsFunction::Run() {
   SettingsPrivateDelegate* delegate =
       SettingsPrivateDelegateFactory::GetForBrowserContext(browser_context());
-
-  if (delegate == nullptr)
-    return RespondNow(Error(kDelegateIsNull));
-  else
-    return RespondNow(OneArgument(delegate->GetAllPrefs()));
+  DCHECK(delegate);
+  return RespondNow(OneArgument(delegate->GetAllPrefs()));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -92,8 +84,7 @@
 
   SettingsPrivateDelegate* delegate =
       SettingsPrivateDelegateFactory::GetForBrowserContext(browser_context());
-  if (delegate == nullptr)
-    return RespondNow(Error(kDelegateIsNull));
+  DCHECK(delegate);
 
   std::unique_ptr<base::Value> value = delegate->GetPref(parameters->name);
   if (value->is_none())
@@ -114,11 +105,8 @@
     SettingsPrivateGetDefaultZoomFunction::Run() {
   SettingsPrivateDelegate* delegate =
       SettingsPrivateDelegateFactory::GetForBrowserContext(browser_context());
-
-  if (delegate == nullptr)
-    return RespondNow(Error(kDelegateIsNull));
-  else
-    return RespondNow(OneArgument(delegate->GetDefaultZoom()));
+  DCHECK(delegate);
+  return RespondNow(OneArgument(delegate->GetDefaultZoom()));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -137,9 +125,7 @@
 
   SettingsPrivateDelegate* delegate =
       SettingsPrivateDelegateFactory::GetForBrowserContext(browser_context());
-  if (delegate == nullptr)
-    return RespondNow(Error(kDelegateIsNull));
-
+  DCHECK(delegate);
   delegate->SetDefaultZoom(parameters->zoom);
   return RespondNow(OneArgument(std::make_unique<base::Value>(true)));
 }
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_browsertest_chromeos.cc b/chrome/browser/extensions/api/settings_private/settings_private_browsertest_chromeos.cc
new file mode 100644
index 0000000..6b7a7b7
--- /dev/null
+++ b/chrome/browser/extensions/api/settings_private/settings_private_browsertest_chromeos.cc
@@ -0,0 +1,57 @@
+// 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 "base/values.h"
+#include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h"
+#include "chrome/browser/chromeos/login/test/guest_session_mixin.h"
+#include "chrome/browser/extensions/api/settings_private/prefs_util_enums.h"
+#include "chrome/browser/extensions/api/settings_private/settings_private_api.h"
+#include "chrome/browser/extensions/api/settings_private/settings_private_delegate.h"
+#include "chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h"
+#include "chrome/browser/extensions/api/settings_private/settings_private_event_router.h"
+#include "chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+namespace {
+
+class SettingsPrivateGuestModeTest
+    : public chromeos::MixinBasedInProcessBrowserTest {
+ protected:
+  chromeos::GuestSessionMixin guest_session_{&mixin_host_};
+};
+
+// Regression test for https://crbug.com/887383.
+IN_PROC_BROWSER_TEST_F(SettingsPrivateGuestModeTest, GuestMode) {
+  Profile* guest_profile = browser()->profile();
+  EXPECT_TRUE(guest_profile->IsOffTheRecord());
+
+  // SettingsPrivate uses the incognito profile, not the recording profile,
+  // to set preferences.
+  SettingsPrivateDelegate* delegate =
+      SettingsPrivateDelegateFactory::GetForBrowserContext(guest_profile);
+  Profile* delegate_profile = delegate->profile_for_test();
+  EXPECT_EQ(guest_profile, delegate_profile);
+  EXPECT_NE(guest_profile->GetOriginalProfile(), delegate_profile);
+
+  // SettingsPrivate observes the incognito profile, not the recording profile,
+  // for pref changes.
+  SettingsPrivateEventRouter* router =
+      SettingsPrivateEventRouterFactory::GetForProfile(guest_profile);
+  Profile* router_profile = static_cast<Profile*>(router->context_for_test());
+  EXPECT_EQ(guest_profile, router_profile);
+  EXPECT_NE(guest_profile->GetOriginalProfile(), router_profile);
+
+  // Page zoom preferences cannot be changed in guest mode and always return a
+  // default value.
+  EXPECT_EQ(settings_private::SetPrefResult::PREF_NOT_MODIFIABLE,
+            delegate->SetDefaultZoom(0.5));
+  EXPECT_EQ(delegate->GetDefaultZoom()->GetDouble(), 0.0);
+}
+
+}  // namespace
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc b/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
index adf858f..9eb36246 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
+++ b/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/extensions/api/settings_private/prefs_util.h"
+#include "chrome/browser/extensions/api/settings_private/prefs_util_enums.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
 #include "chrome/common/pref_names.h"
@@ -58,14 +59,21 @@
 }
 
 std::unique_ptr<base::Value> SettingsPrivateDelegate::GetDefaultZoom() {
+  // Zoom level prefs aren't available for off-the-record profiles (like guest
+  // mode on Chrome OS). The setting isn't visible to users anyway, so return a
+  // default value.
+  if (profile_->IsOffTheRecord())
+    return std::make_unique<base::Value>(0.0);
   double zoom = content::ZoomLevelToZoomFactor(
       profile_->GetZoomLevelPrefs()->GetDefaultZoomLevelPref());
-  std::unique_ptr<base::Value> value(new base::Value(zoom));
-  return value;
+  return std::make_unique<base::Value>(zoom);
 }
 
 settings_private::SetPrefResult SettingsPrivateDelegate::SetDefaultZoom(
     double zoom) {
+  // See comment in GetDefaultZoom().
+  if (profile_->IsOffTheRecord())
+    return settings_private::SetPrefResult::PREF_NOT_MODIFIABLE;
   double zoom_factor = content::ZoomFactorToZoomLevel(zoom);
   profile_->GetZoomLevelPrefs()->SetDefaultZoomLevelPref(zoom_factor);
   return settings_private::SetPrefResult::SUCCESS;
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_delegate.h b/chrome/browser/extensions/api/settings_private/settings_private_delegate.h
index e2d07ca2..bbe82f1 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_delegate.h
+++ b/chrome/browser/extensions/api/settings_private/settings_private_delegate.h
@@ -50,6 +50,8 @@
   // Sets the pref.
   virtual settings_private::SetPrefResult SetDefaultZoom(double zoom);
 
+  Profile* profile_for_test() { return profile_; }
+
  protected:
   Profile* profile_;  // weak; not owned by us
   std::unique_ptr<PrefsUtil> prefs_util_;
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc b/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc
index e855cac0..fd8ed79c 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc
+++ b/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc
@@ -6,7 +6,6 @@
 
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/settings_private/settings_private_delegate.h"
-#include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "extensions/browser/extension_system_provider.h"
@@ -39,8 +38,6 @@
 content::BrowserContext* SettingsPrivateDelegateFactory::GetBrowserContextToUse(
     content::BrowserContext* context) const {
   // Use the incognito profile when in Guest mode.
-  if (context->IsOffTheRecord())
-    return chrome::GetBrowserContextRedirectedInIncognito(context);
   return context;
 }
 
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc b/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc
index c6804a17..5184bad 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc
+++ b/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc
@@ -59,7 +59,9 @@
     event_router->UnregisterObserver(this);
 
   if (listening_) {
+#if defined(OS_CHROMEOS)
     cros_settings_subscription_map_.clear();
+#endif
     const PrefsUtil::TypedPrefMap& keys = prefs_util_->GetWhitelistedKeys();
     settings_private::GeneratedPrefs* generated_prefs =
         settings_private::GeneratedPrefsFactory::GetForBrowserContext(context_);
@@ -128,12 +130,15 @@
   } else if (!should_listen && listening_) {
     const PrefsUtil::TypedPrefMap& keys = prefs_util_->GetWhitelistedKeys();
     for (const auto& it : keys) {
-      if (prefs_util_->IsCrosSetting(it.first))
+      if (prefs_util_->IsCrosSetting(it.first)) {
+#if defined(OS_CHROMEOS)
         cros_settings_subscription_map_.erase(it.first);
-      else if (generated_prefs && generated_prefs->HasPref(it.first))
+#endif
+      } else if (generated_prefs && generated_prefs->HasPref(it.first)) {
         generated_prefs->RemoveObserver(it.first, this);
-      else
+      } else {
         FindRegistrarForPref(it.first)->Remove(it.first);
+      }
     }
   }
   listening_ = should_listen;
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_event_router.h b/chrome/browser/extensions/api/settings_private/settings_private_event_router.h
index 4d28143e..ddad6b08 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_event_router.h
+++ b/chrome/browser/extensions/api/settings_private/settings_private_event_router.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_API_SETTINGS_PRIVATE_SETTINGS_PRIVATE_EVENT_ROUTER_H_
 #define CHROME_BROWSER_EXTENSIONS_API_SETTINGS_PRIVATE_SETTINGS_PRIVATE_EVENT_ROUTER_H_
 
+#include <map>
 #include <memory>
 
 #include "base/macros.h"
@@ -15,12 +16,9 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "extensions/browser/event_router.h"
 
-// TODO(wychen): ChromeOS headers should only be included when building
-//               ChromeOS, and the following headers should be guarded by
-//               #if defined(OS_CHROMEOS). However, the types are actually
-//               used, and it takes another CL to clean them up.
-//               Reference: crbug.com/720159
+#if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/settings/cros_settings.h"
+#endif
 
 namespace content {
 class BrowserContext;
@@ -43,6 +41,8 @@
   // settings_private::GeneratedPref::Observer implementation.
   void OnGeneratedPrefChanged(const std::string& pref_name) override;
 
+  content::BrowserContext* context_for_test() { return context_; }
+
  protected:
   explicit SettingsPrivateEventRouter(content::BrowserContext* context);
 
@@ -73,10 +73,12 @@
 
   PrefChangeRegistrar* FindRegistrarForPref(const std::string& pref_name);
 
+#if defined(OS_CHROMEOS)
   using SubscriptionMap =
       std::map<std::string,
                std::unique_ptr<chromeos::CrosSettings::ObserverSubscription>>;
   SubscriptionMap cros_settings_subscription_map_;
+#endif
 
   content::BrowserContext* context_;
   bool listening_;
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc b/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc
index 227e15d0..b491f73a 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc
+++ b/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc
@@ -44,7 +44,8 @@
 content::BrowserContext*
 SettingsPrivateEventRouterFactory::GetBrowserContextToUse(
     content::BrowserContext* context) const {
-  return ExtensionsBrowserClient::Get()->GetOriginalContext(context);
+  // Use the incognito profile in guest mode.
+  return context;
 }
 
 bool SettingsPrivateEventRouterFactory::ServiceIsCreatedWithBrowserContext()
@@ -53,7 +54,7 @@
 }
 
 bool SettingsPrivateEventRouterFactory::ServiceIsNULLWhileTesting() const {
-  return false;
+  return true;
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 965ec44..142506f 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -935,7 +935,7 @@
   },
   {
     "name": "enable-chrome-duet-labels",
-    "owners": [ "amaralp" ],
+    "owners": [ "mdjones" ],
     "expiry_milestone": 79
   },
   {
diff --git a/chrome/browser/metrics/oom/out_of_memory_reporter_browsertest.cc b/chrome/browser/metrics/oom/out_of_memory_reporter_browsertest.cc
index 6002754..4ef0524 100644
--- a/chrome/browser/metrics/oom/out_of_memory_reporter_browsertest.cc
+++ b/chrome/browser/metrics/oom/out_of_memory_reporter_browsertest.cc
@@ -15,6 +15,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/common/url_constants.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/service_manager/embedder/switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -59,6 +60,8 @@
 #define MAYBE_MemoryExhaust MemoryExhaust
 #endif
 IN_PROC_BROWSER_TEST_F(OutOfMemoryReporterBrowserTest, MAYBE_MemoryExhaust) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   OutOfMemoryReporter::FromWebContents(web_contents)->AddObserver(this);
diff --git a/chrome/browser/resources/local_ntp/customize.js b/chrome/browser/resources/local_ntp/customize.js
index 9086715f..e03519b3 100644
--- a/chrome/browser/resources/local_ntp/customize.js
+++ b/chrome/browser/resources/local_ntp/customize.js
@@ -220,6 +220,18 @@
 customize.richerPicker_selectedOption = null;
 
 /**
+ * The currently selected option in the Colors menu.
+ * @type {?Element}
+ */
+customize.selectedColorTile = null;
+
+/**
+ * Whether tiles for Colors menu already loaded.
+ * @type {boolean}
+ */
+customize.colorMenuLoaded = false;
+
+/**
  * Sets the visibility of the settings menu and individual options depending on
  * their respective features.
  */
@@ -376,14 +388,55 @@
 };
 
 /**
- * Create a tile for a Chrome Backgrounds collection.
+ * Creates a tile for the customization menu with a title.
+ * @param {string} id The id for the new element.
+ * @param {string} imageUrl The background image url for the new element.
+ * @param {string} name The name for the title of the new element.
+ * @param {Object} dataset The dataset for the new element.
+ * @param {?Function} onClickInteraction Function for onclick interaction.
+ * @param {?Function} onKeyInteraction Function for onkeydown interaction.
  */
-customize.createChromeBackgroundTile = function(data) {
+customize.createTileWithTitle = function(
+    id, imageUrl, name, dataset, onClickInteraction, onKeyInteraction) {
+  const tile = customize.createTile(
+      id, imageUrl, dataset, onClickInteraction, onKeyInteraction);
+  customize.fadeInImageTile(tile, imageUrl, null);
+
+  const title = document.createElement('div');
+  title.classList.add(customize.CLASSES.COLLECTION_TITLE);
+  title.textContent = name;
+  tile.appendChild(title);
+
+  const tileBackground = document.createElement('div');
+  tileBackground.classList.add(customize.CLASSES.COLLECTION_TILE_BG);
+  tileBackground.appendChild(tile);
+  return tileBackground;
+};
+
+/**
+ * Create a tile for customization menu.
+ * @param {string} id The id for the new element.
+ * @param {string} imageUrl The background image url for the new element.
+ * @param {Object} dataset The dataset for the new element.
+ * @param {?Function} onClickInteraction Function for onclick interaction.
+ * @param {?Function} onKeyInteraction Function for onkeydown interaction.
+ */
+customize.createTile = function(
+    id, imageUrl, dataset, onClickInteraction, onKeyInteraction) {
   const tile = document.createElement('div');
-  tile.style.backgroundImage = 'url(' + data.previewImageUrl + ')';
-  tile.dataset.id = data.collectionId;
-  tile.dataset.name = data.collectionName;
-  customize.fadeInImageTile(tile, data.previewImageUrl, null);
+  tile.id = id;
+  tile.classList.add(customize.CLASSES.COLLECTION_TILE);
+  tile.style.backgroundImage = 'url(' + imageUrl + ')';
+  for (const key in dataset) {
+    tile.dataset[key] = dataset[key];
+  }
+  tile.tabIndex = -1;
+
+  // Accessibility support for screen readers.
+  tile.setAttribute('role', 'button');
+
+  tile.onclick = onClickInteraction;
+  tile.onkeydown = onKeyInteraction;
   return tile;
 };
 
@@ -472,111 +525,102 @@
     menu.classList.remove(customize.CLASSES.IMAGE_DIALOG);
   }
 
+  const tileOnClickInteraction = function(event) {
+    let tile = event.target;
+    if (tile.classList.contains(customize.CLASSES.COLLECTION_TITLE)) {
+      tile = tile.parentNode;
+    }
+
+    // Load images for selected collection.
+    const imgElement = $('ntp-images-loader');
+    if (imgElement) {
+      imgElement.parentNode.removeChild(imgElement);
+    }
+    const imgScript = document.createElement('script');
+    imgScript.id = 'ntp-images-loader';
+    imgScript.src = 'chrome-search://local-ntp/ntp-background-images.js?' +
+        'collection_id=' + tile.dataset.id;
+    ntpApiHandle.logEvent(
+        customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
+            .NTP_CUSTOMIZE_CHROME_BACKGROUND_SELECT_COLLECTION);
+
+    document.body.appendChild(imgScript);
+
+    imgScript.onload = function() {
+      // Verify that the individual image data was successfully loaded.
+      const imageDataLoaded =
+          (collImg.length > 0 && collImg[0].collectionId == tile.dataset.id);
+
+      // Dependent upon the success of the load, populate the image selection
+      // dialog or close the current dialog.
+      if (imageDataLoaded) {
+        $(customize.IDS.BACKGROUNDS_MENU)
+            .classList.toggle(customize.CLASSES.MENU_SHOWN, false);
+        $(customize.IDS.BACKGROUNDS_IMAGE_MENU)
+            .classList.toggle(customize.CLASSES.MENU_SHOWN, true);
+
+        // In the RP the upload or default tile may be selected.
+        if (configData.richerPicker) {
+          customize.richerPicker_deselectTile(customize.selectedTile);
+        } else {
+          customize.resetSelectionDialog();
+        }
+        customize.showImageSelectionDialog(tile.dataset.name);
+      } else {
+        customize.handleError(collImgErrors);
+      }
+    };
+  };
+
+  const tileOnKeyDownInteraction = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER) {
+      event.preventDefault();
+      event.stopPropagation();
+      tileOnClickInteraction(event);
+    } else if (
+        event.keyCode === customize.KEYCODES.LEFT ||
+        event.keyCode === customize.KEYCODES.UP ||
+        event.keyCode === customize.KEYCODES.RIGHT ||
+        event.keyCode === customize.KEYCODES.DOWN) {
+      // Handle arrow key navigation.
+      event.preventDefault();
+      event.stopPropagation();
+
+      let target = null;
+      if (event.keyCode === customize.KEYCODES.LEFT) {
+        target = customize.getNextTile(
+            document.documentElement.classList.contains('rtl') ? 1 : -1, 0,
+            event.currentTarget.dataset.tileNum);
+      } else if (event.keyCode === customize.KEYCODES.UP) {
+        target =
+            customize.getNextTile(0, -1, event.currentTarget.dataset.tileNum);
+      } else if (event.keyCode === customize.KEYCODES.RIGHT) {
+        target = customize.getNextTile(
+            document.documentElement.classList.contains('rtl') ? -1 : 1, 0,
+            event.currentTarget.dataset.tileNum);
+      } else if (event.keyCode === customize.KEYCODES.DOWN) {
+        target =
+            customize.getNextTile(0, 1, event.currentTarget.dataset.tileNum);
+      }
+      if (target) {
+        target.focus();
+      } else {
+        event.currentTarget.focus();
+      }
+    }
+  };
+
   // Create dialog tiles.
   for (let i = 0; i < coll.length; ++i) {
-    const tileBackground = document.createElement('div');
-    tileBackground.classList.add(customize.CLASSES.COLLECTION_TILE_BG);
-    const tile = customize.createChromeBackgroundTile(coll[i]);
-    tile.classList.add(customize.CLASSES.COLLECTION_TILE);
-    tile.id = 'coll_tile_' + i;
-    tile.dataset.tile_num = i;
-    tile.tabIndex = -1;
-    // Accessibility support for screen readers.
-    tile.setAttribute('role', 'button');
+    const id = coll[i].collectionId;
+    const name = coll[i].collectionName;
+    const imageUrl = coll[i].previewImageUrl;
+    const dataset = {'id': id, 'name': name, 'tileNum': i};
 
-    const title = document.createElement('div');
-    title.classList.add(customize.CLASSES.COLLECTION_TITLE);
-    title.textContent = tile.dataset.name;
-
-    const tileInteraction = function(event) {
-      let tile = event.target;
-      if (tile.classList.contains(customize.CLASSES.COLLECTION_TITLE)) {
-        tile = tile.parentNode;
-      }
-
-      // Load images for selected collection.
-      const imgElement = $('ntp-images-loader');
-      if (imgElement) {
-        imgElement.parentNode.removeChild(imgElement);
-      }
-      const imgScript = document.createElement('script');
-      imgScript.id = 'ntp-images-loader';
-      imgScript.src = 'chrome-search://local-ntp/ntp-background-images.js?' +
-          'collection_id=' + tile.dataset.id;
-      ntpApiHandle.logEvent(
-          customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
-              .NTP_CUSTOMIZE_CHROME_BACKGROUND_SELECT_COLLECTION);
-
-      document.body.appendChild(imgScript);
-
-      imgScript.onload = function() {
-        // Verify that the individual image data was successfully loaded.
-        const imageDataLoaded =
-            (collImg.length > 0 && collImg[0].collectionId == tile.dataset.id);
-
-        // Dependent upon the success of the load, populate the image selection
-        // dialog or close the current dialog.
-        if (imageDataLoaded) {
-          $(customize.IDS.BACKGROUNDS_MENU)
-              .classList.toggle(customize.CLASSES.MENU_SHOWN, false);
-          $(customize.IDS.BACKGROUNDS_IMAGE_MENU)
-              .classList.toggle(customize.CLASSES.MENU_SHOWN, true);
-
-          // In the RP the upload or default tile may be selected.
-          if (configData.richerPicker) {
-            customize.richerPicker_deselectTile(customize.selectedTile);
-          } else {
-            customize.resetSelectionDialog();
-          }
-          customize.showImageSelectionDialog(tile.dataset.name);
-        } else {
-          customize.handleError(collImgErrors);
-        }
-      };
-    };
-
-    tile.onclick = tileInteraction;
-    tile.onkeydown = function(event) {
-      if (event.keyCode === customize.KEYCODES.ENTER) {
-        event.preventDefault();
-        event.stopPropagation();
-        tileInteraction(event);
-      } else if (
-          event.keyCode === customize.KEYCODES.LEFT ||
-          event.keyCode === customize.KEYCODES.UP ||
-          event.keyCode === customize.KEYCODES.RIGHT ||
-          event.keyCode === customize.KEYCODES.DOWN) {
-        // Handle arrow key navigation.
-        event.preventDefault();
-        event.stopPropagation();
-
-        let target = null;
-        if (event.keyCode === customize.KEYCODES.LEFT) {
-          target = customize.getNextTile(
-              document.documentElement.classList.contains('rtl') ? 1 : -1, 0,
-              event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode === customize.KEYCODES.UP) {
-          target = customize.getNextTile(
-              0, -1, event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode === customize.KEYCODES.RIGHT) {
-          target = customize.getNextTile(
-              document.documentElement.classList.contains('rtl') ? -1 : 1, 0,
-              event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode === customize.KEYCODES.DOWN) {
-          target =
-              customize.getNextTile(0, 1, event.currentTarget.dataset.tile_num);
-        }
-        if (target) {
-          target.focus();
-        } else {
-          event.currentTarget.focus();
-        }
-      }
-    };
-
-    tile.appendChild(title);
-    tileBackground.appendChild(tile);
-    tileContainer.appendChild(tileBackground);
+    const tile = customize.createTileWithTitle(
+        'coll_tile_' + i, imageUrl, name, dataset, tileOnClickInteraction,
+        tileOnKeyDownInteraction);
+    tileContainer.appendChild(tile);
   }
 
   $(customize.IDS.TILES).focus();
@@ -701,40 +745,122 @@
     menu.classList.add(customize.CLASSES.IMAGE_DIALOG);
   }
 
+  const tileInteraction = function(tile) {
+    if (customize.selectedTile) {
+      if (configData.richerPicker) {
+        const id = customize.selectedTile.id;
+        customize.richerPicker_deselectTile(customize.selectedTile);
+        if (id === tile.id) {
+          return;
+        }
+      } else {
+        customize.removeSelectedState(customize.selectedTile);
+        if (customize.selectedTile.id === tile.id) {
+          customize.unselectTile();
+          return;
+        }
+      }
+    }
+
+    if (configData.richerPicker) {
+      customize.richerPicker_selectTile(tile);
+    } else {
+      customize.applySelectedState(tile);
+      customize.selectedTile = tile;
+    }
+
+    $(customize.IDS.DONE).tabIndex = 0;
+
+    // Turn toggle off when an image is selected.
+    $(customize.IDS.DONE).disabled = false;
+    ntpApiHandle.logEvent(customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
+                              .NTP_CUSTOMIZE_CHROME_BACKGROUND_SELECT_IMAGE);
+  };
+
+  const tileOnClickInteraction = function(event) {
+    const clickCount = event.detail;
+    // Control + option + space will fire the onclick event with 0 clickCount.
+    if (clickCount <= 1) {
+      tileInteraction(event.currentTarget);
+    } else if (
+        clickCount === 2 && customize.selectedTile === event.currentTarget) {
+      customize.setBackground(
+          event.currentTarget.dataset.url,
+          event.currentTarget.dataset.attributionLine1,
+          event.currentTarget.dataset.attributionLine2,
+          event.currentTarget.dataset.attributionActionUrl);
+    }
+  };
+
+  const tileOnKeyDownInteraction = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER) {
+      event.preventDefault();
+      event.stopPropagation();
+      tileInteraction(event.currentTarget);
+    } else if (
+        event.keyCode === customize.KEYCODES.LEFT ||
+        event.keyCode === customize.KEYCODES.UP ||
+        event.keyCode === customize.KEYCODES.RIGHT ||
+        event.keyCode === customize.KEYCODES.DOWN) {
+      // Handle arrow key navigation.
+      event.preventDefault();
+      event.stopPropagation();
+
+      let target = null;
+      if (event.keyCode == customize.KEYCODES.LEFT) {
+        target = customize.getNextTile(
+            document.documentElement.classList.contains('rtl') ? 1 : -1, 0,
+            event.currentTarget.dataset.tileNum);
+      } else if (event.keyCode == customize.KEYCODES.UP) {
+        target =
+            customize.getNextTile(0, -1, event.currentTarget.dataset.tileNum);
+      } else if (event.keyCode == customize.KEYCODES.RIGHT) {
+        target = customize.getNextTile(
+            document.documentElement.classList.contains('rtl') ? -1 : 1, 0,
+            event.currentTarget.dataset.tileNum);
+      } else if (event.keyCode == customize.KEYCODES.DOWN) {
+        target =
+            customize.getNextTile(0, 1, event.currentTarget.dataset.tileNum);
+      }
+      if (target) {
+        target.focus();
+      } else {
+        event.currentTarget.focus();
+      }
+    }
+  };
+
   const preLoadTiles = [];
   const postLoadTiles = [];
 
   for (let i = 0; i < collImg.length; ++i) {
-    const tileBackground = document.createElement('div');
-    tileBackground.classList.add(customize.CLASSES.COLLECTION_TILE_BG);
-    const tile = document.createElement('div');
-    tile.classList.add(customize.CLASSES.COLLECTION_TILE);
-    // Accessibility support for screen readers.
-    tile.setAttribute('role', 'button');
+    const dataset = {};
 
     // TODO(crbug.com/854028): Remove this hardcoded check when wallpaper
     // previews are supported.
     if (collImg[i].collectionId === 'solidcolors') {
-      tile.dataset.attributionLine1 = '';
-      tile.dataset.attributionLine2 = '';
-      tile.dataset.attributionActionUrl = '';
+      dataset.attributionLine1 = '';
+      dataset.attributionLine2 = '';
+      dataset.attributionActionUrl = '';
     } else {
-      tile.dataset.attributionLine1 =
+      dataset.attributionLine1 =
           (collImg[i].attributions[0] !== undefined ?
                collImg[i].attributions[0] :
                '');
-      tile.dataset.attributionLine2 =
+      dataset.attributionLine2 =
           (collImg[i].attributions[1] !== undefined ?
                collImg[i].attributions[1] :
                '');
-      tile.dataset.attributionActionUrl = collImg[i].attributionActionUrl;
+      dataset.attributionActionUrl = collImg[i].attributionActionUrl;
     }
-    tile.setAttribute('aria-label', collImg[i].attributions[0]);
-    tile.dataset.url = collImg[i].imageUrl;
+    dataset.url = collImg[i].imageUrl;
+    dataset.tileNum = i;
 
-    tile.id = 'img_tile_' + i;
-    tile.dataset.tile_num = i;
-    tile.tabIndex = -1;
+    const tile = customize.createTile(
+        'img_tile_' + i, collImg[i].imageUrl, dataset, tileOnClickInteraction,
+        tileOnKeyDownInteraction);
+
+    tile.setAttribute('aria-label', collImg[i].attributions[0]);
 
     // Load the first |ROWS_TO_PRELOAD| rows of tiles.
     if (i < firstNTile) {
@@ -743,90 +869,8 @@
       postLoadTiles.push(tile);
     }
 
-    const tileInteraction = function(tile) {
-      if (customize.selectedTile) {
-        if (configData.richerPicker) {
-          const id = customize.selectedTile.id;
-          customize.richerPicker_deselectTile(customize.selectedTile);
-          if (id === tile.id) {
-            return;
-          }
-        } else {
-          customize.removeSelectedState(customize.selectedTile);
-          if (customize.selectedTile.id === tile.id) {
-            customize.unselectTile();
-            return;
-          }
-        }
-      }
-
-      if (configData.richerPicker) {
-        customize.richerPicker_selectTile(tile);
-      } else {
-        customize.applySelectedState(tile);
-        customize.selectedTile = tile;
-      }
-
-      $(customize.IDS.DONE).tabIndex = 0;
-
-      // Turn toggle off when an image is selected.
-      $(customize.IDS.DONE).disabled = false;
-      ntpApiHandle.logEvent(customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
-                                .NTP_CUSTOMIZE_CHROME_BACKGROUND_SELECT_IMAGE);
-    };
-
-    tile.onclick = function(event) {
-      const clickCount = event.detail;
-      // Control + option + space will fire the onclick event with 0 clickCount.
-      if (clickCount <= 1) {
-        tileInteraction(event.currentTarget);
-      } else if (
-          clickCount === 2 && customize.selectedTile === event.currentTarget) {
-        customize.setBackground(
-            event.currentTarget.dataset.url,
-            event.currentTarget.dataset.attributionLine1,
-            event.currentTarget.dataset.attributionLine2,
-            event.currentTarget.dataset.attributionActionUrl);
-      }
-    };
-    tile.onkeydown = function(event) {
-      if (event.keyCode === customize.KEYCODES.ENTER) {
-        event.preventDefault();
-        event.stopPropagation();
-        tileInteraction(event.currentTarget);
-      } else if (
-          event.keyCode === customize.KEYCODES.LEFT ||
-          event.keyCode === customize.KEYCODES.UP ||
-          event.keyCode === customize.KEYCODES.RIGHT ||
-          event.keyCode === customize.KEYCODES.DOWN) {
-        // Handle arrow key navigation.
-        event.preventDefault();
-        event.stopPropagation();
-
-        let target = null;
-        if (event.keyCode == customize.KEYCODES.LEFT) {
-          target = customize.getNextTile(
-              document.documentElement.classList.contains('rtl') ? 1 : -1, 0,
-              event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode == customize.KEYCODES.UP) {
-          target = customize.getNextTile(
-              0, -1, event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode == customize.KEYCODES.RIGHT) {
-          target = customize.getNextTile(
-              document.documentElement.classList.contains('rtl') ? -1 : 1, 0,
-              event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode == customize.KEYCODES.DOWN) {
-          target =
-              customize.getNextTile(0, 1, event.currentTarget.dataset.tile_num);
-        }
-        if (target) {
-          target.focus();
-        } else {
-          event.currentTarget.focus();
-        }
-      }
-    };
-
+    const tileBackground = document.createElement('div');
+    tileBackground.classList.add(customize.CLASSES.COLLECTION_TILE_BG);
     tileBackground.appendChild(tile);
     tileContainer.appendChild(tileBackground);
   }
@@ -854,17 +898,17 @@
  * loading.
  */
 customize.loadTile = function(tile, imageData, countLoad) {
-  if (imageData[tile.dataset.tile_num].collectionId === 'solidcolors') {
+  if (imageData[tile.dataset.tileNum].collectionId === 'solidcolors') {
     tile.style.backgroundImage = [
       customize.CUSTOM_BACKGROUND_OVERLAY,
-      'url(' + imageData[tile.dataset.tile_num].thumbnailImageUrl + ')'
+      'url(' + imageData[tile.dataset.tileNum].thumbnailImageUrl + ')'
     ].join(',').trim();
   } else {
     tile.style.backgroundImage =
-        'url(' + imageData[tile.dataset.tile_num].thumbnailImageUrl + ')';
+        'url(' + imageData[tile.dataset.tileNum].thumbnailImageUrl + ')';
   }
   customize.fadeInImageTile(
-      tile, imageData[tile.dataset.tile_num].thumbnailImageUrl, countLoad);
+      tile, imageData[tile.dataset.tileNum].thumbnailImageUrl, countLoad);
 };
 
 /**
@@ -1018,6 +1062,7 @@
     if (configData.richerPicker) {
       customize.richerPicker_setCustomizationMenuToDefaultState();
       customize.loadChromeBackgrounds();
+      customize.loadColorTiles();
       $(customize.IDS.CUSTOMIZATION_MENU).showModal();
     } else {
       editDialog.showModal();
@@ -1467,6 +1512,7 @@
     customize.richerPicker_resetCustomizationMenu();
     customize.richerPicker_selectMenuOption(
         $(customize.IDS.COLORS_BUTTON), $(customize.IDS.COLORS_MENU));
+    ntpApiHandle.getColorsInfo();
   };
 };
 
@@ -1498,3 +1544,39 @@
   // Generic error when we can't tell what went wrong.
   customize.showErrorNotification(unavailableString);
 };
+
+/**
+ * Handles color tile selection.
+ * @param {Object} event The event attributes for the interaction.
+ */
+customize.colorTileInteraction = function(event) {
+  if (customize.selectedColorTile) {
+    customize.richerPicker_deselectTile(customize.selectedColorTile);
+  }
+  customize.richerPicker_selectTile(event.target);
+  customize.selectedColorTile = event.target;
+  ntpApiHandle.applyAutogeneratedTheme(event.target.dataset.color.split(','));
+};
+
+/**
+ * Loads tiles for colors menu.
+ */
+customize.loadColorTiles = function() {
+  if (customize.colorMenuLoaded) {
+    return;
+  }
+
+  const colorsColl = ntpApiHandle.getColorsInfo();
+  for (let i = 0; i < colorsColl.length; ++i) {
+    const id = 'color_' + i;
+    const imageUrl = colorsColl[i].icon;
+    const name = colorsColl[i].label;
+    const dataset = {'color': colorsColl[i].color};
+
+    const tile = customize.createTileWithTitle(
+        id, imageUrl, name, dataset, customize.colorTileInteraction,
+        customize.colorTileInteraction);
+    $(customize.IDS.COLORS_MENU).appendChild(tile);
+  }
+  customize.colorMenuLoaded = true;
+};
\ No newline at end of file
diff --git a/chrome/browser/resources/local_ntp/externs.js b/chrome/browser/resources/local_ntp/externs.js
index 6f83ae2..63b6f3e8 100644
--- a/chrome/browser/resources/local_ntp/externs.js
+++ b/chrome/browser/resources/local_ntp/externs.js
@@ -157,6 +157,11 @@
 window.chrome.embeddedSearch.newTabPage;
 
 /**
+ * @param {Array} color
+ */
+window.chrome.embeddedSearch.newTabPage.applyAutogeneratedTheme;
+
+/**
  * @param {number} task_version
  * @param {number} task_id
  */
@@ -180,6 +185,11 @@
 window.chrome.embeddedSearch.newTabPage.fixupAndValidateUrl;
 
 /**
+ * No params.
+ */
+window.chrome.embeddedSearch.newTabPage.getColorsInfo;
+
+/**
  * @param {number} rid
  */
 window.chrome.embeddedSearch.newTabPage.getMostVisitedItemData;
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index 0cb8db9..522ba2a 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -1006,7 +1006,8 @@
 }
 
 #backgrounds-menu .bg-sel-tile-bg,
-#backgrounds-image-menu .bg-sel-tile-bg {
+#backgrounds-image-menu .bg-sel-tile-bg,
+#colors-menu .bg-sel-tile-bg {
   border-radius: 4px;
   height: 176px;
   margin-bottom: 45px;
@@ -1016,28 +1017,33 @@
   width: 176px;
 }
 
-#backgrounds-image-menu .bg-sel-tile-bg {
+#backgrounds-image-menu .bg-sel-tile-bg,
+#colors-menu .bg-sel-tile-bg {
   margin-bottom: 8px;
 }
 
 .selected #backgrounds-default-icon,
-#backgrounds-image-menu .bg-sel-tile-bg.selected .bg-sel-tile {
+#backgrounds-image-menu .bg-sel-tile-bg.selected .bg-sel-tile,
+#colors-menu .bg-sel-tile-bg.selected .bg-sel-tile {
   height: 144px;
   margin: 16px 16px 0 16px;
   width: 144px;
 }
 
-#backgrounds-image-menu .bg-sel-tile-bg.selected .bg-sel-tile:focus {
+#backgrounds-image-menu .bg-sel-tile-bg.selected .bg-sel-tile:focus,
+#colors-menu .bg-sel-tile-bg.selected .bg-sel-tile:focus {
   outline: none;
 }
 
 #backgrounds-menu .bg-sel-tile,
-#backgrounds-image-menu .bg-sel-tile {
+#backgrounds-image-menu .bg-sel-tile,
+#colors-menu .bg-sel-tile {
   background-position: center;
   border-radius: 4px;
 }
 
-#backgrounds-menu .bg-sel-tile-title {
+#backgrounds-menu .bg-sel-tile-title,
+#colors-menu .bg-sel-tile-title {
   background-color: unset;
   color: rgb(var(--GG700-rgb));
   font-size: 13px;
@@ -1046,7 +1052,8 @@
   padding: 8px 0 24px 0;
 }
 
-html[darkmode=true] #backgrounds-menu .bg-sel-tile-title {
+html[darkmode=true] #backgrounds-menu .bg-sel-tile-title,
+html[darkmode=true] #colors-menu .bg-sel-tile-title {
   color: rgb(var(--GG200-rgb));
 }
 
@@ -1059,7 +1066,8 @@
 }
 
 #backgrounds-default.selected,
-#backgrounds-image-menu .bg-sel-tile-bg.selected {
+#backgrounds-image-menu .bg-sel-tile-bg.selected,
+#colors-menu .bg-sel-tile-bg.selected  {
   background-color: rgba(var(--GB900-rgb), .08);
 }
 
diff --git a/chrome/browser/resources/settings/about_page/about_page.html b/chrome/browser/resources/settings/about_page/about_page.html
index 8bec0a04..82136b5 100644
--- a/chrome/browser/resources/settings/about_page/about_page.html
+++ b/chrome/browser/resources/settings/about_page/about_page.html
@@ -269,8 +269,7 @@
         </div>
 </if>
         <img src="[[regulatoryInfo_.url]]" alt="[[regulatoryInfo_.text]]"
-            hidden$="[[!shouldShowRegulatoryInfo_(regulatoryInfo_)]]"
-            role="presentation">
+            hidden$="[[!shouldShowRegulatoryInfo_(regulatoryInfo_)]]">
       </div>
 </if>
     </settings-section>
diff --git a/chrome/browser/resources/settings/device_page/display.js b/chrome/browser/resources/settings/device_page/display.js
index e15ae9e..371dd50 100644
--- a/chrome/browser/resources/settings/device_page/display.js
+++ b/chrome/browser/resources/settings/device_page/display.js
@@ -719,14 +719,17 @@
   /**
    * Handles the event where the display size slider is being dragged, i.e. the
    * mouse or tap has not been released.
-   * @param {!Event} e
    * @private
    */
-  onDisplaySizeSliderDrag_: function(e) {
+  onDisplaySizeSliderDrag_: function() {
     if (!this.selectedDisplay) {
       return;
     }
-    this.updateLogicalResolutionText_(/** @type {number} */ (e.detail.value));
+
+    const sliderValue = this.$.displaySizeSlider.$$('#slider').value;
+    const zoomFactor = this.$.displaySizeSlider.ticks[sliderValue].value;
+    this.updateLogicalResolutionText_(
+        /** @type {number} */ (zoomFactor));
   },
 
   /**
diff --git a/chrome/browser/resources/settings/os_settings_manifest.json b/chrome/browser/resources/settings/os_settings_manifest.json
deleted file mode 100644
index 17d5330..0000000
--- a/chrome/browser/resources/settings/os_settings_manifest.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "name": "Settings",
-  "display": "standalone",
-  "icons": [
-    {
-      "src": "icon-192.png",
-      "sizes": "192x192",
-      "type": "image/png"
-    }
-  ],
-  "start_url": "/",
-  "theme_color": "#F8F9FA"
-}
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index 4f553c9..819d51d4 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -40,7 +40,7 @@
                  file="a11y_page/tts_subpage.html"
                  type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_MANIFEST"
-                 file="os_settings_manifest.json"
+                 file="manifest.json"
                  type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_ADD_SITE_DIALOG_HTML"
                  file="site_settings/add_site_dialog.html"
diff --git a/chrome/browser/resources/settings/os_settings_resources_vulcanized.grd b/chrome/browser/resources/settings/os_settings_resources_vulcanized.grd
index 24bcdcc..04aae4ac 100644
--- a/chrome/browser/resources/settings/os_settings_resources_vulcanized.grd
+++ b/chrome/browser/resources/settings/os_settings_resources_vulcanized.grd
@@ -38,10 +38,7 @@
                flattenhtml="true"
                type="BINDATA"
                compress="gzip" />
-      <include name="IDR_OS_SETTINGS_MANIFEST"
-               file="os_settings_manifest.json"
-               type="BINDATA"
-               compress="gzip" />
+      <include name="IDR_OS_SETTINGS_MANIFEST" file="manifest.json" type="BINDATA" compress="gzip" />
     </includes>
   </release>
 </grit>
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
index 8bf6fb7..b8ede66 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
@@ -178,14 +178,65 @@
     this.$$('add-printer-dialog').close();
   },
 
+  /**
+   * Handler for getPrinterInfo success.
+   * @param {!PrinterMakeModel} info
+   * @private
+   * */
+  onPrinterFound_: function(info) {
+    const newPrinter =
+        /** @type {CupsPrinterInfo}  */ (Object.assign({}, this.newPrinter));
+
+    newPrinter.printerManufacturer = info.manufacturer;
+    newPrinter.printerModel = info.model;
+    newPrinter.printerMakeAndModel = info.makeAndModel;
+    newPrinter.printerPpdReference.userSuppliedPpdUrl =
+        info.ppdRefUserSuppliedPpdUrl;
+    newPrinter.printerPpdReference.effectiveMakeAndModel =
+        info.ppdRefEffectiveMakeAndModel;
+    newPrinter.printerPpdReference.autoconf = info.autoconf;
+    newPrinter.printerPpdReferenceResolved = info.ppdReferenceResolved;
+
+    this.newPrinter = newPrinter;
+
+
+    // Add the printer if it's configurable. Otherwise, forward to the
+    // manufacturer dialog.
+    this.$$('add-printer-dialog').close();
+    if (this.newPrinter.printerPpdReferenceResolved) {
+      settings.CupsPrintersBrowserProxyImpl.getInstance().addCupsPrinter(
+          this.newPrinter);
+    } else {
+      this.fire('open-manufacturer-model-dialog');
+    }
+  },
+
+  /**
+   * Handler for getPrinterInfo failure.
+   * @param {*} rejected
+   * @private
+   */
+  infoFailed_: function(rejected) {
+    this.$$('add-printer-dialog').close();
+    this.fire('open-manufacturer-model-dialog');
+  },
+
   /** @private */
   addPressed_: function() {
     // Set the default printer queue to be "ipp/print".
     if (!this.newPrinter.printerQueue) {
       this.set('newPrinter.printerQueue', 'ipp/print');
     }
-    this.$$('add-printer-dialog').close();
-    this.fire('open-configuring-printer-dialog');
+
+    if (this.newPrinter.printerProtocol == 'ipp' ||
+        this.newPrinter.printerProtocol == 'ipps') {
+      settings.CupsPrintersBrowserProxyImpl.getInstance()
+          .getPrinterInfo(this.newPrinter)
+          .then(this.onPrinterFound_.bind(this), this.infoFailed_.bind(this));
+    } else {
+      this.$$('add-printer-dialog').close();
+      this.fire('open-manufacturer-model-dialog');
+    }
   },
 
   /**
@@ -515,41 +566,7 @@
   /** @private */
   switchToManufacturerDialog_: function() {
     this.$$('add-printer-configuring-dialog').close();
-    this.fire('open-manufacturer-model-dialog');
-  },
-
-  /**
-   * Handler for getPrinterInfo success.
-   * @param {!PrinterMakeModel} info
-   * @private
-   * */
-  onPrinterFound_: function(info) {
-    this.newPrinter.printerManufacturer = info.manufacturer;
-    this.newPrinter.printerModel = info.model;
-    this.newPrinter.printerMakeAndModel = info.makeAndModel;
-    this.newPrinter.printerPpdReference.userSuppliedPpdUrl =
-        info.ppdRefUserSuppliedPpdUrl;
-    this.newPrinter.printerPpdReference.effectiveMakeAndModel =
-        info.ppdRefEffectiveMakeAndModel;
-    this.newPrinter.printerPpdReference.autoconf = info.autoconf;
-    this.newPrinter.printerPpdReferenceResolved = info.ppdReferenceResolved;
-
-    // Add the printer if it's configurable. Otherwise, forward to the
-    // manufacturer dialog.
-    if (this.newPrinter.printerPpdReferenceResolved) {
-      this.addPrinter_();
-    } else {
-      this.switchToManufacturerDialog_();
-    }
-  },
-
-  /**
-   * Handler for getPrinterInfo failure.
-   * @param {*} rejected
-   * @private
-   */
-  infoFailed_: function(rejected) {
-    this.switchToManufacturerDialog_();
+    this.openManufacturerModelDialog_();
   },
 
   /** @private */
@@ -566,18 +583,8 @@
       this.configuringDialogTitle =
           loadTimeData.getString('manufacturerAndModelDialogTitle');
       this.addPrinter_();
-    } else if (this.previousDialog_ == AddPrinterDialogs.MANUALLY) {
-      this.configuringDialogTitle =
-          loadTimeData.getString('addPrintersManuallyTitle');
-      if (this.newPrinter.printerProtocol == 'ipp' ||
-          this.newPrinter.printerProtocol == 'ipps') {
-        settings.CupsPrintersBrowserProxyImpl.getInstance()
-            .getPrinterInfo(this.newPrinter)
-            .then(this.onPrinterFound_.bind(this), this.infoFailed_.bind(this));
-      } else {
-        // Defer the switch until all the elements are drawn.
-        this.async(this.switchToManufacturerDialog_.bind(this));
-      }
+    } else {
+      assertNotReached('Opening configuring dialog from invalid place');
     }
   },
 
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.js b/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.js
index 1be440a..662cdb8 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.js
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.js
@@ -15,7 +15,7 @@
      * |setPIN|. In this case the second number is either the number of tries
      * remaining to correctly specify the current PIN, or else null to indicate
      * that no PIN is currently set.
-     * @return {!Promise<Array<number>>}
+     * @return {!Promise<!Array<number>>}
      */
     startSetPIN() {}
 
@@ -24,7 +24,7 @@
      * whose meaning is the same as with |startSetPIN|. The first number will
      * always be 1 to indicate that the process has completed and thus the
      * second will be the CTAP error code.
-     * @return {!Promise<Array<number>>}
+     * @return {!Promise<!Array<number>>}
      */
     setPIN(oldPIN, newPIN) {}
 
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_reset_dialog.html b/chrome/browser/resources/settings/privacy_page/security_keys_reset_dialog.html
index 108da71..9313a82 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_reset_dialog.html
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_reset_dialog.html
@@ -20,7 +20,8 @@
          on-close="closeDialog_">
       <div slot="title">[[title_]]</div>
       <div slot="body">
-        <iron-pages attr-for-selected="id" selected="[[shown_]]">
+        <iron-pages attr-for-selected="id" selected="[[shown_]]"
+            on-iron-select="onIronSelect_">
           <div id="initial">
             <p>$i18n{securityKeysResetStep1}</p>
             <paper-spinner-lite active></paper-spinner-lite>
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_reset_dialog.js b/chrome/browser/resources/settings/privacy_page/security_keys_reset_dialog.js
index 8d433ba8..1f0f35aa 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_reset_dialog.js
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_reset_dialog.js
@@ -96,6 +96,16 @@
   },
 
   /**
+   * @param {!Event} e
+   * @private
+   */
+  onIronSelect_: function(e) {
+    // Prevent this event from bubbling since it is unnecessarily triggering the
+    // listener within settings-animated-pages.
+    e.stopPropagation();
+  },
+
+  /**
      @param {number} code CTAP error code.
      @return {string} Contents of the error string that may be displayed
           to the user. Used automatically by Polymer.
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_set_pin_dialog.html b/chrome/browser/resources/settings/privacy_page/security_keys_set_pin_dialog.html
index 9e4f34d..2e6c0a49 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_set_pin_dialog.html
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_set_pin_dialog.html
@@ -2,6 +2,8 @@
 
 <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_icon_button/cr_icon_button.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html">
@@ -15,10 +17,13 @@
     <style include="settings-shared">
       cr-input {
         display: inline-block;
-        padding-inline-end: 2em;
         --cr-input-width: 8em;
       }
 
+      #newPIN {
+        padding-inline-end: 2em;
+      }
+
       #newPINRow {
         display: flex;
         flex-direction: row;
@@ -45,30 +50,57 @@
           </div>
 
           <div id="pinPrompt">
-            <div id="currentPINEntry">
+            <div id="currentPINEntry" hidden="[[!showCurrentEntry_]]">
               <p>$i18nRaw{securityKeysCurrentPINIntro}</p>
 
-              <cr-input id="currentPIN" value="{{currentPIN_}}" minLength="4"
-                  maxLength="255" spellcheck="false"
-                  on-input="validateCurrentPIN_" invalid="[[!currentPINValid_]]"
-                  label="$i18n{securityKeysCurrentPIN}" tabindex="0"
-                  type="password"
-                  error-message="[[mismatchErrorText_(mismatchErrorVisible_, retries_)]]">
-              </cr-input>
+              <div id="currentPINRow">
+                <cr-input id="currentPIN" value="{{currentPIN_}}" minLength="4"
+                    maxLength="255" spellcheck="false"
+                    on-input="onCurrentPINInput_"
+                    invalid="[[isNonEmpty_(currentPINError_)]]"
+                    label="$i18n{securityKeysCurrentPIN}" tabindex="0"
+                    type$="[[inputType_(pinsVisible_)]]"
+                    error-message="[[currentPINError_]]">
+                  <cr-icon-button slot="suffix" id="showPINsButton"
+                      class$="[[showPINsClass_(pinsVisible_)]]"
+                      title="[[showPINsTitle_(pinsVisible_)]]"
+                      focus-row-control focus-type="showPassword"
+                      on-click="showPINsClick_"></cr-icon-button>
+                </cr-input>
+
+              </div>
             </div>
 
             <p>$i18n{securityKeysNewPIN}</p>
 
             <div id="newPINRow">
               <cr-input id="newPIN" value="{{newPIN_}}" minLength="4"
-                  maxLength="255" spellcheck="false" on-input="validateNewPIN_"
-                  invalid="[[!newPINValid_]]" label="$i18n{securityKeysPIN}"
-                  tabindex="0" type="password"></cr-input>
+                  maxLength="255" spellcheck="false" on-input="onNewPINInput_"
+                  label="$i18n{securityKeysPIN}"
+                  tabindex="0" type$="[[inputType_(pinsVisible_)]]"
+                  invalid="[[isNonEmpty_(newPINError_)]]"
+                  error-message="[[newPINError_]]">
+                <!-- If a show/hide icon is included in this row, this div is
+                     needed to ensure that the cr-input is the same height
+                     as the one to the right. Otherwise they don't vertically
+                     align -->
+                <div style="height: 36px" slot="suffix"
+                     hidden="[[showCurrentEntry_]]"></div>
+              </cr-input>
               <cr-input id="confirmPIN" value="{{confirmPIN_}}" minLength="4"
                   maxLength="255" spellcheck="false"
-                  on-input="validateConfirmPIN_" invalid="[[!confirmPINValid_]]"
+                  on-input="onConfirmPINInput_"
                   label="$i18n{securityKeysConfirmPIN}" tabindex="0"
-                  type="password"></cr-input>
+                  invalid="[[isNonEmpty_(confirmPINError_)]]"
+                  type$="[[inputType_(pinsVisible_)]]"
+                  error-message="[[confirmPINError_]]">
+                <cr-icon-button slot="suffix"
+                    class$="[[showPINsClass_(pinsVisible_)]]"
+                    title="[[showPINsTitle_(pinsVisible_)]]"
+                    hidden="[[showCurrentEntry_]]"
+                    focus-row-control focus-type="showPassword"
+                    on-click="showPINsClick_"></cr-icon-button>
+              </cr-input>
             </div>
           </div>
 
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_set_pin_dialog.js b/chrome/browser/resources/settings/privacy_page/security_keys_set_pin_dialog.js
index d61b566..2f4d2ff 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_set_pin_dialog.js
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_set_pin_dialog.js
@@ -6,33 +6,6 @@
 'use strict';
 
 /**
-   @param {string} pin A candidate PIN.
-   @return {boolean} Whether the parameter was a valid PIN.
-   @private
- */
-function isValidPIN(pin) {
-  // The UTF-8 encoding of the PIN must be between 4 and 63 bytes, and the
-  // final byte cannot be zero.
-  const utf8Encoded = new TextEncoder().encode(pin);
-  if (utf8Encoded.length < 4 || utf8Encoded.length > 63 ||
-      utf8Encoded[utf8Encoded.length - 1] == 0) {
-    return false;
-  }
-
-  // A PIN must contain at least four code-points. Javascript strings are UCS-2
-  // and the |length| property counts UCS-2 elements, not code-points. (For
-  // example, '\u{1f6b4}'.length == 2, but it's a single code-point.) Therefore,
-  // iterate over the string (which does yield codepoints) and check that four
-  // or more were seen.
-  let length = 0;
-  for (const codepoint of pin) {
-    length++;
-  }
-
-  return length >= 4;
-}
-
-/**
  * @fileoverview 'settings-security-keys-set-pin-dialog' is a dialog for
  * setting and changing security key PINs.
  */
@@ -99,15 +72,43 @@
     errorCode_: Number,
 
     /**
-     * Whether the error message indicating an incorrect PIN should be visible.
+     * Whether an entry for the current PIN should be displayed. (If no PIN
+     * has been set then it won't be shown.)
      * @private
      */
-    mismatchErrorVisible_: {
+    showCurrentEntry_: {
       type: Boolean,
       value: false,
     },
 
     /**
+     * Error string to display under the current PIN entry, or empty.
+     * @private
+     */
+    currentPINError_: {
+      type: String,
+      value: '',
+    },
+
+    /**
+     * Error string to display under the new PIN entry, or empty.
+     * @private
+     */
+    newPINError_: {
+      type: String,
+      value: '',
+    },
+
+    /**
+     * Error string to display under the confirmation PIN entry, or empty.
+     * @private
+     */
+    confirmPINError_: {
+      type: String,
+      value: '',
+    },
+
+    /**
      * Whether the dialog process has completed, successfully or otherwise.
      * @private
      */
@@ -125,6 +126,16 @@
       value: 'initial',
     },
 
+    /**
+     * Whether the contents of the PIN entries are visible, or are displayed
+     * like passwords.
+     * @private
+     */
+    pinsVisible_: {
+      type: Boolean,
+      value: false,
+    },
+
     /** @private */
     title_: String,
   },
@@ -138,25 +149,25 @@
     this.browserProxy_ = settings.SecurityKeysBrowserProxyImpl.getInstance();
     this.$.dialog.showModal();
 
-    this.browserProxy_.startSetPIN().then(result => {
-      if (result[0]) {
-        // Operation is complete. result[1] is a CTAP error code. See
+    this.browserProxy_.startSetPIN().then(([success, errorCode]) => {
+      if (success) {
+        // Operation is complete. errorCode is a CTAP error code. See
         // https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#error-responses
-        if (result[1] == 1 /* INVALID_COMMAND */) {
+        if (errorCode == 1 /* INVALID_COMMAND */) {
           this.shown_ = 'noPINSupport';
           this.finish_();
-        } else if (result[1] == 52 /* temporarily locked */) {
+        } else if (errorCode == 52 /* temporarily locked */) {
           this.shown_ = 'reinsert';
           this.finish_();
-        } else if (result[1] == 50 /* locked */) {
+        } else if (errorCode == 50 /* locked */) {
           this.shown_ = 'locked';
           this.finish_();
         } else {
-          this.errorCode_ = result[1];
+          this.errorCode_ = errorCode;
           this.shown_ = 'error';
           this.finish_();
         }
-      } else if (result[1] == 0) {
+      } else if (errorCode == 0) {
         // A device can also signal that it is locked by returning zero retries.
         this.shown_ = 'locked';
         this.finish_();
@@ -166,16 +177,17 @@
         this.currentPINValid_ = true;
         this.newPINValid_ = true;
         this.confirmPINValid_ = true;
+        this.setPINButtonValid_ = true;
 
-        this.retries_ = result[1];
+        this.retries_ = errorCode;
         // retries_ may be null to indicate that there is currently no PIN set.
         let focusTarget;
         if (this.retries_ === null) {
-          this.$.currentPINEntry.hidden = true;
+          this.showCurrentEntry_ = false;
           focusTarget = this.$.newPIN;
           this.title_ = this.i18n('securityKeysSetPINCreateTitle');
         } else {
-          this.$.currentPINEntry.hidden = false;
+          this.showCurrentEntry_ = true;
           focusTarget = this.$.currentPIN;
           this.title_ = this.i18n('securityKeysSetPINChangeTitle');
         }
@@ -221,40 +233,97 @@
   },
 
   /** @private */
-  updatePINButtonValid_: function() {
-    this.setPINButtonValid_ =
-        (this.$.currentPINEntry.hidden ||
-         (this.currentPINValid_ && this.currentPIN_.length > 0)) &&
-        this.newPINValid_ && this.newPIN_.length > 0 && this.confirmPINValid_ &&
-        this.confirmPIN_.length > 0;
-  },
-
-  /** @private */
-  validateCurrentPIN_: function() {
-    this.currentPINValid_ = isValidPIN(this.currentPIN_);
-    this.updatePINButtonValid_();
+  onCurrentPINInput_: function() {
     // Typing in the current PIN box after an error makes the error message
     // disappear.
-    this.mismatchErrorVisible_ = false;
+    this.currentPINError_ = '';
   },
 
   /** @private */
-  validateNewPIN_: function() {
-    this.newPINValid_ = isValidPIN(this.newPIN_);
-    // The new PIN might have been changed to match the confirmation PIN, thus
-    // changing it might make the confirmation PIN valid. An empty value is
-    // considered valid to stop it immediately turning red before the user has
-    // entered anything, but |updatePINButtonValid_| knows that it needs to be
-    // non-empty before the dialog can be submitted.
-    this.confirmPINValid_ = this.confirmPIN_.length == 0 ||
-        (this.newPINValid_ && this.confirmPIN_ == this.newPIN_);
-    this.updatePINButtonValid_();
+  onNewPINInput_: function() {
+    // Typing in the new PIN box after an error makes the error message
+    // disappear.
+    this.newPINError_ = '';
   },
 
   /** @private */
-  validateConfirmPIN_: function() {
-    this.confirmPINValid_ = this.confirmPIN_ == this.newPIN_;
-    this.updatePINButtonValid_();
+  onConfirmPINInput_: function() {
+    // Typing in the confirm PIN box after an error makes the error message
+    // disappear.
+    this.confirmPINError_ = '';
+  },
+
+  /**
+    @param {string} pin A candidate PIN.
+    @return {string} An error string or else '' to indicate validity.
+    @private
+  */
+  isValidPIN_: function(pin) {
+    // The UTF-8 encoding of the PIN must be between 4 and 63 bytes, and the
+    // final byte cannot be zero.
+    const utf8Encoded = new TextEncoder().encode(pin);
+    if (utf8Encoded.length < 4) {
+      return this.i18n('securityKeysPINTooShort');
+    }
+    if (utf8Encoded.length > 63 ||
+        // If the PIN somehow has a NUL at the end then it's invalid, but this
+        // is so obscure that we don't try to message it. Rather we just say
+        // that it's too long because trimming the final character is the best
+        // response by the user.
+        utf8Encoded[utf8Encoded.length - 1] == 0) {
+      return this.i18n('securityKeysPINTooLong');
+    }
+
+    // A PIN must contain at least four code-points. Javascript strings are
+    // UCS-2 and the |length| property counts UCS-2 elements, not code-points.
+    // (For example, '\u{1f6b4}'.length == 2, but it's a single code-point.)
+    // Therefore, iterate over the string (which does yield codepoints) and
+    // check that four or more were seen.
+    let length = 0;
+    for (const codepoint of pin) {
+      length++;
+    }
+
+    if (length < 4) {
+      return this.i18n('securityKeysPINTooShort');
+    }
+
+    return '';
+  },
+
+  /**
+   * @param {number} retries The number of PIN attempts remaining.
+   * @return {string} The message to show under the text box.
+   * @private
+   */
+  mismatchError_: function(retries) {
+    // Warn the user if the number of retries is getting low.
+    if (1 < retries && retries <= 3) {
+      return this.i18n('securityKeysPINIncorrectRetriesPl', retries.toString());
+    }
+    if (retries == 1) {
+      return this.i18n('securityKeysPINIncorrectRetriesSin');
+    }
+    return this.i18n('securityKeysPINIncorrect');
+  },
+
+  /**
+   * Called to set focus from inside a callback.
+   * @private
+   */
+  focusOn_: function(focusTarget) {
+    // Focus cannot be set directly from within a backend callback. Also,
+    // directly focusing |currentPIN| doesn't always seem to work(!). Thus
+    // focus something else first, which is a hack that seems to solve the
+    // problem.
+    let preFocusTarget = this.$.newPIN;
+    if (preFocusTarget == focusTarget) {
+      preFocusTarget = this.$.currentPIN;
+    }
+    window.setTimeout(function() {
+      preFocusTarget.focus();
+      focusTarget.focus();
+    }, 0);
   },
 
   /**
@@ -262,6 +331,29 @@
    * @private
    */
   pinSubmitNew_: function() {
+    if (this.showCurrentEntry_) {
+      this.currentPINError_ = this.isValidPIN_(this.currentPIN_);
+      if (this.currentPINError_ != '') {
+        this.focusOn_(this.$.currentPIN);
+        this.fire('ui-ready');  // for test synchronization.
+        return;
+      }
+    }
+
+    this.newPINError_ = this.isValidPIN_(this.newPIN_);
+    if (this.newPINError_ != '') {
+      this.focusOn_(this.$.newPIN);
+      this.fire('ui-ready');  // for test synchronization.
+      return;
+    }
+
+    if (this.newPIN_ != this.confirmPIN_) {
+      this.confirmPINError_ = this.i18n('securityKeysPINMismatch');
+      this.focusOn_(this.$.confirmPIN);
+      this.fire('ui-ready');  // for test synchronization.
+      return;
+    }
+
     this.setPINButtonValid_ = false;
     this.browserProxy_.setPIN(this.currentPIN_, this.newPIN_).then(result => {
       // This call always completes the process so result[0] is always 1.
@@ -277,21 +369,11 @@
         this.shown_ = 'locked';
         this.finish_();
       } else if (result[1] == 49 /* PIN_INVALID */) {
-        this.currentPIN_ = '';
         this.currentPINValid_ = false;
         this.retries_--;
-        this.mismatchErrorVisible_ = true;
-
-        // Focus cannot be set directly from within a backend callback. Also,
-        // directly focusing |currentPIN| doesn't always seem to work(!). Thus
-        // focus something else first, which is a hack that seems to solve the
-        // problem.
-        const preFocusTarget = this.$.newPIN;
-        const focusTarget = this.$.currentPIN;
-        window.setTimeout(function() {
-          preFocusTarget.focus();
-          focusTarget.focus();
-        }, 0);
+        this.currentPINError_ = this.mismatchError_(this.retries_);
+        this.setPINButtonValid_ = true;
+        this.focusOn_(this.$.currentPIN);
         this.fire('ui-ready');  // for test synchronization.
       } else {
         // Unknown error.
@@ -303,56 +385,73 @@
   },
 
   /**
+   * onClick handler for the show/hide icon.
+   * @private
+   */
+  showPINsClick_: function() {
+    this.pinsVisible_ = !this.pinsVisible_;
+  },
+
+  /**
+   * Polymer helper function to detect when an error string is empty.
+   * @param {string} s Arbitrary string
+   * @return {boolean} True iff |s| is non-empty.
+   * @private
+   */
+  isNonEmpty_: function(s) {
+    return s != '';
+  },
+
+  /**
    * Called by Polymer when |errorCode_| changes to set the error string.
-   * @param {number} code A CTAP error code.
    * @private
    */
-  pinFailed_: function(code) {
-    if (code === null) {
+  pinFailed_: function() {
+    if (this.errorCode_ === null) {
       return '';
     }
-    return this.i18n('securityKeysPINError', code.toString());
+    return this.i18n('securityKeysPINError', this.errorCode_.toString());
   },
 
   /**
-   * Called by Polymer to set the error text displayed when the user enters an
-   * incorrect PIN.
-   * @param {boolean} show Whether or not an error message should be shown.
-   * @param {number} retries The number of PIN attempts remaining.
-   * @return {string} The message to show under the text box.
-   * @private
-   */
-  mismatchErrorText_: function(show, retries) {
-    if (!show) {
-      return '';
-    }
-
-    // Warn the user if the number of retries is getting low.
-    if (1 < retries && retries <= 3) {
-      return this.i18n('securityKeysPINIncorrectRetriesPl', retries.toString());
-    }
-    if (retries == 1) {
-      return this.i18n('securityKeysPINIncorrectRetriesSin');
-    }
-    return this.i18n('securityKeysPINIncorrect');
-  },
-
-  /**
-   * @param {boolean} complete Whether the dialog process is complete.
    * @return {string} The class of the Ok / Cancel button.
    * @private
    */
-  maybeActionButton_: function(complete) {
-    return complete ? 'action-button' : 'cancel-button';
+  maybeActionButton_: function() {
+    return this.complete_ ? 'action-button' : 'cancel-button';
   },
 
   /**
-   * @param {boolean} complete Whether the dialog process is complete.
    * @return {string} The label of the Ok / Cancel button.
    * @private
    */
-  closeText_: function(complete) {
-    return this.i18n(complete ? 'ok' : 'cancel');
+  closeText_: function() {
+    return this.i18n(this.complete_ ? 'ok' : 'cancel');
+  },
+
+  /**
+   * @return {string} The class (and thus icon) to be displayed.
+   * @private
+   */
+  showPINsClass_: function() {
+    return 'icon-visibility' + (this.pinsVisible_ ? '-off' : '');
+  },
+
+  /**
+   * @return {string} The tooltip for the icon.
+   * @private
+   */
+  showPINsTitle_: function() {
+    return this.i18n(
+        this.pinsVisible_ ? 'securityKeysHidePINs' : 'securityKeysShowPINs');
+  },
+
+  /**
+   * @return {string} The PIN-input element type.
+   * @private
+   */
+  inputType_: function() {
+    return this.pinsVisible_ ? 'text' : 'password';
   },
 });
 })();
diff --git a/chrome/browser/resources_util_unittest.cc b/chrome/browser/resources_util_unittest.cc
index 2693879a..0f7059b3 100644
--- a/chrome/browser/resources_util_unittest.cc
+++ b/chrome/browser/resources_util_unittest.cc
@@ -24,7 +24,7 @@
     // IDRs from chrome/app/theme/theme_resources.grd should be valid.
     {"IDR_ERROR_NETWORK_GENERIC", IDR_ERROR_NETWORK_GENERIC},
     // IDRs from ui/resources/ui_resources.grd should be valid.
-    {"IDR_FOLDER_CLOSED", IDR_FOLDER_CLOSED},
+    {"IDR_DEFAULT_FAVICON", IDR_DEFAULT_FAVICON},
 #if defined(OS_CHROMEOS)
     // Check IDRs from ui/chromeos/resources/ui_chromeos_resources.grd.
     {"IDR_LOGIN_DEFAULT_USER", IDR_LOGIN_DEFAULT_USER},
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index c0fc1f7..067e1db 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -235,8 +235,6 @@
     "webui/download_internals/download_internals_ui_message_handler.h",
     "webui/engagement/site_engagement_ui.cc",
     "webui/engagement/site_engagement_ui.h",
-    "webui/favicon_source.cc",
-    "webui/favicon_source.h",
     "webui/fileicon_source.cc",
     "webui/fileicon_source.h",
     "webui/flags_ui.cc",
@@ -1173,6 +1171,8 @@
       "webui/extensions/extensions_internals_source.h",
       "webui/extensions/extensions_ui.cc",
       "webui/extensions/extensions_ui.h",
+      "webui/favicon_source.cc",
+      "webui/favicon_source.h",
       "webui/foreign_session_handler.cc",
       "webui/foreign_session_handler.h",
       "webui/hats/hats_handler.cc",
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.cc b/chrome/browser/ui/app_list/app_list_client_impl.cc
index 78d603f..e85a86e2 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -190,6 +190,7 @@
   requested_model_updater->ActivateChromeItem(id, event_flags);
 
   // Send a training signal to the search controller.
+  CHECK(current_model_updater_);
   const auto* item = current_model_updater_->FindItem(id);
   if (item) {
     search_controller_->Train(
diff --git a/chrome/browser/ui/app_list/extension_app_utils.cc b/chrome/browser/ui/app_list/extension_app_utils.cc
index 002ddce..9affea2 100644
--- a/chrome/browser/ui/app_list/extension_app_utils.cc
+++ b/chrome/browser/ui/app_list/extension_app_utils.cc
@@ -16,11 +16,6 @@
 #include "ui/views/controls/menu/menu_config.h"
 #include "ui/views/vector_icons.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/web_applications/system_web_app_ui_utils_chromeos.h"
-#endif
-
 namespace app_list {
 
 namespace {
@@ -32,26 +27,6 @@
 
 bool ShouldShowInLauncher(const extensions::Extension* extension,
                           content::BrowserContext* context) {
-  // TODO(crbug.com/971029): Make this a per System Web App property that is
-  // accessible by querying the SystemWebAppManager.
-#if defined(OS_CHROMEOS)
-  Profile* profile = Profile::FromBrowserContext(context);
-  // These System Web Apps should not show in the launcher as they are added
-  // as internal apps.
-  web_app::SystemAppType hidden_system_web_apps[] = {
-      // TODO(crbug.com/836128): Remove this once OS Settings is launched, and
-      // permanently migrate OS Settings from an internal app to a full System
-      // Web App.
-      web_app::SystemAppType::SETTINGS,
-  };
-  for (auto app_type : hidden_system_web_apps) {
-    if (extension->id() ==
-        web_app::GetAppIdForSystemWebApp(profile, app_type)) {
-      return false;
-    }
-  }
-#endif
-
   return !HideInLauncherById(extension->id()) &&
          chromeos::DemoSession::ShouldDisplayInAppLauncher(extension->id()) &&
          extensions::ui_util::ShouldDisplayInAppLauncher(extension, context);
diff --git a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
index 6db89e1..d94fc04 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
@@ -15,8 +15,6 @@
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/settings_window_manager_observer_chromeos.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/system_web_app_manager.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/account_id/account_id.h"
 #include "components/session_manager/core/session_manager.h"
@@ -174,11 +172,6 @@
     int new_settings_count_ = 0;
   } observer;
 
-  // Install the Settings App.
-  web_app::WebAppProvider::Get(browser()->profile())
-      ->system_web_app_manager()
-      .InstallSystemAppsForTesting();
-
   auto* settings = chrome::SettingsWindowManager::GetInstance();
   settings->AddObserver(&observer);
 
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index 1323b8e..ae2edff 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -53,8 +53,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/test/test_app_window_icon_observer.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
-#include "chrome/browser/web_applications/system_web_app_manager.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -1733,10 +1731,6 @@
 
 // Ensure opening settings and task manager windows create new shelf items.
 IN_PROC_BROWSER_TEST_F(ShelfAppBrowserTest, SettingsAndTaskManagerWindows) {
-  // Install the Settings App.
-  web_app::WebAppProvider::Get(browser()->profile())
-      ->system_web_app_manager()
-      .InstallSystemAppsForTesting();
   chrome::SettingsWindowManager* settings_manager =
       chrome::SettingsWindowManager::GetInstance();
 
diff --git a/chrome/browser/ui/ash/test_wallpaper_controller.cc b/chrome/browser/ui/ash/test_wallpaper_controller.cc
index 0293d6e..2d78c9f 100644
--- a/chrome/browser/ui/ash/test_wallpaper_controller.cc
+++ b/chrome/browser/ui/ash/test_wallpaper_controller.cc
@@ -34,7 +34,7 @@
 }
 
 void TestWallpaperController::SetCustomWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id,
     const std::string& file_name,
     ash::WallpaperLayout layout,
@@ -44,7 +44,7 @@
 }
 
 void TestWallpaperController::SetOnlineWallpaperIfExists(
-    const ash::WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& url,
     ash::WallpaperLayout layout,
     bool preview_mode,
@@ -53,7 +53,7 @@
 }
 
 void TestWallpaperController::SetOnlineWallpaperFromData(
-    const ash::WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& image_data,
     const std::string& url,
     ash::WallpaperLayout layout,
@@ -63,7 +63,7 @@
 }
 
 void TestWallpaperController::SetDefaultWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id,
     bool show_wallpaper) {
   ++set_default_wallpaper_count_;
@@ -76,7 +76,7 @@
 }
 
 void TestWallpaperController::SetPolicyWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id,
     const std::string& data) {
   NOTIMPLEMENTED();
@@ -88,7 +88,7 @@
 }
 
 bool TestWallpaperController::SetThirdPartyWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id,
     const std::string& file_name,
     ash::WallpaperLayout layout,
@@ -106,13 +106,12 @@
 }
 
 void TestWallpaperController::UpdateCustomWallpaperLayout(
-    const ash::WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     ash::WallpaperLayout layout) {
   NOTIMPLEMENTED();
 }
 
-void TestWallpaperController::ShowUserWallpaper(
-    const ash::WallpaperUserInfo& user_info) {
+void TestWallpaperController::ShowUserWallpaper(const AccountId& account_id) {
   NOTIMPLEMENTED();
 }
 
@@ -135,13 +134,13 @@
 }
 
 void TestWallpaperController::RemoveUserWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id) {
   ++remove_user_wallpaper_count_;
 }
 
 void TestWallpaperController::RemovePolicyWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+    const AccountId& account_id,
     const std::string& wallpaper_files_id) {
   NOTIMPLEMENTED();
 }
diff --git a/chrome/browser/ui/ash/test_wallpaper_controller.h b/chrome/browser/ui/ash/test_wallpaper_controller.h
index 25cfff8..e1353ca 100644
--- a/chrome/browser/ui/ash/test_wallpaper_controller.h
+++ b/chrome/browser/ui/ash/test_wallpaper_controller.h
@@ -42,53 +42,53 @@
             const base::FilePath& wallpapers,
             const base::FilePath& custom_wallpapers,
             const base::FilePath& device_policy_wallpaper) override;
-  void SetCustomWallpaper(const ash::WallpaperUserInfo& user_info,
+  void SetCustomWallpaper(const AccountId& account_id,
                           const std::string& wallpaper_files_id,
                           const std::string& file_name,
                           ash::WallpaperLayout layout,
                           const gfx::ImageSkia& image,
                           bool preview_mode) override;
   void SetOnlineWallpaperIfExists(
-      const ash::WallpaperUserInfo& user_info,
+      const AccountId& account_id,
       const std::string& url,
       ash::WallpaperLayout layout,
       bool preview_mode,
       SetOnlineWallpaperIfExistsCallback callback) override;
   void SetOnlineWallpaperFromData(
-      const ash::WallpaperUserInfo& user_info,
+      const AccountId& account_id,
       const std::string& image_data,
       const std::string& url,
       ash::WallpaperLayout layout,
       bool preview_mode,
       SetOnlineWallpaperFromDataCallback callback) override;
-  void SetDefaultWallpaper(const ash::WallpaperUserInfo& user_info,
+  void SetDefaultWallpaper(const AccountId& account_id,
                            const std::string& wallpaper_files_id,
                            bool show_wallpaper) override;
   void SetCustomizedDefaultWallpaperPaths(
       const base::FilePath& customized_default_small_path,
       const base::FilePath& customized_default_large_path) override;
-  void SetPolicyWallpaper(const ash::WallpaperUserInfo& user_info,
+  void SetPolicyWallpaper(const AccountId& account_id,
                           const std::string& wallpaper_files_id,
                           const std::string& data) override;
   void SetDevicePolicyWallpaperPath(
       const base::FilePath& device_policy_wallpaper_path) override;
-  bool SetThirdPartyWallpaper(const ash::WallpaperUserInfo& user_info,
+  bool SetThirdPartyWallpaper(const AccountId& account_id,
                               const std::string& wallpaper_files_id,
                               const std::string& file_name,
                               ash::WallpaperLayout layout,
                               const gfx::ImageSkia& image) override;
   void ConfirmPreviewWallpaper() override;
   void CancelPreviewWallpaper() override;
-  void UpdateCustomWallpaperLayout(const ash::WallpaperUserInfo& user_info,
+  void UpdateCustomWallpaperLayout(const AccountId& account_id,
                                    ash::WallpaperLayout layout) override;
-  void ShowUserWallpaper(const ash::WallpaperUserInfo& user_info) override;
+  void ShowUserWallpaper(const AccountId& account_id) override;
   void ShowSigninWallpaper() override;
   void ShowOneShotWallpaper(const gfx::ImageSkia& image) override;
   void ShowAlwaysOnTopWallpaper(const base::FilePath& image_path) override;
   void RemoveAlwaysOnTopWallpaper() override;
-  void RemoveUserWallpaper(const ash::WallpaperUserInfo& user_info,
+  void RemoveUserWallpaper(const AccountId& account_id,
                            const std::string& wallpaper_files_id) override;
-  void RemovePolicyWallpaper(const ash::WallpaperUserInfo& user_info,
+  void RemovePolicyWallpaper(const AccountId& account_id,
                              const std::string& wallpaper_files_id) override;
   void GetOfflineWallpaperList(
       GetOfflineWallpaperListCallback callback) override;
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client.cc b/chrome/browser/ui/ash/wallpaper_controller_client.cc
index 298ba684..e3b718e 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
 
-#include "ash/public/cpp/wallpaper_user_info.h"
 #include "base/bind.h"
 #include "base/hash/sha1.h"
 #include "base/path_service.h"
@@ -40,28 +39,8 @@
 
 WallpaperControllerClient* g_wallpaper_controller_client_instance = nullptr;
 
-// Creates a WallpaperUserInfo for the account id. Returns nullptr if user
-// manager cannot find the user.
-base::Optional<ash::WallpaperUserInfo> AccountIdToWallpaperUserInfo(
-    const AccountId& account_id) {
-  if (!account_id.is_valid()) {
-    // |account_id| may be invalid in tests.
-    return base::nullopt;
-  }
-  const user_manager::User* user =
-      user_manager::UserManager::Get()->FindUser(account_id);
-  if (!user)
-    return base::nullopt;
-
-  ash::WallpaperUserInfo wallpaper_user_info;
-  wallpaper_user_info.account_id = account_id;
-  wallpaper_user_info.type = user->GetType();
-  wallpaper_user_info.is_ephemeral =
-      user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
-          account_id);
-  wallpaper_user_info.has_gaia_account = user->HasGaiaAccount();
-
-  return wallpaper_user_info;
+bool IsKnownUser(const AccountId& account_id) {
+  return user_manager::UserManager::Get()->IsKnownUser(account_id);
 }
 
 // This has once been copied from
@@ -198,12 +177,10 @@
     ash::WallpaperLayout layout,
     const gfx::ImageSkia& image,
     bool preview_mode) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
-  if (!user_info)
+  if (!IsKnownUser(account_id))
     return;
-  wallpaper_controller_->SetCustomWallpaper(user_info.value(),
-                                            wallpaper_files_id, file_name,
-                                            layout, image, preview_mode);
+  wallpaper_controller_->SetCustomWallpaper(
+      account_id, wallpaper_files_id, file_name, layout, image, preview_mode);
 }
 
 void WallpaperControllerClient::SetOnlineWallpaperIfExists(
@@ -212,11 +189,10 @@
     ash::WallpaperLayout layout,
     bool preview_mode,
     ash::WallpaperController::SetOnlineWallpaperIfExistsCallback callback) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
-  if (!user_info)
+  if (!IsKnownUser(account_id))
     return;
   wallpaper_controller_->SetOnlineWallpaperIfExists(
-      user_info.value(), url, layout, preview_mode, std::move(callback));
+      account_id, url, layout, preview_mode, std::move(callback));
 }
 
 void WallpaperControllerClient::SetOnlineWallpaperFromData(
@@ -226,18 +202,15 @@
     ash::WallpaperLayout layout,
     bool preview_mode,
     ash::WallpaperController::SetOnlineWallpaperFromDataCallback callback) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
-  if (!user_info)
+  if (!IsKnownUser(account_id))
     return;
   wallpaper_controller_->SetOnlineWallpaperFromData(
-      user_info.value(), image_data, url, layout, preview_mode,
-      std::move(callback));
+      account_id, image_data, url, layout, preview_mode, std::move(callback));
 }
 
 void WallpaperControllerClient::SetDefaultWallpaper(const AccountId& account_id,
                                                     bool show_wallpaper) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
-  if (!user_info)
+  if (!IsKnownUser(account_id))
     return;
 
   // Postpone setting the wallpaper until we can get files id.
@@ -251,8 +224,8 @@
     return;
   }
 
-  wallpaper_controller_->SetDefaultWallpaper(
-      user_info.value(), GetFilesId(account_id), show_wallpaper);
+  wallpaper_controller_->SetDefaultWallpaper(account_id, GetFilesId(account_id),
+                                             show_wallpaper);
 }
 
 void WallpaperControllerClient::SetCustomizedDefaultWallpaperPaths(
@@ -265,11 +238,7 @@
 void WallpaperControllerClient::SetPolicyWallpaper(
     const AccountId& account_id,
     std::unique_ptr<std::string> data) {
-  if (!data)
-    return;
-
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
-  if (!user_info)
+  if (!data || !IsKnownUser(account_id))
     return;
 
   // Postpone setting the wallpaper until we can get files id. See
@@ -281,8 +250,8 @@
     return;
   }
 
-  wallpaper_controller_->SetPolicyWallpaper(user_info.value(),
-                                            GetFilesId(account_id), *data);
+  wallpaper_controller_->SetPolicyWallpaper(account_id, GetFilesId(account_id),
+                                            *data);
 }
 
 bool WallpaperControllerClient::SetThirdPartyWallpaper(
@@ -291,11 +260,9 @@
     const std::string& file_name,
     ash::WallpaperLayout layout,
     const gfx::ImageSkia& image) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
-  if (!user_info)
-    return false;
-  return wallpaper_controller_->SetThirdPartyWallpaper(
-      user_info.value(), wallpaper_files_id, file_name, layout, image);
+  return IsKnownUser(account_id) &&
+         wallpaper_controller_->SetThirdPartyWallpaper(
+             account_id, wallpaper_files_id, file_name, layout, image);
 }
 
 void WallpaperControllerClient::ConfirmPreviewWallpaper() {
@@ -309,17 +276,13 @@
 void WallpaperControllerClient::UpdateCustomWallpaperLayout(
     const AccountId& account_id,
     ash::WallpaperLayout layout) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
-  if (!user_info)
-    return;
-  wallpaper_controller_->UpdateCustomWallpaperLayout(user_info.value(), layout);
+  if (IsKnownUser(account_id))
+    wallpaper_controller_->UpdateCustomWallpaperLayout(account_id, layout);
 }
 
 void WallpaperControllerClient::ShowUserWallpaper(const AccountId& account_id) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
-  if (!user_info)
-    return;
-  wallpaper_controller_->ShowUserWallpaper(user_info.value());
+  if (IsKnownUser(account_id))
+    wallpaper_controller_->ShowUserWallpaper(account_id);
 }
 
 void WallpaperControllerClient::ShowSigninWallpaper() {
@@ -337,8 +300,7 @@
 
 void WallpaperControllerClient::RemoveUserWallpaper(
     const AccountId& account_id) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
-  if (!user_info)
+  if (!IsKnownUser(account_id))
     return;
 
   // Postpone removing the wallpaper until we can get files id.
@@ -352,14 +314,13 @@
     return;
   }
 
-  wallpaper_controller_->RemoveUserWallpaper(user_info.value(),
+  wallpaper_controller_->RemoveUserWallpaper(account_id,
                                              GetFilesId(account_id));
 }
 
 void WallpaperControllerClient::RemovePolicyWallpaper(
     const AccountId& account_id) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
-  if (!user_info)
+  if (!IsKnownUser(account_id))
     return;
 
   // Postpone removing the wallpaper until we can get files id.
@@ -373,7 +334,7 @@
     return;
   }
 
-  wallpaper_controller_->RemovePolicyWallpaper(user_info.value(),
+  wallpaper_controller_->RemovePolicyWallpaper(account_id,
                                                GetFilesId(account_id));
 }
 
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index c4c42a3..d84caea 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -2100,7 +2100,6 @@
     if (index < new_active_index)
       session_service->SetSelectedTabInWindow(session_id(), new_active_index);
   }
-  contents->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
 }
 
 void Browser::OnTabClosing(WebContents* contents) {
diff --git a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
index 4b0a321..b276528 100644
--- a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
@@ -17,8 +17,6 @@
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/system_web_app_manager.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -40,10 +38,6 @@
 
 // This test verifies that the settings page is opened in a new browser window.
 IN_PROC_BROWSER_TEST_F(BrowserNavigatorTestChromeOS, NavigateToSettings) {
-  // Install the Settings App.
-  web_app::WebAppProvider::Get(browser()->profile())
-      ->system_web_app_manager()
-      .InstallSystemAppsForTesting();
   GURL old_url = browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
   {
     content::WindowedNotificationObserver observer(
diff --git a/chrome/browser/ui/content_settings/framebust_block_browsertest.cc b/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
index 1965154..ebc5e5c 100644
--- a/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
+++ b/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
@@ -37,11 +37,6 @@
 #include "ui/events/event_constants.h"
 #include "url/gurl.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/web_applications/system_web_app_manager.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
-#endif
-
 namespace {
 
 const int kAllowRadioButtonIndex = 0;
@@ -211,12 +206,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FramebustBlockBrowserTest, ManageButtonClicked) {
-#if defined(OS_CHROMEOS)
-  web_app::WebAppProvider::Get(browser()->profile())
-      ->system_web_app_manager()
-      .InstallSystemAppsForTesting();
-#endif
-
   const GURL url = embedded_test_server()->GetURL("/iframe.html");
   ui_test_utils::NavigateToURL(browser(), url);
 
diff --git a/chrome/browser/ui/manifest_web_app_browser_controller.cc b/chrome/browser/ui/manifest_web_app_browser_controller.cc
index ed5db30..fee6513 100644
--- a/chrome/browser/ui/manifest_web_app_browser_controller.cc
+++ b/chrome/browser/ui/manifest_web_app_browser_controller.cc
@@ -93,4 +93,5 @@
   if (app_launch_url_.is_empty())
     app_launch_url_ = contents->GetURL();
   AppBrowserController::OnTabInserted(contents);
+  UpdateToolbarVisibility(false);
 }
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index 90f205de..c77f594 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -27,8 +27,6 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/system_web_app_manager.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -1644,11 +1642,6 @@
   OmniboxView* omnibox_view = nullptr;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
 #if defined(OS_CHROMEOS)
-  // Install the Settings App.
-  web_app::WebAppProvider::Get(browser()->profile())
-      ->system_web_app_manager()
-      .InstallSystemAppsForTesting();
-
   EXPECT_FALSE(
       chrome::SettingsWindowManager::GetInstance()->FindBrowserForProfile(
           browser()->profile()));
diff --git a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
index 7c5179f..5477afae 100644
--- a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
+++ b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
@@ -63,7 +63,6 @@
     if (!EnableSystemWebApps())
       return;
 
-    // Install the Settings App.
     web_app::WebAppProvider::Get(browser()->profile())
         ->system_web_app_manager()
         .InstallSystemAppsForTesting();
diff --git a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.cc b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.cc
index 12febc67..b53229e 100644
--- a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.cc
+++ b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.cc
@@ -55,21 +55,21 @@
 base::string16 TabSharingInfoBarDelegate::GetButtonLabel(
     InfoBarButton button) const {
   return l10n_util::GetStringUTF16((button == BUTTON_OK)
-                                       ? IDS_TAB_SHARING_INFOBAR_SHARE_BUTTON
-                                       : IDS_TAB_SHARING_INFOBAR_STOP_BUTTON);
+                                       ? IDS_TAB_SHARING_INFOBAR_STOP_BUTTON
+                                       : IDS_TAB_SHARING_INFOBAR_SHARE_BUTTON);
 }
 
 int TabSharingInfoBarDelegate::GetButtons() const {
-  return shared_tab_name_.empty() ? BUTTON_CANCEL : BUTTON_OK | BUTTON_CANCEL;
+  return shared_tab_name_.empty() ? BUTTON_OK : BUTTON_OK | BUTTON_CANCEL;
 }
 
 bool TabSharingInfoBarDelegate::Accept() {
-  ui_->StartSharing(infobar());
+  ui_->StopSharing();
   return false;
 }
 
 bool TabSharingInfoBarDelegate::Cancel() {
-  ui_->StopSharing();
+  ui_->StartSharing(infobar());
   return false;
 }
 
diff --git a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
index 88437cbb..c1aefc5d 100644
--- a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
+++ b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
@@ -56,7 +56,7 @@
 
  private:
   // Overridden from views::BubbleDialogDelegateView:
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   int GetDialogButtons() const override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   void Init() override;
@@ -93,13 +93,13 @@
 InvertBubbleView::~InvertBubbleView() {
 }
 
-views::View* InvertBubbleView::CreateExtraView() {
+std::unique_ptr<views::View> InvertBubbleView::CreateExtraView() {
   auto learn_more = views::CreateVectorImageButton(this);
   views::SetImageFromVectorIcon(learn_more.get(),
                                 vector_icons::kHelpOutlineIcon);
   learn_more->SetTooltipText(l10n_util::GetStringUTF16(IDS_LEARN_MORE));
   learn_more->set_tag(kLearnMoreButton);
-  return learn_more.release();
+  return learn_more;
 }
 
 int InvertBubbleView::GetDialogButtons() const {
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index e82ea18a..a03f4c3 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -938,9 +938,9 @@
     }
 
     scroll_view_ = new views::ScrollView();
-    scroll_view_->set_hide_horizontal_scrollbar(true);
+    scroll_view_->SetHideHorizontalScrollBar(true);
     body_container_ = scroll_view_->SetContents(std::move(body_container));
-    scroll_view_->set_draw_overflow_indicator(false);
+    scroll_view_->SetDrawOverflowIndicator(false);
     scroll_view_->ClipHeightTo(0, body_container_->GetPreferredSize().height());
 
     // Use an additional container to apply padding outside the scroll view, so
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.cc b/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.cc
index 411b76d5..598f2b1a 100644
--- a/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.cc
+++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.cc
@@ -161,9 +161,9 @@
   }
 
   auto card_list_scroll_view = std::make_unique<views::ScrollView>();
-  card_list_scroll_view->set_hide_horizontal_scrollbar(true);
+  card_list_scroll_view->SetHideHorizontalScrollBar(true);
   card_list_scroll_view->SetContents(std::move(card_list_view));
-  card_list_scroll_view->set_draw_overflow_indicator(false);
+  card_list_scroll_view->SetDrawOverflowIndicator(false);
   constexpr int kCardListScrollViewHeight = 140;
   card_list_scroll_view->ClipHeightTo(0, kCardListScrollViewHeight);
   return card_list_scroll_view;
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
index 281c2dd..31f6e8e 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
@@ -83,8 +83,6 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/browser/web_applications/system_web_app_manager.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
 #endif
 
 using base::Bucket;
@@ -163,11 +161,6 @@
         IdentityManagerFactory::GetForProfile(browser()->profile())
             ->GetPrimaryAccountInfo();
     username = info.email;
-
-    // Install the Settings App.
-    web_app::WebAppProvider::Get(browser()->profile())
-        ->system_web_app_manager()
-        .InstallSystemAppsForTesting();
 #endif
     if (username.empty())
       username = "user@gmail.com";
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.cc b/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.cc
index d56d5f93..a7ced9a 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.cc
@@ -65,11 +65,11 @@
 #endif
 }
 
-views::View* SaveCardManageCardsBubbleViews::CreateExtraView() {
+std::unique_ptr<views::View> SaveCardManageCardsBubbleViews::CreateExtraView() {
   auto manage_cards_button = views::MdTextButton::CreateSecondaryUiButton(
       this, l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_CARDS));
   manage_cards_button->SetID(DialogViewId::MANAGE_CARDS_BUTTON);
-  return manage_cards_button.release();
+  return manage_cards_button;
 }
 
 int SaveCardManageCardsBubbleViews::GetDialogButtons() const {
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.h b/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.h
index d6bdca367..71acf1e 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.h
+++ b/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.h
@@ -27,7 +27,7 @@
 
   // views::WidgetDelegate:
   views::View* CreateFootnoteView() override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   int GetDialogButtons() const override;
 
  private:
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.cc b/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.cc
index bd34641..5c9ba838 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.cc
@@ -60,7 +60,7 @@
     : SaveCardBubbleViews(anchor_view, anchor_point, web_contents, controller) {
 }
 
-views::View* SaveCardOfferBubbleViews::CreateExtraView() {
+std::unique_ptr<views::View> SaveCardOfferBubbleViews::CreateExtraView() {
   if (controller()->GetSyncState() !=
       AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled) {
     return nullptr;
@@ -68,12 +68,12 @@
 
   // CreateMainContentView() must happen prior to this so that |prefilled_name|
   // gets populated.
-  auto* upload_explanation_tooltip =
-      new views::TooltipIcon(l10n_util::GetStringUTF16(
-          (cardholder_name_textfield_ &&
-           !cardholder_name_textfield_->text().empty())
-              ? IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_AND_CARDHOLDER_NAME_TOOLTIP
-              : IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_TOOLTIP));
+  auto upload_explanation_tooltip = std::make_unique<
+      views::TooltipIcon>(l10n_util::GetStringUTF16(
+      (cardholder_name_textfield_ &&
+       !cardholder_name_textfield_->text().empty())
+          ? IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_AND_CARDHOLDER_NAME_TOOLTIP
+          : IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_TOOLTIP));
   upload_explanation_tooltip->set_bubble_width(kTooltipBubbleWidth);
   upload_explanation_tooltip->set_anchor_point_arrow(
       views::BubbleBorder::Arrow::TOP_RIGHT);
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.h b/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.h
index c4b4aa4..a81efb8 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.h
+++ b/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.h
@@ -34,7 +34,7 @@
                            SaveCardBubbleController* controller);
 
   // BubbleDialogDelegateView:
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   views::View* CreateFootnoteView() override;
   bool Accept() override;
   int GetDialogButtons() const override;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
index 309ded0..db6d98ad 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -143,12 +143,12 @@
                                        : IDS_BOOKMARK_BUBBLE_REMOVE_BOOKMARK);
 }
 
-views::View* BookmarkBubbleView::CreateExtraView() {
+std::unique_ptr<views::View> BookmarkBubbleView::CreateExtraView() {
   auto edit_button = views::MdTextButton::CreateSecondaryUiButton(
       this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_OPTIONS));
   edit_button->AddAccelerator(ui::Accelerator(ui::VKEY_E, ui::EF_ALT_DOWN));
-  edit_button_ = edit_button.release();
-  return edit_button_;
+  edit_button_ = edit_button.get();
+  return edit_button;
 }
 
 bool BookmarkBubbleView::GetExtraViewPadding(int* padding) {
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
index d870d0c4..1d13539f 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
@@ -65,7 +65,7 @@
   bool ShouldShowWindowIcon() const override;
   void WindowClosing() override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool GetExtraViewPadding(int* padding) override;
   views::View* CreateFootnoteView() override;
   bool Cancel() override;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
index 31a06268..b8bb857 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -95,8 +95,8 @@
   return true;
 }
 
-views::View* BookmarkEditorView::CreateExtraView() {
-  return new_folder_button_.get();
+std::unique_ptr<views::View> BookmarkEditorView::CreateExtraView() {
+  return std::move(new_folder_button_);
 }
 
 ui::ModalType BookmarkEditorView::GetModalType() const {
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
index f4caf00..283d066e 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
@@ -82,7 +82,7 @@
   // views::DialogDelegateView:
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   ui::ModalType GetModalType() const override;
   bool CanResize() const override;
   bool ShouldShowCloseButton() const override;
diff --git a/chrome/browser/ui/views/certificate_selector.cc b/chrome/browser/ui/views/certificate_selector.cc
index 9b6e4bf..098d0025 100644
--- a/chrome/browser/ui/views/certificate_selector.cc
+++ b/chrome/browser/ui/views/certificate_selector.cc
@@ -281,12 +281,12 @@
   return table_;
 }
 
-views::View* CertificateSelector::CreateExtraView() {
+std::unique_ptr<views::View> CertificateSelector::CreateExtraView() {
   DCHECK(!view_cert_button_);
   auto view_cert_button = views::MdTextButton::CreateSecondaryUiButton(
       this, l10n_util::GetStringUTF16(IDS_PAGE_INFO_CERT_INFO_BUTTON));
-  view_cert_button_ = view_cert_button.release();
-  return view_cert_button_;
+  view_cert_button_ = view_cert_button.get();
+  return view_cert_button;
 }
 
 ui::ModalType CertificateSelector::GetModalType() const {
diff --git a/chrome/browser/ui/views/certificate_selector.h b/chrome/browser/ui/views/certificate_selector.h
index 30bdbe9..a932cb13 100644
--- a/chrome/browser/ui/views/certificate_selector.h
+++ b/chrome/browser/ui/views/certificate_selector.h
@@ -69,7 +69,7 @@
   base::string16 GetWindowTitle() const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   views::View* GetInitiallyFocusedView() override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   ui::ModalType GetModalType() const override;
 
   // views::ButtonListener:
diff --git a/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc b/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc
index 6f1d640..260c867 100644
--- a/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc
+++ b/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc
@@ -150,14 +150,14 @@
              : DialogDelegate::GetDialogButtonLabel(button);
 }
 
-views::View* ChromeCleanerDialog::CreateExtraView() {
+std::unique_ptr<views::View> ChromeCleanerDialog::CreateExtraView() {
   DCHECK(!details_button_);
 
   auto details_button = views::MdTextButton::CreateSecondaryUiButton(
       this, l10n_util::GetStringUTF16(
                 IDS_CHROME_CLEANUP_PROMPT_DETAILS_BUTTON_LABEL));
-  details_button_ = details_button.release();
-  return details_button_;
+  details_button_ = details_button.get();
+  return details_button;
 }
 
 bool ChromeCleanerDialog::Accept() {
diff --git a/chrome/browser/ui/views/chrome_cleaner_dialog_win.h b/chrome/browser/ui/views/chrome_cleaner_dialog_win.h
index f7ca70d7..7a531b4 100644
--- a/chrome/browser/ui/views/chrome_cleaner_dialog_win.h
+++ b/chrome/browser/ui/views/chrome_cleaner_dialog_win.h
@@ -56,7 +56,7 @@
 
   // views::DialogDelegate overrides.
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Accept() override;
   bool Cancel() override;
   bool Close() override;
diff --git a/chrome/browser/ui/views/collected_cookies_views.cc b/chrome/browser/ui/views/collected_cookies_views.cc
index 0e88ed3..43c2ca5 100644
--- a/chrome/browser/ui/views/collected_cookies_views.cc
+++ b/chrome/browser/ui/views/collected_cookies_views.cc
@@ -325,11 +325,11 @@
   return false;
 }
 
-views::View* CollectedCookiesViews::CreateExtraView() {
+std::unique_ptr<views::View> CollectedCookiesViews::CreateExtraView() {
   // The code in |Init|, which runs before this does, needs the button pane to
   // already exist, so it is created there and this class holds ownership until
   // this method is called.
-  return buttons_pane_.release();
+  return std::move(buttons_pane_);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/collected_cookies_views.h b/chrome/browser/ui/views/collected_cookies_views.h
index efdfa93..a3d8e1ec 100644
--- a/chrome/browser/ui/views/collected_cookies_views.h
+++ b/chrome/browser/ui/views/collected_cookies_views.h
@@ -52,7 +52,7 @@
   bool Accept() override;
   ui::ModalType GetModalType() const override;
   bool ShouldShowCloseButton() const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
 
   // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
diff --git a/chrome/browser/ui/views/confirm_bubble_views.cc b/chrome/browser/ui/views/confirm_bubble_views.cc
index c14aa702..bbc8d16 100644
--- a/chrome/browser/ui/views/confirm_bubble_views.cc
+++ b/chrome/browser/ui/views/confirm_bubble_views.cc
@@ -79,13 +79,13 @@
   }
 }
 
-views::View* ConfirmBubbleViews::CreateExtraView() {
+std::unique_ptr<views::View> ConfirmBubbleViews::CreateExtraView() {
   auto help_button = CreateVectorImageButton(this);
   help_button->SetFocusForPlatform();
   help_button->SetTooltipText(l10n_util::GetStringUTF16(IDS_LEARN_MORE));
-  help_button_ = help_button.release();
+  help_button_ = help_button.get();
   SetImageFromVectorIcon(help_button_, vector_icons::kHelpOutlineIcon);
-  return help_button_;
+  return help_button;
 }
 
 bool ConfirmBubbleViews::Cancel() {
diff --git a/chrome/browser/ui/views/confirm_bubble_views.h b/chrome/browser/ui/views/confirm_bubble_views.h
index decea20..c8ed94f 100644
--- a/chrome/browser/ui/views/confirm_bubble_views.h
+++ b/chrome/browser/ui/views/confirm_bubble_views.h
@@ -39,7 +39,7 @@
   // views::DialogDelegate implementation.
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Cancel() override;
   bool Accept() override;
 
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc
index 3134546c..4aefdbd 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.cc
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -540,7 +540,7 @@
   content_setting_bubble_model_->set_owner(this);
 }
 
-views::View* ContentSettingBubbleContents::CreateExtraView() {
+std::unique_ptr<views::View> ContentSettingBubbleContents::CreateExtraView() {
   DCHECK(content_setting_bubble_model_);
   const auto& bubble_content = content_setting_bubble_model_->bubble_content();
   const auto* layout = ChromeLayoutProvider::Get();
@@ -573,8 +573,8 @@
   if (extra_views.empty())
     return nullptr;
   if (extra_views.size() == 1)
-    return extra_views.front().release();
-  views::View* container = new views::View();
+    return std::move(extra_views.front());
+  auto container = std::make_unique<views::View>();
   container->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::kHorizontal, gfx::Insets(),
       layout->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL)));
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.h b/chrome/browser/ui/views/content_setting_bubble_contents.h
index 11721cb1..24ffc9a 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.h
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.h
@@ -67,7 +67,7 @@
 
   // views::BubbleDialogDelegateView:
   void Init() override;
-  View* CreateExtraView() override;
+  std::unique_ptr<View> CreateExtraView() override;
   bool Accept() override;
   bool Close() override;
   int GetDialogButtons() const override;
diff --git a/chrome/browser/ui/views/crostini/crostini_app_installer_view.cc b/chrome/browser/ui/views/crostini/crostini_app_installer_view.cc
index f5c247d5..f322acb 100644
--- a/chrome/browser/ui/views/crostini/crostini_app_installer_view.cc
+++ b/chrome/browser/ui/views/crostini/crostini_app_installer_view.cc
@@ -73,7 +73,7 @@
   message_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 
   views::ScrollView* scroll_view = new views::ScrollView;
-  scroll_view->set_draw_overflow_indicator(true);
+  scroll_view->SetDrawOverflowIndicator(true);
   scroll_view->ClipHeightTo(crostini::kMinScrollHeight,
                             crostini::kMaxScrollHeight);
   message_label_ = scroll_view->SetContents(std::move(message_label));
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
index 7af42ae..5671323 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
@@ -118,7 +118,7 @@
         screen_scroll_view->ClipHeightTo(
             kGenericScreenStyle.item_size.height(),
             kGenericScreenStyle.item_size.height() * 2);
-        screen_scroll_view->set_hide_horizontal_scrollbar(true);
+        screen_scroll_view->SetHideHorizontalScrollBar(true);
 
         panes.push_back(
             std::make_pair(screen_title_text, std::move(screen_scroll_view)));
@@ -149,7 +149,7 @@
 
         window_scroll_view->ClipHeightTo(kWindowStyle.item_size.height(),
                                          kWindowStyle.item_size.height() * 2);
-        window_scroll_view->set_hide_horizontal_scrollbar(true);
+        window_scroll_view->SetHideHorizontalScrollBar(true);
 
         panes.push_back(
             std::make_pair(window_title_text, std::move(window_scroll_view)));
@@ -193,11 +193,8 @@
 
   DCHECK(!source_types_.empty());
 
-  if (params.request_audio) {
-    audio_share_checkbox_ = new views::Checkbox(
-        l10n_util::GetStringUTF16(IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE));
-    audio_share_checkbox_->SetChecked(params.approve_audio_by_default);
-  }
+  request_audio_ = params.request_audio;
+  approve_audio_by_default_ = params.approve_audio_by_default;
 
   // Focus on the first non-null media_list.
   OnSourceTypeSwitched(0);
@@ -360,8 +357,16 @@
                                        : IDS_CANCEL);
 }
 
-views::View* DesktopMediaPickerDialogView::CreateExtraView() {
-  return audio_share_checkbox_;
+std::unique_ptr<views::View> DesktopMediaPickerDialogView::CreateExtraView() {
+  std::unique_ptr<views::Checkbox> audio_share_checkbox;
+  if (request_audio_) {
+    audio_share_checkbox = std::make_unique<views::Checkbox>(
+        l10n_util::GetStringUTF16(IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE));
+    audio_share_checkbox->SetChecked(approve_audio_by_default_);
+    audio_share_checkbox_ = audio_share_checkbox.get();
+  }
+  OnSourceTypeSwitched(0);
+  return audio_share_checkbox;
 }
 
 bool DesktopMediaPickerDialogView::Accept() {
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
index f6f6ba1f..86553ff 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
@@ -51,7 +51,7 @@
   views::View* GetInitiallyFocusedView() override;
   int GetDefaultDialogButton() const override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
-  View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Accept() override;
   bool ShouldShowCloseButton() const override;
   void DeleteDelegate() override;
@@ -69,6 +69,8 @@
 
   views::Label* description_label_ = nullptr;
 
+  bool request_audio_;
+  bool approve_audio_by_default_;
   views::Checkbox* audio_share_checkbox_ = nullptr;
 
   views::TabbedPane* tabbed_pane_ = nullptr;
diff --git a/chrome/browser/ui/views/extensions/chooser_dialog_view.cc b/chrome/browser/ui/views/extensions/chooser_dialog_view.cc
index a491ad7f..26cd872b 100644
--- a/chrome/browser/ui/views/extensions/chooser_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/chooser_dialog_view.cc
@@ -78,10 +78,8 @@
   return GetDialogClientView()->cancel_button();
 }
 
-views::View* ChooserDialogView::CreateExtraView() {
-  std::unique_ptr<views::View> extra_view =
-      device_chooser_content_view_->CreateExtraView();
-  return extra_view ? extra_view.release() : nullptr;
+std::unique_ptr<views::View> ChooserDialogView::CreateExtraView() {
+  return device_chooser_content_view_->CreateExtraView();
 }
 
 bool ChooserDialogView::Accept() {
diff --git a/chrome/browser/ui/views/extensions/chooser_dialog_view.h b/chrome/browser/ui/views/extensions/chooser_dialog_view.h
index 1226158..c5a4596 100644
--- a/chrome/browser/ui/views/extensions/chooser_dialog_view.h
+++ b/chrome/browser/ui/views/extensions/chooser_dialog_view.h
@@ -32,7 +32,7 @@
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   views::View* GetInitiallyFocusedView() override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Accept() override;
   bool Cancel() override;
   bool Close() override;
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
index 6ea9df92..ee68846 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
@@ -363,11 +363,11 @@
   GetBubbleFrameView()->SetTitleView(std::move(title_container));
 }
 
-views::View* ExtensionInstallDialogView::CreateExtraView() {
+std::unique_ptr<views::View> ExtensionInstallDialogView::CreateExtraView() {
   if (!prompt_->has_webstore_data())
     return nullptr;
 
-  views::Link* store_link = new views::Link(
+  auto store_link = std::make_unique<views::Link>(
       l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_STORE_LINK));
   store_link->set_listener(this);
   return store_link;
@@ -535,7 +535,7 @@
   }
 
   scroll_view_ = new views::ScrollView();
-  scroll_view_->set_hide_horizontal_scrollbar(true);
+  scroll_view_->SetHideHorizontalScrollBar(true);
   scroll_view_->SetContents(std::move(extension_info_container));
   scroll_view_->ClipHeightTo(
       0, provider->GetDistanceMetric(
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
index b3826e1..7adc6948 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
@@ -57,7 +57,7 @@
   gfx::Size CalculatePreferredSize() const override;
   void VisibilityChanged(views::View* starting_from, bool is_visible) override;
   void AddedToWidget() override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Cancel() override;
   bool Accept() override;
   bool IsDialogDraggable() const override;
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
index 3eeb04d..abee5979 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
@@ -95,8 +95,8 @@
   constexpr int kMaxExtensionButtonsHeightDp = 600;
   auto scroll_view = std::make_unique<views::ScrollView>();
   scroll_view->ClipHeightTo(0, kMaxExtensionButtonsHeightDp);
-  scroll_view->set_draw_overflow_indicator(false);
-  scroll_view->set_hide_horizontal_scrollbar(true);
+  scroll_view->SetDrawOverflowIndicator(false);
+  scroll_view->SetHideHorizontalScrollBar(true);
   scroll_view->SetContents(std::move(extension_buttons));
   AddChildView(std::move(scroll_view));
 
diff --git a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
index 52b7c6b..923c639 100644
--- a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
+++ b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
@@ -251,15 +251,16 @@
   return ui::MODAL_TYPE_CHILD;
 }
 
-views::View* MediaGalleriesDialogViews::CreateExtraView() {
+std::unique_ptr<views::View> MediaGalleriesDialogViews::CreateExtraView() {
   DCHECK(!auxiliary_button_);
   base::string16 button_label = controller_->GetAuxiliaryButtonText();
+  std::unique_ptr<views::LabelButton> auxiliary_button;
   if (!button_label.empty()) {
-    auto auxiliary_button =
+    auxiliary_button =
         views::MdTextButton::CreateSecondaryUiButton(this, button_label);
-    auxiliary_button_ = auxiliary_button.release();
+    auxiliary_button_ = auxiliary_button.get();
   }
-  return auxiliary_button_;
+  return auxiliary_button;
 }
 
 bool MediaGalleriesDialogViews::Cancel() {
diff --git a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.h b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.h
index 2de366d..c796d09 100644
--- a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.h
+++ b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.h
@@ -48,7 +48,7 @@
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   ui::ModalType GetModalType() const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Cancel() override;
   bool Accept() override;
 
diff --git a/chrome/browser/ui/views/first_run_dialog.cc b/chrome/browser/ui/views/first_run_dialog.cc
index f92c4f4..aadc3d90 100644
--- a/chrome/browser/ui/views/first_run_dialog.cc
+++ b/chrome/browser/ui/views/first_run_dialog.cc
@@ -101,9 +101,9 @@
   quit_runloop_.Run();
 }
 
-views::View* FirstRunDialog::CreateExtraView() {
-  views::Link* link = new views::Link(l10n_util::GetStringUTF16(
-      IDS_LEARN_MORE));
+std::unique_ptr<views::View> FirstRunDialog::CreateExtraView() {
+  auto link =
+      std::make_unique<views::Link>(l10n_util::GetStringUTF16(IDS_LEARN_MORE));
   link->set_listener(this);
   return link;
 }
diff --git a/chrome/browser/ui/views/first_run_dialog.h b/chrome/browser/ui/views/first_run_dialog.h
index 84b487e..47569724 100644
--- a/chrome/browser/ui/views/first_run_dialog.h
+++ b/chrome/browser/ui/views/first_run_dialog.h
@@ -31,7 +31,7 @@
   void Done();
 
   // views::DialogDelegate:
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Accept() override;
   int GetDialogButtons() const override;
 
diff --git a/chrome/browser/ui/views/frame/browser_frame_browsertest.cc b/chrome/browser/ui/views/frame/browser_frame_browsertest.cc
index 70c57f8..a0cf11f 100644
--- a/chrome/browser/ui/views/frame/browser_frame_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_browsertest.cc
@@ -30,10 +30,7 @@
 class BrowserFrameTest : public InProcessBrowserTest {
  public:
   BrowserFrameTest()
-      : InProcessBrowserTest(
-            base::BindOnce([]() -> std::unique_ptr<views::ViewsDelegate> {
-              return std::make_unique<BrowserFrameBoundsChecker>();
-            })) {}
+      : InProcessBrowserTest(std::make_unique<BrowserFrameBoundsChecker>()) {}
 };
 
 // Verifies that the tools are loaded with initial bounds.
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 27c3d4a..4d98d30 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -708,8 +708,6 @@
   } else if (browser_view()->IsBrowserTypeHostedApp()) {
     active_color = browser_view()->browser()->app_controller()->GetThemeColor();
   } else if (!browser_view()->browser()->is_app()) {
-    // TODO(crbug.com/836128): Remove when System Web Apps flag is removed, as
-    // the above Hosted App branch will render the theme color.
     active_color =
         base::FeatureList::IsEnabled(chromeos::features::kSplitSettings)
             ? gfx::kGoogleGrey050
diff --git a/chrome/browser/ui/views/global_error_bubble_view.cc b/chrome/browser/ui/views/global_error_bubble_view.cc
index f8f054f9..c6f2525a 100644
--- a/chrome/browser/ui/views/global_error_bubble_view.cc
+++ b/chrome/browser/ui/views/global_error_bubble_view.cc
@@ -192,13 +192,13 @@
   return error_->GetDefaultDialogButton();
 }
 
-views::View* GlobalErrorBubbleView::CreateExtraView() {
+std::unique_ptr<views::View> GlobalErrorBubbleView::CreateExtraView() {
   if (!error_ || error_->GetBubbleViewCancelButtonLabel().empty() ||
       !error_->ShouldUseExtraView())
     return nullptr;
   auto view = views::MdTextButton::CreateSecondaryUiButton(
       this, error_->GetBubbleViewCancelButtonLabel());
-  return view.release();
+  return view;
 }
 
 bool GlobalErrorBubbleView::Cancel() {
diff --git a/chrome/browser/ui/views/global_error_bubble_view.h b/chrome/browser/ui/views/global_error_bubble_view.h
index ad0dfb9..e9adef9 100644
--- a/chrome/browser/ui/views/global_error_bubble_view.h
+++ b/chrome/browser/ui/views/global_error_bubble_view.h
@@ -40,7 +40,7 @@
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   int GetDialogButtons() const override;
   int GetDefaultDialogButton() const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Cancel() override;
   bool Accept() override;
   bool Close() override;
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
index b5f46fc..012ca6e6 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
@@ -314,10 +314,12 @@
 
 base::string16 ZoomBubbleView::GetAccessibleWindowTitle() const {
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
+  if (!browser)
+    return {};
   OmniboxPageActionIconContainerView* page_action_icon_container_view =
       GetAnchorViewForBrowser(browser);
   if (!page_action_icon_container_view)
-    return base::string16();
+    return {};
 
   PageActionIconView* zoom_view =
       page_action_icon_container_view->GetPageActionIconView(
@@ -505,6 +507,7 @@
   if (sender == image_button_) {
     DCHECK(extension_info_.icon_image) << "Invalid button press.";
     Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
+    DCHECK(browser);
     chrome::AddSelectedTabWithURL(
         browser, GURL(base::StringPrintf("chrome://extensions?id=%s",
                                          extension_info_.id.c_str())),
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view.cc b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
index f2eae8a..0aceea0e 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
@@ -134,13 +134,14 @@
   return ui::DIALOG_BUTTON_NONE;
 }
 
-views::View* CastDialogView::CreateExtraView() {
-  sources_button_ = new views::MdTextButtonWithDownArrow(
+std::unique_ptr<views::View> CastDialogView::CreateExtraView() {
+  auto sources_button = std::make_unique<views::MdTextButtonWithDownArrow>(
       this,
       l10n_util::GetStringUTF16(IDS_MEDIA_ROUTER_ALTERNATIVE_SOURCES_BUTTON));
-  sources_button_->SetID(kAlternativeSourceButtonId);
-  sources_button_->SetEnabled(false);
-  return sources_button_;
+  sources_button->SetID(kAlternativeSourceButtonId);
+  sources_button->SetEnabled(false);
+  sources_button_ = sources_button.get();
+  return sources_button;
 }
 
 bool CastDialogView::Close() {
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view.h b/chrome/browser/ui/views/media_router/cast_dialog_view.h
index 61fa985..c50445c 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view.h
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view.h
@@ -85,7 +85,7 @@
 
   // views::DialogDelegate:
   int GetDialogButtons() const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Close() override;
 
   // CastDialogController::Observer:
diff --git a/chrome/browser/ui/views/network_profile_bubble_view.cc b/chrome/browser/ui/views/network_profile_bubble_view.cc
index 7b4d7d7..dbba8a4 100644
--- a/chrome/browser/ui/views/network_profile_bubble_view.cc
+++ b/chrome/browser/ui/views/network_profile_bubble_view.cc
@@ -38,7 +38,7 @@
 
   // views::BubbleDialogDelegateView:
   void Init() override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   int GetDialogButtons() const override;
   bool Accept() override;
 
@@ -83,9 +83,9 @@
   AddChildView(label);
 }
 
-views::View* NetworkProfileBubbleView::CreateExtraView() {
-  views::Link* learn_more =
-      new views::Link(l10n_util::GetStringUTF16(IDS_LEARN_MORE));
+std::unique_ptr<views::View> NetworkProfileBubbleView::CreateExtraView() {
+  auto learn_more =
+      std::make_unique<views::Link>(l10n_util::GetStringUTF16(IDS_LEARN_MORE));
   learn_more->set_listener(this);
   return learn_more;
 }
diff --git a/chrome/browser/ui/views/passwords/password_items_view.cc b/chrome/browser/ui/views/passwords/password_items_view.cc
index cec1f4f..34deb56 100644
--- a/chrome/browser/ui/views/passwords/password_items_view.cc
+++ b/chrome/browser/ui/views/passwords/password_items_view.cc
@@ -289,12 +289,11 @@
   model()->OnPasswordAction(password_form, action);
 }
 
-views::View* PasswordItemsView::CreateExtraView() {
+std::unique_ptr<views::View> PasswordItemsView::CreateExtraView() {
   auto view = views::MdTextButton::CreateSecondaryUiButton(
       this,
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS_BUTTON));
-
-  return view.release();
+  return view;
 }
 
 int PasswordItemsView::GetDialogButtons() const {
diff --git a/chrome/browser/ui/views/passwords/password_items_view.h b/chrome/browser/ui/views/passwords/password_items_view.h
index 548287fc..d992a7f 100644
--- a/chrome/browser/ui/views/passwords/password_items_view.h
+++ b/chrome/browser/ui/views/passwords/password_items_view.h
@@ -51,7 +51,7 @@
   void RecreateLayout();
 
   // LocationBarBubbleDelegateView:
-  View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   int GetDialogButtons() const override;
   bool ShouldShowCloseButton() const override;
   gfx::Size CalculatePreferredSize() const override;
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_browsertest.cc
index 976f17e0..67b6cc1 100644
--- a/chrome/browser/ui/views/payments/payment_request_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_browsertest.cc
@@ -6,7 +6,6 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
@@ -25,11 +24,6 @@
 #include "ui/views/controls/styled_label.h"
 #include "url/gurl.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/web_applications/system_web_app_manager.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
-#endif
-
 namespace payments {
 
 using ::testing::UnorderedElementsAre;
@@ -406,13 +400,6 @@
 
 // Tests that clicking the settings link brings the user to settings.
 IN_PROC_BROWSER_TEST_F(PaymentRequestSettingsLinkTest, ClickSettingsLink) {
-#if defined(OS_CHROMEOS)
-  // Install the Settings App.
-  web_app::WebAppProvider::Get(browser()->profile())
-      ->system_web_app_manager()
-      .InstallSystemAppsForTesting();
-#endif
-
   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_sheet_controller.cc b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
index 2a0bdd4..041113a 100644
--- a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
@@ -274,7 +274,7 @@
                 ? std::make_unique<BorderedScrollView>()
                 : std::make_unique<views::ScrollView>();
   scroll_->set_owned_by_client();
-  scroll_->set_hide_horizontal_scrollbar(true);
+  scroll_->SetHideHorizontalScrollBar(true);
   scroll_->SetContents(std::move(pane));
   layout->AddView(scroll_.get());
 
diff --git a/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc b/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc
index 5efe6af..c36940ae 100644
--- a/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc
+++ b/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc
@@ -48,7 +48,7 @@
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   views::View* GetInitiallyFocusedView() override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Accept() override;
   bool Cancel() override;
   bool Close() override;
@@ -120,10 +120,9 @@
   return device_chooser_content_view_->IsDialogButtonEnabled(button);
 }
 
-views::View* ChooserBubbleUiViewDelegate::CreateExtraView() {
-  std::unique_ptr<views::View> extra_view =
-      device_chooser_content_view_->CreateExtraView();
-  return extra_view ? extra_view.release() : nullptr;
+std::unique_ptr<views::View> ChooserBubbleUiViewDelegate::CreateExtraView() {
+  auto extra_view = device_chooser_content_view_->CreateExtraView();
+  return extra_view;
 }
 
 bool ChooserBubbleUiViewDelegate::Accept() {
diff --git a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc
index def2bb5..2961fa0 100644
--- a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc
+++ b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc
@@ -170,10 +170,10 @@
   return ui::MODAL_TYPE_NONE;
 }
 
-views::View* EnterpriseStartupDialogView::CreateExtraView() {
+std::unique_ptr<views::View> EnterpriseStartupDialogView::CreateExtraView() {
 #if defined(GOOGLE_CHROME_BUILD)
   // Show Google Chrome Enterprise logo only for official build.
-  views::ImageView* logo_image = new views::ImageView();
+  auto logo_image = std::make_unique<views::ImageView>();
   logo_image->SetImage(ui::ResourceBundle::GetSharedInstance()
                            .GetImageNamed(IDR_PRODUCT_LOGO_ENTERPRISE)
                            .AsImageSkia());
diff --git a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h
index 48120fc..9a1a4f05 100644
--- a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h
+++ b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h
@@ -47,7 +47,7 @@
   bool IsDialogDraggable() const override;
   bool ShouldShowWindowTitle() const override;
   ui::ModalType GetModalType() const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
 
   // override ui::DialogModal
   int GetDialogButtons() const override;
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index 6040b07..50f63ddb 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -394,9 +394,9 @@
 
   // Create a scroll view to hold contents view.
   auto scroll_view = std::make_unique<views::ScrollView>();
-  scroll_view->set_hide_horizontal_scrollbar(true);
+  scroll_view->SetHideHorizontalScrollBar(true);
   // TODO(https://crbug.com/871762): it's a workaround for the crash.
-  scroll_view->set_draw_overflow_indicator(false);
+  scroll_view->SetDrawOverflowIndicator(false);
   scroll_view->ClipHeightTo(0, GetMaxHeight());
   scroll_view->SetContents(std::move(contents_view));
 
diff --git a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
index 41c3ff8a..a2a2ac2 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
+++ b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
@@ -128,13 +128,14 @@
   dialog_view_ = NULL;
 }
 
-views::View* OneClickSigninDialogView::CreateExtraView() {
-  advanced_link_ = new views::Link(
+std::unique_ptr<views::View> OneClickSigninDialogView::CreateExtraView() {
+  auto advanced_link = std::make_unique<views::Link>(
       l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_ADVANCED));
 
-  advanced_link_->set_listener(this);
-  advanced_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  return advanced_link_;
+  advanced_link->set_listener(this);
+  advanced_link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  advanced_link_ = advanced_link.get();
+  return advanced_link;
 }
 
 base::string16 OneClickSigninDialogView::GetDialogButtonLabel(
diff --git a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h
index a5f690b..f28f3dc4 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h
+++ b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h
@@ -62,7 +62,7 @@
   base::string16 GetWindowTitle() const override;
   ui::ModalType GetModalType() const override;
   void WindowClosing() override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool Accept() override;
 
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
index 65e87848..805445c 100644
--- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
@@ -113,16 +113,13 @@
   return ui::DIALOG_BUTTON_NONE;
 }
 
-views::View* ProfileSigninConfirmationDialogViews::CreateExtraView() {
+std::unique_ptr<views::View>
+ProfileSigninConfirmationDialogViews::CreateExtraView() {
   if (!prompt_for_new_profile_)
     return nullptr;
 
-  const base::string16 continue_signin_text =
-      l10n_util::GetStringUTF16(IDS_ENTERPRISE_SIGNIN_CONTINUE);
-
-  return views::MdTextButton::CreateSecondaryUiButton(this,
-                                                      continue_signin_text)
-      .release();
+  return views::MdTextButton::CreateSecondaryUiButton(
+      this, l10n_util::GetStringUTF16(IDS_ENTERPRISE_SIGNIN_CONTINUE));
 }
 
 bool ProfileSigninConfirmationDialogViews::Accept() {
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
index abd3cc2c..a939943 100644
--- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
@@ -42,7 +42,7 @@
   base::string16 GetWindowTitle() const override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   int GetDefaultDialogButton() const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Accept() override;
   bool Cancel() override;
   ui::ModalType GetModalType() const override;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc
index ec149b36..f6e7ff6 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc
@@ -54,7 +54,7 @@
   GetWidget()->Show();
 }
 
-views::View* ToolbarActionsBarBubbleViews::CreateExtraView() {
+std::unique_ptr<views::View> ToolbarActionsBarBubbleViews::CreateExtraView() {
   std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
       extra_view_info = delegate_->GetExtraViewInfo();
 
@@ -86,7 +86,7 @@
   }
 
   if (icon && extra_view) {
-    views::View* parent = new views::View();
+    std::unique_ptr<views::View> parent = std::make_unique<views::View>();
     parent->SetLayoutManager(std::make_unique<views::BoxLayout>(
         views::BoxLayout::kHorizontal, gfx::Insets(),
         ChromeLayoutProvider::Get()->GetDistanceMetric(
@@ -95,9 +95,7 @@
     parent->AddChildView(std::move(extra_view));
     return parent;
   }
-
-  return icon ? static_cast<views::View*>(icon.release())
-              : static_cast<views::View*>(extra_view.release());
+  return icon ? std::move(icon) : std::move(extra_view);
 }
 
 base::string16 ToolbarActionsBarBubbleViews::GetWindowTitle() const {
diff --git a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h
index cb82009..89cb1c8 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h
@@ -43,7 +43,7 @@
   // views::BubbleDialogDelegateView:
   base::string16 GetWindowTitle() const override;
   bool ShouldShowCloseButton() const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Cancel() override;
   bool Accept() override;
   bool Close() override;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc
index b33559f7..ae5d30c 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc
@@ -33,7 +33,7 @@
 
 class ToolbarActionsBarBubbleViewsTest : public ChromeViewsTestBase {
  public:
-  views::View* TestCreateExtraView() {
+  std::unique_ptr<views::View> TestCreateExtraView() {
     DCHECK(bubble_);
     return bubble_->CreateExtraView();
   }
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index 0b310d5..1dddfae 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -219,6 +219,7 @@
 
   should_always_translate_ = model_->ShouldAlwaysTranslate();
   // Create different view based on user selection in
+  // kUseButtonTranslateBubbleUI.
   if (bubble_ui_model_ != language::TranslateUIBubbleModel::TAB) {
     before_translate_view_ = CreateViewBeforeTranslate();
     translating_view_ = CreateViewTranslating();
@@ -309,7 +310,7 @@
     }
     // TODO(crbug.com/963148): implement handler to take care of RESET action.
     case BUTTON_ID_RESET: {
-      GetWidget()->Close();
+      ResetLanguage();
       break;
     }
   }
@@ -327,6 +328,31 @@
   return bubble_ui_model_ != language::TranslateUIBubbleModel::TAB;
 }
 
+void TranslateBubbleView::ResetLanguage() {
+  if (model_->GetViewState() ==
+      TranslateBubbleModel::VIEW_STATE_SOURCE_LANGUAGE) {
+    if (source_language_combobox_->GetSelectedIndex() ==
+        previous_source_language_index_ + 1) {
+      return;
+    }
+    source_language_combobox_->SetSelectedIndex(
+        previous_source_language_index_ + 1);
+    model_->UpdateOriginalLanguageIndex(
+        source_language_combobox_->GetSelectedIndex() - 1);
+    UpdateAdvancedView();
+  } else {
+    if (target_language_combobox_->GetSelectedIndex() ==
+        previous_target_language_index_) {
+      return;
+    }
+    target_language_combobox_->SetSelectedIndex(
+        previous_target_language_index_);
+    model_->UpdateTargetLanguageIndex(
+        target_language_combobox_->GetSelectedIndex());
+    UpdateAdvancedView();
+  }
+}
+
 void TranslateBubbleView::WindowClosing() {
   // The operations for |model_| are valid only when a WebContents is alive.
   // TODO(hajimehoshi): TranslateBubbleViewModel(Impl) should not hold a
@@ -643,8 +669,18 @@
       SwitchView(TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE);
       SizeToContents();
     } else {
-      model_->Translate();
+      RemoveChildView(before_translate_view_);
+      RemoveChildView(translating_view_);
+      RemoveChildView(after_translate_view_);
+      tab_translate_view_ = CreateViewTab();
+      before_translate_view_ = tab_translate_view_;
+      translating_view_ = tab_translate_view_;
+      after_translate_view_ = tab_translate_view_;
+      AddChildView(before_translate_view_);
+      AddChildView(translating_view_);
+      AddChildView(after_translate_view_);
       SwitchView(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE);
+      tabbed_pane_->SelectTabAt(1);
     }
   }
   translate::ReportUiAction(translate::DONE_BUTTON_CLICKED);
@@ -668,6 +704,7 @@
       if (model_->GetOriginalLanguageIndex() ==
           // Selected Index is increased by 1 because we added "Unknown".
           source_language_combobox_->GetSelectedIndex() - 1) {
+        UpdateAdvancedView();
         break;
       }
       model_->UpdateOriginalLanguageIndex(
@@ -679,6 +716,7 @@
     case COMBOBOX_ID_TARGET_LANGUAGE: {
       if (model_->GetTargetLanguageIndex() ==
           target_language_combobox_->GetSelectedIndex()) {
+        UpdateAdvancedView();
         break;
       }
       model_->UpdateTargetLanguageIndex(
@@ -792,8 +830,10 @@
 views::View* TranslateBubbleView::CreateViewTab() {
   base::string16 original_language_name =
       model_->GetLanguageNameAt(model_->GetOriginalLanguageIndex());
+  previous_source_language_index_ = model_->GetOriginalLanguageIndex();
   base::string16 target_language_name =
       model_->GetLanguageNameAt(model_->GetTargetLanguageIndex());
+  previous_target_language_index_ = model_->GetTargetLanguageIndex();
   if (original_language_name.empty()) {
     original_language_name =
         l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_UNKNOWN_LANGUAGE);
@@ -818,7 +858,7 @@
   // when we release ownership of the unique_ptr.
   // TODO(crbug.com/963148): Initial auto translate doesn't trigger tabbed pane
   // to highlight the correct pane.
-  views::TabbedPane* tabbed_pane = new views::TabbedPane();
+  tabbed_pane_ = new views::TabbedPane();
 
   // Three dots options menu button
   const SkColor option_icon_color = gfx::kChromeIconGrey;
@@ -879,14 +919,14 @@
 
   layout->StartRow(1, kColumnSetId);
   layout->AddView(language_icon.release());
-  layout->AddView(tabbed_pane);
+  layout->AddView(tabbed_pane_);
   layout->AddView(tab_translate_options_button.release());
   layout->AddView(close_button.release());
 
   // NOTE: Panes must be added after |tabbed_pane| has been added to its parent.
-  tabbed_pane->AddTab(original_language_name, CreateEmptyPane());
-  tabbed_pane->AddTab(target_language_name, CreateEmptyPane());
-  tabbed_pane->set_listener(this);
+  tabbed_pane_->AddTab(original_language_name, CreateEmptyPane());
+  tabbed_pane_->AddTab(target_language_name, CreateEmptyPane());
+  tabbed_pane_->set_listener(this);
 
   return view;
 }
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.h b/chrome/browser/ui/views/translate/translate_bubble_view.h
index 4243cd6..2fb896b 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.h
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.h
@@ -29,6 +29,7 @@
 #include "ui/views/controls/link_listener.h"
 #include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/controls/styled_label_listener.h"
+#include "ui/views/controls/tabbed_pane/tabbed_pane.h"
 #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h"
 
 class Browser;
@@ -294,6 +295,9 @@
   // Return true if the current state is in advanced state for TAB UI.
   bool TabUiIsAdvancedState(TranslateBubbleModel::ViewState view_state);
 
+  // Handles the reset button in advanced view under Tab Ui.
+  void ResetLanguage();
+
   static TranslateBubbleView* translate_bubble_view_;
 
   views::View* before_translate_view_;
@@ -313,10 +317,15 @@
 
   views::Checkbox* before_always_translate_checkbox_;
   views::Checkbox* advanced_always_translate_checkbox_;
+  views::TabbedPane* tabbed_pane_;
 
   views::LabelButton* advanced_cancel_button_;
   views::LabelButton* advanced_done_button_;
 
+  // Default source/target language without user interaction.
+  int previous_source_language_index_;
+  int previous_target_language_index_;
+
   // Used to trigger the options menu in tests.
   views::Button* before_translate_options_button_;
 
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.cc b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.cc
index 0677674..cfb898b 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.cc
@@ -87,11 +87,13 @@
   return gfx::Size(width, GetHeightForWidth(width));
 }
 
-views::View* AuthenticatorRequestDialogView::CreateExtraView() {
-  other_transports_button_ = new views::MdTextButtonWithDownArrow(
-      this, l10n_util::GetStringUTF16(IDS_WEBAUTHN_TRANSPORT_POPUP_LABEL));
+std::unique_ptr<views::View> AuthenticatorRequestDialogView::CreateExtraView() {
+  auto other_transports_button =
+      std::make_unique<views::MdTextButtonWithDownArrow>(
+          this, l10n_util::GetStringUTF16(IDS_WEBAUTHN_TRANSPORT_POPUP_LABEL));
+  other_transports_button_ = other_transports_button.get();
   ToggleOtherTransportsButtonVisibility();
-  return other_transports_button_;
+  return other_transports_button;
 }
 
 bool AuthenticatorRequestDialogView::Accept() {
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h
index aaba1f0..1ef60d6 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h
+++ b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h
@@ -66,7 +66,7 @@
 
   // views::DialogDelegateView:
   gfx::Size CalculatePreferredSize() const override;
-  views::View* CreateExtraView() override;
+  std::unique_ptr<views::View> CreateExtraView() override;
   bool Accept() override;
   bool Cancel() override;
   bool Close() override;
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
index 3c7e072c..cfab0568 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
@@ -114,7 +114,6 @@
   if (web_app::SystemWebAppManager::IsEnabled()) {
     html_source->AddResourcePath("icon-192.png", IDR_SETTINGS_LOGO_192);
     html_source->AddResourcePath("pwa.html", IDR_PWA_HTML);
-    html_source->AddResourcePath("manifest.json", IDR_OS_SETTINGS_MANIFEST);
   }
 
 #if BUILDFLAG(OPTIMIZE_WEBUI)
@@ -124,6 +123,7 @@
   html_source->AddResourcePath("chromeos/lazy_load.html",
                                IDR_OS_SETTINGS_LAZY_LOAD_VULCANIZED_HTML);
   html_source->SetDefaultResource(IDR_OS_SETTINGS_VULCANIZED_HTML);
+  html_source->AddResourcePath("manifest.json", IDR_OS_SETTINGS_MANIFEST);
 #else
   // Add all settings resources.
   for (size_t i = 0; i < kOsSettingsResourcesSize; ++i) {
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 97ec564..84af670 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2952,6 +2952,7 @@
       {"securityKeysCurrentPINIntro",
        IDS_SETTINGS_SECURITY_KEYS_CURRENT_PIN_INTRO},
       {"securityKeysDesc", IDS_SETTINGS_SECURITY_KEYS_DESC},
+      {"securityKeysHidePINs", IDS_SETTINGS_SECURITY_KEYS_HIDE_PINS},
       {"securityKeysNewPIN", IDS_SETTINGS_SECURITY_KEYS_NEW_PIN},
       {"securityKeysNoPIN", IDS_SETTINGS_SECURITY_KEYS_NO_PIN},
       {"securityKeysNoReset", IDS_SETTINGS_SECURITY_KEYS_NO_RESET},
@@ -2959,12 +2960,17 @@
       {"securityKeysPINError", IDS_SETTINGS_SECURITY_KEYS_PIN_ERROR},
       {"securityKeysPINHardLock", IDS_SETTINGS_SECURITY_KEYS_PIN_HARD_LOCK},
       {"securityKeysPINIncorrect", IDS_SETTINGS_SECURITY_KEYS_PIN_INCORRECT},
-      {"securityKeysPINIncorrectRetriesSin",
-       IDS_SETTINGS_SECURITY_KEYS_PIN_INCORRECT_RETRIES_SIN},
       {"securityKeysPINIncorrectRetriesPl",
        IDS_SETTINGS_SECURITY_KEYS_PIN_INCORRECT_RETRIES_PL},
+      {"securityKeysPINIncorrectRetriesSin",
+       IDS_SETTINGS_SECURITY_KEYS_PIN_INCORRECT_RETRIES_SIN},
+      {"securityKeysPINMismatch",
+       IDS_SETTINGS_SECURITY_KEYS_PIN_ERROR_MISMATCH},
       {"securityKeysPINSoftLock", IDS_SETTINGS_SECURITY_KEYS_PIN_SOFT_LOCK},
       {"securityKeysPINSuccess", IDS_SETTINGS_SECURITY_KEYS_PIN_SUCCESS},
+      {"securityKeysPINTooLong", IDS_SETTINGS_SECURITY_KEYS_PIN_ERROR_TOO_LONG},
+      {"securityKeysPINTooShort",
+       IDS_SETTINGS_SECURITY_KEYS_PIN_ERROR_TOO_SHORT_SMALL},
       {"securityKeysPINTouch", IDS_SETTINGS_SECURITY_KEYS_PIN_TOUCH},
       {"securityKeysReset", IDS_SETTINGS_SECURITY_KEYS_RESET},
       {"securityKeysResetConfirmTitle",
@@ -2986,6 +2992,7 @@
       {"securityKeysSetPINDesc", IDS_SETTINGS_SECURITY_KEYS_SET_PIN_DESC},
       {"securityKeysSetPINInitialTitle",
        IDS_SETTINGS_SECURITY_KEYS_SET_PIN_INITIAL_TITLE},
+      {"securityKeysShowPINs", IDS_SETTINGS_SECURITY_KEYS_SHOW_PINS},
       {"securityKeysTitle", IDS_SETTINGS_SECURITY_KEYS_TITLE},
   };
   AddLocalizedStringsBulk(html_source, kSecurityKeysStrings,
diff --git a/chrome/browser/web_applications/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_app_manager.cc
index dad7c61..1b1b863 100644
--- a/chrome/browser/web_applications/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_app_manager.cc
@@ -22,29 +22,17 @@
 #include "components/version_info/version_info.h"
 #include "content/public/common/content_switches.h"
 
-#if defined(OS_CHROMEOS)
-#include "chromeos/constants/chromeos_features.h"
-#endif  // defined(OS_CHROMEOS)
-
 namespace web_app {
 
 namespace {
 
 base::flat_map<SystemAppType, GURL> CreateSystemWebApps() {
   base::flat_map<SystemAppType, GURL> urls;
-
 // TODO(calamity): Split this into per-platform functions.
 #if defined(OS_CHROMEOS)
-  if (base::FeatureList::IsEnabled(chromeos::features::kDiscoverApp))
-    urls[SystemAppType::DISCOVER] = GURL(chrome::kChromeUIDiscoverURL);
-
-  if (base::FeatureList::IsEnabled(chromeos::features::kSplitSettings)) {
-    constexpr char kChromeSettingsPWAURL[] = "chrome://os-settings/pwa.html";
-    urls[SystemAppType::SETTINGS] = GURL(kChromeSettingsPWAURL);
-  } else {
-    constexpr char kChromeSettingsPWAURL[] = "chrome://settings/pwa.html";
-    urls[SystemAppType::SETTINGS] = GURL(kChromeSettingsPWAURL);
-  }
+  urls[SystemAppType::DISCOVER] = GURL(chrome::kChromeUIDiscoverURL);
+  constexpr char kChromeSettingsPWAURL[] = "chrome://settings/pwa.html";
+  urls[SystemAppType::SETTINGS] = GURL(kChromeSettingsPWAURL);
 #endif  // OS_CHROMEOS
 
   return urls;
diff --git a/chrome/browser/web_applications/system_web_app_manager.h b/chrome/browser/web_applications/system_web_app_manager.h
index 9d87e05..3d25523f 100644
--- a/chrome/browser/web_applications/system_web_app_manager.h
+++ b/chrome/browser/web_applications/system_web_app_manager.h
@@ -57,11 +57,8 @@
   static bool IsEnabled();
 
   // The SystemWebAppManager is disabled in browser tests by default because it
-  // pollutes the startup state (several tests expect the Extensions state to be
-  // clean).
-  //
-  // Call this to install apps for SystemWebApp specific tests, e.g if a test
-  // needs to open OS Settings.
+  // pollutes the startup state. Call this to enable them for SystemWebApp
+  // specific tests.
   void InstallSystemAppsForTesting();
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index f1834cd..454636e 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -623,7 +623,7 @@
 
 // Enables or disables the System Web App manager.
 const base::Feature kSystemWebApps{"SystemWebApps",
-                                   base::FEATURE_ENABLED_BY_DEFAULT};
+                                   base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables or disables the App Management UI.
 const base::Feature kAppManagement{"AppManagement",
diff --git a/chrome/credential_provider/gaiacp/BUILD.gn b/chrome/credential_provider/gaiacp/BUILD.gn
index 77dd53d..bceadab 100644
--- a/chrome/credential_provider/gaiacp/BUILD.gn
+++ b/chrome/credential_provider/gaiacp/BUILD.gn
@@ -205,7 +205,6 @@
     "dllmain.cc",
     "dllmain.h",
     "gaia_credential_provider.def",
-    "gaia_credential_provider.rgs",
   ]
   deps = [
     ":common",
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator.cc b/chrome/credential_provider/gaiacp/associated_user_validator.cc
index e51e638..648742f 100644
--- a/chrome/credential_provider/gaiacp/associated_user_validator.cc
+++ b/chrome/credential_provider/gaiacp/associated_user_validator.cc
@@ -12,6 +12,7 @@
 #include "base/strings/string16.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
+#include "base/win/win_util.h"
 #include "chrome/credential_provider/common/gcp_strings.h"
 #include "chrome/credential_provider/gaiacp/gaia_credential_provider.h"
 #include "chrome/credential_provider/gaiacp/gcp_utils.h"
@@ -253,8 +254,10 @@
     CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus) {
   base::AutoLock locker(validator_lock_);
 
-  if (block_deny_access_update_)
+  if (block_deny_access_update_) {
+    LOGFN(INFO) << "Block the deny access update";
     return false;
+  }
 
   if (!IsUserAccessBlockingEnforced(cpus))
     return false;
@@ -268,12 +271,15 @@
   auto policy = ScopedLsaPolicy::Create(POLICY_ALL_ACCESS);
 
   bool user_denied_signin = false;
+  OSUserManager* manager = OSUserManager::Get();
   for (const auto& user_info : user_to_token_handle_info_) {
     const base::string16& sid = user_info.first;
     if (locked_user_sids_.find(sid) != locked_user_sids_.end())
       continue;
 
-    if (!IsTokenHandleValidForUserInternal(sid)) {
+    // Note that logon hours cannot be changed on domain joined AD user account.
+    if (!IsTokenHandleValidForUserInternal(sid) &&
+        !manager->IsUserDomainJoined(sid)) {
       LOGFN(INFO) << "Revoking access for sid=" << sid;
       HRESULT hr = ModifyUserAccess(policy, sid, false);
       if (FAILED(hr)) {
@@ -282,6 +288,9 @@
         locked_user_sids_.insert(sid);
         user_denied_signin = true;
       }
+    } else if (manager->IsUserDomainJoined(sid)) {
+      // TODO(crbug.com/973160): Description provided in the bug.
+      LOGFN(INFO) << "Not denying signin for AD user accounts.";
     }
   }
 
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator.h b/chrome/credential_provider/gaiacp/associated_user_validator.h
index da28902..73565c0f 100644
--- a/chrome/credential_provider/gaiacp/associated_user_validator.h
+++ b/chrome/credential_provider/gaiacp/associated_user_validator.h
@@ -24,19 +24,19 @@
 // NOTE: This class is thread safe.
 //
 // The following functions are called at a time when it is impossible for
-// the valaditor to be accessed by multiple threads. The validator will only
+// the validator to be accessed by multiple threads. The validator will only
 // be accessed from another thread through the BackgroundTokenHandleUpdater
 // that is created in CGaiaCredentialProvider::Advise and destroyed in
 // CGaiaCredentialProvider::Unadvise:
 // StartRefreshingTokenHandleValidity: Only called on the main thread during
 // a call to DllGetClassObject.
 // IsUserAccessBlockingEnforced: Only called on the main thread in
-// CGaiaCredentialProvider::Advise and in
-// CGaiaCredentialProviderFilter::UpdateRemoteCredential.
+//   CGaiaCredentialProvider::Advise and in
+//   CGaiaCredentialProviderFilter::UpdateRemoteCredential.
 // AllowSigninForUsersWithInvalidTokenHandles: Only called on the main thread
-// in CGaiaCredentialProvider::FinalRelease.
+//   in CGaiaCredentialProvider::FinalRelease.
 // AllowSigninForAllAssociatedUsers: Only called on the main thread in
-// CGaiaCredentialProviderFilter::Filter.
+//   CGaiaCredentialProviderFilter::Filter.
 //
 // The following functions can be called while the validator can be accessed
 // from another thread:
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc b/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc
index 96dfd57..5da5854b 100644
--- a/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc
+++ b/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc
@@ -295,6 +295,54 @@
   EXPECT_EQ(1u, fake_http_url_fetcher_factory()->requests_created());
 }
 
+// Deny user access when the gaia handle is invalidated for a
+// local OS user.
+TEST_F(AssociatedUserValidatorTest,
+       DenySigninForLocalUserWithInvalidTokenHandle) {
+  FakeAssociatedUserValidator validator;
+
+  ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com"));
+
+  CComBSTR sid;
+  // Created a local test os user that is not domain joined.
+  ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
+                      L"username", L"password", L"fullname", L"comment",
+                      L"gaia-id", base::string16(), &sid));
+
+  // Invalid token fetch result.
+  fake_http_url_fetcher_factory()->SetFakeResponse(
+      GURL(AssociatedUserValidator::kTokenInfoUrl),
+      FakeWinHttpUrlFetcher::Headers(), "{}");
+
+  validator.StartRefreshingTokenHandleValidity();
+  EXPECT_FALSE(validator.IsTokenHandleValidForUser(OLE2W(sid)));
+  EXPECT_TRUE(validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON));
+}
+
+// Donot deny user access even when the gaia handle is invalidated for a
+// domain joined OS user.
+TEST_F(AssociatedUserValidatorTest,
+       DonotDenySigninForADUserWithInvalidTokenHandle) {
+  FakeAssociatedUserValidator validator;
+
+  ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com"));
+
+  CComBSTR sid;
+  // Created a test os user with an assigned domain.
+  ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
+                      L"username", L"password", L"fullname", L"comment",
+                      L"gaia-id", base::string16(), L"domain", &sid));
+
+  // Invalid token fetch result.
+  fake_http_url_fetcher_factory()->SetFakeResponse(
+      GURL(AssociatedUserValidator::kTokenInfoUrl),
+      FakeWinHttpUrlFetcher::Headers(), "{}");
+
+  validator.StartRefreshingTokenHandleValidity();
+  EXPECT_FALSE(validator.IsTokenHandleValidForUser(OLE2W(sid)));
+  EXPECT_FALSE(validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON));
+}
+
 // Tests various scenarios where user access is blocked.
 // Parameters are:
 // 1. CREDENTIAL_PROVIDER_USAGE_SCENARIO - Usage scenario.
diff --git a/chrome/credential_provider/gaiacp/os_user_manager.cc b/chrome/credential_provider/gaiacp/os_user_manager.cc
index 0d2ce8c..80e0299 100644
--- a/chrome/credential_provider/gaiacp/os_user_manager.cc
+++ b/chrome/credential_provider/gaiacp/os_user_manager.cc
@@ -26,6 +26,7 @@
 #include "base/macros.h"
 #include "base/scoped_native_library.h"
 #include "base/stl_util.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/registry.h"
@@ -551,6 +552,22 @@
   return hr;
 }
 
+bool OSUserManager::IsUserDomainJoined(const base::string16& sid) {
+  wchar_t username[kWindowsUsernameBufferLength];
+  wchar_t domain[kWindowsDomainBufferLength];
+
+  HRESULT hr = FindUserBySID(sid.c_str(), username, base::size(username),
+                             domain, base::size(domain));
+
+  if (FAILED(hr)) {
+    LOGFN(ERROR) << "IsUserDomainJoined sid=" << sid << " hr=" << putHR(hr);
+    return hr;
+  }
+
+  return !base::EqualsCaseInsensitiveASCII(
+      domain, OSUserManager::GetLocalDomain().c_str());
+}
+
 HRESULT OSUserManager::RemoveUser(const wchar_t* username,
                                   const wchar_t* password) {
   DCHECK(username);
diff --git a/chrome/credential_provider/gaiacp/os_user_manager.h b/chrome/credential_provider/gaiacp/os_user_manager.h
index 85c2e64..ff982534 100644
--- a/chrome/credential_provider/gaiacp/os_user_manager.h
+++ b/chrome/credential_provider/gaiacp/os_user_manager.h
@@ -77,6 +77,9 @@
                                 DWORD username_size, wchar_t* domain,
                                 DWORD domain_size);
 
+  // Verify if a user with provided sid is domain joined.
+  virtual bool IsUserDomainJoined(const base::string16& sid);
+
   // Removes the user from the machine.
   virtual HRESULT RemoveUser(const wchar_t* username, const wchar_t* password);
 
diff --git a/chrome/credential_provider/test/gcp_fakes.cc b/chrome/credential_provider/test/gcp_fakes.cc
index 613664d..3100f7c 100644
--- a/chrome/credential_provider/test/gcp_fakes.cc
+++ b/chrome/credential_provider/test/gcp_fakes.cc
@@ -136,6 +136,17 @@
                                    bool add_to_users_group,
                                    BSTR* sid,
                                    DWORD* error) {
+  return AddUser(username, password, fullname, comment, add_to_users_group,
+                 OSUserManager::GetLocalDomain().c_str(), sid, error);
+}
+HRESULT FakeOSUserManager::AddUser(const wchar_t* username,
+                                   const wchar_t* password,
+                                   const wchar_t* fullname,
+                                   const wchar_t* comment,
+                                   bool add_to_users_group,
+                                   const wchar_t* domain,
+                                   BSTR* sid,
+                                   DWORD* error) {
   USES_CONVERSION;
 
   DCHECK(sid);
@@ -168,8 +179,7 @@
 
   *sid = ::SysAllocString(W2COLE(sidstr));
   username_to_info_.emplace(
-      username, UserInfo(OSUserManager::GetLocalDomain().c_str(), password,
-                         fullname, comment, sidstr));
+      username, UserInfo(domain, password, fullname, comment, sidstr));
   ::LocalFree(sidstr);
 
   return S_OK;
@@ -359,6 +369,7 @@
   return CreateArbitrarySid(++next_rid_, sid);
 }
 
+// Creates a test OS user using the local domain.
 HRESULT FakeOSUserManager::CreateTestOSUser(const base::string16& username,
                                             const base::string16& password,
                                             const base::string16& fullname,
@@ -366,9 +377,21 @@
                                             const base::string16& gaia_id,
                                             const base::string16& email,
                                             BSTR* sid) {
+  return CreateTestOSUser(username, password, fullname, comment, gaia_id, email,
+                          OSUserManager::GetLocalDomain(), sid);
+}
+
+HRESULT FakeOSUserManager::CreateTestOSUser(const base::string16& username,
+                                            const base::string16& password,
+                                            const base::string16& fullname,
+                                            const base::string16& comment,
+                                            const base::string16& gaia_id,
+                                            const base::string16& email,
+                                            const base::string16& domain,
+                                            BSTR* sid) {
   DWORD error;
   HRESULT hr = AddUser(username.c_str(), password.c_str(), fullname.c_str(),
-                       comment.c_str(), true, sid, &error);
+                       comment.c_str(), true, domain.c_str(), sid, &error);
   if (FAILED(hr))
     return hr;
 
diff --git a/chrome/credential_provider/test/gcp_fakes.h b/chrome/credential_provider/test/gcp_fakes.h
index 653db567..4720a0b3 100644
--- a/chrome/credential_provider/test/gcp_fakes.h
+++ b/chrome/credential_provider/test/gcp_fakes.h
@@ -142,6 +142,20 @@
                            const base::string16& email,
                            BSTR* sid);
 
+  // Creates a fake user with the given |username|, |password|, |fullname|,
+  // |comment| and |domain|. If |gaia_id| is non-empty, also associates the
+  // user with the given gaia id. If |email| is non-empty, sets the email to
+  // use for reauth to be this one.
+  // |sid| is allocated and filled with the SID of the new user.
+  HRESULT CreateTestOSUser(const base::string16& username,
+                           const base::string16& password,
+                           const base::string16& fullname,
+                           const base::string16& comment,
+                           const base::string16& gaia_id,
+                           const base::string16& email,
+                           const base::string16& domain,
+                           BSTR* sid);
+
   size_t GetUserCount() const { return username_to_info_.size(); }
   std::vector<std::pair<base::string16, base::string16>> GetUsers() const;
 
@@ -150,6 +164,16 @@
   DWORD next_rid_ = 0;
   std::map<base::string16, UserInfo> username_to_info_;
   bool should_fail_user_creation_ = false;
+
+  // Add a user to the OS with domain associated with it.
+  HRESULT AddUser(const wchar_t* username,
+                  const wchar_t* password,
+                  const wchar_t* fullname,
+                  const wchar_t* comment,
+                  bool add_to_users_group,
+                  const wchar_t* domain,
+                  BSTR* sid,
+                  DWORD* error);
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/installer/gcapi/BUILD.gn b/chrome/installer/gcapi/BUILD.gn
index a6a5018..678b8d9 100644
--- a/chrome/installer/gcapi/BUILD.gn
+++ b/chrome/installer/gcapi/BUILD.gn
@@ -13,7 +13,6 @@
   sources = [
     "gcapi.def",
     "gcapi_dll.cc",
-    "gcapi_dll_version.rc.version",
   ]
 
   deps = [
diff --git a/chrome/installer/mini_installer/BUILD.gn b/chrome/installer/mini_installer/BUILD.gn
index 397c151b..7a779fc 100644
--- a/chrome/installer/mini_installer/BUILD.gn
+++ b/chrome/installer/mini_installer/BUILD.gn
@@ -28,7 +28,6 @@
 source_set("lib") {
   sources = [
     "appid.h",
-    "chrome.release",
     "chrome_appid.cc",
     "configuration.cc",
     "configuration.h",
diff --git a/chrome/installer/setup/BUILD.gn b/chrome/installer/setup/BUILD.gn
index 33d3e80..35fec76a 100644
--- a/chrome/installer/setup/BUILD.gn
+++ b/chrome/installer/setup/BUILD.gn
@@ -19,7 +19,6 @@
   executable("setup") {
     sources = [
       "setup.rc",
-      "setup_exe_version.rc.version",
       "setup_main.cc",
       "setup_main.h",
       "setup_resource.h",
diff --git a/chrome/services/noop/BUILD.gn b/chrome/services/noop/BUILD.gn
deleted file mode 100644
index b11be7f..0000000
--- a/chrome/services/noop/BUILD.gn
+++ /dev/null
@@ -1,16 +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.
-
-source_set("lib") {
-  sources = [
-    "noop_service.cc",
-    "noop_service.h",
-  ]
-
-  deps = [
-    "//chrome/services/noop/public/mojom",
-    "//mojo/public/cpp/bindings",
-    "//services/service_manager/public/cpp",
-  ]
-}
diff --git a/chrome/services/noop/OWNERS b/chrome/services/noop/OWNERS
deleted file mode 100644
index f197d36..0000000
--- a/chrome/services/noop/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-cduvall@chromium.org
diff --git a/chrome/services/noop/noop_service.cc b/chrome/services/noop/noop_service.cc
deleted file mode 100644
index e95c435..0000000
--- a/chrome/services/noop/noop_service.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/services/noop/noop_service.h"
-#include "base/bind.h"
-
-namespace chrome {
-
-NoopService::NoopService(service_manager::mojom::ServiceRequest request)
-    : service_binding_(this, std::move(request)) {
-  registry_.AddInterface<mojom::Noop>(base::BindRepeating(
-      &NoopService::BindNoopRequest, base::Unretained(this)));
-}
-
-NoopService::~NoopService() = default;
-
-void NoopService::OnBindInterface(
-    const service_manager::BindSourceInfo& source_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle interface_pipe) {
-  registry_.BindInterface(interface_name, std::move(interface_pipe));
-}
-
-void NoopService::BindNoopRequest(mojom::NoopRequest request) {
-  bindings_.AddBinding(this, std::move(request));
-}
-
-}  // namespace chrome
diff --git a/chrome/services/noop/noop_service.h b/chrome/services/noop/noop_service.h
deleted file mode 100644
index 4a3ddcf..0000000
--- a/chrome/services/noop/noop_service.h
+++ /dev/null
@@ -1,41 +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 CHROME_SERVICES_NOOP_NOOP_SERVICE_H_
-#define CHROME_SERVICES_NOOP_NOOP_SERVICE_H_
-
-#include "chrome/services/noop/public/mojom/noop.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_binding.h"
-#include "services/service_manager/public/mojom/service.mojom.h"
-
-namespace chrome {
-
-// No-op service that does nothing. Will be used to analyze memory usage of
-// starting an extra process.
-class NoopService : public service_manager::Service, public mojom::Noop {
- public:
-  explicit NoopService(service_manager::mojom::ServiceRequest request);
-  ~NoopService() override;
-
- private:
-  // service_manager::Service:
-  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
-                       const std::string& interface_name,
-                       mojo::ScopedMessagePipeHandle interface_pipe) override;
-
-  void BindNoopRequest(mojom::NoopRequest request);
-
-  service_manager::ServiceBinding service_binding_;
-  service_manager::BinderRegistry registry_;
-  mojo::BindingSet<mojom::Noop> bindings_;
-
-  DISALLOW_COPY_AND_ASSIGN(NoopService);
-};
-
-}  // namespace chrome
-
-#endif  // CHROME_SERVICES_NOOP_NOOP_SERVICE_H_
diff --git a/chrome/services/noop/public/cpp/BUILD.gn b/chrome/services/noop/public/cpp/BUILD.gn
deleted file mode 100644
index cb59483..0000000
--- a/chrome/services/noop/public/cpp/BUILD.gn
+++ /dev/null
@@ -1,30 +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.
-
-source_set("cpp") {
-  sources = [
-    "utils.cc",
-    "utils.h",
-  ]
-
-  deps = [
-    "//base",
-    "//chrome/services/noop/public/mojom",
-    "//services/service_manager/public/cpp",
-  ]
-}
-
-source_set("manifest") {
-  sources = [
-    "manifest.cc",
-    "manifest.h",
-  ]
-
-  deps = [
-    "//base",
-    "//chrome:strings",
-    "//chrome/services/noop/public/mojom",
-    "//services/service_manager/public/cpp",
-  ]
-}
diff --git a/chrome/services/noop/public/cpp/OWNERS b/chrome/services/noop/public/cpp/OWNERS
deleted file mode 100644
index 6faeaa47..0000000
--- a/chrome/services/noop/public/cpp/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-per-file manifest.cc=set noparent
-per-file manifest.cc=file://ipc/SECURITY_OWNERS
-per-file manifest.h=set noparent
-per-file manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/chrome/services/noop/public/cpp/manifest.cc b/chrome/services/noop/public/cpp/manifest.cc
deleted file mode 100644
index 01c35be1..0000000
--- a/chrome/services/noop/public/cpp/manifest.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/services/noop/public/cpp/manifest.h"
-
-#include "base/no_destructor.h"
-#include "chrome/grit/generated_resources.h"
-#include "chrome/services/noop/public/mojom/noop.mojom.h"
-#include "services/service_manager/public/cpp/manifest_builder.h"
-
-const service_manager::Manifest& GetNoopManifest() {
-  static base::NoDestructor<service_manager::Manifest> manifest{
-      service_manager::ManifestBuilder()
-          .WithServiceName(chrome::mojom::kNoopServiceName)
-          .WithDisplayName(IDS_UTILITY_PROCESS_NOOP_SERVICE_NAME)
-          .WithOptions(
-              service_manager::ManifestOptionsBuilder()
-                  .WithExecutionMode(service_manager::Manifest::ExecutionMode::
-                                         kOutOfProcessBuiltin)
-                  .WithSandboxType("network")
-                  .WithInstanceSharingPolicy(
-                      service_manager::Manifest::InstanceSharingPolicy::
-                          kSharedAcrossGroups)
-                  .Build())
-          .ExposeCapability(
-              "noop",
-              service_manager::Manifest::InterfaceList<chrome::mojom::Noop>())
-
-          .Build()};
-  return *manifest;
-}
diff --git a/chrome/services/noop/public/cpp/manifest.h b/chrome/services/noop/public/cpp/manifest.h
deleted file mode 100644
index 8526c36..0000000
--- a/chrome/services/noop/public/cpp/manifest.h
+++ /dev/null
@@ -1,12 +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 CHROME_SERVICES_NOOP_PUBLIC_CPP_MANIFEST_H_
-#define CHROME_SERVICES_NOOP_PUBLIC_CPP_MANIFEST_H_
-
-#include "services/service_manager/public/cpp/manifest.h"
-
-const service_manager::Manifest& GetNoopManifest();
-
-#endif  // CHROME_SERVICES_NOOP_PUBLIC_CPP_MANIFEST_H_
diff --git a/chrome/services/noop/public/cpp/utils.cc b/chrome/services/noop/public/cpp/utils.cc
deleted file mode 100644
index 899fc56..0000000
--- a/chrome/services/noop/public/cpp/utils.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/services/noop/public/cpp/utils.h"
-
-#include "base/bind.h"
-#include "base/feature_list.h"
-#include "chrome/services/noop/public/mojom/noop.mojom.h"
-#include "content/public/common/service_manager_connection.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace chrome {
-namespace {
-
-// If enabled, starts a service that does nothing. This will be used to analyze
-// memory usage of starting an extra process.
-const base::Feature kNoopService{"NoopService",
-                                 base::FEATURE_DISABLED_BY_DEFAULT};
-
-}  // namespace
-
-void MaybeStartNoopService() {
-  if (!IsNoopServiceEnabled())
-    return;
-
-  static mojom::NoopPtr* noop_ptr = new mojom::NoopPtr;
-  if (!noop_ptr->is_bound() || noop_ptr->encountered_error()) {
-    content::ServiceManagerConnection::GetForProcess()
-        ->GetConnector()
-        ->BindInterface(mojom::kNoopServiceName, noop_ptr);
-    noop_ptr->set_connection_error_handler(
-        base::BindOnce(&MaybeStartNoopService));
-  }
-}
-
-bool IsNoopServiceEnabled() {
-  return base::FeatureList::IsEnabled(kNoopService);
-}
-
-}  // namespace chrome
diff --git a/chrome/services/noop/public/cpp/utils.h b/chrome/services/noop/public/cpp/utils.h
deleted file mode 100644
index 25928bb..0000000
--- a/chrome/services/noop/public/cpp/utils.h
+++ /dev/null
@@ -1,18 +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 CHROME_SERVICES_NOOP_PUBLIC_CPP_UTILS_H_
-#define CHROME_SERVICES_NOOP_PUBLIC_CPP_UTILS_H_
-
-namespace chrome {
-
-// Make sure the no-op service is started if it is enabled.
-void MaybeStartNoopService();
-
-// Whether the no-op service should be launched.
-bool IsNoopServiceEnabled();
-
-}  // namespace chrome
-
-#endif  // CHROME_SERVICES_NOOP_PUBLIC_CPP_UTILS_H_
diff --git a/chrome/services/noop/public/mojom/BUILD.gn b/chrome/services/noop/public/mojom/BUILD.gn
deleted file mode 100644
index 971500f..0000000
--- a/chrome/services/noop/public/mojom/BUILD.gn
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("mojom") {
-  sources = [
-    "noop.mojom",
-  ]
-}
diff --git a/chrome/services/noop/public/mojom/OWNERS b/chrome/services/noop/public/mojom/OWNERS
deleted file mode 100644
index 08850f4..0000000
--- a/chrome/services/noop/public/mojom/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chrome/services/noop/public/mojom/noop.mojom b/chrome/services/noop/public/mojom/noop.mojom
deleted file mode 100644
index ab4f3f0..0000000
--- a/chrome/services/noop/public/mojom/noop.mojom
+++ /dev/null
@@ -1,12 +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.
-
-module chrome.mojom;
-
-const string kNoopServiceName = "noop";
-
-// No-op interface that does nothing. Will be used to analyze memory usage of
-// starting an extra process.
-interface Noop {
-};
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3aac3c35..7fe5143d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -730,7 +730,6 @@
     sources = [
       "../../apps/app_restore_service_browsertest.cc",
       "../../apps/load_and_launch_browsertest.cc",
-      "../app/chrome_version.rc.version",
       "../browser/accessibility/accessibility_labels_service_browsertest.cc",
       "../browser/accessibility/browser_accessibility_state_browsertest.cc",
       "../browser/accessibility/image_annotation_browsertest.cc",
@@ -2127,6 +2126,7 @@
         "../browser/extensions/api/certificate_provider/certificate_provider_apitest.cc",
         "../browser/extensions/api/networking_private/networking_private_apitest.cc",
         "../browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc",
+        "../browser/extensions/api/settings_private/settings_private_browsertest_chromeos.cc",
         "../browser/extensions/api/vpn_provider/vpn_provider_apitest.cc",
         "../browser/extensions/chromeos_component_extensions_browsertest.cc",
         "../browser/notifications/notification_platform_bridge_chromeos_browsertest.cc",
@@ -2348,8 +2348,6 @@
       if (is_chrome_branded) {
         deps += [ "//chrome/browser/win/conflicts:browser_tests" ]
       }
-    } else {  # Not Windows.
-      sources -= [ "../app/chrome_version.rc.version" ]
     }
     if (is_mac || is_win) {
       sources += [
@@ -3113,7 +3111,6 @@
     "../browser/ui/passwords/settings/password_manager_presenter_unittest.cc",
     "../browser/ui/search_engines/keyword_editor_controller_unittest.cc",
     "../browser/ui/sync/tab_contents_synced_tab_delegate_unittest.cc",
-    "../browser/ui/webui/favicon_source_unittest.cc",
     "../browser/ui/webui/fileicon_source_unittest.cc",
     "../browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc",
     "../browser/ui/webui/local_state/local_state_ui_unittest.cc",
@@ -3218,6 +3215,7 @@
       "../browser/ui/sync/sync_promo_ui_unittest.cc",
       "../browser/ui/thumbnails/thumbnail_utils_unittest.cc",
       "../browser/ui/toolbar/app_menu_icon_controller_unittest.cc",
+      "../browser/ui/webui/favicon_source_unittest.cc",
     ]
   }
   if (is_chromeos) {
@@ -5449,15 +5447,6 @@
         "../browser/notifications/win/fake_itoastnotifier.cc",
         "../browser/notifications/win/fake_itoastnotifier.h",
         "../browser/ui/views/touch_events_interactive_uitest_win.cc",
-        "//ui/resources/cursors/aliasb.cur",
-        "//ui/resources/cursors/cell.cur",
-        "//ui/resources/cursors/col_resize.cur",
-        "//ui/resources/cursors/copy.cur",
-        "//ui/resources/cursors/none.cur",
-        "//ui/resources/cursors/row_resize.cur",
-        "//ui/resources/cursors/vertical_text.cur",
-        "//ui/resources/cursors/zoom_in.cur",
-        "//ui/resources/cursors/zoom_out.cur",
       ]
       deps += [
         "//chrome:other_version",
@@ -5744,7 +5733,6 @@
 
   test("sync_integration_tests") {
     sources = [
-      "../app/chrome_version.rc.version",
       "../browser/sync/test/integration/enable_disable_test.cc",
       "../browser/sync/test/integration/local_sync_test.cc",
       "../browser/sync/test/integration/migration_test.cc",
@@ -5862,8 +5850,6 @@
       configs -= [ "//build/config/win:default_incremental_linking" ]
       configs +=
           [ "//build/config/win:default_large_module_incremental_linking" ]
-    } else {
-      sources -= [ "../app/chrome_version.rc.version" ]
     }
     if (is_chromeos) {
       sources += [
@@ -5890,7 +5876,6 @@
 
   test("sync_performance_tests") {
     sources = [
-      "../app/chrome_version.rc.version",
       "../browser/sync/test/integration/performance/autofill_sync_perf_test.cc",
       "../browser/sync/test/integration/performance/bookmarks_sync_perf_test.cc",
       "../browser/sync/test/integration/performance/dictionary_sync_perf_test.cc",
@@ -5934,8 +5919,6 @@
       configs -= [ "//build/config/win:default_incremental_linking" ]
       configs +=
           [ "//build/config/win:default_large_module_incremental_linking" ]
-    } else {
-      sources -= [ "../app/chrome_version.rc.version" ]
     }
 
     if (toolkit_views) {
diff --git a/chrome/test/base/android/android_browser_test.cc b/chrome/test/base/android/android_browser_test.cc
index b7174ac..5f542f1 100644
--- a/chrome/test/base/android/android_browser_test.cc
+++ b/chrome/test/base/android/android_browser_test.cc
@@ -4,11 +4,27 @@
 
 #include "chrome/test/base/android/android_browser_test.h"
 
+#include "chrome/test/base/test_launcher_utils.h"
 #include "content/public/test/test_utils.h"
 
 AndroidBrowserTest::AndroidBrowserTest() = default;
 AndroidBrowserTest::~AndroidBrowserTest() = default;
 
+void AndroidBrowserTest::SetUp() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  SetUpCommandLine(command_line);
+  SetUpDefaultCommandLine(command_line);
+
+  BrowserTestBase::SetUp();
+}
+
+void AndroidBrowserTest::SetUpDefaultCommandLine(
+    base::CommandLine* command_line) {
+  test_launcher_utils::PrepareBrowserCommandLineForTests(command_line);
+  test_launcher_utils::PrepareBrowserCommandLineForBrowserTests(
+      command_line, /*open_about_blank_on_launch=*/true);
+}
+
 void AndroidBrowserTest::PreRunTestOnMainThread() {
   // Pump startup related events.
   content::RunAllPendingInMessageLoop();
diff --git a/chrome/test/base/android/android_browser_test.h b/chrome/test/base/android/android_browser_test.h
index d3c4912a..58c6a65 100644
--- a/chrome/test/base/android/android_browser_test.h
+++ b/chrome/test/base/android/android_browser_test.h
@@ -12,7 +12,15 @@
   AndroidBrowserTest();
   ~AndroidBrowserTest() override;
 
+  // Sets up default command line that will be visible to the code under test.
+  // Called by SetUp() after SetUpCommandLine() to add default command line
+  // switches. A default implementation is provided in this class. If a test
+  // does not want to use the default implementation, it should override this
+  // method.
+  virtual void SetUpDefaultCommandLine(base::CommandLine* command_line);
+
   // content::BrowserTestBase implementation.
+  void SetUp() override;
   void PreRunTestOnMainThread() override;
   void PostRunTestOnMainThread() override;
 };
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index 632e2f9..0b0aea46 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -69,7 +69,6 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "extensions/buildflags/buildflags.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "ui/display/display_switches.h"
 
 #if defined(OS_MACOSX)
 #include "base/mac/foundation_util.h"
@@ -100,6 +99,7 @@
 #include "chromeos/services/device_sync/device_sync_impl.h"
 #include "chromeos/services/device_sync/fake_device_sync.h"
 #include "components/user_manager/user_names.h"
+#include "ui/display/display_switches.h"
 #include "ui/events/test/event_generator.h"
 #endif  // defined(OS_CHROMEOS)
 
@@ -118,9 +118,6 @@
 
 namespace {
 
-// Passed as value of kTestType.
-const char kBrowserTestType[] = "browser";
-
 #if defined(OS_CHROMEOS)
 class FakeDeviceSyncImplFactory
     : public chromeos::device_sync::DeviceSyncImpl::Factory {
@@ -155,25 +152,22 @@
 InProcessBrowserTest::SetUpBrowserFunction*
     InProcessBrowserTest::global_browser_set_up_function_ = nullptr;
 
-InProcessBrowserTest::InProcessBrowserTest()
+InProcessBrowserTest::InProcessBrowserTest() {
+  Initialize();
 #if defined(TOOLKIT_VIEWS)
-    : InProcessBrowserTest(
-          base::BindOnce([]() -> std::unique_ptr<views::ViewsDelegate> {
-            return std::make_unique<AccessibilityChecker>();
-          })) {
+  views_delegate_ = std::make_unique<AccessibilityChecker>();
+#endif
 }
 
+#if defined(TOOLKIT_VIEWS)
 InProcessBrowserTest::InProcessBrowserTest(
-    DelegateCallback viewsDelegateCallback)
-#endif  // defined(TOOLKIT_VIEWS)
-    : browser_(NULL),
-      exit_when_last_browser_closes_(true),
-      open_about_blank_on_browser_launch_(true)
-#if defined(OS_MACOSX)
-      ,
-      autorelease_pool_(NULL)
-#endif  // OS_MACOSX
-{
+    std::unique_ptr<views::ViewsDelegate> views_delegate) {
+  Initialize();
+  views_delegate_ = std::move(views_delegate);
+}
+#endif
+
+void InProcessBrowserTest::Initialize() {
 #if defined(OS_MACOSX)
   base::mac::SetOverrideAmIBundled(true);
 
@@ -203,17 +197,13 @@
                                     src_dir.Append(GetChromeTestDataDir())));
 
 #if defined(OS_MACOSX)
-  bundle_swizzler_.reset(new ScopedBundleSwizzlerMac);
+  bundle_swizzler_ = std::make_unique<ScopedBundleSwizzlerMac>();
 #endif
 
 #if defined(OS_CHROMEOS)
   ui::test::EventGeneratorDelegate::SetFactoryFunction(
       base::BindRepeating(&CreateAshEventGeneratorDelegate));
 #endif
-
-#if defined(TOOLKIT_VIEWS)
-  views_delegate_ = std::move(viewsDelegateCallback).Run();
-#endif
 }
 
 InProcessBrowserTest::~InProcessBrowserTest() = default;
@@ -327,22 +317,13 @@
 
 void InProcessBrowserTest::SetUpDefaultCommandLine(
     base::CommandLine* command_line) {
-  // Propagate commandline settings from test_launcher_utils.
   test_launcher_utils::PrepareBrowserCommandLineForTests(command_line);
-
-  // This is a Browser test.
-  command_line->AppendSwitchASCII(switches::kTestType, kBrowserTestType);
-
-  // Use an sRGB color profile to ensure that the machine's color profile does
-  // not affect the results.
-  command_line->AppendSwitchASCII(switches::kForceDisplayColorProfile, "srgb");
+  test_launcher_utils::PrepareBrowserCommandLineForBrowserTests(
+      command_line, open_about_blank_on_browser_launch_);
 
   // TODO(pkotwicz): Investigate if we can remove this switch.
   if (exit_when_last_browser_closes_)
     command_line->AppendSwitch(switches::kDisableZeroBrowsersOpenForTests);
-
-  if (open_about_blank_on_browser_launch_ && command_line->GetArgs().empty())
-    command_line->AppendArg(url::kAboutBlankURL);
 }
 
 bool InProcessBrowserTest::CreateUserDataDirectory() {
diff --git a/chrome/test/base/in_process_browser_test.h b/chrome/test/base/in_process_browser_test.h
index b80d04147..bc1accf 100644
--- a/chrome/test/base/in_process_browser_test.h
+++ b/chrome/test/base/in_process_browser_test.h
@@ -117,14 +117,15 @@
 class InProcessBrowserTest : public content::BrowserTestBase {
  public:
   InProcessBrowserTest();
+
 #if defined(TOOLKIT_VIEWS)
-  using DelegateCallback =
-      base::OnceCallback<std::unique_ptr<views::ViewsDelegate>()>;
-  // |viewsDelegateCallback| is used for tests that want to use a derived class
-  // of ViewsDelegate to observe or modify things like window placement and
-  // Widget params.
-  explicit InProcessBrowserTest(DelegateCallback viewsDelegateCallback);
-#endif  // defined(TOOLKIT_VIEWS)
+  // |views_delegate| is used for tests that want to use a derived class of
+  // ViewsDelegate to observe or modify things like window placement and Widget
+  // params.
+  explicit InProcessBrowserTest(
+      std::unique_ptr<views::ViewsDelegate> views_delegate);
+#endif
+
   ~InProcessBrowserTest() override;
 
   // Configures everything for an in process browser test, then invokes
@@ -255,6 +256,8 @@
   }
 
  private:
+  void Initialize();
+
   // Creates a user data directory for the test if one is needed. Returns true
   // if successful.
   virtual bool CreateUserDataDirectory() WARN_UNUSED_RESULT;
@@ -265,7 +268,7 @@
   static SetUpBrowserFunction* global_browser_set_up_function_;
 
   // Browser created in BrowserMain().
-  Browser* browser_;
+  Browser* browser_ = nullptr;
 
   // Used to run the process until the BrowserProcess signals the test to quit.
   std::unique_ptr<base::RunLoop> run_loop_;
@@ -275,10 +278,10 @@
   base::ScopedTempDir temp_user_data_dir_;
 
   // True if we should exit the tests after the last browser instance closes.
-  bool exit_when_last_browser_closes_;
+  bool exit_when_last_browser_closes_ = true;
 
   // True if the about:blank tab should be opened when the browser is launched.
-  bool open_about_blank_on_browser_launch_;
+  bool open_about_blank_on_browser_launch_ = true;
 
   // We use hardcoded quota settings to have a consistent testing environment.
   storage::QuotaSettings quota_settings_;
@@ -290,7 +293,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 
 #if defined(OS_MACOSX)
-  base::mac::ScopedNSAutoreleasePool* autorelease_pool_;
+  base::mac::ScopedNSAutoreleasePool* autorelease_pool_ = nullptr;
   std::unique_ptr<ScopedBundleSwizzlerMac> bundle_swizzler_;
 
   // Enable fake full keyboard access by default, so that tests don't depend on
diff --git a/chrome/test/base/test_launcher_utils.cc b/chrome/test/base/test_launcher_utils.cc
index 22a199b..a1abde2 100644
--- a/chrome/test/base/test_launcher_utils.cc
+++ b/chrome/test/base/test_launcher_utils.cc
@@ -15,8 +15,10 @@
 #include "build/build_config.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
 #include "components/os_crypt/os_crypt_switches.h"
 #include "content/public/common/content_switches.h"
+#include "ui/display/display_switches.h"
 
 #if defined(USE_AURA)
 #include "ui/wm/core/wm_core_switches.h"
@@ -68,6 +70,20 @@
   command_line->AppendSwitch(switches::kDisableComponentUpdate);
 }
 
+void PrepareBrowserCommandLineForBrowserTests(base::CommandLine* command_line,
+                                              bool open_about_blank_on_launch) {
+  // This is a Browser test.
+  command_line->AppendSwitchASCII(switches::kTestType, "browser");
+
+  // Some browser tests produce pixel results and compare them. Use an sRGB
+  // color profile to ensure that the machine's color profile does not affect
+  // the results.
+  command_line->AppendSwitchASCII(switches::kForceDisplayColorProfile, "srgb");
+
+  if (open_about_blank_on_launch && command_line->GetArgs().empty())
+    command_line->AppendArg(url::kAboutBlankURL);
+}
+
 void RemoveCommandLineSwitch(const base::CommandLine& in_command_line,
                              const std::string& switch_to_remove,
                              base::CommandLine* out_command_line) {
diff --git a/chrome/test/base/test_launcher_utils.h b/chrome/test/base/test_launcher_utils.h
index 8aea122..476a478 100644
--- a/chrome/test/base/test_launcher_utils.h
+++ b/chrome/test/base/test_launcher_utils.h
@@ -21,6 +21,12 @@
 // when running under tests.
 void PrepareBrowserCommandLineForTests(base::CommandLine* command_line);
 
+// Appends browser switches to provided |command_line| to be used
+// when running under browser tests. These are in addition to the flags from
+// PrepareBrowserCommandLineForTests().
+void PrepareBrowserCommandLineForBrowserTests(base::CommandLine* command_line,
+                                              bool open_about_blank_on_launch);
+
 // Appends all switches from |in_command_line| to |out_command_line| except for
 // |switch_to_remove|.
 // TODO(xhwang): Add CommandLine::RemoveSwitch() so we don't need this hack.
diff --git a/chrome/test/data/webui/settings/a11y/about_a11y_test.js b/chrome/test/data/webui/settings/a11y/about_a11y_test.js
index 4302bc0..abce2b2 100644
--- a/chrome/test/data/webui/settings/a11y/about_a11y_test.js
+++ b/chrome/test/data/webui/settings/a11y/about_a11y_test.js
@@ -11,16 +11,48 @@
   'settings_accessibility_test.js',
 ]);
 
-AccessibilityTest.define('SettingsAccessibilityTest', {
+/**
+ * Test fixture for ABOUT
+ * @constructor
+ * @extends {PolymerTest}
+ */
+function SettingsA11yAbout() {}
+
+SettingsA11yAbout.prototype = {
+  __proto__: SettingsAccessibilityTest.prototype,
+
+  extraLibraries: SettingsAccessibilityTest.prototype.extraLibraries.concat([
+    '../../test_browser_proxy.js',
+    '../test_about_page_browser_proxy.js',
+  ]),
+};
+
+AccessibilityTest.define('SettingsA11yAbout', {
   /** @override */
   name: 'ABOUT',
   /** @override */
   axeOptions: SettingsAccessibilityTest.axeOptions,
   /** @override */
   setup: function() {
-    settings.router.navigateTo(settings.routes.ABOUT);
-    Polymer.dom.flush();
+    // Reset to a blank page.
+    PolymerTest.clearBody();
+
+    // Set the URL to be that of specific route to load upon injecting
+    // settings-ui. Simply calling settings.navigateTo(route) prevents
+    // use of mock APIs for fake data.
+    window.history.pushState(
+        'object or string', 'Test', settings.routes.ABOUT.path);
+
+    if (AccessibilityTest.isChromeOS) {
+      let aboutPageProxy = new TestAboutPageBrowserProxy();
+      // Regulatory info is added when the image is loaded async.
+      // Add a fake string to mimic the image text.
+      aboutPageProxy.setRegulatoryInfo('This is fake regulatory info');
+    }
+    const settingsUi = document.createElement('settings-ui');
+    document.body.appendChild(settingsUi);
   },
+
   /** @override */
   tests: {'Accessible with No Changes': function() {}},
   /** @override */
diff --git a/chrome/test/data/webui/settings/about_page_tests.js b/chrome/test/data/webui/settings/about_page_tests.js
index 75c10e2..9284b2f 100644
--- a/chrome/test/data/webui/settings/about_page_tests.js
+++ b/chrome/test/data/webui/settings/about_page_tests.js
@@ -3,185 +3,6 @@
 // found in the LICENSE file.
 
 cr.define('settings_about_page', function() {
-  /** @implements {settings.AboutPageBrowserProxy} */
-  class TestAboutPageBrowserProxy extends TestBrowserProxy {
-    constructor() {
-      const methodNames = [
-        'pageReady',
-        'refreshUpdateStatus',
-        'openHelpPage',
-        'openFeedbackDialog',
-      ];
-
-      if (cr.isChromeOS) {
-        methodNames.push(
-            'getChannelInfo', 'getVersionInfo', 'getRegulatoryInfo',
-            'getHasEndOfLife', 'refreshTPMFirmwareUpdateStatus', 'setChannel');
-      }
-
-      if (cr.isMac) {
-        methodNames.push('promoteUpdater');
-      }
-
-      super(methodNames);
-
-      /** @private {!UpdateStatus} */
-      this.updateStatus_ = UpdateStatus.UPDATED;
-
-      if (cr.isChromeOS) {
-        /** @private {!VersionInfo} */
-        this.versionInfo_ = {
-          arcVersion: '',
-          osFirmware: '',
-          osVersion: '',
-        };
-
-        /** @private {!ChannelInfo} */
-        this.channelInfo_ = {
-          currentChannel: BrowserChannel.BETA,
-          targetChannel: BrowserChannel.BETA,
-          canChangeChannel: true,
-        };
-
-        /** @private {?RegulatoryInfo} */
-        this.regulatoryInfo_ = null;
-
-        /** @private {!TPMFirmwareUpdateStatus} */
-        this.tpmFirmwareUpdateStatus_ = {
-          updateAvailable: false,
-        };
-
-        /** @private {boolean|Promise} */
-        this.hasEndOfLife_ = false;
-      }
-    }
-
-    /** @param {!UpdateStatus} updateStatus */
-    setUpdateStatus(updateStatus) {
-      this.updateStatus_ = updateStatus;
-    }
-
-    sendStatusNoInternet() {
-      cr.webUIListenerCallback('update-status-changed', {
-        progress: 0,
-        status: UpdateStatus.FAILED,
-        message: 'offline',
-        connectionTypes: 'no internet',
-      });
-    }
-
-    /** @override */
-    pageReady() {
-      this.methodCalled('pageReady');
-    }
-
-    /** @override */
-    refreshUpdateStatus() {
-      cr.webUIListenerCallback('update-status-changed', {
-        progress: 1,
-        status: this.updateStatus_,
-      });
-      this.methodCalled('refreshUpdateStatus');
-    }
-
-    /** @override */
-    openFeedbackDialog() {
-      this.methodCalled('openFeedbackDialog');
-    }
-
-    /** @override */
-    openHelpPage() {
-      this.methodCalled('openHelpPage');
-    }
-  }
-
-  if (cr.isMac) {
-    /** @override */
-    TestAboutPageBrowserProxy.prototype.promoteUpdater = function() {
-      this.methodCalled('promoteUpdater');
-    };
-  }
-
-  if (cr.isChromeOS) {
-    /** @param {!VersionInfo} */
-    TestAboutPageBrowserProxy.prototype.setVersionInfo = function(versionInfo) {
-      this.versionInfo_ = versionInfo;
-    };
-
-    /** @param {boolean} canChangeChannel */
-    TestAboutPageBrowserProxy.prototype.setCanChangeChannel = function(
-        canChangeChannel) {
-      this.channelInfo_.canChangeChannel = canChangeChannel;
-    };
-
-    /**
-     * @param {!BrowserChannel} current
-     * @param {!BrowserChannel} target
-     */
-    TestAboutPageBrowserProxy.prototype.setChannels = function(
-        current, target) {
-      this.channelInfo_.currentChannel = current;
-      this.channelInfo_.targetChannel = target;
-    };
-
-    /** @param {?RegulatoryInfo} regulatoryInfo */
-    TestAboutPageBrowserProxy.prototype.setRegulatoryInfo = function(
-        regulatoryInfo) {
-      this.regulatoryInfo_ = regulatoryInfo;
-    };
-
-    /** @param {boolean|Promise} hasEndOfLife */
-    TestAboutPageBrowserProxy.prototype.setHasEndOfLife = function(
-        hasEndOfLife) {
-      this.hasEndOfLife_ = hasEndOfLife;
-    };
-
-    /** @override */
-    TestAboutPageBrowserProxy.prototype.getChannelInfo = function() {
-      this.methodCalled('getChannelInfo');
-      return Promise.resolve(this.channelInfo_);
-    };
-
-    /** @override */
-    TestAboutPageBrowserProxy.prototype.getVersionInfo = function() {
-      this.methodCalled('getVersionInfo');
-      return Promise.resolve(this.versionInfo_);
-    };
-
-    /** @override */
-    TestAboutPageBrowserProxy.prototype.getRegulatoryInfo = function() {
-      this.methodCalled('getRegulatoryInfo');
-      return Promise.resolve(this.regulatoryInfo_);
-    };
-
-    /** @override */
-    TestAboutPageBrowserProxy.prototype.getHasEndOfLife = function() {
-      this.methodCalled('getHasEndOfLife');
-      return Promise.resolve(this.hasEndOfLife_);
-    };
-
-    /** @override */
-    TestAboutPageBrowserProxy.prototype.setChannel = function(
-        channel, isPowerwashAllowed) {
-      this.methodCalled('setChannel', [channel, isPowerwashAllowed]);
-    };
-
-    /** @param {!TPMFirmwareUpdateStatus} status */
-    TestAboutPageBrowserProxy.prototype.setTPMFirmwareUpdateStatus = function(
-        status) {
-      this.tpmFirmwareUpdateStatus_ = status;
-    };
-
-    /** @override */
-    TestAboutPageBrowserProxy.prototype.refreshTPMFirmwareUpdateStatus =
-        function() {
-      this.methodCalled('refreshTPMFirmwareUpdateStatus');
-      cr.webUIListenerCallback(
-          'tpm-firmware-update-status-changed', this.tpmFirmwareUpdateStatus_);
-    };
-  }
-
-
   function registerAboutPageTests() {
     /**
      * @param {!UpdateStatus} status
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 54f8876..03f6fcc 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -209,6 +209,7 @@
     'test_util.js',
     '../test_browser_proxy.js',
     'test_lifetime_browser_proxy.js',
+    'test_about_page_browser_proxy.js',
     'about_page_tests.js',
   ]),
 };
diff --git a/chrome/test/data/webui/settings/cups_printer_page_tests.js b/chrome/test/data/webui/settings/cups_printer_page_tests.js
index ace6d37..0fa10fc 100644
--- a/chrome/test/data/webui/settings/cups_printer_page_tests.js
+++ b/chrome/test/data/webui/settings/cups_printer_page_tests.js
@@ -297,8 +297,6 @@
 
     addDialog.$$('.action-button').click();
     Polymer.dom.flush();
-    // Configure is shown until getPrinterInfo is rejected.
-    assertTrue(!!dialog.$$('add-printer-configuring-dialog'));
 
     // Upon rejection, show model.
     return cupsPrintersBrowserProxy
@@ -494,38 +492,60 @@
    * Test that the close button exists on the configure dialog.
    */
   test('ConfigureDialogCancelDisabled', function() {
-    // Starts in discovery dialog, select add manually button.
-    const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
-    assertTrue(!!discoveryDialog);
-    discoveryDialog.$.manuallyAddPrinterButton.click();
-    Polymer.dom.flush();
+    const newPrinter = {
+      ppdManufacturer: '',
+      ppdModel: '',
+      printerAddress: 'EEAADDAA',
+      printerAutoconf: false,
+      printerDescription: '',
+      printerId: 'printerId',
+      printerManufacturer: '',
+      printerModel: '',
+      printerMakeAndModel: '',
+      printerName: 'printer',
+      printerPPDPath: '',
+      printerPpdReference: {
+        userSuppliedPpdUrl: '',
+        effectiveMakeAndModel: '',
+        autoconf: false,
+      },
+      printerProtocol: 'usb',
+      printerQueue: 'moreinfohere',
+      printerStatus: '',
+      printerUsbInfo: '',
+    };
 
-    // Now we should be in the manually add dialog.
-    const addDialog = dialog.$$('add-printer-manually-dialog');
-    assertTrue(!!addDialog);
-    fillAddManuallyDialog(addDialog);
+    dialog.fire('open-discovery-printers-dialog');
 
-    // Advance to the configure dialog.
-    addDialog.$$('.action-button').click();
-    Polymer.dom.flush();
+    return cupsPrintersBrowserProxy.whenCalled('startDiscoveringPrinters')
+        .then(function() {
+          // Select the printer.
+          let discoveryDialog = dialog.$$('add-printer-discovery-dialog');
+          assertTrue(!!discoveryDialog, 'Cannot find discovery dialog');
+          discoveryDialog.selectedPrinter = newPrinter;
+          // Run printer setup.
+          clickAddButton(discoveryDialog);
+          return cupsPrintersBrowserProxy.whenCalled('addDiscoveredPrinter');
+        })
+        .then(function(printerId) {
+          const configureDialog = dialog.$$('add-printer-configuring-dialog');
+          assertTrue(!!configureDialog);
 
-    // Configure is shown.
-    const configureDialog = dialog.$$('add-printer-configuring-dialog');
-    assertTrue(!!configureDialog);
+          const closeButton = configureDialog.$$('.cancel-button');
+          assertTrue(!!closeButton);
+          assertFalse(closeButton.disabled);
 
-    const closeButton = configureDialog.$$('.cancel-button');
-    assertTrue(!!closeButton);
-    assertFalse(closeButton.disabled);
+          const waitForClose =
+              test_util.eventToPromise('close', configureDialog);
 
-    const waitForClose = test_util.eventToPromise('close', configureDialog);
+          closeButton.click();
+          Polymer.dom.flush();
 
-    closeButton.click();
-    Polymer.dom.flush();
-
-    return waitForClose.then(() => {
-      dialog = page.$$('settings-cups-add-printer-dialog');
-      assertFalse(dialog.showConfiguringDialog_);
-    });
+          return waitForClose.then(() => {
+            dialog = page.$$('settings-cups-add-printer-dialog');
+            assertFalse(dialog.showConfiguringDialog_);
+          });
+        });
   });
 
   /**
diff --git a/chrome/test/data/webui/settings/security_keys_subpage_test.js b/chrome/test/data/webui/settings/security_keys_subpage_test.js
index 9c2ba307..acd8b0c 100644
--- a/chrome/test/data/webui/settings/security_keys_subpage_test.js
+++ b/chrome/test/data/webui/settings/security_keys_subpage_test.js
@@ -291,11 +291,18 @@
   function setNewPINEntries(pinValue, confirmPINValue) {
     setPINEntry(dialog.$.newPIN, pinValue);
     setPINEntry(dialog.$.confirmPIN, confirmPINValue);
+    const ret = test_util.eventToPromise('ui-ready', dialog);
+    dialog.$.pinSubmit.click();
+    return ret;
   }
 
   function setChangePINEntries(currentPINValue, pinValue, confirmPINValue) {
-    setNewPINEntries(pinValue, confirmPINValue);
+    setPINEntry(dialog.$.newPIN, pinValue);
+    setPINEntry(dialog.$.confirmPIN, confirmPINValue);
     setPINEntry(dialog.$.currentPIN, currentPINValue);
+    const ret = test_util.eventToPromise('ui-ready', dialog);
+    dialog.$.pinSubmit.click();
+    return ret;
   }
 
   test('SetPIN', async function() {
@@ -312,21 +319,21 @@
     assertShown('pinPrompt');
     assertTrue(dialog.$.currentPINEntry.hidden);
 
-    setNewPINEntries('123', '');
-    assertTrue(dialog.$.pinSubmit.disabled);
+    await setNewPINEntries('123', '');
+    assertTrue(dialog.$.newPIN.invalid);
+    assertFalse(dialog.$.confirmPIN.invalid);
 
-    setNewPINEntries('123', '123');
-    assertTrue(dialog.$.pinSubmit.disabled);
+    await setNewPINEntries('123', '123');
+    assertTrue(dialog.$.newPIN.invalid);
+    assertFalse(dialog.$.confirmPIN.invalid);
 
-    setNewPINEntries('1234', '123');
-    assertTrue(dialog.$.pinSubmit.disabled);
-
-    setNewPINEntries('1234', '1234');
-    assertFalse(dialog.$.pinSubmit.disabled);  // Note True -> False
+    await setNewPINEntries('1234', '123');
+    assertFalse(dialog.$.newPIN.invalid);
+    assertTrue(dialog.$.confirmPIN.invalid);
 
     const setPINResolver = new PromiseResolver();
     browserProxy.setResponseFor('setPIN', setPINResolver.promise);
-    dialog.$.pinSubmit.click();
+    setNewPINEntries('1234', '1234');
     ({oldPIN, newPIN} = await browserProxy.whenCalled('setPIN'));
     assertTrue(dialog.$.pinSubmit.disabled);
     assertEquals(oldPIN, '');
@@ -352,11 +359,10 @@
       startSetPINResolver.resolve(
           [0 /* not yet complete */, null /* no current PIN */]);
       await uiReady;
-      setNewPINEntries('1234', '1234');
 
       browserProxy.setResponseFor(
           'setPIN', Promise.resolve([1 /* complete */, testCase[0]]));
-      dialog.$.pinSubmit.click();
+      setNewPINEntries('1234', '1234');
       await browserProxy.whenCalled('setPIN');
       await browserProxy.whenCalled('close');
       assertComplete();
@@ -384,31 +390,30 @@
     assertFalse(dialog.$.currentPINEntry.hidden);
 
     setChangePINEntries('123', '', '');
-    assertTrue(dialog.$.pinSubmit.disabled);
+    assertTrue(dialog.$.currentPIN.invalid);
+    assertFalse(dialog.$.newPIN.invalid);
+    assertFalse(dialog.$.confirmPIN.invalid);
 
     setChangePINEntries('123', '123', '');
-    assertTrue(dialog.$.pinSubmit.disabled);
+    assertTrue(dialog.$.currentPIN.invalid);
+    assertFalse(dialog.$.newPIN.invalid);
+    assertFalse(dialog.$.confirmPIN.invalid);
 
     setChangePINEntries('1234', '123', '1234');
-    assertTrue(dialog.$.pinSubmit.disabled);
+    assertFalse(dialog.$.currentPIN.invalid);
+    assertTrue(dialog.$.newPIN.invalid);
+    assertFalse(dialog.$.confirmPIN.invalid);
 
     setChangePINEntries('123', '1234', '1234');
-    assertTrue(dialog.$.pinSubmit.disabled);
-
-    setChangePINEntries('4321', '1234', '1234');
-    assertFalse(dialog.$.pinSubmit.disabled);  // Note True -> False
-
-    // Changing the new PIN so that it no longer matches the confirm PIN should
-    // prevent submitting the dialog.
-    setNewPINEntry('12345');
-    assertTrue(dialog.$.pinSubmit.disabled);
-
-    // Fixing the new PIN should be sufficient to address that.
-    setNewPINEntry('1234');
-    assertFalse(dialog.$.pinSubmit.disabled);
+    assertTrue(dialog.$.currentPIN.invalid);
+    assertFalse(dialog.$.newPIN.invalid);
+    assertFalse(dialog.$.confirmPIN.invalid);
 
     let setPINResolver = new PromiseResolver();
     browserProxy.setResponseFor('setPIN', setPINResolver.promise);
+    setPINEntry(dialog.$.currentPIN, '4321');
+    setPINEntry(dialog.$.newPIN, '1234');
+    setPINEntry(dialog.$.confirmPIN, '1234');
     dialog.$.pinSubmit.click();
     let {oldPIN, newPIN} = await browserProxy.whenCalled('setPIN');
     assertShown('pinPrompt');
@@ -421,12 +426,11 @@
     uiReady = test_util.eventToPromise('ui-ready', dialog);
     setPINResolver.resolve([1 /* complete */, 49 /* incorrect PIN */]);
     await uiReady;
-    // Text box for current PIN should be cleared.
-    assertEquals(dialog.$.currentPIN.value, '');
-    assertTrue(dialog.$.pinSubmit.disabled);
+    assertTrue(dialog.$.currentPIN.invalid);
+    // Text box for current PIN should not be cleared.
+    assertEquals(dialog.$.currentPIN.value, '4321');
 
-    setChangePINEntries('43211', '1234', '1234');
-    assertFalse(dialog.$.pinSubmit.disabled);
+    setPINEntry(dialog.$.currentPIN, '43211');
 
     browserProxy.resetResolver('setPIN');
     setPINResolver = new PromiseResolver();
diff --git a/chrome/test/data/webui/settings/test_about_page_browser_proxy.js b/chrome/test/data/webui/settings/test_about_page_browser_proxy.js
new file mode 100644
index 0000000..d8b2f89
--- /dev/null
+++ b/chrome/test/data/webui/settings/test_about_page_browser_proxy.js
@@ -0,0 +1,179 @@
+// 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.
+
+/** @implements {settings.AboutPageBrowserProxy} */
+class TestAboutPageBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    const methodNames = [
+      'pageReady',
+      'refreshUpdateStatus',
+      'openHelpPage',
+      'openFeedbackDialog',
+    ];
+
+    if (cr.isChromeOS) {
+      methodNames.push(
+          'getChannelInfo', 'getVersionInfo', 'getRegulatoryInfo',
+          'getHasEndOfLife', 'refreshTPMFirmwareUpdateStatus', 'setChannel');
+    }
+
+    if (cr.isMac) {
+      methodNames.push('promoteUpdater');
+    }
+
+    super(methodNames);
+
+    /** @private {!UpdateStatus} */
+    this.updateStatus_ = UpdateStatus.UPDATED;
+
+    if (cr.isChromeOS) {
+      /** @private {!VersionInfo} */
+      this.versionInfo_ = {
+        arcVersion: '',
+        osFirmware: '',
+        osVersion: '',
+      };
+
+      /** @private {!ChannelInfo} */
+      this.channelInfo_ = {
+        currentChannel: BrowserChannel.BETA,
+        targetChannel: BrowserChannel.BETA,
+        canChangeChannel: true,
+      };
+
+      /** @private {?RegulatoryInfo} */
+      this.regulatoryInfo_ = null;
+
+      /** @private {!TPMFirmwareUpdateStatus} */
+      this.tpmFirmwareUpdateStatus_ = {
+        updateAvailable: false,
+      };
+
+      /** @private {boolean|Promise} */
+      this.hasEndOfLife_ = false;
+    }
+  }
+
+  /** @param {!UpdateStatus} updateStatus */
+  setUpdateStatus(updateStatus) {
+    this.updateStatus_ = updateStatus;
+  }
+
+  sendStatusNoInternet() {
+    cr.webUIListenerCallback('update-status-changed', {
+      progress: 0,
+      status: UpdateStatus.FAILED,
+      message: 'offline',
+      connectionTypes: 'no internet',
+    });
+  }
+
+  /** @override */
+  pageReady() {
+    this.methodCalled('pageReady');
+  }
+
+  /** @override */
+  refreshUpdateStatus() {
+    cr.webUIListenerCallback('update-status-changed', {
+      progress: 1,
+      status: this.updateStatus_,
+    });
+    this.methodCalled('refreshUpdateStatus');
+  }
+
+  /** @override */
+  openFeedbackDialog() {
+    this.methodCalled('openFeedbackDialog');
+  }
+
+  /** @override */
+  openHelpPage() {
+    this.methodCalled('openHelpPage');
+  }
+}
+
+if (cr.isMac) {
+  /** @override */
+  TestAboutPageBrowserProxy.prototype.promoteUpdater = function() {
+    this.methodCalled('promoteUpdater');
+  };
+}
+
+if (cr.isChromeOS) {
+  /** @param {!VersionInfo} */
+  TestAboutPageBrowserProxy.prototype.setVersionInfo = function(versionInfo) {
+    this.versionInfo_ = versionInfo;
+  };
+
+  /** @param {boolean} canChangeChannel */
+  TestAboutPageBrowserProxy.prototype.setCanChangeChannel = function(
+      canChangeChannel) {
+    this.channelInfo_.canChangeChannel = canChangeChannel;
+  };
+
+  /**
+   * @param {!BrowserChannel} current
+   * @param {!BrowserChannel} target
+   */
+  TestAboutPageBrowserProxy.prototype.setChannels = function(current, target) {
+    this.channelInfo_.currentChannel = current;
+    this.channelInfo_.targetChannel = target;
+  };
+
+  /** @param {?RegulatoryInfo} regulatoryInfo */
+  TestAboutPageBrowserProxy.prototype.setRegulatoryInfo = function(
+      regulatoryInfo) {
+    this.regulatoryInfo_ = regulatoryInfo;
+  };
+
+  /** @param {boolean|Promise} hasEndOfLife */
+  TestAboutPageBrowserProxy.prototype.setHasEndOfLife = function(hasEndOfLife) {
+    this.hasEndOfLife_ = hasEndOfLife;
+  };
+
+  /** @override */
+  TestAboutPageBrowserProxy.prototype.getChannelInfo = function() {
+    this.methodCalled('getChannelInfo');
+    return Promise.resolve(this.channelInfo_);
+  };
+
+  /** @override */
+  TestAboutPageBrowserProxy.prototype.getVersionInfo = function() {
+    this.methodCalled('getVersionInfo');
+    return Promise.resolve(this.versionInfo_);
+  };
+
+  /** @override */
+  TestAboutPageBrowserProxy.prototype.getRegulatoryInfo = function() {
+    this.methodCalled('getRegulatoryInfo');
+    return Promise.resolve(this.regulatoryInfo_);
+  };
+
+  /** @override */
+  TestAboutPageBrowserProxy.prototype.getHasEndOfLife = function() {
+    this.methodCalled('getHasEndOfLife');
+    return Promise.resolve(this.hasEndOfLife_);
+  };
+
+  /** @override */
+  TestAboutPageBrowserProxy.prototype.setChannel = function(
+      channel, isPowerwashAllowed) {
+    this.methodCalled('setChannel', [channel, isPowerwashAllowed]);
+  };
+
+  /** @param {!TPMFirmwareUpdateStatus} status */
+  TestAboutPageBrowserProxy.prototype.setTPMFirmwareUpdateStatus = function(
+      status) {
+    this.tpmFirmwareUpdateStatus_ = status;
+  };
+
+  /** @override */
+  TestAboutPageBrowserProxy.prototype.refreshTPMFirmwareUpdateStatus =
+      function() {
+    this.methodCalled('refreshTPMFirmwareUpdateStatus');
+    cr.webUIListenerCallback(
+        'tpm-firmware-update-status-changed', this.tpmFirmwareUpdateStatus_);
+  };
+}
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn
index ab42d815..287a9553 100644
--- a/chrome/utility/BUILD.gn
+++ b/chrome/utility/BUILD.gn
@@ -30,8 +30,6 @@
     "//chrome:strings",
     "//chrome/common",
     "//chrome/common:mojo_bindings",
-    "//chrome/services/noop:lib",
-    "//chrome/services/noop/public/cpp",
     "//components/mirroring/mojom:constants",
     "//components/mirroring/service:mirroring_service",
     "//components/search_engines",
diff --git a/chrome/utility/DEPS b/chrome/utility/DEPS
index 935fe0d2..4540dd8 100644
--- a/chrome/utility/DEPS
+++ b/chrome/utility/DEPS
@@ -10,8 +10,6 @@
   "+chrome/services/isolated_xr_device",
   "+chrome/services/media_gallery_util/media_gallery_util_service.h",
   "+chrome/services/media_gallery_util/public/mojom",
-  "+chrome/services/noop/noop_service.h",
-  "+chrome/services/noop/public/cpp",
   # TODO(crbug.com/798782): remove dependency to pdf_to_emf_converter_factory.h
   # when Cloud print chrome/service is removed.
   "+chrome/services/printing/pdf_to_emf_converter_factory.h",
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 51e2117..74b83489 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -16,8 +16,6 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "chrome/common/buildflags.h"
-#include "chrome/services/noop/noop_service.h"
-#include "chrome/services/noop/public/cpp/utils.h"
 #include "components/mirroring/mojom/constants.mojom.h"
 #include "components/mirroring/service/features.h"
 #include "components/mirroring/service/mirroring_service.h"
@@ -253,11 +251,6 @@
   if (service_name == patch::mojom::kServiceName)
     return std::make_unique<patch::PatchService>(std::move(request));
 
-  if (service_name == chrome::mojom::kNoopServiceName &&
-      chrome::IsNoopServiceEnabled()) {
-    return std::make_unique<chrome::NoopService>(std::move(request));
-  }
-
 #if BUILDFLAG(ENABLE_PRINTING)
   if (service_name == printing::mojom::kServiceName)
     return printing::CreatePdfCompositorService(std::move(request));
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index 2bd24ea..2735d250f 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -13,6 +13,7 @@
 
 manifest_variables = [
   "cast_build_enable_background_activities=$enable_background_activities",
+  "is_android_things=$is_android_things",
   "is_android_things_non_public=$is_android_things_non_public",
 ]
 
diff --git a/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 b/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
index 59cf24a..3b4a1d5 100644
--- a/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
+++ b/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
@@ -27,6 +27,9 @@
                   android:exported="true"
                   android:hardwareAccelerated="true"
                   android:launchMode="standard"
+                  {% if is_android_things == "true" %}
+                  android:persistent="true"
+                  {% endif %}
                   android:screenOrientation="landscape"
                   android:taskAffinity=".CastWebContentsActivity"
                   android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
diff --git a/components/autofill/core/browser/payments/full_card_request.cc b/components/autofill/core/browser/payments/full_card_request.cc
index d3a489a..f324f30 100644
--- a/components/autofill/core/browser/payments/full_card_request.cc
+++ b/components/autofill/core/browser/payments/full_card_request.cc
@@ -48,8 +48,30 @@
                                   AutofillClient::UnmaskCardReason reason,
                                   base::WeakPtr<ResultDelegate> result_delegate,
                                   base::WeakPtr<UIDelegate> ui_delegate) {
-  DCHECK(result_delegate);
   DCHECK(ui_delegate);
+  GetFullCard(card, reason, result_delegate, ui_delegate, base::Value());
+}
+
+void FullCardRequest::GetFullCardViaFIDO(
+    const CreditCard& card,
+    AutofillClient::UnmaskCardReason reason,
+    base::WeakPtr<ResultDelegate> result_delegate,
+    base::Value fido_assertion_info) {
+  DCHECK(fido_assertion_info.is_dict());
+  GetFullCard(card, reason, result_delegate, nullptr,
+              std::move(fido_assertion_info));
+}
+
+void FullCardRequest::GetFullCard(const CreditCard& card,
+                                  AutofillClient::UnmaskCardReason reason,
+                                  base::WeakPtr<ResultDelegate> result_delegate,
+                                  base::WeakPtr<UIDelegate> ui_delegate,
+                                  base::Value fido_assertion_info) {
+  // Retrieval of card information should happen via CVC auth or FIDO, but not
+  // both. Use |ui_delegate|'s existence as evidence of doing CVC auth and
+  // |fido_assertion_info| as evidence of doing FIDO auth.
+  DCHECK_NE(fido_assertion_info.is_dict(), !!ui_delegate);
+  DCHECK(result_delegate);
 
   // Only one request can be active at a time. If the member variable
   // |result_delegate_| is already set, then immediately reject the new request
@@ -60,7 +82,6 @@
   }
 
   result_delegate_ = result_delegate;
-  ui_delegate_ = ui_delegate;
   request_.reset(new payments::PaymentsClient::UnmaskRequestDetails);
   request_->card = card;
   should_unmask_card_ = card.record_type() == CreditCard::MASKED_SERVER_CARD ||
@@ -72,8 +93,15 @@
         personal_data_manager_, /*should_log_validity=*/true);
   }
 
-  ui_delegate_->ShowUnmaskPrompt(request_->card, reason,
-                                 weak_ptr_factory_.GetWeakPtr());
+  request_->fido_assertion_info = std::move(fido_assertion_info);
+  ui_delegate_ = ui_delegate;
+
+  // If there is a UI delegate, then perform a CVC check.
+  // Otherwise, continue and use |fido_assertion_info| to unmask.
+  if (ui_delegate_) {
+    ui_delegate_->ShowUnmaskPrompt(request_->card, reason,
+                                   weak_ptr_factory_.GetWeakPtr());
+  }
 
   if (should_unmask_card_) {
     risk_data_loader_->LoadRiskData(
@@ -124,7 +152,8 @@
 
 void FullCardRequest::OnDidGetUnmaskRiskData(const std::string& risk_data) {
   request_->risk_data = risk_data;
-  if (!request_->user_response.cvc.empty())
+  if (!request_->user_response.cvc.empty() ||
+      !request_->fido_assertion_info.is_none())
     SendUnmaskCardRequest();
 }
 
diff --git a/components/autofill/core/browser/payments/full_card_request.h b/components/autofill/core/browser/payments/full_card_request.h
index 661ddad..c8267f8 100644
--- a/components/autofill/core/browser/payments/full_card_request.h
+++ b/components/autofill/core/browser/payments/full_card_request.h
@@ -59,7 +59,7 @@
                   base::TimeTicks form_parsed_timestamp);
   ~FullCardRequest();
 
-  // Retrieves the pan and cvc for |card| and invokes
+  // Retrieves the pan for |card| after querying the user for CVC and invokes
   // Delegate::OnFullCardRequestSucceeded() or
   // Delegate::OnFullCardRequestFailed(). Only one request should be active at a
   // time.
@@ -72,6 +72,19 @@
                    base::WeakPtr<ResultDelegate> result_delegate,
                    base::WeakPtr<UIDelegate> ui_delegate);
 
+  // Retrieves the pan for |card| through a FIDO assertion and invokes
+  // Delegate::OnFullCardRequestSucceeded() or
+  // Delegate::OnFullCardRequestFailed(). Only one request should be active at a
+  // time.
+  //
+  // If the card is local, has a non-empty GUID, and the user has updated its
+  // expiration date, then this function will write the new information to
+  // autofill table on disk.
+  void GetFullCardViaFIDO(const CreditCard& card,
+                          AutofillClient::UnmaskCardReason reason,
+                          base::WeakPtr<ResultDelegate> result_delegate,
+                          base::Value fido_assertion_info);
+
   // Returns true if there's a pending request to get the full card.
   bool IsGettingFullCard() const;
 
@@ -84,6 +97,23 @@
   }
 
  private:
+  // Retrieves the pan for |card| and invokes
+  // Delegate::OnFullCardRequestSucceeded() or
+  // Delegate::OnFullCardRequestFailed(). Only one request should be active at a
+  // time.
+  //
+  // If |ui_delegate| is set, then the user is queried for CVC.
+  // Else if |fido_assertion_info| is a dictionary, FIDO verification is used.
+  //
+  // If the card is local, has a non-empty GUID, and the user has updated its
+  // expiration date, then this function will write the new information to
+  // autofill table on disk.
+  void GetFullCard(const CreditCard& card,
+                   AutofillClient::UnmaskCardReason reason,
+                   base::WeakPtr<ResultDelegate> result_delegate,
+                   base::WeakPtr<UIDelegate> ui_delegate,
+                   base::Value fido_assertion_info);
+
   // CardUnmaskDelegate:
   void OnUnmaskResponse(const UnmaskResponse& response) override;
   void OnUnmaskPromptClosed() override;
diff --git a/components/autofill/core/browser/payments/full_card_request_unittest.cc b/components/autofill/core/browser/payments/full_card_request_unittest.cc
index e3c33f9..4919848 100644
--- a/components/autofill/core/browser/payments/full_card_request_unittest.cc
+++ b/components/autofill/core/browser/payments/full_card_request_unittest.cc
@@ -137,7 +137,7 @@
 }
 
 // Verify getting the full PAN and the CVC for a masked server card.
-TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForMaskedServerCard) {
+TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForMaskedServerCardViaCvc) {
   EXPECT_CALL(*result_delegate(),
               OnFullCardRequestSucceeded(
                   testing::Ref(*request()),
@@ -158,6 +158,21 @@
   card_unmask_delegate()->OnUnmaskPromptClosed();
 }
 
+// Verify getting the full PAN for a masked server card.
+TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForMaskedServerCardViaFido) {
+  EXPECT_CALL(*result_delegate(),
+              OnFullCardRequestSucceeded(
+                  testing::Ref(*request()),
+                  CardMatches(CreditCard::FULL_SERVER_CARD, "4111"),
+                  base::ASCIIToUTF16("")));
+
+  request()->GetFullCardViaFIDO(
+      CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"),
+      AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(),
+      base::Value(base::Value::Type::DICTIONARY));
+  OnDidGetRealPan(AutofillClient::SUCCESS, "4111");
+}
+
 // Verify getting the CVC for a local card.
 TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForLocalCard) {
   EXPECT_CALL(
diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc
index 0f95336..15502b4 100644
--- a/components/autofill/core/browser/payments/payments_client.cc
+++ b/components/autofill/core/browser/payments/payments_client.cc
@@ -371,6 +371,11 @@
     if (base::StringToInt(request_details_.user_response.exp_year, &value))
       request_dict.SetKey("expiration_year", base::Value(value));
 
+    if (request_details_.fido_assertion_info.is_dict()) {
+      request_dict.SetKey("fido_assertion_info",
+                          std::move(request_details_.fido_assertion_info));
+    }
+
     std::string json_request;
     base::JSONWriter::Write(request_dict, &json_request);
     std::string request_content = base::StringPrintf(
@@ -835,7 +840,13 @@
 
 PaymentsClient::UnmaskRequestDetails::UnmaskRequestDetails() {}
 PaymentsClient::UnmaskRequestDetails::UnmaskRequestDetails(
-    const UnmaskRequestDetails& other) = default;
+    const UnmaskRequestDetails& other) {
+  billing_customer_number = other.billing_customer_number;
+  card = other.card;
+  risk_data = other.risk_data;
+  user_response = other.user_response;
+  fido_assertion_info = other.fido_assertion_info.Clone();
+}
 PaymentsClient::UnmaskRequestDetails::~UnmaskRequestDetails() {}
 
 PaymentsClient::UploadRequestDetails::UploadRequestDetails() {}
diff --git a/components/autofill/core/browser/payments/payments_client.h b/components/autofill/core/browser/payments/payments_client.h
index 9f5807f9..72ac874 100644
--- a/components/autofill/core/browser/payments/payments_client.h
+++ b/components/autofill/core/browser/payments/payments_client.h
@@ -85,6 +85,7 @@
     CreditCard card;
     std::string risk_data;
     CardUnmaskDelegate::UnmaskResponse user_response;
+    base::Value fido_assertion_info;
   };
 
   // A collection of the information required to make a credit card upload
diff --git a/components/autofill/core/browser/payments/payments_client_unittest.cc b/components/autofill/core/browser/payments/payments_client_unittest.cc
index e65e5ce4..bfd515ac 100644
--- a/components/autofill/core/browser/payments/payments_client_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_client_unittest.cc
@@ -164,12 +164,17 @@
 
   // Issue an UnmaskCard request. This requires an OAuth token before starting
   // the request.
-  void StartUnmasking() {
+  void StartUnmasking(bool use_fido = false) {
     PaymentsClient::UnmaskRequestDetails request_details;
     request_details.billing_customer_number = 111222333444;
     request_details.card = test::GetMaskedServerCard();
-    request_details.user_response.cvc = base::ASCIIToUTF16("123");
     request_details.risk_data = "some risk data";
+    if (use_fido) {
+      request_details.fido_assertion_info =
+          base::Value(base::Value::Type::DICTIONARY);
+    } else {
+      request_details.user_response.cvc = base::ASCIIToUTF16("123");
+    }
     client_->UnmaskCard(request_details,
                         base::BindOnce(&PaymentsClientTest::OnDidGetRealPan,
                                        weak_ptr_factory_.GetWeakPtr()));
@@ -353,8 +358,16 @@
       std::string::npos);
 }
 
-TEST_F(PaymentsClientTest, UnmaskSuccess) {
-  StartUnmasking();
+TEST_F(PaymentsClientTest, UnmaskSuccessViaCVC) {
+  StartUnmasking(/*use_fido=*/false);
+  IssueOAuthToken();
+  ReturnResponse(net::HTTP_OK, "{ \"pan\": \"1234\" }");
+  EXPECT_EQ(AutofillClient::SUCCESS, result_);
+  EXPECT_EQ("1234", real_pan_);
+}
+
+TEST_F(PaymentsClientTest, UnmaskSuccessViaFIDO) {
+  StartUnmasking(/*use_fido=*/true);
   IssueOAuthToken();
   ReturnResponse(net::HTTP_OK, "{ \"pan\": \"1234\" }");
   EXPECT_EQ(AutofillClient::SUCCESS, result_);
diff --git a/components/cdm/browser/media_drm_storage_impl.cc b/components/cdm/browser/media_drm_storage_impl.cc
index 97c1bc1..59ed724 100644
--- a/components/cdm/browser/media_drm_storage_impl.cc
+++ b/components/cdm/browser/media_drm_storage_impl.cc
@@ -51,9 +51,14 @@
 
 namespace cdm {
 
-namespace {
+namespace prefs {
 
 const char kMediaDrmStorage[] = "media.media_drm_storage";
+
+}  // namespace prefs
+
+namespace {
+
 const char kCreationTime[] = "creation_time";
 const char kSessions[] = "sessions";
 const char kKeySetId[] = "key_set_id";
@@ -477,10 +482,12 @@
 
     // Check if the preference has an existing origin ID.
     const base::DictionaryValue* storage_dict =
-        pref_service->GetDictionary(kMediaDrmStorage);
+        pref_service->GetDictionary(prefs::kMediaDrmStorage);
     base::UnguessableToken origin_id =
         GetOriginIdForOrigin(storage_dict, origin);
     if (origin_id) {
+      DVLOG(3) << __func__
+               << ": Found origin ID in pref service dictionary: " << origin_id;
       std::move(origin_id_obtained_cb).Run(true, origin_id);
       return;
     }
@@ -505,6 +512,7 @@
     // which will call all the callbacks saved for this preference and origin
     // pair. Use of base::Unretained() is valid as |this| is a singleton stored
     // in a static variable.
+    DVLOG(3) << __func__ << ": Call |get_origin_id_cb| to get origin ID.";
     get_origin_id_cb.Run(
         base::BindOnce(&InitializationSerializer::OnOriginIdObtained,
                        base::Unretained(this), pref_service, origin));
@@ -521,7 +529,7 @@
 
     // Save the origin ID in the preference as long as it is not null.
     if (origin_id) {
-      DictionaryPrefUpdate update(pref_service, kMediaDrmStorage);
+      DictionaryPrefUpdate update(pref_service, prefs::kMediaDrmStorage);
       CreateOriginDictAndReturnSessionsDict(update.Get(), origin,
                                             origin_id.value());
     }
@@ -552,7 +560,7 @@
 
 // static
 void MediaDrmStorageImpl::RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterDictionaryPref(kMediaDrmStorage);
+  registry->RegisterDictionaryPref(prefs::kMediaDrmStorage);
 }
 
 // static
@@ -561,7 +569,7 @@
   DCHECK(pref_service);
 
   const base::DictionaryValue* storage_dict =
-      pref_service->GetDictionary(kMediaDrmStorage);
+      pref_service->GetDictionary(prefs::kMediaDrmStorage);
   if (!storage_dict)
     return std::set<GURL>();
 
@@ -582,12 +590,12 @@
   DCHECK(pref_service);
 
   const base::DictionaryValue* storage_dict =
-      pref_service->GetDictionary(kMediaDrmStorage);
+      pref_service->GetDictionary(prefs::kMediaDrmStorage);
   if (!storage_dict)
     return {};
 
   // Check each origin to see if it has been modified since |modified_since|.
-  // If there are any errors in kMediaDrmStorage, ignore them.
+  // If there are any errors in prefs::kMediaDrmStorage, ignore them.
   std::vector<GURL> matching_origins;
   for (const auto& key_value : storage_dict->DictItems()) {
     GURL origin(key_value.first);
@@ -634,7 +642,7 @@
     const base::RepeatingCallback<bool(const GURL&)>& filter) {
   DVLOG(1) << __func__ << ": Clear licenses [" << start << ", " << end << "]";
 
-  DictionaryPrefUpdate update(pref_service, kMediaDrmStorage);
+  DictionaryPrefUpdate update(pref_service, prefs::kMediaDrmStorage);
 
   return ClearMatchingLicenseData(update.Get(), start, end, filter);
 }
@@ -729,7 +737,7 @@
     return;
   }
 
-  DictionaryPrefUpdate update(pref_service_, kMediaDrmStorage);
+  DictionaryPrefUpdate update(pref_service_, prefs::kMediaDrmStorage);
   base::DictionaryValue* storage_dict = update.Get();
   DCHECK(storage_dict);
 
@@ -761,7 +769,7 @@
     return;
   }
 
-  DictionaryPrefUpdate update(pref_service_, kMediaDrmStorage);
+  DictionaryPrefUpdate update(pref_service_, prefs::kMediaDrmStorage);
   base::DictionaryValue* storage_dict = update.Get();
   DCHECK(storage_dict);
 
@@ -816,7 +824,8 @@
 
   const base::Value* sessions_dict =
       GetSessionsDictFromStorageDict<const base::Value>(
-          pref_service_->GetDictionary(kMediaDrmStorage), origin().Serialize());
+          pref_service_->GetDictionary(prefs::kMediaDrmStorage),
+          origin().Serialize());
   if (!sessions_dict) {
     std::move(callback).Run(nullptr);
     return;
@@ -861,7 +870,7 @@
     return;
   }
 
-  DictionaryPrefUpdate update(pref_service_, kMediaDrmStorage);
+  DictionaryPrefUpdate update(pref_service_, prefs::kMediaDrmStorage);
 
   base::Value* sessions_dict = GetSessionsDictFromStorageDict<base::Value>(
       update.Get(), origin().Serialize());
diff --git a/components/cdm/browser/media_drm_storage_impl.h b/components/cdm/browser/media_drm_storage_impl.h
index c520380..009668d4 100644
--- a/components/cdm/browser/media_drm_storage_impl.h
+++ b/components/cdm/browser/media_drm_storage_impl.h
@@ -32,6 +32,12 @@
 
 namespace cdm {
 
+namespace prefs {
+
+extern const char kMediaDrmStorage[];
+
+}  // namespace prefs
+
 // Implements media::mojom::MediaDrmStorage using PrefService.
 // This file is located under components/ so that it can be shared by multiple
 // content embedders (e.g. chrome and chromecast).
diff --git a/components/cdm/browser/media_drm_storage_impl_unittest.cc b/components/cdm/browser/media_drm_storage_impl_unittest.cc
index 50c44d48..f052a54 100644
--- a/components/cdm/browser/media_drm_storage_impl_unittest.cc
+++ b/components/cdm/browser/media_drm_storage_impl_unittest.cc
@@ -26,7 +26,6 @@
 
 namespace {
 
-const char kMediaDrmStorage[] = "media.media_drm_storage";
 const char kTestOrigin[] = "https://www.testorigin.com:80";
 const char kTestOrigin2[] = "https://www.testorigin2.com:80";
 
@@ -124,7 +123,7 @@
 
     // Verify the origin dictionary is created.
     const base::DictionaryValue* storage_dict =
-        pref_service_->GetDictionary(kMediaDrmStorage);
+        pref_service_->GetDictionary(prefs::kMediaDrmStorage);
     EXPECT_TRUE(storage_dict->FindKey(kTestOrigin));
 
     DCHECK(*origin_id);
@@ -292,7 +291,7 @@
 
   // Verify the origin dictionary is created.
   const base::DictionaryValue* storage_dict =
-      pref_service_->GetDictionary(kMediaDrmStorage);
+      pref_service_->GetDictionary(prefs::kMediaDrmStorage);
   EXPECT_TRUE(storage_dict->FindKey(kTestOrigin));
 }
 
diff --git a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/ModuleInstaller.java b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/ModuleInstaller.java
index c87f1d36..d01bc2c 100644
--- a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/ModuleInstaller.java
+++ b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/ModuleInstaller.java
@@ -40,6 +40,7 @@
 
     /** Needs to be called before trying to access a module. */
     public static void init() {
+        if (sSplitCompatted) return;
         // SplitCompat.install may copy modules into Chrome's internal folder or clean them up.
         try (StrictModeContext unused = StrictModeContext.allowDiskWrites()) {
             SplitCompat.install(ContextUtils.getApplicationContext());
diff --git a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/PlayCoreModuleInstallerBackend.java b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/PlayCoreModuleInstallerBackend.java
index 13ba7c7..828c74c 100644
--- a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/PlayCoreModuleInstallerBackend.java
+++ b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/PlayCoreModuleInstallerBackend.java
@@ -91,6 +91,8 @@
 
     /** Records via UMA all modules that have been requested and are currently installed. */
     public static void recordModuleAvailability() {
+        // MUST call init before creating a SplitInstallManager.
+        ModuleInstaller.init();
         SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
         Set<String> requestedModules = new HashSet<>();
         requestedModules.addAll(
@@ -124,6 +126,8 @@
 
     public PlayCoreModuleInstallerBackend(OnFinishedListener listener) {
         super(listener);
+        // MUST call init before creating a SplitInstallManager.
+        ModuleInstaller.init();
         mManager = SplitInstallManagerFactory.create(ContextUtils.getApplicationContext());
         mManager.registerListener(this);
     }
diff --git a/components/security_interstitials/core/browser/resources/interstitial_ssl.css b/components/security_interstitials/core/browser/resources/interstitial_ssl.css
index 97fff226..d2190676 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_ssl.css
+++ b/components/security_interstitials/core/browser/resources/interstitial_ssl.css
@@ -8,7 +8,7 @@
       url(images/2x/triangle_red.png) 2x);
 }
 
-.ssl-opt-in .checkbox {
+.ssl-opt-in .checkbox::after {
   border-color: #696969;
 }
 
diff --git a/components/security_interstitials/core/common/resources/interstitial_common.css b/components/security_interstitials/core/common/resources/interstitial_common.css
index 9dd0775b..f43f92c 100644
--- a/components/security_interstitials/core/common/resources/interstitial_common.css
+++ b/components/security_interstitials/core/common/resources/interstitial_common.css
@@ -113,7 +113,7 @@
   opacity: 0;
 }
 
-input[type=checkbox]:focus ~ .checkbox {
+input[type=checkbox]:focus ~ .checkbox:after {
   outline: -webkit-focus-ring-color auto 5px;
 }
 
@@ -136,9 +136,9 @@
 }
 
 #extended-reporting-opt-in label {
+  display: grid;
+  grid-template-columns: 1.8em 1fr;
   position: relative;
-  display: flex;
-  align-items: flex-start;
 }
 
 .nav-wrapper {
@@ -162,16 +162,27 @@
 }
 
 .checkbox {
+  --padding: .9em;
   background: transparent;
-  border: 1px solid white;
-  border-radius: 2px;
   display: block;
-  height: 14px;
-  left: 0;
+  height: 1em;
+  left: -1em;
+  padding: var(--padding);
   position: absolute;
   right: 0;
-  top: 3px;
-  width: 14px;
+  top: -.5em;
+  width: 1em;
+}
+
+.checkbox::after {
+  border: 1px solid white;
+  border-radius: 2px;
+  content: '';
+  height: 1em;
+  position: absolute;
+  top: var(--padding);
+  left: var(--padding);
+  width: 1em;
 }
 
 .checkbox::before {
@@ -180,13 +191,13 @@
   border-right-width: 0;
   border-top-width: 0;
   content: '';
-  height: 4px;
-  left: 2px;
+  height: .2em;
+  left: calc(.3em + var(--padding));
   opacity: 0;
   position: absolute;
-  top: 3px;
+  top: calc(.3em  + var(--padding));
   transform: rotate(-45deg);
-  width: 9px;
+  width: .5em;
 }
 
 input[type=checkbox]:checked ~ .checkbox::before {
diff --git a/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.cc b/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.cc
index 289284d..1cf20b9 100644
--- a/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.cc
+++ b/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.cc
@@ -156,8 +156,8 @@
   return account_id.compare(0u, kAccountIdPrefixLength, kAccountIdPrefix) != 0;
 }
 
-std::string RemoveAccountIdPrefix(const std::string& prefixed_account_id) {
-  return prefixed_account_id.substr(kAccountIdPrefixLength);
+CoreAccountId RemoveAccountIdPrefix(const std::string& prefixed_account_id) {
+  return CoreAccountId(prefixed_account_id.substr(kAccountIdPrefixLength));
 }
 
 OAuth2TokenServiceDelegate::LoadCredentialsState
@@ -203,7 +203,7 @@
   // Do not migrate if some accounts are not valid.
   for (auto iter = db_tokens.begin(); iter != db_tokens.end(); ++iter) {
     const std::string& prefixed_account_id = iter->first;
-    std::string account_id = RemoveAccountIdPrefix(prefixed_account_id);
+    CoreAccountId account_id = RemoveAccountIdPrefix(prefixed_account_id);
     AccountInfo account_info = account_tracker->GetAccountInfo(account_id);
     if (!account_info.IsValid()) {
       return false;
@@ -454,7 +454,7 @@
 }
 
 std::string MutableProfileOAuth2TokenServiceDelegate::GetRefreshToken(
-    const std::string& account_id) const {
+    const CoreAccountId& account_id) const {
   auto iter = refresh_tokens_.find(account_id);
   if (iter != refresh_tokens_.end()) {
     const std::string refresh_token = iter->second.refresh_token;
@@ -465,7 +465,7 @@
 }
 
 std::string MutableProfileOAuth2TokenServiceDelegate::GetRefreshTokenForTest(
-    const std::string& account_id) const {
+    const CoreAccountId& account_id) const {
   return GetRefreshToken(account_id);
 }
 
@@ -530,7 +530,7 @@
   // gaia IDs.
   if (primary_account_id.id.find('@') != std::string::npos) {
     loading_primary_account_id_ =
-        gaia::CanonicalizeEmail(primary_account_id.id);
+        CoreAccountId(gaia::CanonicalizeEmail(primary_account_id.id));
   } else {
     loading_primary_account_id_ = primary_account_id;
   }
@@ -584,7 +584,7 @@
   }
 #endif
 
-  loading_primary_account_id_.clear();
+  loading_primary_account_id_ = CoreAccountId();
   FinishLoadingCredentials();
 }
 
@@ -617,20 +617,20 @@
         }
       } else {
         DCHECK(!refresh_token.empty());
-        std::string account_id = RemoveAccountIdPrefix(prefixed_account_id);
+        CoreAccountId account_id = RemoveAccountIdPrefix(prefixed_account_id);
 
         switch (migration_state) {
           case AccountTrackerService::MIGRATION_IN_PROGRESS: {
             // Migrate to gaia-ids.
             AccountInfo account_info =
-                account_tracker_service_->FindAccountInfoByEmail(account_id);
-            // |account_info.gaia| could be empty if |account_id| is already
-            // gaia id. This could happen if the chrome was closed in the middle
-            // of migration.
-            if (!account_info.gaia.empty()) {
+                account_tracker_service_->FindAccountInfoByEmail(account_id.id);
+            // |account_info| can be empty if |account_id| was already migrated.
+            // This could happen if the chrome was closed in the middle of the
+            // account id migration.
+            if (!account_info.IsEmpty()) {
               ClearPersistedCredentials(account_id);
-              PersistCredentials(account_info.gaia, refresh_token);
-              account_id = account_info.gaia;
+              account_id = account_info.account_id;
+              PersistCredentials(account_id, refresh_token);
             }
 
             // Skip duplicate accounts, this could happen if migration was
@@ -643,24 +643,24 @@
             // If the account_id is an email address, then canonicalize it. This
             // is to support legacy account_ids, and will not be needed after
             // switching to gaia-ids.
-            if (account_id.find('@') != std::string::npos) {
+            if (account_id.id.find('@') != std::string::npos) {
               // If the canonical account id is not the same as the loaded
               // account id, make sure not to overwrite a refresh token from
               // a canonical version.  If no canonical version was loaded, then
               // re-persist this refresh token with the canonical account id.
-              std::string canon_account_id =
-                  gaia::CanonicalizeEmail(account_id);
+              CoreAccountId canon_account_id =
+                  CoreAccountId(gaia::CanonicalizeEmail(account_id.id));
               if (canon_account_id != account_id) {
                 ClearPersistedCredentials(account_id);
-                if (db_tokens.count(ApplyAccountIdPrefix(canon_account_id)) ==
-                    0)
+                if (db_tokens.count(
+                        ApplyAccountIdPrefix(canon_account_id.id)) == 0)
                   PersistCredentials(canon_account_id, refresh_token);
               }
               account_id = canon_account_id;
             }
             break;
           case AccountTrackerService::MIGRATION_DONE:
-            DCHECK_EQ(std::string::npos, account_id.find('@'));
+            DCHECK_EQ(std::string::npos, account_id.id.find('@'));
             break;
           case AccountTrackerService::NUM_MIGRATION_STATES:
             NOTREACHED();
@@ -752,7 +752,7 @@
 }
 
 void MutableProfileOAuth2TokenServiceDelegate::UpdateCredentialsInMemory(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const std::string& refresh_token) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!account_id.empty());
@@ -798,13 +798,13 @@
 }
 
 void MutableProfileOAuth2TokenServiceDelegate::PersistCredentials(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const std::string& refresh_token) {
   DCHECK(!account_id.empty());
   DCHECK(!refresh_token.empty());
   if (token_web_data_) {
     VLOG(1) << "MutablePO2TS::PersistCredentials for account_id=" << account_id;
-    token_web_data_->SetTokenForService(ApplyAccountIdPrefix(account_id),
+    token_web_data_->SetTokenForService(ApplyAccountIdPrefix(account_id.id),
                                         refresh_token);
   }
 }
@@ -847,12 +847,12 @@
 }
 
 void MutableProfileOAuth2TokenServiceDelegate::ClearPersistedCredentials(
-    const std::string& account_id) {
+    const CoreAccountId& account_id) {
   DCHECK(!account_id.empty());
   if (token_web_data_) {
     VLOG(1) << "MutablePO2TS::ClearPersistedCredentials for account_id="
             << account_id;
-    token_web_data_->RemoveTokenForService(ApplyAccountIdPrefix(account_id));
+    token_web_data_->RemoveTokenForService(ApplyAccountIdPrefix(account_id.id));
   }
 }
 
@@ -914,7 +914,7 @@
 }
 
 void MutableProfileOAuth2TokenServiceDelegate::AddAccountStatus(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     const std::string& refresh_token,
     const GoogleServiceAuthError& error) {
   DCHECK_EQ(0u, refresh_tokens_.count(account_id));
@@ -927,7 +927,7 @@
 }
 
 void MutableProfileOAuth2TokenServiceDelegate::RevokeCredentialsImpl(
-    const std::string& account_id,
+    const CoreAccountId& account_id,
     bool revoke_on_server) {
   ValidateAccountId(account_id);
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
diff --git a/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.h b/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.h
index 8163e62..da24032 100644
--- a/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.h
+++ b/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.h
@@ -82,7 +82,7 @@
   bool FixRequestErrorIfPossible() override;
 
   // Returns the account's refresh token used for testing purposes.
-  std::string GetRefreshTokenForTest(const std::string& account_id) const;
+  std::string GetRefreshTokenForTest(const CoreAccountId& account_id) const;
 
  private:
   friend class MutableProfileOAuth2TokenServiceDelegateTest;
@@ -147,7 +147,7 @@
       const std::map<std::string, std::string>& db_tokens);
 
   // Updates the in-memory representation of the credentials.
-  void UpdateCredentialsInMemory(const std::string& account_id,
+  void UpdateCredentialsInMemory(const CoreAccountId& account_id,
                                  const std::string& refresh_token);
 
   // Sets refresh token in error.
@@ -156,12 +156,12 @@
 
   // Persists credentials for |account_id|. Enables overriding for
   // testing purposes, or other cases, when accessing the DB is not desired.
-  void PersistCredentials(const std::string& account_id,
+  void PersistCredentials(const CoreAccountId& account_id,
                           const std::string& refresh_token);
 
   // Clears credentials persisted for |account_id|. Enables overriding for
   // testing purposes, or other cases, when accessing the DB is not desired.
-  void ClearPersistedCredentials(const std::string& account_id);
+  void ClearPersistedCredentials(const CoreAccountId& account_id);
 
   // Revokes the refresh token on the server.
   void RevokeCredentialsOnServer(const std::string& refresh_token);
@@ -169,11 +169,11 @@
   // Cancels any outstanding fetch for tokens from the web database.
   void CancelWebTokenFetch();
 
-  std::string GetRefreshToken(const std::string& account_id) const;
+  std::string GetRefreshToken(const CoreAccountId& account_id) const;
 
   // Creates a new AccountStatus and adds it to the AccountStatusMap.
   // The account must not be already in the map.
-  void AddAccountStatus(const std::string& account_id,
+  void AddAccountStatus(const CoreAccountId& account_id,
                         const std::string& refresh_token,
                         const GoogleServiceAuthError& error);
 
@@ -184,12 +184,12 @@
   // Deletes the credential locally and notifies observers through
   // OnRefreshTokenRevoked(). If |revoke_on_server| is true, the token is also
   // revoked on the server.
-  void RevokeCredentialsImpl(const std::string& account_id,
+  void RevokeCredentialsImpl(const CoreAccountId& account_id,
                              bool revoke_on_server);
 
   // Maps the |account_id| of accounts known to ProfileOAuth2TokenService
   // to information about the account.
-  typedef std::map<std::string, AccountStatus> AccountStatusMap;
+  typedef std::map<CoreAccountId, AccountStatus> AccountStatusMap;
   // In memory refresh token store mapping account_id to refresh_token.
   AccountStatusMap refresh_tokens_;
 
@@ -198,7 +198,7 @@
 
   // The primary account id of this service's profile during the loading of
   // credentials.  This member is empty otherwise.
-  std::string loading_primary_account_id_;
+  CoreAccountId loading_primary_account_id_;
 
   std::vector<std::unique_ptr<RevokeServerRefreshToken>> server_revokes_;
 
diff --git a/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc b/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc
index 9ece20e..7c0afd7 100644
--- a/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc
+++ b/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc
@@ -276,7 +276,7 @@
 
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceDBUpgrade) {
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kMirror);
-  std::string main_account_id("account_id");
+  CoreAccountId main_account_id("account_id");
   std::string main_refresh_token("old_refresh_token");
 
   // Populate DB with legacy tokens.
@@ -344,9 +344,9 @@
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
        PersistenceRevokeCredentials) {
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
-  std::string account_id_1 = "account_id_1";
+  CoreAccountId account_id_1("account_id_1");
   std::string refresh_token_1 = "refresh_token_1";
-  std::string account_id_2 = "account_id_2";
+  CoreAccountId account_id_2("account_id_2");
   std::string refresh_token_2 = "refresh_token_2";
 
   EXPECT_FALSE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id_1));
@@ -391,6 +391,8 @@
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
        PersistenceLoadCredentials) {
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kMirror);
+  const CoreAccountId account_id("account_id");
+  const CoreAccountId account_id2("account_id_2");
 
   // Ensure DB is clean.
   oauth2_service_delegate_->RevokeAllCredentials();
@@ -428,23 +430,23 @@
   EXPECT_EQ(1U, oauth2_service_delegate_->refresh_tokens_.size());
   EXPECT_EQ(
       MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken,
-      oauth2_service_delegate_->refresh_tokens_["account_id"].refresh_token);
+      oauth2_service_delegate_->refresh_tokens_[account_id].refresh_token);
   // Setup a DB with tokens that don't require upgrade and clear memory.
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
-  oauth2_service_delegate_->UpdateCredentials("account_id2", "refresh_token2");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token");
+  oauth2_service_delegate_->UpdateCredentials(account_id2, "refresh_token2");
   oauth2_service_delegate_->refresh_tokens_.clear();
   EXPECT_EQ(2, end_batch_changes_);
   EXPECT_EQ(2, auth_error_changed_count_);
   ResetObserverCounts();
 
-  oauth2_service_delegate_->LoadCredentials("account_id");
+  oauth2_service_delegate_->LoadCredentials(account_id);
   EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS,
             oauth2_service_delegate_->load_credentials_state());
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS,
             oauth2_service_delegate_->load_credentials_state());
   EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
-            oauth2_service_delegate_->GetAuthError("account_id"));
+            oauth2_service_delegate_->GetAuthError(account_id));
   EXPECT_EQ(2, token_available_count_);
   EXPECT_EQ(0, token_revoked_count_);
   EXPECT_EQ(1, tokens_loaded_count_);
@@ -452,8 +454,8 @@
   EXPECT_EQ(2, auth_error_changed_count_);
   ResetObserverCounts();
 
-  EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable("account_id"));
-  EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable("account_id2"));
+  EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id));
+  EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id2));
 
   oauth2_service_delegate_->RevokeAllCredentials();
   EXPECT_EQ(0, token_available_count_);
@@ -469,6 +471,8 @@
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
        PersistenceLoadCredentialsEmptyPrimaryAccountId_DiceEnabled) {
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice);
+  const CoreAccountId account_id("account_id");
+  const CoreAccountId account_id2("account_id_2");
 
   // Ensure DB is clean.
   oauth2_service_delegate_->RevokeAllCredentials();
@@ -491,8 +495,8 @@
   EXPECT_TRUE(oauth2_service_delegate_->refresh_tokens_.empty());
 
   // Setup a DB with tokens that don't require upgrade and clear memory.
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
-  oauth2_service_delegate_->UpdateCredentials("account_id2", "refresh_token2");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token");
+  oauth2_service_delegate_->UpdateCredentials(account_id2, "refresh_token2");
   oauth2_service_delegate_->refresh_tokens_.clear();
   EXPECT_EQ(2, end_batch_changes_);
   EXPECT_EQ(2, auth_error_changed_count_);
@@ -511,8 +515,8 @@
   EXPECT_EQ(2, auth_error_changed_count_);
   ResetObserverCounts();
 
-  EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable("account_id"));
-  EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable("account_id2"));
+  EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id));
+  EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id2));
 
   oauth2_service_delegate_->RevokeAllCredentials();
   EXPECT_EQ(0, token_available_count_);
@@ -721,16 +725,18 @@
 // Tests that calling UpdateCredentials revokes the old token, without sending
 // the notification.
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RevokeOnUpdate) {
+  const CoreAccountId account_id("account_id");
+
   // Add a token.
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
   ASSERT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token");
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
   ExpectOneTokenAvailableNotification();
 
   // Updating the token does not revoke the old one.
   // Regression test for http://crbug.com/865189
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token2");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token2");
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
   ExpectOneTokenAvailableNotification();
 
@@ -739,7 +745,7 @@
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
 
   // Set the same token again.
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token2");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token2");
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
   ExpectNoNotifications();
 
@@ -754,11 +760,13 @@
 }
 
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, DelayedRevoke) {
+  const CoreAccountId account_id("account_id");
+
   client_->SetNetworkCallsDelayed(true);
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token");
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
-  oauth2_service_delegate_->RevokeCredentials("account_id");
+  oauth2_service_delegate_->RevokeCredentials(account_id);
 
   // The revoke does not start until network calls are unblocked.
   EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size());
@@ -772,11 +780,13 @@
 }
 
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ShutdownDuringRevoke) {
+  const CoreAccountId account_id("account_id");
+
   // Shutdown cancels the revocation.
   client_->SetNetworkCallsDelayed(true);
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
-  oauth2_service_delegate_->RevokeCredentials("account_id");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token");
+  oauth2_service_delegate_->RevokeCredentials(account_id);
   EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size());
 
   // Shutdown.
@@ -791,14 +801,15 @@
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RevokeRetries) {
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
   const std::string url = GaiaUrls::GetInstance()->oauth2_revoke_url().spec();
+  const CoreAccountId account_id("account_id");
   // Revokes will remain in "pending" state.
   client_->GetTestURLLoaderFactory()->ClearResponses();
 
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token");
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
   EXPECT_FALSE(client_->GetTestURLLoaderFactory()->IsPending(url));
 
-  oauth2_service_delegate_->RevokeCredentials("account_id");
+  oauth2_service_delegate_->RevokeCredentials(account_id);
   EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size());
   EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url));
   // Fail and retry.
@@ -818,8 +829,8 @@
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
 
   // No retry after success.
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
-  oauth2_service_delegate_->RevokeCredentials("account_id");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token");
+  oauth2_service_delegate_->RevokeCredentials(account_id);
   EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size());
   EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url));
   client_->GetTestURLLoaderFactory()->SimulateResponseForPendingRequest(
@@ -829,11 +840,12 @@
 }
 
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, UpdateInvalidToken) {
+  const CoreAccountId account_id("account_id");
   // Add the invalid token.
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
   ASSERT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
   oauth2_service_delegate_->UpdateCredentials(
-      "account_id",
+      account_id,
       MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken);
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
   EXPECT_EQ(1, auth_error_changed_count_);
@@ -844,16 +856,16 @@
                 GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
                     GoogleServiceAuthError::InvalidGaiaCredentialsReason::
                         CREDENTIALS_REJECTED_BY_CLIENT)),
-            oauth2_service_delegate_->GetAuthError("account_id"));
+            oauth2_service_delegate_->GetAuthError(account_id));
 
   // Update the token: authentication error is fixed, no actual server
   // revocation.
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token");
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
   EXPECT_EQ(1, auth_error_changed_count_);
   ExpectOneTokenAvailableNotification();
   EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
-            oauth2_service_delegate_->GetAuthError("account_id"));
+            oauth2_service_delegate_->GetAuthError(account_id));
 }
 
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
@@ -901,28 +913,29 @@
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, LoadInvalidToken) {
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice);
   std::map<std::string, std::string> tokens;
+  const CoreAccountId account_id("account_id");
   tokens["AccountId-account_id"] =
       MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken;
 
   oauth2_service_delegate_->LoadAllCredentialsIntoMemory(tokens);
 
   EXPECT_EQ(1u, oauth2_service_delegate_->GetAccounts().size());
-  EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable("account_id"));
+  EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id));
   EXPECT_STREQ(MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken,
-               oauth2_service_delegate_->GetRefreshToken("account_id").c_str());
+               oauth2_service_delegate_->GetRefreshToken(account_id).c_str());
 
   // The account is in authentication error.
   EXPECT_EQ(GoogleServiceAuthError(
                 GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
                     GoogleServiceAuthError::InvalidGaiaCredentialsReason::
                         CREDENTIALS_REJECTED_BY_CLIENT)),
-            oauth2_service_delegate_->GetAuthError("account_id"));
+            oauth2_service_delegate_->GetAuthError(account_id));
 }
 
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, GetTokenForMultilogin) {
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice);
-  const std::string account_id1 = "account_id1";
-  const std::string account_id2 = "account_id2";
+  const CoreAccountId account_id1("account_id1");
+  const CoreAccountId account_id2("account_id2");
 
   oauth2_service_delegate_->UpdateCredentials(account_id1, "refresh_token1");
   oauth2_service_delegate_->UpdateCredentials(account_id2, "refresh_token2");
@@ -939,20 +952,22 @@
 }
 
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceNotifications) {
+  const CoreAccountId account_id("account_id");
+
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token");
   ExpectOneTokenAvailableNotification();
 
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token");
   ExpectNoNotifications();
 
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token2");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token2");
   ExpectOneTokenAvailableNotification();
 
-  oauth2_service_delegate_->RevokeCredentials("account_id");
+  oauth2_service_delegate_->RevokeCredentials(account_id);
   ExpectOneTokenRevokedNotification();
 
-  oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token2");
+  oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token2");
   ExpectOneTokenAvailableNotification();
 
   oauth2_service_delegate_->RevokeAllCredentials();
@@ -960,30 +975,35 @@
 }
 
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, GetAccounts) {
+  const CoreAccountId account_id1("account_id1");
+  const CoreAccountId account_id2("account_id2");
+
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
   EXPECT_TRUE(oauth2_service_delegate_->GetAccounts().empty());
-  oauth2_service_delegate_->UpdateCredentials("account_id1", "refresh_token1");
-  oauth2_service_delegate_->UpdateCredentials("account_id2", "refresh_token2");
+  oauth2_service_delegate_->UpdateCredentials(account_id1, "refresh_token1");
+  oauth2_service_delegate_->UpdateCredentials(account_id2, "refresh_token2");
   std::vector<std::string> accounts = oauth2_service_delegate_->GetAccounts();
   EXPECT_EQ(2u, accounts.size());
-  EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id1"));
-  EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id2"));
-  oauth2_service_delegate_->RevokeCredentials("account_id2");
+  EXPECT_EQ(1, count(accounts.begin(), accounts.end(), account_id1));
+  EXPECT_EQ(1, count(accounts.begin(), accounts.end(), account_id2));
+  oauth2_service_delegate_->RevokeCredentials(account_id2);
   accounts = oauth2_service_delegate_->GetAccounts();
   EXPECT_EQ(1u, oauth2_service_delegate_->GetAccounts().size());
-  EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id1"));
+  EXPECT_EQ(1, count(accounts.begin(), accounts.end(), account_id1));
 }
 
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, FetchPersistentError) {
+  const CoreAccountId email(kEmail);
+
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
-  oauth2_service_delegate_->UpdateCredentials(kEmail, "refreshToken");
+  oauth2_service_delegate_->UpdateCredentials(email, "refreshToken");
   EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
-            oauth2_service_delegate_->GetAuthError(kEmail));
+            oauth2_service_delegate_->GetAuthError(email));
 
   GoogleServiceAuthError authfail(GoogleServiceAuthError::ACCOUNT_DELETED);
-  oauth2_service_delegate_->UpdateAuthError(kEmail, authfail);
+  oauth2_service_delegate_->UpdateAuthError(email, authfail);
   EXPECT_NE(GoogleServiceAuthError::AuthErrorNone(),
-            oauth2_service_delegate_->GetAuthError(kEmail));
+            oauth2_service_delegate_->GetAuthError(email));
 
   // Create a "success" fetch we don't expect to get called.
   AddSuccessfulOAuhTokenResponse();
@@ -994,7 +1014,7 @@
   scope_list.push_back("scope");
   std::unique_ptr<OAuth2AccessTokenFetcher> fetcher(
       oauth2_service_delegate_->CreateAccessTokenFetcher(
-          kEmail, oauth2_service_delegate_->GetURLLoaderFactory(), this));
+          email, oauth2_service_delegate_->GetURLLoaderFactory(), this));
   fetcher->Start("foo", "bar", scope_list);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0, access_token_success_count_);
@@ -1002,15 +1022,17 @@
 }
 
 TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RetryBackoff) {
+  const CoreAccountId email(kEmail);
+
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
-  oauth2_service_delegate_->UpdateCredentials(kEmail, "refreshToken");
+  oauth2_service_delegate_->UpdateCredentials(email, "refreshToken");
   EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
-            oauth2_service_delegate_->GetAuthError(kEmail));
+            oauth2_service_delegate_->GetAuthError(email));
 
   GoogleServiceAuthError authfail(GoogleServiceAuthError::SERVICE_UNAVAILABLE);
-  oauth2_service_delegate_->UpdateAuthError(kEmail, authfail);
+  oauth2_service_delegate_->UpdateAuthError(email, authfail);
   EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
-            oauth2_service_delegate_->GetAuthError(kEmail));
+            oauth2_service_delegate_->GetAuthError(email));
 
   // Create a "success" fetch we don't expect to get called just yet.
   AddSuccessfulOAuhTokenResponse();
@@ -1022,7 +1044,7 @@
   scope_list.push_back("scope");
   std::unique_ptr<OAuth2AccessTokenFetcher> fetcher1(
       oauth2_service_delegate_->CreateAccessTokenFetcher(
-          kEmail, oauth2_service_delegate_->GetURLLoaderFactory(), this));
+          email, oauth2_service_delegate_->GetURLLoaderFactory(), this));
   fetcher1->Start("foo", "bar", scope_list);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0, access_token_success_count_);
@@ -1036,7 +1058,7 @@
       base::TimeTicks());
   std::unique_ptr<OAuth2AccessTokenFetcher> fetcher2(
       oauth2_service_delegate_->CreateAccessTokenFetcher(
-          kEmail, oauth2_service_delegate_->GetURLLoaderFactory(), this));
+          email, oauth2_service_delegate_->GetURLLoaderFactory(), this));
   fetcher2->Start("foo", "bar", scope_list);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, access_token_success_count_);
diff --git a/components/version_info/android/BUILD.gn b/components/version_info/android/BUILD.gn
index e8c41dc3..40f05812 100644
--- a/components/version_info/android/BUILD.gn
+++ b/components/version_info/android/BUILD.gn
@@ -43,6 +43,7 @@
 }
 
 process_version("generate_version_constants") {
+  process_only = true
   template_file = "java/VersionConstants.java.version"
   output = _version_constants_java_file
   sources = [
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index 687fc68..9095d1a 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -46,6 +46,10 @@
 const base::Feature kEnableVizHitTest{"VizHitTest",
                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Use Skia's readback API instead of GLRendererCopier.
+const base::Feature kUseSkiaForGLReadback{"UseSkiaForGLReadback",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Use the SkiaRenderer.
 const base::Feature kUseSkiaRenderer{"UseSkiaRenderer",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
@@ -112,6 +116,10 @@
           base::FeatureList::IsEnabled(kEnableVizHitTestSurfaceLayer));
 }
 
+bool IsUsingSkiaForGLReadback() {
+  return base::FeatureList::IsEnabled(kUseSkiaForGLReadback);
+}
+
 bool IsUsingSkiaRenderer() {
   // We require OOP-D everywhere but WebView.
   bool enabled = base::FeatureList::IsEnabled(kUseSkiaRenderer);
diff --git a/components/viz/common/features.h b/components/viz/common/features.h
index fd452ba..e575fcf 100644
--- a/components/viz/common/features.h
+++ b/components/viz/common/features.h
@@ -15,6 +15,7 @@
 VIZ_COMMON_EXPORT extern const base::Feature kEnableVizHitTest;
 VIZ_COMMON_EXPORT extern const base::Feature kEnableVizHitTestDrawQuad;
 VIZ_COMMON_EXPORT extern const base::Feature kEnableVizHitTestSurfaceLayer;
+VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaForGLReadback;
 VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaRenderer;
 VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaRendererNonDDL;
 VIZ_COMMON_EXPORT extern const base::Feature kRecordSkPicture;
@@ -26,6 +27,7 @@
 VIZ_COMMON_EXPORT bool IsVizHitTestingDrawQuadEnabled();
 VIZ_COMMON_EXPORT bool IsVizHitTestingEnabled();
 VIZ_COMMON_EXPORT bool IsVizHitTestingSurfaceLayerEnabled();
+VIZ_COMMON_EXPORT bool IsUsingSkiaForGLReadback();
 VIZ_COMMON_EXPORT bool IsUsingSkiaRenderer();
 VIZ_COMMON_EXPORT bool IsUsingSkiaRendererNonDDL();
 VIZ_COMMON_EXPORT bool IsRecordingSkPicture();
diff --git a/components/viz/service/display_embedder/DEPS b/components/viz/service/display_embedder/DEPS
index 2783582..12d77fd 100644
--- a/components/viz/service/display_embedder/DEPS
+++ b/components/viz/service/display_embedder/DEPS
@@ -14,6 +14,7 @@
   "+components/viz/service/display/shared_bitmap_manager.h",
   "+components/viz/service/display/skia_output_surface.h",
   "+components/viz/service/display/software_output_device.h",
+  "+components/viz/common",
   "+gpu/config/gpu_feature_info.h",
   "+components/viz/service/gl/gpu_service_impl.h",
   "+gpu/GLES2",
diff --git a/components/viz/service/display_embedder/output_surface_provider_impl.cc b/components/viz/service/display_embedder/output_surface_provider_impl.cc
index 33e40ae..f617eac 100644
--- a/components/viz/service/display_embedder/output_surface_provider_impl.cc
+++ b/components/viz/service/display_embedder/output_surface_provider_impl.cc
@@ -167,17 +167,18 @@
           image_factory_, gpu_channel_manager_delegate_, renderer_settings);
       context_result = context_provider->BindToCurrentThread();
 
-      if (IsFatalOrSurfaceFailure(context_result)) {
 #if defined(OS_ANDROID)
-        display_client->OnFatalOrSurfaceContextCreationFailure(context_result);
-#elif defined(OS_CHROMEOS) || defined(IS_CHROMECAST)
+      display_client->OnContextCreationResult(context_result);
+#endif
+
+      if (IsFatalOrSurfaceFailure(context_result)) {
+#if defined(OS_CHROMEOS) || defined(IS_CHROMECAST)
         // TODO(kylechar): Chrome OS can't disable GPU compositing. This needs
         // to be handled similar to Android.
         CHECK(false);
-#else
+#elif !defined(OS_ANDROID)
         gpu_service_impl_->DisableGpuCompositing();
 #endif
-
         return nullptr;
       }
     }
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 7cc685f..f11b2ab 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -11,6 +11,7 @@
 #include "base/optional.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/viz/common/features.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/copy_output_util.h"
 #include "components/viz/common/resources/resource_format_utils.h"
@@ -69,6 +70,88 @@
 
 namespace viz {
 
+namespace {
+
+struct ReadPixelsContext {
+  ReadPixelsContext(std::unique_ptr<CopyOutputRequest> request,
+                    const gfx::Rect& result_rect)
+      : request(std::move(request)), result_rect(result_rect) {}
+
+  std::unique_ptr<CopyOutputRequest> request;
+  gfx::Rect result_rect;
+};
+
+class CopyOutputResultYUV : public CopyOutputResult {
+ public:
+  CopyOutputResultYUV(const gfx::Rect& rect,
+                      const void* data[3],
+                      size_t row_bytes[3])
+      : CopyOutputResult(Format::I420_PLANES, rect) {
+    DCHECK_EQ(0, size().width() % 2);
+    DCHECK_EQ(0, size().height() % 2);
+    for (int i = 0; i < 3; ++i) {
+      data_[i] = std::make_unique<uint8_t[]>(row_bytes[i] * height(i));
+      memcpy(data_[i].get(), data[i], row_bytes[i] * height(i));
+      row_bytes_[i] = row_bytes[i];
+    }
+  }
+
+  // CopyOutputResult implementation.
+  bool ReadI420Planes(uint8_t* y_out,
+                      int y_out_stride,
+                      uint8_t* u_out,
+                      int u_out_stride,
+                      uint8_t* v_out,
+                      int v_out_stride) const override {
+    const auto CopyPlane = [](const uint8_t* src, int src_stride, int width,
+                              int height, uint8_t* out, int out_stride) {
+      for (int i = 0; i < height; ++i, src += src_stride, out += out_stride) {
+        memcpy(out, src, width);
+      }
+    };
+    CopyPlane(data_[0].get(), row_bytes_[0], width(0), height(0), y_out,
+              y_out_stride);
+    CopyPlane(data_[1].get(), row_bytes_[1], width(1), height(1), u_out,
+              u_out_stride);
+    CopyPlane(data_[2].get(), row_bytes_[2], width(2), height(2), v_out,
+              v_out_stride);
+    return true;
+  }
+
+ private:
+  uint32_t width(int plane) const {
+    if (plane == 0)
+      return size().width();
+    else
+      return size().width() / 2;
+  }
+
+  uint32_t height(int plane) const {
+    if (plane == 0)
+      return size().height();
+    else
+      return size().height() / 2;
+  }
+
+  std::unique_ptr<uint8_t[]> data_[3];
+  size_t row_bytes_[3];
+};
+
+void OnYUVReadbackDone(void* c, const void* data[3], size_t row_bytes[3]) {
+  std::unique_ptr<ReadPixelsContext> context(
+      static_cast<ReadPixelsContext*>(c));
+  if (!data) {
+    // This will automatically send an empty result.
+    return;
+  }
+  std::unique_ptr<CopyOutputResult> result =
+      std::make_unique<CopyOutputResultYUV>(context->result_rect, data,
+                                            row_bytes);
+  context->request->SendResult(std::move(result));
+}
+
+}  // namespace
+
 class SkiaOutputSurfaceImplOnGpu::ScopedPromiseImageAccess {
  public:
   ScopedPromiseImageAccess(SkiaOutputSurfaceImplOnGpu* impl_on_gpu,
@@ -745,7 +828,7 @@
   auto* surface =
       from_fbo0 ? output_sk_surface() : offscreen_surfaces_[id].surface();
 
-  if (!is_using_vulkan()) {
+  if (!is_using_vulkan() && !features::IsUsingSkiaForGLReadback()) {
     // Lazy initialize GLRendererCopier.
     if (!copier_) {
       auto client = std::make_unique<DirectContextProviderDelegateImpl>(
@@ -795,6 +878,35 @@
     return;
   }
 
+  if (request->result_format() ==
+      CopyOutputRequest::ResultFormat::I420_PLANES) {
+    base::Optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use;
+    if (dependency_->GetGrShaderCache()) {
+      cache_use.emplace(dependency_->GetGrShaderCache(),
+                        gpu::kInProcessCommandBufferClientId);
+    }
+    // For downscaling, use the GOOD quality setting (appropriate for
+    // thumbnailing); and, for upscaling, use the BEST quality.
+    bool is_downscale_in_both_dimensions =
+        request->scale_to().x() < request->scale_from().x() &&
+        request->scale_to().y() < request->scale_from().y();
+    SkFilterQuality filter_quality = is_downscale_in_both_dimensions
+                                         ? kMedium_SkFilterQuality
+                                         : kHigh_SkFilterQuality;
+    SkIRect srcRect = SkIRect::MakeXYWH(
+        geometry.sampling_bounds.x(), geometry.sampling_bounds.y(),
+        geometry.sampling_bounds.width(), geometry.sampling_bounds.height());
+    std::unique_ptr<ReadPixelsContext> context =
+        std::make_unique<ReadPixelsContext>(std::move(request),
+                                            geometry.result_bounds);
+    surface->asyncRescaleAndReadPixelsYUV420(
+        kRec709_SkYUVColorSpace, SkColorSpace::MakeSRGB(), srcRect,
+        geometry.result_bounds.width(), geometry.result_bounds.height(),
+        SkSurface::RescaleGamma::kSrc, filter_quality, OnYUVReadbackDone,
+        context.release());
+    return;
+  }
+
   SkBitmap bitmap;
   if (request->is_scaled()) {
     SkImageInfo sampling_bounds_info = SkImageInfo::Make(
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 8a32a05..3317ad6 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -193,6 +193,10 @@
 
 GpuServiceImpl::~GpuServiceImpl() {
   DCHECK(main_runner_->BelongsToCurrentThread());
+
+  // Ensure we don't try to exit when already in the process of exiting.
+  is_exiting_.Set();
+
   bind_task_tracker_.TryCancelAll();
   logging::SetLogMessageHandler(nullptr);
   g_log_callback.Get().Reset();
@@ -830,14 +834,15 @@
   if (in_host_process())
     return;
 
-  if (exit_callback_) {
-    if (for_context_loss) {
-      LOG(ERROR) << "Exiting GPU process because some drivers can't recover "
-                    "from errors. GPU process will restart shortly.";
-    }
-    is_exiting_.Set();
-    std::move(exit_callback_).Run();
+  if (IsExiting() || !exit_callback_)
+    return;
+
+  if (for_context_loss) {
+    LOG(ERROR) << "Exiting GPU process because some drivers can't recover "
+                  "from errors. GPU process will restart shortly.";
   }
+  is_exiting_.Set();
+  std::move(exit_callback_).Run();
 }
 
 }  // namespace viz
diff --git a/components/viz/test/mock_display_client.h b/components/viz/test/mock_display_client.h
index f43156e2..ebca77f 100644
--- a/components/viz/test/mock_display_client.h
+++ b/components/viz/test/mock_display_client.h
@@ -30,8 +30,7 @@
 #endif
 #if defined(OS_ANDROID)
   MOCK_METHOD1(DidCompleteSwapWithSize, void(const gfx::Size&));
-  MOCK_METHOD1(OnFatalOrSurfaceContextCreationFailure,
-               void(gpu::ContextResult));
+  MOCK_METHOD1(OnContextCreationResult, void(gpu::ContextResult));
   MOCK_METHOD1(SetPreferredRefreshRate, void(float refresh_rate));
 #endif
 #if defined(USE_X11)
diff --git a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
index e29ca1e..6fa54a04 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
@@ -450,14 +450,13 @@
     n_row_headers = row_headers->len;
     g_ptr_array_unref(row_headers);
   } else {
-    row = node->GetTableRow();
-    col = node->GetTableColumn();
-    row_span = node->GetTableRowSpan();
-    col_span = node->GetTableColumnSpan();
+    row = node->GetTableRow().value_or(-1);
+    col = node->GetTableColumn().value_or(-1);
+    row_span = node->GetTableRowSpan().value_or(0);
+    col_span = node->GetTableColumnSpan().value_or(0);
     if (role == ATK_ROLE_TABLE_CELL) {
-      auto* delegate = node->GetTable()->GetDelegate();
-      n_column_headers = delegate->GetColHeaderNodeIds(col).size();
-      n_row_headers = delegate->GetRowHeaderNodeIds(row).size();
+      n_column_headers = node->GetDelegate()->GetColHeaderNodeIds(col).size();
+      n_row_headers = node->GetDelegate()->GetRowHeaderNodeIds(row).size();
     }
   }
 
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index ed8995b..398fda3 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -1434,55 +1434,57 @@
   return node()->IsTable();
 }
 
-int32_t BrowserAccessibility::GetTableRowCount() const {
+base::Optional<int> BrowserAccessibility::GetTableRowCount() const {
   return node()->GetTableRowCount();
 }
 
-int32_t BrowserAccessibility::GetTableColCount() const {
+base::Optional<int> BrowserAccessibility::GetTableColCount() const {
   return node()->GetTableColCount();
 }
 
-base::Optional<int32_t> BrowserAccessibility::GetTableAriaColCount() const {
+base::Optional<int> BrowserAccessibility::GetTableAriaColCount() const {
   return node()->GetTableAriaColCount();
 }
 
-base::Optional<int32_t> BrowserAccessibility::GetTableAriaRowCount() const {
+base::Optional<int> BrowserAccessibility::GetTableAriaRowCount() const {
   return node()->GetTableAriaRowCount();
 }
 
-int32_t BrowserAccessibility::GetTableCellCount() const {
+base::Optional<int> BrowserAccessibility::GetTableCellCount() const {
   return node()->GetTableCellCount();
 }
 
-const std::vector<int32_t> BrowserAccessibility::GetColHeaderNodeIds() const {
+std::vector<int32_t> BrowserAccessibility::GetColHeaderNodeIds() const {
   std::vector<int32_t> result;
   node()->GetTableCellColHeaderNodeIds(&result);
   return result;
 }
 
-const std::vector<int32_t> BrowserAccessibility::GetColHeaderNodeIds(
-    int32_t col_index) const {
+std::vector<int32_t> BrowserAccessibility::GetColHeaderNodeIds(
+    int col_index) const {
   std::vector<int32_t> result;
   node()->GetTableColHeaderNodeIds(col_index, &result);
   return result;
 }
 
-const std::vector<int32_t> BrowserAccessibility::GetRowHeaderNodeIds() const {
+std::vector<int32_t> BrowserAccessibility::GetRowHeaderNodeIds() const {
   std::vector<int32_t> result;
   node()->GetTableCellRowHeaderNodeIds(&result);
   return result;
 }
 
-const std::vector<int32_t> BrowserAccessibility::GetRowHeaderNodeIds(
-    int32_t row_index) const {
+std::vector<int32_t> BrowserAccessibility::GetRowHeaderNodeIds(
+    int row_index) const {
   std::vector<int32_t> result;
   node()->GetTableRowHeaderNodeIds(row_index, &result);
   return result;
 }
 
-ui::AXPlatformNode* BrowserAccessibility::GetTableCaption() {
-  if (ui::AXNode* caption = node()->GetTableCaption())
-    return GetFromNodeID(caption->id());
+ui::AXPlatformNode* BrowserAccessibility::GetTableCaption() const {
+  ui::AXNode* caption = node()->GetTableCaption();
+  if (caption)
+    return const_cast<BrowserAccessibility*>(this)->GetFromNodeID(
+        caption->id());
 
   return nullptr;
 }
@@ -1491,7 +1493,7 @@
   return node()->IsTableRow();
 }
 
-int32_t BrowserAccessibility::GetTableRowRowIndex() const {
+base::Optional<int> BrowserAccessibility::GetTableRowRowIndex() const {
   return node()->GetTableRowRowIndex();
 }
 
@@ -1499,48 +1501,48 @@
   return node()->IsTableCellOrHeader();
 }
 
-int32_t BrowserAccessibility::GetTableCellColIndex() const {
+base::Optional<int> BrowserAccessibility::GetTableCellColIndex() const {
   return node()->GetTableCellColIndex();
 }
 
-int32_t BrowserAccessibility::GetTableCellRowIndex() const {
+base::Optional<int> BrowserAccessibility::GetTableCellRowIndex() const {
   return node()->GetTableCellRowIndex();
 }
 
-int32_t BrowserAccessibility::GetTableCellColSpan() const {
+base::Optional<int> BrowserAccessibility::GetTableCellColSpan() const {
   return node()->GetTableCellColSpan();
 }
 
-int32_t BrowserAccessibility::GetTableCellRowSpan() const {
+base::Optional<int> BrowserAccessibility::GetTableCellRowSpan() const {
   return node()->GetTableCellRowSpan();
 }
 
-int32_t BrowserAccessibility::GetTableCellAriaColIndex() const {
+base::Optional<int> BrowserAccessibility::GetTableCellAriaColIndex() const {
   return node()->GetTableCellAriaColIndex();
 }
 
-int32_t BrowserAccessibility::GetTableCellAriaRowIndex() const {
+base::Optional<int> BrowserAccessibility::GetTableCellAriaRowIndex() const {
   return node()->GetTableCellAriaRowIndex();
 }
 
-int32_t BrowserAccessibility::GetCellId(int32_t row_index,
-                                        int32_t col_index) const {
+base::Optional<int32_t> BrowserAccessibility::GetCellId(int row_index,
+                                                        int col_index) const {
   ui::AXNode* cell = node()->GetTableCellFromCoords(row_index, col_index);
-  if (cell)
-    return cell->id();
-
-  return -1;
+  if (!cell)
+    return base::nullopt;
+  return cell->id();
 }
 
-int32_t BrowserAccessibility::GetTableCellIndex() const {
+base::Optional<int> BrowserAccessibility::GetTableCellIndex() const {
   return node()->GetTableCellIndex();
 }
 
-int32_t BrowserAccessibility::CellIndexToId(int32_t cell_index) const {
+base::Optional<int32_t> BrowserAccessibility::CellIndexToId(
+    int cell_index) const {
   ui::AXNode* cell = node()->GetTableCellFromIndex(cell_index);
-  if (cell)
-    return cell->id();
-  return -1;
+  if (!cell)
+    return base::nullopt;
+  return cell->id();
 }
 
 bool BrowserAccessibility::IsCellOrHeaderOfARIATable() const {
@@ -1683,11 +1685,11 @@
   return node()->IsOrderedSet();
 }
 
-int32_t BrowserAccessibility::GetPosInSet() const {
+base::Optional<int> BrowserAccessibility::GetPosInSet() const {
   return node()->GetPosInSet();
 }
 
-int32_t BrowserAccessibility::GetSetSize() const {
+base::Optional<int> BrowserAccessibility::GetSetSize() const {
   return node()->GetSetSize();
 }
 
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 4654bf71..405b95d6 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -5,15 +5,17 @@
 #ifndef CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_H_
 #define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_H_
 
-#include <cstdint>
+#include <stdint.h>
 
 #include <map>
+#include <memory>
 #include <set>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_split.h"
 #include "build/build_config.h"
@@ -417,32 +419,31 @@
   const std::vector<gfx::NativeViewAccessible> GetDescendants() const override;
 
   bool IsTable() const override;
-  int32_t GetTableColCount() const override;
-  int32_t GetTableRowCount() const override;
-  base::Optional<int32_t> GetTableAriaColCount() const override;
-  base::Optional<int32_t> GetTableAriaRowCount() const override;
-  int32_t GetTableCellCount() const override;
-  const std::vector<int32_t> GetColHeaderNodeIds() const override;
-  const std::vector<int32_t> GetColHeaderNodeIds(
-      int32_t col_index) const override;
-  const std::vector<int32_t> GetRowHeaderNodeIds() const override;
-  const std::vector<int32_t> GetRowHeaderNodeIds(
-      int32_t row_index) const override;
-  ui::AXPlatformNode* GetTableCaption() override;
+  base::Optional<int> GetTableColCount() const override;
+  base::Optional<int> GetTableRowCount() const override;
+  base::Optional<int> GetTableAriaColCount() const override;
+  base::Optional<int> GetTableAriaRowCount() const override;
+  base::Optional<int> GetTableCellCount() const override;
+  std::vector<int32_t> GetColHeaderNodeIds() const override;
+  std::vector<int32_t> GetColHeaderNodeIds(int col_index) const override;
+  std::vector<int32_t> GetRowHeaderNodeIds() const override;
+  std::vector<int32_t> GetRowHeaderNodeIds(int row_index) const override;
+  ui::AXPlatformNode* GetTableCaption() const override;
 
   bool IsTableRow() const override;
-  int32_t GetTableRowRowIndex() const override;
+  base::Optional<int> GetTableRowRowIndex() const override;
 
   bool IsTableCellOrHeader() const override;
-  int32_t GetTableCellIndex() const override;
-  int32_t GetTableCellColIndex() const override;
-  int32_t GetTableCellRowIndex() const override;
-  int32_t GetTableCellColSpan() const override;
-  int32_t GetTableCellRowSpan() const override;
-  int32_t GetTableCellAriaColIndex() const override;
-  int32_t GetTableCellAriaRowIndex() const override;
-  int32_t GetCellId(int32_t row_index, int32_t col_index) const override;
-  int32_t CellIndexToId(int32_t cell_index) const override;
+  base::Optional<int> GetTableCellIndex() const override;
+  base::Optional<int> GetTableCellColIndex() const override;
+  base::Optional<int> GetTableCellRowIndex() const override;
+  base::Optional<int> GetTableCellColSpan() const override;
+  base::Optional<int> GetTableCellRowSpan() const override;
+  base::Optional<int> GetTableCellAriaColIndex() const override;
+  base::Optional<int> GetTableCellAriaRowIndex() const override;
+  base::Optional<int32_t> GetCellId(int row_index,
+                                    int col_index) const override;
+  base::Optional<int32_t> CellIndexToId(int cell_index) const override;
 
   bool IsCellOrHeaderOfARIATable() const override;
   bool IsCellOrHeaderOfARIAGrid() const override;
@@ -467,8 +468,8 @@
       ax::mojom::IntListAttribute attr) override;
   bool IsOrderedSetItem() const override;
   bool IsOrderedSet() const override;
-  int32_t GetPosInSet() const override;
-  int32_t GetSetSize() const override;
+  base::Optional<int> GetPosInSet() const override;
+  base::Optional<int> GetSetSize() const override;
 
  protected:
   using BrowserAccessibilityPositionInstance =
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 40447035..5b1b4c840 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -1095,15 +1095,9 @@
     if (max > min && value >= min && value <= max)
       index = static_cast<int>(((value - min)) * 100 / (max - min));
   } else {
-    switch (GetRole()) {
-      case ax::mojom::Role::kListItem:
-      case ax::mojom::Role::kListBoxOption:
-      case ax::mojom::Role::kTreeItem:
-        index = node()->GetPosInSet() - 1;
-        break;
-      default:
-        break;
-    }
+    base::Optional<int> pos_in_set = node()->GetPosInSet();
+    if (pos_in_set && *pos_in_set > 0)
+      index = *pos_in_set - 1;
   }
   return index;
 }
@@ -1116,15 +1110,8 @@
     // in RangeMin and RangeMax.
     count = 100;
   } else {
-    switch (GetRole()) {
-      case ax::mojom::Role::kList:
-      case ax::mojom::Role::kListBox:
-      case ax::mojom::Role::kDescriptionList:
-        count = node()->GetSetSize();
-        break;
-      default:
-        break;
-    }
+    if (IsCollection() && node()->GetSetSize())
+      count = *node()->GetSetSize();
   }
   return count;
 }
@@ -1435,46 +1422,38 @@
 }
 
 int BrowserAccessibilityAndroid::RowCount() const {
-  if (ui::IsTableLike(GetRole()))
-    return node()->GetTableRowCount();
+  if (!IsCollection())
+    return 0;
 
-  if (GetRole() == ax::mojom::Role::kList ||
-      GetRole() == ax::mojom::Role::kListBox ||
-      GetRole() == ax::mojom::Role::kDescriptionList ||
-      GetRole() == ax::mojom::Role::kTree) {
-    return node()->GetSetSize();
-  }
+  if (node()->GetSetSize())
+    return *node()->GetSetSize();
 
-  return 0;
+  return node()->GetTableRowCount().value_or(0);
 }
 
 int BrowserAccessibilityAndroid::ColumnCount() const {
-  if (ui::IsTableLike(GetRole()))
-    return node()->GetTableColCount();
-
+  if (IsCollection())
+    return node()->GetTableColCount().value_or(0);
   return 0;
 }
 
 int BrowserAccessibilityAndroid::RowIndex() const {
-  if (GetRole() == ax::mojom::Role::kListItem ||
-      GetRole() == ax::mojom::Role::kListBoxOption ||
-      GetRole() == ax::mojom::Role::kTreeItem) {
-    return node()->GetPosInSet() - 1;
-  }
-
-  return node()->GetTableCellRowIndex();
+  base::Optional<int> pos_in_set = node()->GetPosInSet();
+  if (pos_in_set && pos_in_set > 0)
+    return *pos_in_set - 1;
+  return node()->GetTableCellRowIndex().value_or(0);
 }
 
 int BrowserAccessibilityAndroid::RowSpan() const {
-  return node()->GetTableCellRowSpan();
+  return node()->GetTableCellRowSpan().value_or(0);
 }
 
 int BrowserAccessibilityAndroid::ColumnIndex() const {
-  return node()->GetTableCellColIndex();
+  return node()->GetTableCellColIndex().value_or(0);
 }
 
 int BrowserAccessibilityAndroid::ColumnSpan() const {
-  return node()->GetTableCellColSpan();
+  return node()->GetTableCellColSpan().value_or(0);
 }
 
 float BrowserAccessibilityAndroid::RangeMin() const {
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index e15681f..d3f1fa83 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -706,21 +706,22 @@
 }
 
 - (NSNumber*)ariaColumnCount {
-  if (!ui::IsTableLike(owner_->GetRole()))
+  if (![self instanceActive])
     return nil;
-  DCHECK(owner_->node());
-  base::Optional<int32_t> aria_col_count =
-      owner_->node()->GetTableAriaColCount();
+  base::Optional<int> aria_col_count = owner_->node()->GetTableAriaColCount();
   if (!aria_col_count)
     return nil;
-  return [NSNumber numberWithInt:aria_col_count.value()];
+  return [NSNumber numberWithInt:*aria_col_count];
 }
 
 - (NSNumber*)ariaColumnIndex {
-  if (!ui::IsCellOrTableHeader(owner_->GetRole()))
+  if (![self instanceActive])
     return nil;
-  DCHECK(owner_->node());
-  return [NSNumber numberWithInt:owner_->node()->GetTableCellAriaColIndex()];
+  base::Optional<int> aria_col_index =
+      owner_->node()->GetTableCellAriaColIndex();
+  if (!aria_col_index)
+    return nil;
+  return [NSNumber numberWithInt:*aria_col_index];
 }
 
 - (NSString*)ariaLive {
@@ -733,7 +734,10 @@
 - (NSNumber*)ariaPosInSet {
   if (![self instanceActive])
     return nil;
-  return [NSNumber numberWithInt:owner_->node()->GetPosInSet()];
+  base::Optional<int> pos_in_set = owner_->node()->GetPosInSet();
+  if (!pos_in_set)
+    return nil;
+  return [NSNumber numberWithInt:*pos_in_set];
 }
 
 - (NSString*)ariaRelevant {
@@ -744,27 +748,31 @@
 }
 
 - (NSNumber*)ariaRowCount {
-  if (!ui::IsTableLike(owner_->GetRole()))
+  if (![self instanceActive])
     return nil;
-  DCHECK(owner_->node());
-  base::Optional<int32_t> aria_row_count =
-      owner_->node()->GetTableAriaRowCount();
+  base::Optional<int> aria_row_count = owner_->node()->GetTableAriaRowCount();
   if (!aria_row_count)
     return nil;
-  return [NSNumber numberWithInt:aria_row_count.value()];
+  return [NSNumber numberWithInt:*aria_row_count];
 }
 
 - (NSNumber*)ariaRowIndex {
-  if (!ui::IsCellOrTableHeader(owner_->GetRole()))
+  if (![self instanceActive])
     return nil;
-  DCHECK(owner_->node());
-  return [NSNumber numberWithInt:owner_->node()->GetTableCellAriaRowIndex()];
+  base::Optional<int> aria_row_index =
+      owner_->node()->GetTableCellAriaRowIndex();
+  if (!aria_row_index)
+    return nil;
+  return [NSNumber numberWithInt:*aria_row_index];
 }
 
 - (NSNumber*)ariaSetSize {
   if (![self instanceActive])
     return nil;
-  return [NSNumber numberWithInt:owner_->node()->GetSetSize()];
+  base::Optional<int> set_size = owner_->node()->GetSetSize();
+  if (!set_size)
+    return nil;
+  return [NSNumber numberWithInt:*set_size];
 }
 
 - (NSString*)autocompleteValue {
@@ -853,11 +861,10 @@
     return nil;
 
   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
-
   if (is_table_like) {
     // If this is a table, return all column headers.
     std::set<int32_t> headerIds;
-    for (int i = 0; i < table->GetTableColCount(); i++) {
+    for (int i = 0; i < *owner_->GetTableColCount(); i++) {
       std::vector<int32_t> colHeaderIds = table->GetColHeaderNodeIds(i);
       std::copy(colHeaderIds.begin(), colHeaderIds.end(),
                 std::inserter(headerIds, headerIds.end()));
@@ -869,7 +876,7 @@
     }
   } else {
     // Otherwise this is a cell, return the column headers for this cell.
-    int column = owner_->node()->GetTableCellColIndex();
+    int column = *owner_->GetTableCellColIndex();
 
     std::vector<int32_t> colHeaderIds = table->GetColHeaderNodeIds(column);
     for (int32_t id : colHeaderIds) {
@@ -888,8 +895,8 @@
   if (!ui::IsCellOrTableHeader(owner_->GetRole()))
     return nil;
 
-  int column = owner_->node()->GetTableCellColIndex();
-  int colspan = owner_->node()->GetTableCellColSpan();
+  int column = *owner_->node()->GetTableCellColIndex();
+  int colspan = *owner_->node()->GetTableCellColSpan();
   if (column >= 0 && colspan >= 1)
     return [NSValue valueWithRange:NSMakeRange(column, colspan)];
   return nil;
@@ -1269,10 +1276,10 @@
     return nil;
   if ([self internalRole] == ax::mojom::Role::kColumn) {
     DCHECK(owner_->node());
-    return @(owner_->node()->GetTableColColIndex());
+    return @(*owner_->node()->GetTableColColIndex());
   } else if ([self internalRole] == ax::mojom::Role::kRow) {
     DCHECK(owner_->node());
-    return @(owner_->node()->GetTableRowRowIndex());
+    return @(*owner_->node()->GetTableRowRowIndex());
   }
 
   return nil;
@@ -1847,7 +1854,7 @@
   if (is_table_like) {
     // If this is a table, return all row headers.
     std::set<int32_t> headerIds;
-    for (int i = 0; i < table->GetTableRowCount(); i++) {
+    for (int i = 0; i < *table->GetTableRowCount(); i++) {
       std::vector<int32_t> rowHeaderIds = table->GetRowHeaderNodeIds(i);
       for (int32_t id : rowHeaderIds)
         headerIds.insert(id);
@@ -1877,8 +1884,8 @@
   if (!ui::IsCellOrTableHeader(owner_->GetRole()))
     return nil;
 
-  int row = owner_->node()->GetTableCellRowIndex();
-  int rowspan = owner_->node()->GetTableCellRowSpan();
+  int row = *owner_->node()->GetTableCellRowIndex();
+  int rowspan = *owner_->node()->GetTableCellRowSpan();
   if (row >= 0 && rowspan >= 1)
     return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
   return nil;
diff --git a/content/browser/accessibility/browser_accessibility_mac.mm b/content/browser/accessibility/browser_accessibility_mac.mm
index 0315432..d1cba4c 100644
--- a/content/browser/accessibility/browser_accessibility_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_mac.mm
@@ -53,7 +53,7 @@
   // If this is a table, include the extra fake nodes generated by
   // AXTableInfo, for the column nodes and the table header container, all of
   // which are only important on macOS.
-  std::vector<ui::AXNode*>* extra_mac_nodes = node()->GetExtraMacNodes();
+  const std::vector<ui::AXNode*>* extra_mac_nodes = node()->GetExtraMacNodes();
   if (!extra_mac_nodes)
     return child_count;
 
@@ -69,7 +69,7 @@
   // If this is a table, include the extra fake nodes generated by
   // AXTableInfo, for the column nodes and the table header container, all of
   // which are only important on macOS.
-  std::vector<ui::AXNode*>* extra_mac_nodes = node()->GetExtraMacNodes();
+  const std::vector<ui::AXNode*>* extra_mac_nodes = node()->GetExtraMacNodes();
   if (!extra_mac_nodes)
     return nullptr;
 
diff --git a/content/browser/android/content_feature_list.cc b/content/browser/android/content_feature_list.cc
index d7e21b9..35e7941 100644
--- a/content/browser/android/content_feature_list.cc
+++ b/content/browser/android/content_feature_list.cc
@@ -23,7 +23,6 @@
 // in other locations in the code base (e.g. content_features.h).
 const base::Feature* kFeaturesExposedToJava[] = {
     &features::kBackgroundMediaRendererHasModerateBinding,
-    &kEnhancedSelectionInsertionHandle,
     &kServiceGroupImportance,
 };
 
@@ -40,8 +39,6 @@
 }  // namespace
 
 // Alphabetical:
-const base::Feature kEnhancedSelectionInsertionHandle{
-    "EnhancedSelectionInsertionHandle", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kRequestUnbufferedDispatch{
     "RequestUnbufferedDispatch", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kServiceGroupImportance{"ServiceGroupImportance",
diff --git a/content/browser/android/content_feature_list.h b/content/browser/android/content_feature_list.h
index 68333f8..41333477 100644
--- a/content/browser/android/content_feature_list.h
+++ b/content/browser/android/content_feature_list.h
@@ -11,7 +11,6 @@
 namespace android {
 
 // Alphabetical:
-extern const base::Feature kEnhancedSelectionInsertionHandle;
 extern const base::Feature kRequestUnbufferedDispatch;
 extern const base::Feature kServiceGroupImportance;
 
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index 296fe15..b52f5fb 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -9839,4 +9839,65 @@
               ElementsAre(1, 1, 2, 1));
 }
 
+// When running OpenURL to an invalid URL on a frame proxy it should not spoof
+// the url by canceling a main frame navigation.
+// See https://crbug.com/966914.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
+                       CrossProcessIframeToInvalidURLCancelsRedirectSpoof) {
+  const GURL main_frame_url(embedded_test_server()->GetURL(
+      "a.com",
+      "/cross_site_iframe_factory.html?a(b{sandbox-allow-scripts,sandbox-allow-"
+      "top-navigation}())"));
+  const GURL main_frame_url_2(embedded_test_server()->GetURL("/title2.html"));
+
+  // Load the initial page, containing a fully scriptable cross-site iframe.
+  EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
+
+  // Setup the message trigger on the main frame and the handler on the iframe.
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  std::string script =
+      "var messageUnloadEventSent = false;"
+      "window.addEventListener(\"beforeunload\", function "
+      "onBeforeUnload(event) {"
+      "if(messageUnloadEventSent) {"
+      "return;"
+      "}"
+      "messageUnloadEventSent = true;"
+      "var iframe = document.getElementById(\"child-0\");"
+      "iframe.contentWindow.postMessage(\"\", \"*\");"
+      "});";
+  EXPECT_TRUE(ExecJs(root, script));
+
+  script =
+      "window.addEventListener(\"message\", function (event) {"
+      "parent.location.href = \"chrome-guest://1234\";"
+      "});";
+  EXPECT_TRUE(ExecJs(root->child_at(0), script));
+
+  // This navigation will be raced by a navigation started in the iframe.
+  // It should still be prevalent compared to a non user-initiated render frame
+  // navigation.
+  // TODO(ahemery): Remove first block when https://crbug.com/973415 is fixed.
+  // The navigation started in the iframe should be canceled. When we are not
+  // cross process, this does not happen because has_user_gesture is true. The
+  // navigation is automatic and it should not be the case. This causes no spoof
+  // however, as verified below.
+  GURL check_url;
+  if (!SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) {
+    EXPECT_FALSE(NavigateToURL(shell(), main_frame_url_2));
+    check_url = main_frame_url;
+  } else {
+    EXPECT_TRUE(NavigateToURL(shell(), main_frame_url_2));
+    check_url = main_frame_url_2;
+  }
+
+  // Check that no spoof happened.
+  NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
+      shell()->web_contents()->GetController());
+  EXPECT_EQ(check_url, shell()->web_contents()->GetLastCommittedURL());
+  EXPECT_EQ(check_url, controller.GetVisibleEntry()->GetVirtualURL());
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/navigator.h b/content/browser/frame_host/navigator.h
index abc1a57..5d13226 100644
--- a/content/browser/frame_host/navigator.h
+++ b/content/browser/frame_host/navigator.h
@@ -135,7 +135,8 @@
       const std::string& method,
       scoped_refptr<network::ResourceRequestBody> post_body,
       const std::string& extra_headers,
-      scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {}
+      scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+      bool has_user_gesture) {}
 
   // Called after receiving a BeforeUnloadACK IPC from the renderer. If
   // |frame_tree_node| has a NavigationRequest waiting for the renderer
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index a22bbd2..ed459a4 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -51,6 +51,31 @@
 
 namespace content {
 
+namespace {
+
+// A renderer-initiated navigation should be ignored iff a) there is an ongoing
+// request b) which is browser initiated and c) the renderer request is not
+// user-initiated.
+bool ShouldIgnoreIncomingRendererRequest(
+    NavigationRequest* ongoing_navigation_request,
+    bool has_user_gesture) {
+  return ongoing_navigation_request &&
+         ongoing_navigation_request->browser_initiated() && !has_user_gesture;
+}
+
+// Informs the RenderFrameImpl associated with the |frame_tree_node| that a
+// navigation it started was dropped.
+void DropNavigation(FrameTreeNode* frame_tree_node) {
+  if (!IsPerNavigationMojoInterfaceEnabled()) {
+    RenderFrameHost* current_frame_host =
+        frame_tree_node->render_manager()->current_frame_host();
+    current_frame_host->Send(
+        new FrameMsg_DroppedNavigation(current_frame_host->GetRoutingID()));
+  }
+}
+
+}  // namespace
+
 struct NavigatorImpl::NavigationMetricsData {
   NavigationMetricsData(base::TimeTicks start_time,
                         GURL url,
@@ -481,7 +506,8 @@
     const std::string& method,
     scoped_refptr<network::ResourceRequestBody> post_body,
     const std::string& extra_headers,
-    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
+    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+    bool has_user_gesture) {
   // |method != "POST"| should imply absence of |post_body|.
   if (method != "POST" && post_body) {
     NOTREACHED();
@@ -522,6 +548,13 @@
     is_renderer_initiated = false;
   }
 
+  if (is_renderer_initiated &&
+      ShouldIgnoreIncomingRendererRequest(
+          render_frame_host->frame_tree_node()->navigation_request(),
+          has_user_gesture)) {
+    return;
+  }
+
   base::Optional<url::Origin> final_initiator_origin = initiator_origin;
   GetContentClient()->browser()->OverrideNavigationParams(
       current_site_instance, &page_transition, &is_renderer_initiated,
@@ -620,18 +653,10 @@
     frame_tree_node->ResetNavigationRequest(false, true);
   }
 
-  // The renderer-initiated navigation request is ignored iff a) there is an
-  // ongoing request b) which is browser initiated and c) the renderer request
-  // is not user-initiated.
-  if (ongoing_navigation_request &&
-      ongoing_navigation_request->browser_initiated() &&
-      !common_params.has_user_gesture) {
-    if (!IsPerNavigationMojoInterfaceEnabled()) {
-      RenderFrameHost* current_frame_host =
-          frame_tree_node->render_manager()->current_frame_host();
-      current_frame_host->Send(
-          new FrameMsg_DroppedNavigation(current_frame_host->GetRoutingID()));
-    }
+  // Verify this navigation has precedence.
+  if (ShouldIgnoreIncomingRendererRequest(ongoing_navigation_request,
+                                          common_params.has_user_gesture)) {
+    DropNavigation(frame_tree_node);
     return;
   }
 
diff --git a/content/browser/frame_host/navigator_impl.h b/content/browser/frame_host/navigator_impl.h
index 7c0db61..714ee4bc 100644
--- a/content/browser/frame_host/navigator_impl.h
+++ b/content/browser/frame_host/navigator_impl.h
@@ -87,8 +87,8 @@
       const std::string& method,
       scoped_refptr<network::ResourceRequestBody> post_body,
       const std::string& extra_headers,
-      scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory)
-      override;
+      scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+      bool has_user_gesture) override;
   void OnBeforeUnloadACK(FrameTreeNode* frame_tree_node,
                          bool proceed,
                          const base::TimeTicks& proceed_time) override;
diff --git a/content/browser/frame_host/render_frame_proxy_host.cc b/content/browser/frame_host/render_frame_proxy_host.cc
index a28f8049..ab3be343 100644
--- a/content/browser/frame_host/render_frame_proxy_host.cc
+++ b/content/browser/frame_host/render_frame_proxy_host.cc
@@ -322,8 +322,6 @@
       frame_tree_node_->navigator()->GetController()->GetWebContents(),
       current_rfh, params.user_gesture, &download_policy);
 
-  // TODO(alexmos, creis): Figure out whether |params.user_gesture| needs to be
-  // passed in as well.
   // TODO(lfg, lukasza): Remove |extra_headers| parameter from
   // RequestTransferURL method once both RenderFrameProxyHost and
   // RenderFrameHostImpl call RequestOpenURL from their OnOpenURL handlers.
@@ -335,7 +333,8 @@
       params.referrer, ui::PAGE_TRANSITION_LINK,
       params.should_replace_current_entry, download_policy,
       params.uses_post ? "POST" : "GET", params.resource_request_body,
-      params.extra_headers, std::move(blob_url_loader_factory));
+      params.extra_headers, std::move(blob_url_loader_factory),
+      params.user_gesture);
 }
 
 void RenderFrameProxyHost::OnCheckCompleted() {
diff --git a/content/browser/mojo_sandbox_browsertest.cc b/content/browser/mojo_sandbox_browsertest.cc
index 05cbca2b..d36c83d 100644
--- a/content/browser/mojo_sandbox_browsertest.cc
+++ b/content/browser/mojo_sandbox_browsertest.cc
@@ -11,7 +11,6 @@
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
 #include "content/browser/utility_process_host.h"
-#include "content/browser/utility_process_host_client.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/bind_interface_helpers.h"
@@ -53,7 +52,7 @@
 
  private:
   void StartUtilityProcessOnIoThread() {
-    host_.reset(new UtilityProcessHost(nullptr, nullptr));
+    host_.reset(new UtilityProcessHost());
     host_->SetMetricsName("mojo_sandbox_test_process");
     ASSERT_TRUE(host_->Start());
   }
diff --git a/content/browser/portal/portal.cc b/content/browser/portal/portal.cc
index 98456b74..5eee6ffa 100644
--- a/content/browser/portal/portal.cc
+++ b/content/browser/portal/portal.cc
@@ -193,8 +193,11 @@
 
   WebContentsDelegate* delegate = outer_contents->GetDelegate();
   bool is_loading = portal_contents_impl_->IsLoading();
+  FrameTreeNode* outer_frame_tree_node = FrameTreeNode::GloballyFindByID(
+      portal_contents_impl_->GetOuterDelegateFrameTreeNodeId());
   std::unique_ptr<WebContents> portal_contents =
       portal_contents_impl_->DetachFromOuterWebContents();
+  owner_render_frame_host_->RemoveChild(outer_frame_tree_node);
 
   auto* outer_contents_main_frame_view = static_cast<RenderWidgetHostViewBase*>(
       outer_contents->GetMainFrame()->GetView());
diff --git a/content/browser/portal/portal_browsertest.cc b/content/browser/portal/portal_browsertest.cc
index 88743034..3d903f32 100644
--- a/content/browser/portal/portal_browsertest.cc
+++ b/content/browser/portal/portal_browsertest.cc
@@ -584,6 +584,45 @@
   input_event_ack_waiter.Wait();
 }
 
+// Tests that the outer FrameTreeNode is deleted after activation.
+IN_PROC_BROWSER_TEST_F(PortalBrowserTest, FrameDeletedAfterActivation) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
+  WebContentsImpl* web_contents_impl =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
+
+  Portal* portal = nullptr;
+  {
+    PortalCreatedObserver portal_created_observer(main_frame);
+    GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+    EXPECT_TRUE(ExecJs(
+        main_frame, JsReplace("var portal = document.createElement('portal');"
+                              "portal.src = $1;"
+                              "document.body.appendChild(portal);",
+                              a_url)));
+    portal = portal_created_observer.WaitUntilPortalCreated();
+  }
+  WebContentsImpl* portal_contents = portal->GetPortalContents();
+
+  // The portal should not have navigated yet; wait for the first navigation.
+  TestNavigationObserver navigation_observer(portal_contents);
+  navigation_observer.Wait();
+
+  FrameTreeNode* outer_frame_tree_node = FrameTreeNode::GloballyFindByID(
+      portal_contents->GetOuterDelegateFrameTreeNodeId());
+  EXPECT_TRUE(outer_frame_tree_node);
+
+  EXPECT_TRUE(ExecJs(portal_contents->GetMainFrame(),
+                     "window.onportalactivate = e => "
+                     "document.body.appendChild(e.adoptPredecessor());"));
+
+  FrameDeletedObserver observer(outer_frame_tree_node->current_frame_host());
+  ExecuteScriptAsync(main_frame,
+                     "document.querySelector('portal').activate();");
+  observer.Wait();
+}
+
 class PortalOOPIFBrowserTest : public PortalBrowserTest {
  protected:
   PortalOOPIFBrowserTest() {}
diff --git a/content/browser/power_monitor_browsertest.cc b/content/browser/power_monitor_browsertest.cc
index e89082e..be44abb1 100644
--- a/content/browser/power_monitor_browsertest.cc
+++ b/content/browser/power_monitor_browsertest.cc
@@ -9,7 +9,6 @@
 #include "base/task/post_task.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/utility_process_host.h"
-#include "content/browser/utility_process_host_client.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/gpu_service_registry.h"
@@ -46,9 +45,7 @@
 }
 
 void StartUtilityProcessOnIOThread(mojom::PowerMonitorTestRequest request) {
-  UtilityProcessHost* host =
-      new UtilityProcessHost(/*client=*/nullptr,
-                             /*client_task_runner=*/nullptr);
+  UtilityProcessHost* host = new UtilityProcessHost();
   host->SetMetricsName("test_process");
   host->SetName(base::ASCIIToUTF16("TestProcess"));
   EXPECT_TRUE(host->Start());
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 5a805d5..b41678e 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -93,6 +93,11 @@
 
 static const char* kBrowser = "Browser";
 
+// NOINLINE to make sure crashes use this for magic signature.
+NOINLINE void FatalSurfaceFailure() {
+  LOG(FATAL) << "Fatal surface initialization failure";
+}
+
 gfx::OverlayTransform RotationToDisplayTransform(
     display::Display::Rotation rotation) {
   // Note that the angle provided by |rotation| here is the opposite direction
@@ -352,9 +357,8 @@
   void DidCompleteSwapWithSize(const gfx::Size& pixel_size) override {
     compositor_->DidSwapBuffers(pixel_size);
   }
-  void OnFatalOrSurfaceContextCreationFailure(
-      gpu::ContextResult context_result) override {
-    compositor_->OnFatalOrSurfaceContextCreationFailure(context_result);
+  void OnContextCreationResult(gpu::ContextResult context_result) override {
+    compositor_->OnContextCreationResult(context_result);
   }
   void SetPreferredRefreshRate(float refresh_rate) override {
     if (compositor_->root_window_)
@@ -791,9 +795,17 @@
                                          requires_alpha_channel_),
           viz::command_buffer_metrics::ContextType::BROWSER_COMPOSITOR);
   auto result = context_provider->BindToCurrentThread();
+
+  if (surface_handle != gpu::kNullSurfaceHandle) {
+    // Only use OnContextCreationResult for onscreen contexts, where recovering
+    // from a surface initialization failure is possible by re-creating the
+    // native window.
+    OnContextCreationResult(result);
+  } else if (result == gpu::ContextResult::kFatalFailure) {
+    LOG(FATAL) << "Fatal failure in creating offscreen context";
+  }
+
   if (result != gpu::ContextResult::kSuccess) {
-    if (gpu::IsFatalOrSurfaceFailure(result))
-      OnFatalOrSurfaceContextCreationFailure(result);
     HandlePendingLayerTreeFrameSinkRequest();
     return;
   }
@@ -1100,12 +1112,26 @@
   return viz::LocalSurfaceIdAllocation();
 }
 
+void CompositorImpl::OnContextCreationResult(
+    gpu::ContextResult context_result) {
+  if (!gpu::IsFatalOrSurfaceFailure(context_result)) {
+    num_of_consecutive_surface_failures_ = 0u;
+    return;
+  }
+
+  OnFatalOrSurfaceContextCreationFailure(context_result);
+}
+
 void CompositorImpl::OnFatalOrSurfaceContextCreationFailure(
     gpu::ContextResult context_result) {
   DCHECK(gpu::IsFatalOrSurfaceFailure(context_result));
   LOG_IF(FATAL, context_result == gpu::ContextResult::kFatalFailure)
       << "Fatal error making Gpu context";
 
+  constexpr size_t kMaxConsecutiveSurfaceFailures = 10u;
+  if (++num_of_consecutive_surface_failures_ > kMaxConsecutiveSurfaceFailures)
+    FatalSurfaceFailure();
+
   if (context_result == gpu::ContextResult::kSurfaceFailure) {
     SetSurface(nullptr, false);
     client_->RecreateSurface();
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index ebda9300..bd9e16e 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -202,7 +202,8 @@
   // Registers the root frame sink ID.
   void RegisterRootFrameSink();
 
-  // Called when we fail to create the context for the root frame sink.
+  // Called with the result of context creation for the root frame sink.
+  void OnContextCreationResult(gpu::ContextResult context_result);
   void OnFatalOrSurfaceContextCreationFailure(
       gpu::ContextResult context_result);
 
@@ -275,6 +276,8 @@
   base::RepeatingCallback<void(const gfx::Size&)>
       swap_completed_with_size_for_testing_;
 
+  size_t num_of_consecutive_surface_failures_ = 0u;
+
   base::WeakPtrFactory<CompositorImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CompositorImpl);
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 2cf928d..d1af81a4 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -121,10 +121,8 @@
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableLongpressDragSelection);
   config.hide_active_handle =
-      base::FeatureList::IsEnabled(
-          content::android::kEnhancedSelectionInsertionHandle) &&
       base::android::BuildInfo::GetInstance()->sdk_int() >=
-          base::android::SDK_VERSION_P;
+      base::android::SDK_VERSION_P;
   return std::make_unique<ui::TouchSelectionController>(client, config);
 }
 
diff --git a/content/browser/service_manager/service_manager_context.cc b/content/browser/service_manager/service_manager_context.cc
index 29fe31a..843b9b3 100644
--- a/content/browser/service_manager/service_manager_context.cc
+++ b/content/browser/service_manager/service_manager_context.cc
@@ -32,7 +32,6 @@
 #include "content/browser/gpu/gpu_process_host.h"
 #include "content/browser/service_manager/common_browser_interfaces.h"
 #include "content/browser/utility_process_host.h"
-#include "content/browser/utility_process_host_client.h"
 #include "content/browser/wake_lock/wake_lock_context_host.h"
 #include "content/common/service_manager/service_manager_connection_impl.h"
 #include "content/public/app/content_browser_manifest.h"
@@ -195,7 +194,7 @@
     }
 
     // Start a new process for this service.
-    UtilityProcessHost* process_host = new UtilityProcessHost(nullptr, nullptr);
+    UtilityProcessHost* process_host = new UtilityProcessHost();
     process_host->SetName(display_name);
     process_host->SetMetricsName(identity.name());
     process_host->SetServiceIdentity(identity);
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index bec943a..cbc9d54d 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -20,7 +20,6 @@
 #include "content/browser/browser_child_process_host_impl.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/service_manager/service_manager_context.h"
-#include "content/browser/utility_process_host_client.h"
 #include "content/common/child_process_host_impl.h"
 #include "content/common/in_process_child_thread_params.h"
 #include "content/common/service_manager/child_connection.h"
@@ -207,12 +206,8 @@
   g_utility_main_thread_factory = create;
 }
 
-UtilityProcessHost::UtilityProcessHost(
-    const scoped_refptr<UtilityProcessHostClient>& client,
-    const scoped_refptr<base::SequencedTaskRunner>& client_task_runner)
-    : client_(client),
-      client_task_runner_(client_task_runner),
-      sandbox_type_(service_manager::SANDBOX_TYPE_UTILITY),
+UtilityProcessHost::UtilityProcessHost()
+    : sandbox_type_(service_manager::SANDBOX_TYPE_UTILITY),
 #if defined(OS_LINUX)
       child_flags_(ChildProcessHost::CHILD_ALLOW_SELF),
 #else
@@ -455,15 +450,6 @@
 }
 
 bool UtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
-  if (!client_.get())
-    return true;
-
-  client_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          base::IgnoreResult(&UtilityProcessHostClient::OnMessageReceived),
-          client_.get(), message));
-
   return true;
 }
 
@@ -479,23 +465,9 @@
   for (auto& callback : pending_run_service_callbacks_)
     std::move(callback).Run(base::nullopt);
   pending_run_service_callbacks_.clear();
-
-  if (!client_.get())
-    return;
-
-  client_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&UtilityProcessHostClient::OnProcessLaunchFailed, client_,
-                     error_code));
 }
 
 void UtilityProcessHost::OnProcessCrashed(int exit_code) {
-  if (!client_.get())
-    return;
-
-  client_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&UtilityProcessHostClient::OnProcessCrashed,
-                                client_, exit_code));
 }
 
 }  // namespace content
diff --git a/content/browser/utility_process_host.h b/content/browser/utility_process_host.h
index 22c0bc9..43ef74ce 100644
--- a/content/browser/utility_process_host.h
+++ b/content/browser/utility_process_host.h
@@ -27,14 +27,12 @@
 #include "services/service_manager/sandbox/sandbox_type.h"
 
 namespace base {
-class SequencedTaskRunner;
 class Thread;
 }  // namespace base
 
 namespace content {
 class BrowserChildProcessHostImpl;
 class InProcessChildThreadParams;
-class UtilityProcessHostClient;
 struct ChildProcessData;
 
 typedef base::Thread* (*UtilityMainThreadFactoryFunction)(
@@ -60,13 +58,7 @@
   static void RegisterUtilityMainThreadFactory(
       UtilityMainThreadFactoryFunction create);
 
-  // |client| is optional. If supplied it will be notified of incoming messages
-  // from the utility process.
-  // |client_task_runner| is required if |client| is supplied and is the task
-  // runner upon which |client| will be invoked.
-  UtilityProcessHost(
-      const scoped_refptr<UtilityProcessHostClient>& client,
-      const scoped_refptr<base::SequencedTaskRunner>& client_task_runner);
+  UtilityProcessHost();
   ~UtilityProcessHost() override;
 
   base::WeakPtr<UtilityProcessHost> AsWeakPtr();
@@ -124,12 +116,6 @@
   void OnProcessLaunchFailed(int error_code) override;
   void OnProcessCrashed(int exit_code) override;
 
-  // Pointer to our client interface used for progress notifications.
-  scoped_refptr<UtilityProcessHostClient> client_;
-
-  // Task runner used for posting progess notifications to |client_|.
-  scoped_refptr<base::SequencedTaskRunner> client_task_runner_;
-
   // Launch the child process with switches that will setup this sandbox type.
   service_manager::SandboxType sandbox_type_;
 
diff --git a/content/browser/utility_process_host_browsertest.cc b/content/browser/utility_process_host_browsertest.cc
index 2dbf010..0c9fd30 100644
--- a/content/browser/utility_process_host_browsertest.cc
+++ b/content/browser/utility_process_host_browsertest.cc
@@ -8,7 +8,6 @@
 #include "base/task/post_task.h"
 #include "build/build_config.h"
 #include "content/browser/utility_process_host.h"
-#include "content/browser/utility_process_host_client.h"
 #include "content/public/browser/browser_child_process_observer.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -64,9 +63,7 @@
 
   void RunUtilityProcessOnIOThread(bool elevated, bool crash) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    UtilityProcessHost* host =
-        new UtilityProcessHost(/*client=*/nullptr,
-                               /*client_task_runner=*/nullptr);
+    UtilityProcessHost* host = new UtilityProcessHost();
     host->SetName(base::ASCIIToUTF16("TestProcess"));
     host->SetMetricsName(kTestProcessName);
 #if defined(OS_WIN)
diff --git a/content/browser/utility_process_host_client.h b/content/browser/utility_process_host_client.h
deleted file mode 100644
index 05622d3c..0000000
--- a/content/browser/utility_process_host_client.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_UTILITY_PROCESS_HOST_CLIENT_H_
-#define CONTENT_BROWSER_UTILITY_PROCESS_HOST_CLIENT_H_
-
-#include "base/memory/ref_counted.h"
-#include "content/common/content_export.h"
-
-namespace IPC {
-class Message;
-}
-
-namespace content {
-
-// An interface to be implemented by consumers of the utility process to
-// get results back.  All functions are called on the thread passed along
-// to UtilityProcessHost.
-class UtilityProcessHostClient
-    : public base::RefCountedThreadSafe<UtilityProcessHostClient> {
- public:
-  // Called when the process has crashed.
-  virtual void OnProcessCrashed(int exit_code) {}
-
-  // Called when the process fails to launch. |error_code| is one of
-  // LaunchResultCode or sandbox::ResultCode containing the error.
-  virtual void OnProcessLaunchFailed(int error_code) {}
-
-  // Allow the client to filter IPC messages.
-  virtual bool OnMessageReceived(const IPC::Message& message) = 0;
-
- protected:
-  friend class base::RefCountedThreadSafe<UtilityProcessHostClient>;
-
-  virtual ~UtilityProcessHostClient() {}
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_UTILITY_PROCESS_HOST_CLIENT_H_
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentClassFactory.java b/content/public/android/java/src/org/chromium/content/browser/ContentClassFactory.java
index fb3f636..84b662e 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentClassFactory.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentClassFactory.java
@@ -49,10 +49,7 @@
      */
     public SelectionInsertionHandleObserver createHandleObserver(
             SelectionPopupControllerImpl.ReadbackViewCallback callback) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P
-                || !ContentFeatureList.isEnabled(
-                           ContentFeatureList.ENHANCED_SELECTION_INSERTION_HANDLE))
-            return null;
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return null;
         return new MagnifierAnimator(new MagnifierWrapperImpl(callback));
     }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentFeatureList.java b/content/public/android/java/src/org/chromium/content/browser/ContentFeatureList.java
index a217470..3e1003a0 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentFeatureList.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentFeatureList.java
@@ -30,9 +30,6 @@
     }
 
     // Alphabetical:
-    public static final String ENHANCED_SELECTION_INSERTION_HANDLE =
-            "EnhancedSelectionInsertionHandle";
-
     public static final String BACKGROUND_MEDIA_RENDERER_HAS_MODERATE_BINDING =
             "BackgroundMediaRendererHasModerateBinding";
 
diff --git a/content/test/data/accessibility/aria/aria-controls-expected-android.txt b/content/test/data/accessibility/aria/aria-controls-expected-android.txt
index 91f79277..b2b7e1f9 100644
--- a/content/test/data/accessibility/aria/aria-controls-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-controls-expected-android.txt
@@ -1,7 +1,7 @@
 android.webkit.WebView focusable focused scrollable
 ++android.view.View role_description='menu bar'
 ++++android.view.MenuItem role_description='menu item' clickable name='File'
-++++android.view.MenuItem role_description='menu item' clickable name='Edit'
+++++android.view.MenuItem role_description='menu item' clickable name='Edit' item_index=1 row_index=1
 ++android.view.View role_description='menu' name='File'
 ++++android.view.MenuItem role_description='menu item' clickable name='New'
-++++android.view.MenuItem role_description='menu item' clickable name='Open'
\ No newline at end of file
+++++android.view.MenuItem role_description='menu item' clickable name='Open' item_index=1 row_index=1
diff --git a/content/test/data/accessibility/aria/aria-expanded-expected-android.txt b/content/test/data/accessibility/aria/aria-expanded-expected-android.txt
index 3cd88e6..3031564 100644
--- a/content/test/data/accessibility/aria/aria-expanded-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-expanded-expected-android.txt
@@ -1,9 +1,9 @@
 android.webkit.WebView focusable focused scrollable
 ++android.view.View role_description='menu'
 ++++android.view.MenuItem role_description='menu item' clickable name='New'
-++++android.view.MenuItem role_description='menu item' clickable name='Open'
-++++android.view.MenuItem role_description='menu item' clickable name='Save'
-++++android.view.MenuItem role_description='menu item' clickable name='Quit'
+++++android.view.MenuItem role_description='menu item' clickable name='Open' item_index=1 row_index=1
+++++android.view.MenuItem role_description='menu item' clickable name='Save' item_index=2 row_index=2
+++++android.view.MenuItem role_description='menu item' clickable name='Quit' item_index=3 row_index=3
 ++android.view.View role_description='splitter'
 ++android.view.View role_description='splitter'
 ++android.view.View role_description='splitter'
diff --git a/content/test/data/accessibility/aria/aria-illegal-val-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-illegal-val-expected-auralinux.txt
index 6c5f965..430178d 100644
--- a/content/test/data/accessibility/aria/aria-illegal-val-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-illegal-val-expected-auralinux.txt
@@ -21,4 +21,4 @@
 ++[entry] name='Required illegal' required selectable-text
 ++[tree] multiselectable
 ++++[tree item] name='Selected illegal' selectable selected
-++[column header] name='Sort illegal' selectable sort:other (row=0, col=0, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
+++[column header] name='Sort illegal' selectable sort:other (row=-1, col=-1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
diff --git a/content/test/data/accessibility/aria/aria-menu-expected-android.txt b/content/test/data/accessibility/aria/aria-menu-expected-android.txt
index 4b50a3d..cc11bdc0 100644
--- a/content/test/data/accessibility/aria/aria-menu-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-menu-expected-android.txt
@@ -1,10 +1,10 @@
 android.webkit.WebView focusable focused scrollable
 ++android.view.View role_description='menu bar'
 ++++android.view.MenuItem role_description='menu item' clickable name='File'
-++++android.view.MenuItem role_description='menu item' clickable name='Edit'
-++++android.view.MenuItem role_description='menu item' clickable name='View'
+++++android.view.MenuItem role_description='menu item' clickable name='Edit' item_index=1 row_index=1
+++++android.view.MenuItem role_description='menu item' clickable name='View' item_index=2 row_index=2
 ++android.view.View role_description='menu' name='File'
 ++++android.view.MenuItem role_description='menu item' clickable name='New'
-++++android.view.MenuItem role_description='menu item' clickable name='Open'
-++++android.view.MenuItem role_description='menu item' clickable name='Save'
-++++android.view.MenuItem role_description='menu item' clickable name='Quit'
\ No newline at end of file
+++++android.view.MenuItem role_description='menu item' clickable name='Open' item_index=1 row_index=1
+++++android.view.MenuItem role_description='menu item' clickable name='Save' item_index=2 row_index=2
+++++android.view.MenuItem role_description='menu item' clickable name='Quit' item_index=3 row_index=3
diff --git a/content/test/data/accessibility/aria/aria-menubar-expected-android.txt b/content/test/data/accessibility/aria/aria-menubar-expected-android.txt
index 0cf068b..ad58c8e 100644
--- a/content/test/data/accessibility/aria/aria-menubar-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-menubar-expected-android.txt
@@ -1,5 +1,5 @@
 android.webkit.WebView focusable focused scrollable
 ++android.view.View role_description='menu bar'
 ++++android.view.MenuItem role_description='menu item' clickable name='File'
-++++android.view.MenuItem role_description='menu item' clickable name='Edit'
-++++android.view.MenuItem role_description='menu item' clickable name='View'
\ No newline at end of file
+++++android.view.MenuItem role_description='menu item' clickable name='Edit' item_index=1 row_index=1
+++++android.view.MenuItem role_description='menu item' clickable name='View' item_index=2 row_index=2
diff --git a/content/test/data/accessibility/aria/aria-menuitem-expected-android.txt b/content/test/data/accessibility/aria/aria-menuitem-expected-android.txt
index 2de55bd0..d4560da 100644
--- a/content/test/data/accessibility/aria/aria-menuitem-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-menuitem-expected-android.txt
@@ -1,8 +1,8 @@
 android.webkit.WebView focusable focused scrollable
 ++android.view.View role_description='menu'
 ++++android.view.MenuItem role_description='menu item' clickable focusable name='File'
-++++android.view.MenuItem role_description='menu item' clickable focusable name='Edit'
-++++android.view.MenuItem role_description='menu item' clickable focusable name='Complex menuitem'
+++++android.view.MenuItem role_description='menu item' clickable focusable name='Edit' item_index=1 row_index=1
+++++android.view.MenuItem role_description='menu item' clickable focusable name='Complex menuitem' item_index=2 row_index=2
 ++++++android.view.View name='Complex '
 ++++++android.widget.EditText clickable editable_text focusable input_type=1
 ++++++android.view.View name=' menuitem'
diff --git a/content/test/data/accessibility/aria/aria-menuitemcheckbox-expected-android.txt b/content/test/data/accessibility/aria/aria-menuitemcheckbox-expected-android.txt
index 933cbfe..0330ee8 100644
--- a/content/test/data/accessibility/aria/aria-menuitemcheckbox-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-menuitemcheckbox-expected-android.txt
@@ -1,5 +1,5 @@
 android.webkit.WebView focusable focused scrollable
 ++android.view.View role_description='menu'
 ++++android.view.MenuItem role_description='checkbox' checkable clickable name='Menu item 1'
-++++android.view.MenuItem role_description='checkbox' checkable checked clickable name='Menu item 2'
-++++android.view.MenuItem role_description='checkbox' checkable clickable name='Menu item 3'
+++++android.view.MenuItem role_description='checkbox' checkable checked clickable name='Menu item 2' item_index=1 row_index=1
+++++android.view.MenuItem role_description='checkbox' checkable clickable name='Menu item 3' item_index=2 row_index=2
diff --git a/content/test/data/accessibility/aria/aria-menuitemradio-expected-android.txt b/content/test/data/accessibility/aria/aria-menuitemradio-expected-android.txt
index 2e854ae..1b93ee55 100644
--- a/content/test/data/accessibility/aria/aria-menuitemradio-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-menuitemradio-expected-android.txt
@@ -1,5 +1,5 @@
 android.webkit.WebView focusable focused scrollable
 ++android.view.View role_description='menu'
 ++++android.view.MenuItem role_description='radio button' checkable clickable name='Menu item 1'
-++++android.view.MenuItem role_description='radio button' checkable checked clickable name='Menu item 2'
-++++android.view.MenuItem role_description='radio button' checkable clickable name='Menu item 3'
+++++android.view.MenuItem role_description='radio button' checkable checked clickable name='Menu item 2' item_index=1 row_index=1
+++++android.view.MenuItem role_description='radio button' checkable clickable name='Menu item 3' item_index=2 row_index=2
diff --git a/content/test/data/accessibility/aria/aria-posinset-expected-android.txt b/content/test/data/accessibility/aria/aria-posinset-expected-android.txt
index dea9552..e22a679 100644
--- a/content/test/data/accessibility/aria/aria-posinset-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-posinset-expected-android.txt
@@ -8,25 +8,25 @@
 ++++android.view.View clickable collection_item focusable name='Item 1'
 ++++android.view.View clickable collection_item focusable name='Item 2' item_index=1 row_index=1
 ++android.view.View
-++++android.widget.RadioButton role_description='radio button' checkable clickable focusable
+++++android.widget.RadioButton role_description='radio button' checkable clickable focusable item_index=2 row_index=2
 ++++android.view.View name='1'
 ++++android.view.View name='<newline>'
-++++android.widget.RadioButton role_description='radio button' checkable clickable focusable
+++++android.widget.RadioButton role_description='radio button' checkable clickable focusable item_index=3 row_index=3
 ++++android.view.View name='2'
 ++android.widget.RadioButton role_description='radio button' checkable clickable focusable
 ++android.view.View name='Apple'
 ++android.view.View name='<newline>'
-++android.widget.RadioButton role_description='radio button' checkable clickable focusable
+++android.widget.RadioButton role_description='radio button' checkable clickable focusable item_index=1 row_index=1
 ++android.view.View name='Banana'
 ++android.view.View name='Cake'
 ++++android.view.View name='Cake'
 ++++android.widget.RadioButton role_description='radio button' checkable checked clickable focusable name='Chiffon cakes'
 ++++android.view.View name='<newline>'
-++++android.widget.RadioButton role_description='radio button' checkable clickable focusable name='Chocolate cakes'
+++++android.widget.RadioButton role_description='radio button' checkable clickable focusable name='Chocolate cakes' item_index=1 row_index=1
 ++android.view.View
 ++++android.view.View
 ++++++android.widget.Button role_description='button' clickable focusable name='changedFromRadio'
 ++++++android.view.View name='red'
 ++++++android.view.View name='<newline>'
 ++++++android.widget.RadioButton role_description='radio button' checkable clickable focusable name='blue'
-++android.view.View name='Done'
\ No newline at end of file
+++android.view.View name='Done'
diff --git a/content/test/data/accessibility/aria/aria-radiogroup-expected-android.txt b/content/test/data/accessibility/aria/aria-radiogroup-expected-android.txt
index 23955f5..11d29a8 100644
--- a/content/test/data/accessibility/aria/aria-radiogroup-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-radiogroup-expected-android.txt
@@ -1,4 +1,4 @@
 android.webkit.WebView focusable focused scrollable
 ++android.view.View role_description='radio group' name='My group'
 ++++android.widget.RadioButton role_description='radio button' checkable clickable focusable name='Radio 1'
-++++android.widget.RadioButton role_description='radio button' checkable clickable focusable name='Radio 2'
\ No newline at end of file
+++++android.widget.RadioButton role_description='radio button' checkable clickable focusable name='Radio 2' item_index=1 row_index=1
diff --git a/content/test/data/accessibility/aria/aria-required-expected-android.txt b/content/test/data/accessibility/aria/aria-required-expected-android.txt
index 139c98df..b625b4b 100644
--- a/content/test/data/accessibility/aria/aria-required-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-required-expected-android.txt
@@ -1,4 +1,4 @@
 android.webkit.WebView focusable focused scrollable
 ++android.view.View role_description='radio group'
 ++++android.widget.RadioButton role_description='radio button' checkable clickable
-++++android.widget.RadioButton role_description='radio button' checkable clickable
\ No newline at end of file
+++++android.widget.RadioButton role_description='radio button' checkable clickable item_index=1 row_index=1
diff --git a/content/test/data/accessibility/aria/aria-row-attr-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-row-attr-expected-auralinux.txt
index 8208642b..cb00691d 100644
--- a/content/test/data/accessibility/aria/aria-row-attr-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-row-attr-expected-auralinux.txt
@@ -7,7 +7,7 @@
 ++++++++[text] name='cell 3'
 ++++++[column header] name='cell 4' selectable rowindex:3 rowspan:2 (row=0, col=2, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
 ++++++++[text] name='cell 4'
-++++[table row] selectable rowindex:4
+++++[table row] selectable
 ++++++[table cell] name='cell 2' selectable rowindex:4 (row=1, col=0, row_span=1, col_span=1, n_row_headers=0, n_col_headers=1)
 ++++++++[text] name='cell 2'
 ++++++[table cell] name='cell 3' selectable rowindex:4 (row=1, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=1)
diff --git a/content/test/data/accessibility/aria/aria-row-attr-expected-win.txt b/content/test/data/accessibility/aria/aria-row-attr-expected-win.txt
index 3c57622c..96cfc110 100644
--- a/content/test/data/accessibility/aria/aria-row-attr-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-row-attr-expected-win.txt
@@ -7,7 +7,7 @@
 ++++++++ROLE_SYSTEM_STATICTEXT name='cell 3'
 ++++++ROLE_SYSTEM_COLUMNHEADER name='cell 4' rowindex:3 rowspan:2
 ++++++++ROLE_SYSTEM_STATICTEXT name='cell 4'
-++++ROLE_SYSTEM_ROW rowindex:4
+++++ROLE_SYSTEM_ROW
 ++++++ROLE_SYSTEM_CELL name='cell 2' rowindex:4
 ++++++++ROLE_SYSTEM_STATICTEXT name='cell 2'
 ++++++ROLE_SYSTEM_CELL name='cell 3' rowindex:4
diff --git a/content/test/data/accessibility/aria/aria-tab-expected-android.txt b/content/test/data/accessibility/aria/aria-tab-expected-android.txt
index 76f3dce..9e08787 100644
--- a/content/test/data/accessibility/aria/aria-tab-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-tab-expected-android.txt
@@ -1,4 +1,4 @@
 android.webkit.WebView focusable focused scrollable
 ++android.widget.TabWidget role_description='tab list'
 ++++android.view.View role_description='tab' clickable name='Tab 1'
-++++android.view.View role_description='tab' clickable name='Tab 2'
+++++android.view.View role_description='tab' clickable name='Tab 2' item_index=1 row_index=1
diff --git a/content/test/data/accessibility/aria/aria-tablist-expected-android.txt b/content/test/data/accessibility/aria/aria-tablist-expected-android.txt
index 27ad604..9e08787 100644
--- a/content/test/data/accessibility/aria/aria-tablist-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-tablist-expected-android.txt
@@ -1,4 +1,4 @@
 android.webkit.WebView focusable focused scrollable
 ++android.widget.TabWidget role_description='tab list'
 ++++android.view.View role_description='tab' clickable name='Tab 1'
-++++android.view.View role_description='tab' clickable name='Tab 2'
\ No newline at end of file
+++++android.view.View role_description='tab' clickable name='Tab 2' item_index=1 row_index=1
diff --git a/content/test/data/accessibility/aria/aria-tree-expected-android.txt b/content/test/data/accessibility/aria/aria-tree-expected-android.txt
index 530969a..53e5e29 100644
--- a/content/test/data/accessibility/aria/aria-tree-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-tree-expected-android.txt
@@ -1,5 +1,5 @@
 android.webkit.WebView focusable focused scrollable
-++android.view.View role_description='tree' collection hierarchical row_count=2
+++android.view.View role_description='tree' collection hierarchical item_count=2 row_count=2
 ++++android.view.View role_description='tree item' checkable collection_item name='Animals'
 ++++++android.view.View role_description='link' clickable focusable link name='Animals'
 ++++++android.view.View
diff --git a/content/test/data/accessibility/html/input-radio-expected-android.txt b/content/test/data/accessibility/html/input-radio-expected-android.txt
index 82571a2..ae15b8c 100644
--- a/content/test/data/accessibility/html/input-radio-expected-android.txt
+++ b/content/test/data/accessibility/html/input-radio-expected-android.txt
@@ -3,11 +3,11 @@
 ++++android.widget.RadioButton role_description='radio button' checkable clickable focusable
 ++++android.view.View name='Radio1'
 ++++android.view.View name='<newline>'
-++++android.widget.RadioButton role_description='radio button' checkable clickable focusable
+++++android.widget.RadioButton role_description='radio button' checkable clickable focusable item_index=1 row_index=1
 ++++android.view.View name='Radio2'
 ++android.view.View
 ++++android.widget.RadioButton role_description='radio button' checkable clickable focusable name='Radio3'
-++++android.widget.RadioButton role_description='radio button' checkable checked clickable focusable name='Radio4'
+++++android.widget.RadioButton role_description='radio button' checkable checked clickable focusable name='Radio4' item_index=1 row_index=1
 ++android.view.View
 ++++android.widget.RadioButton role_description='radio button' checkable clickable focusable name='Radio5'
-++++android.widget.RadioButton role_description='radio button' checkable checked clickable focusable name='Radio6'
\ No newline at end of file
+++++android.widget.RadioButton role_description='radio button' checkable checked clickable focusable name='Radio6' item_index=1 row_index=1
diff --git a/content/test/data/accessibility/html/input-radio-in-menu-expected-android.txt b/content/test/data/accessibility/html/input-radio-in-menu-expected-android.txt
index b382bf3..103fc9a 100644
--- a/content/test/data/accessibility/html/input-radio-in-menu-expected-android.txt
+++ b/content/test/data/accessibility/html/input-radio-in-menu-expected-android.txt
@@ -4,8 +4,8 @@
 ++++android.view.View name='Radio0 '
 ++++android.view.MenuItem role_description='radio button' checkable clickable focusable
 ++++android.view.View name='Radio1 '
-++++android.widget.RadioButton role_description='radio button' checkable clickable focusable name='Radio2'
+++++android.widget.RadioButton role_description='radio button' checkable clickable focusable name='Radio2' item_index=2 row_index=2
 ++android.view.View role_description='menu'
 ++++android.widget.RadioButton role_description='radio button' checkable clickable focusable name='Radio3'
-++++android.view.MenuItem role_description='radio button' checkable clickable focusable
-++++android.view.MenuItem role_description='radio button' checkable checked clickable focusable
+++++android.view.MenuItem role_description='radio button' checkable clickable focusable item_index=1 row_index=1
+++++android.view.MenuItem role_description='radio button' checkable checked clickable focusable item_index=2 row_index=2
diff --git a/content/test/data/accessibility/html/optgroup-expected-android.txt b/content/test/data/accessibility/html/optgroup-expected-android.txt
index f42202e..7b1df97 100644
--- a/content/test/data/accessibility/html/optgroup-expected-android.txt
+++ b/content/test/data/accessibility/html/optgroup-expected-android.txt
@@ -3,10 +3,10 @@
 ++++android.widget.Spinner role_description='pop up button' clickable focusable
 ++++++android.view.View invisible
 ++++++++android.view.View clickable focusable invisible name='One'
-++++++++android.view.View clickable focusable invisible name='Two'
-++++++++android.view.View clickable focusable invisible name='Three'
-++++++++android.view.View clickable focusable invisible name='Four'
-++++++++android.view.View clickable invisible name='One'
-++++++++android.view.View clickable invisible name='Two'
-++++++++android.view.View clickable invisible name='Three'
-++++++++android.view.View clickable invisible name='Four'
\ No newline at end of file
+++++++++android.view.View clickable focusable invisible name='Two' item_index=1 row_index=1
+++++++++android.view.View clickable focusable invisible name='Three' item_index=2 row_index=2
+++++++++android.view.View clickable focusable invisible name='Four' item_index=3 row_index=3
+++++++++android.view.View clickable invisible name='One' item_index=4 row_index=4
+++++++++android.view.View clickable invisible name='Two' item_index=5 row_index=5
+++++++++android.view.View clickable invisible name='Three' item_index=6 row_index=6
+++++++++android.view.View clickable invisible name='Four' item_index=7 row_index=7
diff --git a/content/test/data/accessibility/html/select-expected-android.txt b/content/test/data/accessibility/html/select-expected-android.txt
index c6563ca5..d3d1d22 100644
--- a/content/test/data/accessibility/html/select-expected-android.txt
+++ b/content/test/data/accessibility/html/select-expected-android.txt
@@ -7,5 +7,5 @@
 ++++android.widget.Spinner role_description='pop up button' clickable focusable
 ++++++android.view.View invisible
 ++++++++android.view.View clickable focusable invisible name='Option 1'
-++++++++android.view.View clickable focusable invisible name='Option 2'
-++++++++android.view.View clickable focusable invisible name='Option 3'
\ No newline at end of file
+++++++++android.view.View clickable focusable invisible name='Option 2' item_index=1 row_index=1
+++++++++android.view.View clickable focusable invisible name='Option 3' item_index=2 row_index=2
diff --git a/docs/updating_clang.md b/docs/updating_clang.md
index f40d94b..adff33f 100644
--- a/docs/updating_clang.md
+++ b/docs/updating_clang.md
@@ -42,11 +42,12 @@
 
     ```shell
     git cl try &&
-    git cl try -B luci.chromium.try -b ios-device -b mac_chromium_asan_rel_ng \
+    git cl try -B luci.chromium.try -b mac_chromium_asan_rel_ng \
       -b linux_chromium_cfi_rel_ng \
       -b linux_chromium_chromeos_asan_rel_ng -b linux_chromium_msan_rel_ng \
       -b linux_chromium_chromeos_msan_rel_ng -b linux-chromeos-dbg \
-      -b win-asan -b chromeos-amd64-generic-cfi-thin-lto-rel
+      -b win-asan -b chromeos-amd64-generic-cfi-thin-lto-rel &&
+    git cl try -B luci.chrome.try -b iphone-device -b ipad-device
     ```
 
 1.  Optional: Start Pinpoint perf tryjobs. These are generally too noisy to
diff --git a/extensions/browser/guest_view/web_view/web_view_apitest.cc b/extensions/browser/guest_view/web_view/web_view_apitest.cc
index 31b0123..6ca4eebe 100644
--- a/extensions/browser/guest_view/web_view/web_view_apitest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_apitest.cc
@@ -28,6 +28,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/hit_test_region_observer.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/api/extensions_api_client.h"
@@ -402,6 +403,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewAPITest, TestAssignSrcAfterCrash) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   RunTest("testAssignSrcAfterCrash", "web_view/apitest");
 }
 
@@ -551,6 +553,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewAPITest, TestEventName) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   RunTest("testEventName", "web_view/apitest");
 }
 
@@ -716,6 +719,8 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewAPITest, TestRemoveWebviewOnExit) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+
   std::string app_location = "web_view/apitest";
   StartTestServer(app_location);
 
@@ -752,6 +757,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewAPITest, TestReloadAfterTerminate) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   RunTest("testReloadAfterTerminate", "web_view/apitest");
 }
 
@@ -774,6 +780,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewAPITest, TestTerminateAfterExit) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   RunTest("testTerminateAfterExit", "web_view/apitest");
 }
 
diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn
index a5a7e0707..364bd6e 100644
--- a/extensions/renderer/BUILD.gn
+++ b/extensions/renderer/BUILD.gn
@@ -252,15 +252,23 @@
 
   deps = [
     "//chrome:resources",
+    "//components/crx_file",
+    "//components/guest_view/common",
     "//components/guest_view/renderer",
+    "//components/version_info",
     "//content:resources",
+    "//content/app/strings:strings",
+    "//content/public/renderer",
     "//extensions:extensions_resources",
     "//extensions/common",
     "//extensions/common/api",
     "//gin",
+    "//mojo/public/js:resources",
     "//skia",
+    "//storage/common",
     "//third_party/blink/public:blink",
     "//third_party/cld_3/src/src:cld_3",
+    "//third_party/zlib/google:compression_utils",
   ]
 
   if (proprietary_codecs && enable_wifi_display) {
@@ -324,8 +332,9 @@
   deps = [
     ":renderer",
     "//base",
+    "//base/test:test_support",
     "//components/crx_file",
-    "//content/public/child",
+    "//content/public/renderer",
     "//content/test:test_support",
     "//extensions:test_support",
     "//extensions/common",
@@ -333,6 +342,7 @@
     "//gin:gin_test",
     "//testing/gmock",
     "//testing/gtest",
+    "//third_party/zlib/google:compression_utils",
   ]
 }
 
@@ -392,7 +402,6 @@
     "//base",
     "//base/test:test_support",
     "//components/crx_file:crx_file",
-    "//content/public/child",
     "//content/test:test_support",
     "//extensions:extensions_renderer_resources",
     "//extensions:test_support",
diff --git a/extensions/renderer/DEPS b/extensions/renderer/DEPS
index 381d3c2..2e127d4 100644
--- a/extensions/renderer/DEPS
+++ b/extensions/renderer/DEPS
@@ -1,7 +1,6 @@
 include_rules = [
   "+components/translate/core/language_detection",
   "+content/app/strings/grit",
-  "+content/public/child",
   "+content/public/renderer",
 
   "+gin",
diff --git a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
index f4fe75f..742270d 100644
--- a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
+++ b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
@@ -896,10 +896,10 @@
 
         if (attribute == ax::mojom::IntAttribute::kPosInSet &&
             node->GetPosInSet()) {
-          attr_value = node->GetPosInSet();
+          attr_value = *node->GetPosInSet();
         } else if (attribute == ax::mojom::IntAttribute::kSetSize &&
                    node->GetSetSize()) {
-          attr_value = node->GetSetSize();
+          attr_value = *node->GetSetSize();
         } else if (!node->data().GetIntAttribute(attribute, &attr_value)) {
           return;
         }
@@ -1355,13 +1355,17 @@
   RouteNodeIDFunction(
       "GetTableCellColumnIndex",
       [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
-         AutomationAXTreeWrapper* tree_wrapper,
-         ui::AXNode* node) { result.Set(node->GetTableCellColIndex()); });
+         AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
+        if (node->GetTableCellColIndex())
+          result.Set(*node->GetTableCellColIndex());
+      });
   RouteNodeIDFunction(
       "GetTableCellRowIndex",
       [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
-         AutomationAXTreeWrapper* tree_wrapper,
-         ui::AXNode* node) { result.Set(node->GetTableCellRowIndex()); });
+         AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
+        if (node->GetTableCellRowIndex())
+          result.Set(*node->GetTableCellRowIndex());
+      });
 }
 
 void AutomationInternalCustomBindings::Invalidate() {
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index ff04236..21c22bf 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -60,7 +60,6 @@
 #include "ios/chrome/app/startup/setup_debugging.h"
 #import "ios/chrome/app/startup_tasks.h"
 #include "ios/chrome/app/tests_hook.h"
-#import "ios/chrome/browser/app_startup_parameters.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h"
diff --git a/ios/chrome/app/startup/chrome_app_startup_parameters.h b/ios/chrome/app/startup/chrome_app_startup_parameters.h
index e8f7768..4de3a10 100644
--- a/ios/chrome/app/startup/chrome_app_startup_parameters.h
+++ b/ios/chrome/app/startup/chrome_app_startup_parameters.h
@@ -27,6 +27,7 @@
   CALLER_APP_GOOGLE_CHROME_TODAY_EXTENSION,
   CALLER_APP_GOOGLE_CHROME_SEARCH_EXTENSION,
   CALLER_APP_GOOGLE_CHROME_CONTENT_EXTENSION,
+  CALLER_APP_GOOGLE_CHROME_SHARE_EXTENSION,
   MOBILE_SESSION_CALLER_APP_COUNT,
 };
 
diff --git a/ios/chrome/app/startup/chrome_app_startup_parameters.mm b/ios/chrome/app/startup/chrome_app_startup_parameters.mm
index 5ceb1fe..c0c0372 100644
--- a/ios/chrome/app/startup/chrome_app_startup_parameters.mm
+++ b/ios/chrome/app/startup/chrome_app_startup_parameters.mm
@@ -383,6 +383,9 @@
   if ([_secureSourceApp
           isEqualToString:app_group::kOpenCommandSourceContentExtension])
     return CALLER_APP_GOOGLE_CHROME_CONTENT_EXTENSION;
+  if ([_secureSourceApp
+          isEqualToString:app_group::kOpenCommandSourceShareExtension])
+    return CALLER_APP_GOOGLE_CHROME_SHARE_EXTENSION;
 
   if (![_declaredSourceApp length])
     return CALLER_APP_NOT_AVAILABLE;
diff --git a/ios/chrome/browser/favicon/favicon_loader.h b/ios/chrome/browser/favicon/favicon_loader.h
index 676330c..91b5cf2 100644
--- a/ios/chrome/browser/favicon/favicon_loader.h
+++ b/ios/chrome/browser/favicon/favicon_loader.h
@@ -29,31 +29,33 @@
   ~FaviconLoader() override;
 
   // Tries to find a FaviconAttributes in |favicon_cache_| with |page_url|:
-  // If found, returns it.
-  // If not found, returns a default favicon, and invokes |block| asynchronously
-  // with the favicon fetched by trying following methods:
+  // If found, invokes |faviconBlockHandler| and exits.
+  // If not found, invokes |faviconBlockHandler| with a default placeholder
+  // then invokes it again asynchronously with the favicon fetched by trying
+  // following methods:
   //   1. Use |large_icon_service_| to fetch from local DB managed by
   //      HistoryService;
   //   2. Use |large_icon_service_| to fetch from Google Favicon server if
   //      |fallback_to_google_server|=YES;
   //   3. Create a favicon base on the fallback style from |large_icon_service|.
-  FaviconAttributes* FaviconForPageUrl(const GURL& page_url,
-                                       float size_in_points,
-                                       float min_size_in_points,
-                                       bool fallback_to_google_server,
-                                       FaviconAttributesCompletionBlock block);
+  void FaviconForPageUrl(const GURL& page_url,
+                         float size_in_points,
+                         float min_size_in_points,
+                         bool fallback_to_google_server,
+                         FaviconAttributesCompletionBlock faviconBlockHandler);
 
   // Tries to find a FaviconAttributes in |favicon_cache_| with |icon_url|:
-  // If found, returns it.
-  // If not found, returns a default favicon, and invokes |block| asynchronously
-  // with the favicon fetched by trying following methods:
+  // If found, invokes |faviconBlockHandler| and exits.
+  // If not found, invokes |faviconBlockHandler| with a default placeholder
+  // then invokes it again asynchronously with the favicon fetched by trying
+  // following methods:
   //   1. Use |large_icon_service_| to fetch from local DB managed by
   //      HistoryService;
   //   2. Create a favicon base on the fallback style from |large_icon_service|.
-  FaviconAttributes* FaviconForIconUrl(const GURL& icon_url,
-                                       float size_in_points,
-                                       float min_size_in_points,
-                                       FaviconAttributesCompletionBlock block);
+  void FaviconForIconUrl(const GURL& icon_url,
+                         float size_in_points,
+                         float min_size_in_points,
+                         FaviconAttributesCompletionBlock faviconBlockHandler);
 
   // Cancel all incomplete requests.
   void CancellAllRequests();
diff --git a/ios/chrome/browser/favicon/favicon_loader.mm b/ios/chrome/browser/favicon/favicon_loader.mm
index 23cdb0cc..b162fc7 100644
--- a/ios/chrome/browser/favicon/favicon_loader.mm
+++ b/ios/chrome/browser/favicon/favicon_loader.mm
@@ -55,20 +55,22 @@
 // TODO(pinkerton): How do we update the favicon if it's changed on the web?
 // We can possibly just rely on this class being purged or the app being killed
 // to reset it, but then how do we ensure the FaviconService is updated?
-FaviconAttributes* FaviconLoader::FaviconForPageUrl(
+void FaviconLoader::FaviconForPageUrl(
     const GURL& page_url,
     float size_in_points,
     float min_size_in_points,
     bool fallback_to_google_server,  // retrieve favicon from Google Server if
                                      // GetLargeIconOrFallbackStyle() doesn't
                                      // return valid favicon.
-    FaviconAttributesCompletionBlock block) {
+    FaviconAttributesCompletionBlock faviconBlockHandler) {
+  DCHECK(faviconBlockHandler);
   NSString* key =
       [NSString stringWithFormat:@"%d %@", (int)round(size_in_points),
                                  base::SysUTF8ToNSString(page_url.spec())];
   FaviconAttributes* value = [favicon_cache_ objectForKey:key];
   if (value) {
-    return value;
+    faviconBlockHandler(value);
+    return;
   }
 
   const CGFloat scale = UIScreen.mainScreen.scale;
@@ -88,11 +90,10 @@
       FaviconAttributes* attributes =
           [FaviconAttributes attributesWithImage:favicon];
       [favicon_cache_ setObject:attributes forKey:key];
-      if (block) {
-        DCHECK(favicon.size.width <= size_in_points &&
-               favicon.size.height <= size_in_points);
-        block(attributes);
-      }
+
+      DCHECK(favicon.size.width <= size_in_points &&
+             favicon.size.height <= size_in_points);
+      faviconBlockHandler(attributes);
       return;
     } else if (fallback_to_google_server) {
       void (^favicon_loaded_from_server_block)(
@@ -105,9 +106,9 @@
             // Favicon should be loaded to the db that backs LargeIconService
             // now.  Fetch it again. Even if the request was not successful, the
             // fallback style will be used.
-            FaviconForPageUrl(block_page_url, size_in_points,
-                              min_size_in_points,
-                              /*continueToGoogleServer=*/false, block);
+            FaviconForPageUrl(
+                block_page_url, size_in_points, min_size_in_points,
+                /*continueToGoogleServer=*/false, faviconBlockHandler);
           };
 
       large_icon_service_
@@ -133,30 +134,32 @@
                                is_default_background_color];
 
     [favicon_cache_ setObject:attributes forKey:key];
-    if (block) {
-      block(attributes);
-    }
+    faviconBlockHandler(attributes);
   };
 
+  // First, synchronously return a fallback image.
+  faviconBlockHandler([FaviconAttributes attributesWithDefaultImage]);
+
+  // Now fetch the image synchronously.
   DCHECK(large_icon_service_);
   large_icon_service_->GetLargeIconRawBitmapOrFallbackStyleForPageUrl(
       page_url, min_favicon_size_in_pixels, favicon_size_in_pixels,
       base::BindRepeating(favicon_block), &cancelable_task_tracker_);
-
-  return [FaviconAttributes attributesWithDefaultImage];
 }
 
-FaviconAttributes* FaviconLoader::FaviconForIconUrl(
+void FaviconLoader::FaviconForIconUrl(
     const GURL& icon_url,
     float size_in_points,
     float min_size_in_points,
-    FaviconAttributesCompletionBlock block) {
+    FaviconAttributesCompletionBlock faviconBlockHandler) {
+  DCHECK(faviconBlockHandler);
   NSString* key =
       [NSString stringWithFormat:@"%d %@", (int)round(size_in_points),
                                  base::SysUTF8ToNSString(icon_url.spec())];
   FaviconAttributes* value = [favicon_cache_ objectForKey:key];
   if (value) {
-    return value;
+    faviconBlockHandler(value);
+    return;
   }
 
   const CGFloat scale = UIScreen.mainScreen.scale;
@@ -176,9 +179,7 @@
       FaviconAttributes* attributes =
           [FaviconAttributes attributesWithImage:favicon];
       [favicon_cache_ setObject:attributes forKey:key];
-      if (block) {
-        block(attributes);
-      }
+      faviconBlockHandler(attributes);
       return;
     }
     // Did not get valid favicon back and are not attempting to retrieve one
@@ -193,18 +194,18 @@
                                is_default_background_color];
 
     [favicon_cache_ setObject:attributes forKey:key];
-    if (block) {
-      block(attributes);
-    }
+    faviconBlockHandler(attributes);
   };
 
+  // First, return a fallback synchronously.
+  faviconBlockHandler([FaviconAttributes
+      attributesWithImage:[UIImage imageNamed:@"default_world_favicon"]]);
+
+  // Now call the service for a better async icon.
   DCHECK(large_icon_service_);
   large_icon_service_->GetLargeIconRawBitmapOrFallbackStyleForIconUrl(
       icon_url, min_favicon_size_in_pixels, favicon_size_in_pixels,
       base::BindRepeating(favicon_block), &cancelable_task_tracker_);
-
-  return [FaviconAttributes
-      attributesWithImage:[UIImage imageNamed:@"default_world_favicon"]];
 }
 
 void FaviconLoader::CancellAllRequests() {
diff --git a/ios/chrome/browser/favicon/favicon_loader_unittest.mm b/ios/chrome/browser/favicon/favicon_loader_unittest.mm
index 58dfeca..3883731c 100644
--- a/ios/chrome/browser/favicon/favicon_loader_unittest.mm
+++ b/ios/chrome/browser/favicon/favicon_loader_unittest.mm
@@ -98,16 +98,15 @@
 
   // Returns FaviconLoader::FaviconForPageUrl or
   // FaviconLoader::FaviconForIconUrl depending on the TEST_P param.
-  FaviconAttributes* FaviconForUrl(
-      const GURL& url,
-      FaviconLoader::FaviconAttributesCompletionBlock callback) {
+  void FaviconForUrl(const GURL& url,
+                     FaviconLoader::FaviconAttributesCompletionBlock callback) {
     if (GetParam() == TEST_PAGE_URL) {
-      return favicon_loader_.FaviconForPageUrl(
-          url, kTestFaviconSize, kTestFaviconSize,
-          /*fallback_to_google_server=*/false, callback);
+      favicon_loader_.FaviconForPageUrl(url, kTestFaviconSize, kTestFaviconSize,
+                                        /*fallback_to_google_server=*/false,
+                                        callback);
     } else {
-      return favicon_loader_.FaviconForIconUrl(url, kTestFaviconSize,
-                                               kTestFaviconSize, callback);
+      favicon_loader_.FaviconForIconUrl(url, kTestFaviconSize, kTestFaviconSize,
+                                        callback);
     }
   }
 
@@ -130,32 +129,44 @@
 // Tests that fallback data is provided when no favicon is retrieved from
 // LargeIconService.
 TEST_P(FaviconLoaderTest, FallbackIcon) {
-  __block bool callback_executed = false;
+  __block int callback_executed_count = 0;
   auto confirmation_block = ^(FaviconAttributes* favicon_attributes) {
-    callback_executed = true;
-    EXPECT_FALSE(favicon_attributes.faviconImage);
-    EXPECT_TRUE(favicon_attributes.monogramString);
-    EXPECT_TRUE(favicon_attributes.textColor);
-    EXPECT_TRUE(favicon_attributes.backgroundColor);
+    if (callback_executed_count == 0) {
+      // Check that a placeholder image is received.
+      EXPECT_TRUE(favicon_attributes.faviconImage);
+    } else {
+      // Check that a monogram is used as a fallback.
+      EXPECT_FALSE(favicon_attributes.faviconImage);
+      EXPECT_TRUE(favicon_attributes.monogramString);
+      EXPECT_TRUE(favicon_attributes.textColor);
+      EXPECT_TRUE(favicon_attributes.backgroundColor);
+    }
+
+    ++callback_executed_count;
   };
   FaviconForUrl(GURL(kTestFallbackURL), confirmation_block);
-  EXPECT_TRUE(callback_executed);
+  EXPECT_EQ(callback_executed_count, 2);
 }
 
-// Tests that when favicon is in cache, it is returned immediately. The callback
-// should never be executed.
+// Tests that when favicon is in cache, the callback is synchronously called.
 TEST_P(FaviconLoaderTest, Cache) {
   // Favicon retrieval that should put it in the cache.
-  FaviconForUrl(GURL(kTestFaviconURL), nil);
+  FaviconForUrl(GURL(kTestFaviconURL), ^(FaviconAttributes* attributes){
+                });
   __block bool callback_executed = false;
+  __block UIImage* faviconImage = nil;
+  __block int callback_executed_count = 0;
   auto confirmation_block = ^(FaviconAttributes* faviconAttributes) {
     callback_executed = true;
+    faviconImage = faviconAttributes.faviconImage;
+    ++callback_executed_count;
   };
-  FaviconAttributes* attributes_result =
-      FaviconForUrl(GURL(kTestFaviconURL), confirmation_block);
-  EXPECT_TRUE(attributes_result.faviconImage);
-  // Favicon should be returned by cache, so callback should never exectue.
-  EXPECT_FALSE(callback_executed);
+  FaviconForUrl(GURL(kTestFaviconURL), confirmation_block);
+  EXPECT_EQ(callback_executed_count, 1);
+  // The callback should be immediately executed.
+  EXPECT_TRUE(callback_executed);
+  // The cached image should be available immediately.
+  EXPECT_TRUE(faviconImage);
 }
 
 INSTANTIATE_TEST_SUITE_P(ProgrammaticFaviconLoaderTest,
diff --git a/ios/chrome/browser/share_extension/share_extension_item_receiver.mm b/ios/chrome/browser/share_extension/share_extension_item_receiver.mm
index 02cadfc..ece6bfb 100644
--- a/ios/chrome/browser/share_extension/share_extension_item_receiver.mm
+++ b/ios/chrome/browser/share_extension/share_extension_item_receiver.mm
@@ -38,6 +38,7 @@
   CANCELLED_ENTRY,
   READINGLIST_ENTRY,
   BOOKMARK_ENTRY,
+  OPEN_IN_CHROME_ENTRY,
   SHARE_EXTENSION_ITEM_RECEIVED_COUNT
 };
 
@@ -269,6 +270,12 @@
                                base::UTF8ToUTF16(entryTitle), entryURL);
         break;
       }
+      case app_group::OPEN_IN_CHROME_ITEM: {
+        LogHistogramReceivedItem(OPEN_IN_CHROME_ENTRY);
+        // Open URL command is sent directly by the extension. No processing is
+        // needed here.
+        break;
+      }
     }
 
     if (completion && _taskRunner) {
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm
index 38a6f1b..52ed4ca7 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm
@@ -259,14 +259,12 @@
 
 #pragma mark - TableViewFaviconDataSource
 
-- (FaviconAttributes*)faviconForURL:(const GURL&)URL
-                         completion:(void (^)(FaviconAttributes*))completion {
+- (void)faviconForURL:(const GURL&)URL
+           completion:(void (^)(FaviconAttributes*))completion {
   DCHECK(completion);
-  FaviconAttributes* cachedAttributes = self.faviconLoader->FaviconForPageUrl(
+  self.faviconLoader->FaviconForPageUrl(
       URL, gfx::kFaviconSize, kMinFaviconSizePt,
       /*fallback_to_google_server=*/false, completion);
-  DCHECK(cachedAttributes);
-  return cachedAttributes;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm
index 45a8408c..bcbfdfc 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm
@@ -179,7 +179,7 @@
       base::mac::ObjCCastStrict<ManualFillPasswordCell>(cell);
 
   NSString* itemIdentifier = passwordItem.uniqueIdentifier;
-  FaviconAttributes* cachedAttributes = [self.imageDataSource
+  [self.imageDataSource
       faviconForURL:passwordItem.faviconURL
          completion:^(FaviconAttributes* attributes) {
            // Only set favicon if the cell hasn't been reused.
@@ -188,8 +188,6 @@
              [passwordCell configureWithFaviconAttributes:attributes];
            }
          }];
-  DCHECK(cachedAttributes);
-  [passwordCell configureWithFaviconAttributes:cachedAttributes];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
index eff5712..ac8d6e7 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
@@ -560,11 +560,9 @@
       [BookmarkHomeSharedState desiredFaviconSizePt];
   CGFloat minFaviconSizeInPoints = [BookmarkHomeSharedState minFaviconSizePt];
 
-  FaviconAttributes* cachedAttributes = self.faviconLoader->FaviconForPageUrl(
+  self.faviconLoader->FaviconForPageUrl(
       blockURL, desiredFaviconSizeInPoints, minFaviconSizeInPoints,
       /*fallback_to_google_server=*/fallbackToGoogleServer, faviconLoadedBlock);
-  DCHECK(cachedAttributes);
-  faviconLoadedBlock(cachedAttributes);
 }
 
 - (void)updateTableViewBackgroundStyle:(BookmarkHomeBackgroundStyle)style {
diff --git a/ios/chrome/browser/ui/download/download_manager_egtest.mm b/ios/chrome/browser/ui/download/download_manager_egtest.mm
index a71aab73a..c2ce7a6f 100644
--- a/ios/chrome/browser/ui/download/download_manager_egtest.mm
+++ b/ios/chrome/browser/ui/download/download_manager_egtest.mm
@@ -39,11 +39,6 @@
   return ButtonWithAccessibilityLabelId(IDS_IOS_DOWNLOAD_MANAGER_DOWNLOAD);
 }
 
-// Matcher for "Open In..." button on Download Manager UI.
-id<GREYMatcher> OpenInButton() {
-  return ButtonWithAccessibilityLabelId(IDS_IOS_OPEN_IN);
-}
-
 // Provides downloads landing page with download link.
 std::unique_ptr<net::test_server::HttpResponse> GetResponse(
     const net::test_server::HttpRequest& request) {
@@ -60,7 +55,7 @@
   const NSTimeInterval kLongDownloadTimeout = 35;
   return base::test::ios::WaitUntilConditionOrTimeout(kLongDownloadTimeout, ^{
     NSError* error = nil;
-    [[EarlGrey selectElementWithMatcher:OpenInButton()]
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::OpenInButton()]
         assertWithMatcher:grey_notNil()
                     error:&error];
     return (error == nil);
diff --git a/ios/chrome/browser/ui/history/history_mediator.mm b/ios/chrome/browser/ui/history/history_mediator.mm
index dcd1f833..f27a86fe 100644
--- a/ios/chrome/browser/ui/history/history_mediator.mm
+++ b/ios/chrome/browser/ui/history/history_mediator.mm
@@ -40,16 +40,13 @@
 
 #pragma mark - TableViewFaviconDataSource
 
-- (FaviconAttributes*)faviconForURL:(const GURL&)URL
-                         completion:(void (^)(FaviconAttributes*))completion {
-  FaviconAttributes* cachedAttributes = self.faviconLoader->FaviconForPageUrl(
+- (void)faviconForURL:(const GURL&)URL
+           completion:(void (^)(FaviconAttributes*))completion {
+  self.faviconLoader->FaviconForPageUrl(
       URL, kFaviconWidthHeight, kFaviconMinWidthHeight,
       /*fallback_to_google_server=*/false, ^(FaviconAttributes* attributes) {
         completion(attributes);
       });
-  DCHECK(cachedAttributes);
-
-  return cachedAttributes;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/history/history_table_view_controller.mm b/ios/chrome/browser/ui/history/history_table_view_controller.mm
index fc1490c..3ba97221 100644
--- a/ios/chrome/browser/ui/history/history_table_view_controller.mm
+++ b/ios/chrome/browser/ui/history/history_table_view_controller.mm
@@ -608,7 +608,7 @@
         base::mac::ObjCCastStrict<HistoryEntryItem>(item);
     TableViewURLCell* URLCell =
         base::mac::ObjCCastStrict<TableViewURLCell>(cellToReturn);
-    FaviconAttributes* cachedAttributes = [self.imageDataSource
+    [self.imageDataSource
         faviconForURL:URLItem.URL
            completion:^(FaviconAttributes* attributes) {
              // Only set favicon if the cell hasn't been reused.
@@ -618,8 +618,6 @@
                [URLCell.faviconView configureWithAttributes:attributes];
              }
            }];
-    DCHECK(cachedAttributes);
-    [URLCell.faviconView configureWithAttributes:cachedAttributes];
   }
   if (item.type == ItemTypeEntriesStatusWithLink) {
     TableViewTextLinkCell* tableViewTextLinkCell =
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm b/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
index 765cde2..0fc666b0 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
@@ -102,13 +102,14 @@
 
   if (base::FeatureList::IsEnabled(kNewOmniboxPopupLayout)) {
     __weak OmniboxMediator* weakSelf = self;
-    if (base::FeatureList::IsEnabled(kOmniboxUseDefaultSearchEngineFavicon) &&
-        AutocompleteMatch::IsSearchType(matchType)) {
-      // Show Default Search Engine favicon.
-      __weak __typeof(self) weakSelf = self;
-      [self loadDefaultSearchEngineFaviconWithCompletion:^(UIImage* image) {
-        [weakSelf.consumer updateAutocompleteIcon:image];
-      }];
+
+    if (AutocompleteMatch::IsSearchType(matchType)) {
+      if (base::FeatureList::IsEnabled(kOmniboxUseDefaultSearchEngineFavicon)) {
+        // Show Default Search Engine favicon.
+        [self loadDefaultSearchEngineFaviconWithCompletion:^(UIImage* image) {
+          [weakSelf.consumer updateAutocompleteIcon:image];
+        }];
+      }
     } else {
       // Show favicon.
       [self loadFaviconByPageURL:faviconURL
@@ -146,12 +147,9 @@
 
   // Download the favicon.
   // The code below mimics that in OmniboxPopupMediator.
-  FaviconAttributes* faviconCacheResult = self.faviconLoader->FaviconForPageUrl(
+  self.faviconLoader->FaviconForPageUrl(
       pageURL, kOmniboxIconSize, kOmniboxIconSize,
       /*fallback_to_google_server=*/YES, handleFaviconResult);
-  // Handle the synchronously returned cache result. If the favicon loader did
-  // an async fetch, |handleFaviconResult| may be called again later.
-  handleFaviconResult(faviconCacheResult);
 }
 
 // Loads a favicon for the current default search engine.
@@ -209,7 +207,6 @@
     }
   };
 
-  FaviconAttributes* faviconCacheResult;
   // Prepopulated search engines don't have a favicon URL, so the favicon is
   // loaded with an empty query search page URL.
   if (defaultProvider->prepopulate_id() != 0) {
@@ -219,17 +216,16 @@
     std::string emptyPageUrl = defaultProvider->url_ref().ReplaceSearchTerms(
         TemplateURLRef::SearchTermsArgs(base::string16()),
         _templateURLService->search_terms_data());
-    faviconCacheResult = self.faviconLoader->FaviconForPageUrl(
+    self.faviconLoader->FaviconForPageUrl(
         GURL(emptyPageUrl), kOmniboxIconSize, kOmniboxIconSize,
         /*fallback_to_google_server=*/YES, handleFaviconResult);
   } else {
     // Download the favicon.
     // The code below mimics that in OmniboxPopupMediator.
-    faviconCacheResult = self.faviconLoader->FaviconForIconUrl(
-        defaultProvider->favicon_url(), kOmniboxIconSize, kOmniboxIconSize,
-        handleFaviconResult);
+    self.faviconLoader->FaviconForIconUrl(defaultProvider->favicon_url(),
+                                          kOmniboxIconSize, kOmniboxIconSize,
+                                          handleFaviconResult);
   }
-  handleFaviconResult(faviconCacheResult);
 }
 
 - (void)updateConsumerEmptyTextImage {
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
index 45ea2c25..f14e90d 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
@@ -185,18 +185,12 @@
     return;
   }
 
-  FaviconAttributes* cachedAttributes = self.faviconLoader->FaviconForPageUrl(
+  self.faviconLoader->FaviconForPageUrl(
       pageURL, kOmniboxIconSize, kOmniboxIconSize,
       /*fallback_to_google_server=*/YES, ^(FaviconAttributes* attributes) {
         if (attributes.faviconImage && !attributes.usesDefaultImage)
           completion(attributes.faviconImage);
       });
-
-  // Only use cached attributes when they are a non-default icon. Never show
-  // monograms or default globe icon.
-  if (cachedAttributes.faviconImage && !cachedAttributes.usesDefaultImage) {
-      completion(cachedAttributes.faviconImage);
-  }
 }
 
 @end
diff --git a/ios/chrome/browser/ui/open_in/BUILD.gn b/ios/chrome/browser/ui/open_in/BUILD.gn
index 5267ee9..a66b9bb 100644
--- a/ios/chrome/browser/ui/open_in/BUILD.gn
+++ b/ios/chrome/browser/ui/open_in/BUILD.gn
@@ -49,3 +49,27 @@
     "//third_party/ocmock",
   ]
 }
+
+source_set("eg2_tests") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+
+  sources = [
+    "open_in_controller_egtest.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/test/earl_grey:eg_test_support+eg2",
+    "//ios/testing/earl_grey:eg_test_support+eg2",
+    "//ios/third_party/earl_grey2:test_lib",
+    "//net:test_support",
+    "//ui/base",
+  ]
+
+  libs = [ "UIKit.framework" ]
+}
diff --git a/ios/chrome/browser/ui/open_in/open_in_controller_egtest.mm b/ios/chrome/browser/ui/open_in/open_in_controller_egtest.mm
new file mode 100644
index 0000000..3862a08
--- /dev/null
+++ b/ios/chrome/browser/ui/open_in/open_in_controller_egtest.mm
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <UIKit/UIKit.h>
+
+#include "base/strings/sys_string_conversions.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/chrome/test/earl_grey/chrome_actions.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
+#import "ios/chrome/test/earl_grey/chrome_matchers.h"
+#import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#import "ios/testing/earl_grey/earl_grey_test.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Path which leads to a PDF file.
+const char kPDFPath[] = "/testpage.pdf";
+
+}  // namespace
+
+// Tests Open in Feature for PDF files.
+@interface OpenInManagerTestCase : ChromeTestCase
+@end
+
+@implementation OpenInManagerTestCase
+
+- (void)setUp {
+  [super setUp];
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+}
+
+// Tests that open in button appears when opening a PDF, and that tapping on it
+// will open the activity view.
+- (void)testOpenIn {
+  [ChromeEarlGrey loadURL:self.testServer->GetURL(kPDFPath)];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::OpenInButton()]
+      performAction:grey_tap()];
+  // Check and tap on the Cancel button that appears below the activity menu.
+  // Other actions buttons can't be checked from EarlGrey.
+  [[[EarlGrey selectElementWithMatcher:chrome_test_util::CancelButton()]
+      assertWithMatcher:grey_interactable()] performAction:grey_tap()];
+
+  // Check that after dismissing the activity view, tapping on the web view will
+  // bring the open in bar again.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::OpenInButton()]
+      assertWithMatcher:grey_interactable()];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_mediator.mm b/ios/chrome/browser/ui/reading_list/reading_list_mediator.mm
index a825f27..f6b455b9 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_mediator.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_mediator.mm
@@ -168,11 +168,9 @@
 
         [strongSelf.dataSink itemHasChangedAfterDelay:strongItem];
       };
-  FaviconAttributes* cachedAttributes = self.faviconLoader->FaviconForPageUrl(
+  self.faviconLoader->FaviconForPageUrl(
       item.faviconPageURL, kFaviconWidthHeight, kFaviconMinWidthHeight,
       /*fallback_to_google_server=*/false, completionBlock);
-  DCHECK(cachedAttributes);
-  return completionBlock(cachedAttributes);
 }
 
 - (void)beginBatchUpdates {
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.mm
index 24a3c440..4083ade 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.mm
@@ -115,18 +115,15 @@
 
 #pragma mark - TableViewFaviconDataSource
 
-- (FaviconAttributes*)faviconForURL:(const GURL&)URL
-                         completion:(void (^)(FaviconAttributes*))completion {
+- (void)faviconForURL:(const GURL&)URL
+           completion:(void (^)(FaviconAttributes*))completion {
   FaviconLoader* faviconLoader =
       IOSChromeFaviconLoaderFactory::GetForBrowserState(self.browserState);
-  FaviconAttributes* cachedAttributes = faviconLoader->FaviconForPageUrl(
+  faviconLoader->FaviconForPageUrl(
       URL, kFaviconWidthHeight, kFaviconMinWidthHeight,
       /*fallback_to_google_server=*/false, ^(FaviconAttributes* attributes) {
         completion(attributes);
       });
-  DCHECK(cachedAttributes);
-
-  return cachedAttributes;
 }
 
 #pragma mark - Private
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
index f583622..687715cc 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -745,7 +745,7 @@
   TableViewURLCell* URLCell = base::mac::ObjCCastStrict<TableViewURLCell>(cell);
 
   NSString* itemIdentifier = URLItem.uniqueIdentifier;
-  FaviconAttributes* cachedAttributes = [self.imageDataSource
+  [self.imageDataSource
       faviconForURL:URLItem.URL
          completion:^(FaviconAttributes* attributes) {
            // Only set favicon if the cell hasn't been reused.
@@ -754,8 +754,6 @@
              [URLCell.faviconView configureWithAttributes:attributes];
            }
          }];
-  DCHECK(cachedAttributes);
-  [URLCell.faviconView configureWithAttributes:cachedAttributes];
 }
 
 #pragma mark - Distant Sessions helpers
diff --git a/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn b/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn
index 598551c..fb8c043d 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn
+++ b/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn
@@ -19,5 +19,12 @@
   sources = [
     "send_tab_to_self_modal_presentation_controller.h",
     "send_tab_to_self_modal_presentation_controller.mm",
+    "send_tab_to_self_table_view_controller.h",
+    "send_tab_to_self_table_view_controller.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/browser/ui/table_view",
+    "//ios/chrome/browser/ui/table_view/cells",
   ]
 }
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm
index 87d26dd..c1ed51f 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_presentation_controller.h"
+#import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -24,8 +25,8 @@
 #pragma mark - ChromeCoordinator Methods
 
 - (void)start {
-  UITableViewController* tableViewController =
-      [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
+  SendTabToSelfTableViewController* tableViewController =
+      [[SendTabToSelfTableViewController alloc] init];
   UINavigationController* navigationController = [[UINavigationController alloc]
       initWithRootViewController:tableViewController];
 
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.h b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.h
new file mode 100644
index 0000000..7b8a1ae
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_UI_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_TABLE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_TABLE_VIEW_CONTROLLER_H_
+
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
+
+// SendTabToSelfTableViewController represents the content for the
+// Send Tab To Self Modal dialog.
+@interface SendTabToSelfTableViewController : ChromeTableViewController
+
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithTableViewStyle:(UITableViewStyle)style
+                           appBarStyle:
+                               (ChromeTableViewControllerStyle)appBarStyle
+    NS_UNAVAILABLE;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm
new file mode 100644
index 0000000..4e28edc1
--- /dev/null
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.h"
+
+#include "base/logging.h"
+#include "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+typedef NS_ENUM(NSInteger, SectionIdentifier) {
+  SectionIdentifierDevicesToSend = kSectionIdentifierEnumZero,
+};
+
+@implementation SendTabToSelfTableViewController
+
+- (instancetype)init {
+  return [super initWithTableViewStyle:UITableViewStylePlain
+                           appBarStyle:ChromeTableViewControllerStyleNoAppBar];
+}
+
+#pragma mark - ViewController Lifecycle
+
+- (void)viewDidLoad {
+  NOTIMPLEMENTED();
+}
+
+@end
diff --git a/ios/chrome/browser/ui/settings/language/BUILD.gn b/ios/chrome/browser/ui/settings/language/BUILD.gn
index d6b0aaa..5ed9173 100644
--- a/ios/chrome/browser/ui/settings/language/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/language/BUILD.gn
@@ -39,6 +39,8 @@
     "language_settings_histograms.h",
     "language_settings_table_view_controller.h",
     "language_settings_table_view_controller.mm",
+    "language_settings_ui_constants.h",
+    "language_settings_ui_constants.mm",
   ]
   deps = [
     "//ios/chrome/app/strings:ios_strings_grit",
@@ -63,6 +65,7 @@
     "language_settings_mediator_unittest.mm",
   ]
   deps = [
+    ":language_ui",
     "//base/test:test_support",
     "//components/language/core/browser",
     "//components/pref_registry",
@@ -74,8 +77,27 @@
     "//ios/chrome/browser/prefs:browser_prefs",
     "//ios/chrome/browser/translate",
     "//ios/chrome/browser/ui/settings/language",
-    "//ios/chrome/browser/ui/settings/language:language_ui",
     "//ios/chrome/browser/ui/settings/language/cells",
     "//testing/gtest",
   ]
 }
+
+source_set("eg_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "language_settings_egtest.mm",
+  ]
+  deps = [
+    ":language_ui",
+    "//components/language/core/browser",
+    "//components/translate/core/browser",
+    "//ios/chrome/app/strings:ios_strings_grit",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/translate",
+    "//ios/chrome/browser/ui:feature_flags",
+    "//ios/chrome/test/app:test_support",
+    "//ios/chrome/test/earl_grey:test_support",
+    "//ui/strings:ui_strings_grit",
+  ]
+}
diff --git a/ios/chrome/browser/ui/settings/language/add_language_table_view_controller.mm b/ios/chrome/browser/ui/settings/language/add_language_table_view_controller.mm
index 39cb8f7..7f93e591 100644
--- a/ios/chrome/browser/ui/settings/language/add_language_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/language/add_language_table_view_controller.mm
@@ -10,6 +10,7 @@
 #import "ios/chrome/browser/ui/settings/language/cells/language_item.h"
 #import "ios/chrome/browser/ui/settings/language/language_settings_data_source.h"
 #import "ios/chrome/browser/ui/settings/language/language_settings_histograms.h"
+#import "ios/chrome/browser/ui/settings/language/language_settings_ui_constants.h"
 #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller_constants.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
@@ -23,13 +24,6 @@
 
 namespace {
 
-NSString* const kAddLanguageTableViewAccessibilityIdentifier =
-    @"kAddLanguageTableViewAccessibilityIdentifier";
-NSString* const kAddLanguageSearchControllerAccessibilityIdentifier =
-    @"kAddLanguageSearchControllerAccessibilityIdentifier";
-NSString* const kAddLanguageSearchScrimAccessibilityIdentifier =
-    @"kAddLanguageSearchScrimAccessibilityIdentifier";
-
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
   SectionIdentifierLanguages = kSectionIdentifierEnumZero,
 };
diff --git a/ios/chrome/browser/ui/settings/language/language_details_table_view_controller.mm b/ios/chrome/browser/ui/settings/language/language_details_table_view_controller.mm
index f366918..08bed86a 100644
--- a/ios/chrome/browser/ui/settings/language/language_details_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/language/language_details_table_view_controller.mm
@@ -10,6 +10,7 @@
 #import "ios/chrome/browser/ui/settings/language/cells/language_item.h"
 #import "ios/chrome/browser/ui/settings/language/language_settings_data_source.h"
 #import "ios/chrome/browser/ui/settings/language/language_settings_histograms.h"
+#import "ios/chrome/browser/ui/settings/language/language_settings_ui_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
@@ -23,9 +24,6 @@
 
 namespace {
 
-NSString* const kLanguageDetailsTableViewAccessibilityIdentifier =
-    @"kLanguageDetailsTableViewAccessibilityIdentifier";
-
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
   SectionIdentifierOptions = kSectionIdentifierEnumZero,
 };
@@ -98,9 +96,10 @@
   neverTranslateItem.text =
       l10n_util::GetNSString(IDS_IOS_LANGUAGE_SETTINGS_NEVER_TRANSLATE_TITLE);
   neverTranslateItem.accessibilityTraits |= UIAccessibilityTraitButton;
-  neverTranslateItem.accessoryType = [self.languageItem isBlocked]
-                                         ? UITableViewCellAccessoryCheckmark
-                                         : UITableViewCellAccessoryNone;
+  if ([self.languageItem isBlocked]) {
+    neverTranslateItem.accessibilityTraits |= UIAccessibilityTraitSelected;
+    neverTranslateItem.accessoryType = UITableViewCellAccessoryCheckmark;
+  }
   [model addItem:neverTranslateItem
       toSectionWithIdentifier:SectionIdentifierOptions];
 
@@ -110,9 +109,10 @@
   offerTranslateItem.text = l10n_util::GetNSString(
       IDS_IOS_LANGUAGE_SETTINGS_OFFER_TO_TRANSLATE_TITLE);
   offerTranslateItem.accessibilityTraits |= UIAccessibilityTraitButton;
-  offerTranslateItem.accessoryType = [self.languageItem isBlocked]
-                                         ? UITableViewCellAccessoryNone
-                                         : UITableViewCellAccessoryCheckmark;
+  if (![self.languageItem isBlocked]) {
+    offerTranslateItem.accessibilityTraits |= UIAccessibilityTraitSelected;
+    offerTranslateItem.accessoryType = UITableViewCellAccessoryCheckmark;
+  }
   if (!self.languageItem.canOfferTranslate) {
     offerTranslateItem.enabled = NO;
     offerTranslateItem.textColor =
diff --git a/ios/chrome/browser/ui/settings/language/language_settings_egtest.mm b/ios/chrome/browser/ui/settings/language/language_settings_egtest.mm
new file mode 100644
index 0000000..a8db591
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/language/language_settings_egtest.mm
@@ -0,0 +1,431 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <EarlGrey/EarlGrey.h>
+#import <XCTest/XCTest.h>
+
+#include <memory>
+
+#include "base/test/scoped_feature_list.h"
+#include "components/language/core/browser/pref_names.h"
+#include "components/translate/core/browser/translate_pref_names.h"
+#include "components/translate/core/browser/translate_prefs.h"
+#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/translate/chrome_ios_translate_client.h"
+#import "ios/chrome/browser/ui/settings/language/language_settings_ui_constants.h"
+#include "ios/chrome/browser/ui/ui_feature_flags.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/chrome/test/app/chrome_test_util.h"
+#include "ios/chrome/test/earl_grey/accessibility_util.h"
+#import "ios/chrome/test/earl_grey/chrome_actions.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
+#import "ios/chrome/test/earl_grey/chrome_matchers.h"
+#import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#include "ui/strings/grit/ui_strings.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using chrome_test_util::ButtonWithAccessibilityLabel;
+using chrome_test_util::ButtonWithAccessibilityLabelId;
+using chrome_test_util::GetOriginalBrowserState;
+using chrome_test_util::NavigationBarDoneButton;
+using chrome_test_util::SetBooleanUserPref;
+using chrome_test_util::SettingsMenuBackButton;
+using chrome_test_util::SettingsSwitchCell;
+using chrome_test_util::TurnSettingsSwitchOn;
+using chrome_test_util::VerifyAccessibilityForCurrentScreen;
+using language::prefs::kAcceptLanguages;
+
+namespace {
+
+// Labels used to identify language entries.
+NSString* const kLanguageEntryThreeLabelsTemplate = @"%@, %@, %@";
+NSString* const kLanguageEntryTwoLabelsTemplate = @"%@, %@";
+NSString* const kEnglishLabel = @"English (United States)";
+NSString* const kTurkishLabel = @"Turkish";
+NSString* const kTurkishNativeLabel = @"Türkçe";
+NSString* const kNeverTranslateLabel = @"Never Translate";
+NSString* const kOfferToTranslateLabel = @"Offer to Translate";
+
+// Matcher for the Language Settings's main page table view.
+id<GREYMatcher> LanguageSettingsTableView() {
+  return grey_allOf(
+      grey_accessibilityID(kLanguageSettingsTableViewAccessibilityIdentifier),
+      grey_sufficientlyVisible(), nil);
+}
+
+// Matcher for the Language Settings's Add Language page table view.
+id<GREYMatcher> AddLanguageTableView() {
+  return grey_allOf(
+      grey_accessibilityID(kAddLanguageTableViewAccessibilityIdentifier),
+      grey_sufficientlyVisible(), nil);
+}
+
+// Matcher for the Language Settings's Language Details page table view.
+id<GREYMatcher> LanguageDetailsTableView() {
+  return grey_allOf(
+      grey_accessibilityID(kLanguageDetailsTableViewAccessibilityIdentifier),
+      grey_sufficientlyVisible(), nil);
+}
+
+// Matcher for the Language Settings's general Settings menu entry.
+id<GREYMatcher> LanguageSettingsButton() {
+  return grey_allOf(
+      ButtonWithAccessibilityLabelId(IDS_IOS_LANGUAGE_SETTINGS_TITLE),
+      grey_sufficientlyVisible(), nil);
+}
+
+// Matcher for the Add Language button in Language Settings's main page.
+id<GREYMatcher> AddLanguageButton() {
+  return grey_allOf(ButtonWithAccessibilityLabelId(
+                        IDS_IOS_LANGUAGE_SETTINGS_ADD_LANGUAGE_BUTTON_TITLE),
+                    grey_sufficientlyVisible(), nil);
+}
+
+// Matcher for the nav bar's back button to the Language Settings's main page.
+id<GREYMatcher> NavigationBarLanguageSettingsButton() {
+  return grey_allOf(
+      ButtonWithAccessibilityLabelId(IDS_IOS_LANGUAGE_SETTINGS_TITLE),
+      grey_kindOfClass([UIButton class]),
+      grey_ancestor(grey_kindOfClass([UINavigationBar class])),
+      grey_sufficientlyVisible(), nil);
+}
+
+// Matcher for the search bar.
+id<GREYMatcher> SearchBar() {
+  return grey_allOf(
+      grey_accessibilityID(kAddLanguageSearchControllerAccessibilityIdentifier),
+      grey_sufficientlyVisible(), nil);
+}
+
+// Matcher for the search bar's cancel button.
+id<GREYMatcher> SearchBarCancelButton() {
+  return grey_allOf(ButtonWithAccessibilityLabelId(IDS_APP_CANCEL),
+                    grey_kindOfClass([UIButton class]),
+                    grey_ancestor(grey_kindOfClass([UISearchBar class])),
+                    grey_sufficientlyVisible(), nil);
+}
+
+// Matcher for the search bar's scrim.
+id<GREYMatcher> SearchBarScrim() {
+  return grey_accessibilityID(kAddLanguageSearchScrimAccessibilityIdentifier);
+}
+
+// Matcher for a language entry with the given accessibility label. Matches a
+// button if |tappable| is true.
+id<GREYMatcher> LanguageEntry(NSString* label, BOOL tappable = YES) {
+  return grey_allOf(tappable ? ButtonWithAccessibilityLabel(label)
+                             : grey_accessibilityLabel(label),
+                    grey_sufficientlyVisible(), nil);
+}
+
+// Matcher for the "Never Translate" button in the Language Details page.
+id<GREYMatcher> NeverTranslateButton() {
+  return grey_allOf(ButtonWithAccessibilityLabelId(
+                        IDS_IOS_LANGUAGE_SETTINGS_NEVER_TRANSLATE_TITLE),
+                    grey_sufficientlyVisible(), nil);
+}
+
+// Matcher for the "Offer to Translate" button in the Language Details page.
+id<GREYMatcher> OfferToTranslateButton() {
+  return grey_allOf(ButtonWithAccessibilityLabelId(
+                        IDS_IOS_LANGUAGE_SETTINGS_OFFER_TO_TRANSLATE_TITLE),
+                    grey_sufficientlyVisible(), nil);
+}
+
+// Matcher for an element with or without the
+// UIAccessibilityTraitSelected accessibility trait depending on |selected|.
+id<GREYMatcher> ElementIsSelected(BOOL selected) {
+  return selected
+             ? grey_accessibilityTrait(UIAccessibilityTraitSelected)
+             : grey_not(grey_accessibilityTrait(UIAccessibilityTraitSelected));
+}
+
+}  // namespace
+
+@interface LanguageSettingsTestCase : ChromeTestCase
+@end
+
+@implementation LanguageSettingsTestCase {
+  base::test::ScopedFeatureList featureList_;
+
+  std::unique_ptr<translate::TranslatePrefs> translatePrefs_;
+}
+
+- (void)setUp {
+  [super setUp];
+
+  // Enable the Language Settings UI.
+  featureList_.InitAndEnableFeature(kLanguageSettings);
+
+  // Create TranslatePrefs.
+  ios::ChromeBrowserState* browserState = GetOriginalBrowserState();
+  translatePrefs_ =
+      ChromeIOSTranslateClient::CreateTranslatePrefs(browserState->GetPrefs());
+
+  // Make sure Translate is enabled.
+  SetBooleanUserPref(browserState, prefs::kOfferTranslateEnabled, YES);
+
+  // Make sure "en-US" is the only accept language.
+  std::vector<std::string> languages;
+  translatePrefs_->GetLanguageList(&languages);
+  for (const auto& language : languages) {
+    translatePrefs_->RemoveFromLanguageList(language);
+  }
+  translatePrefs_->AddToLanguageList("en-US", /*force_blocked=*/false);
+}
+
+- (void)tearDown {
+  // Go back to the general Settings menu and close it.
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
+      performAction:grey_tap()];
+
+  [super tearDown];
+}
+
+#pragma mark - Test Cases
+
+// Tests that the Language Settings pages are accessible.
+- (void)testAccessibility {
+  [ChromeEarlGreyUI openSettingsMenu];
+
+  // Test accessibility on the Language Settings's main page.
+  [ChromeEarlGreyUI tapSettingsMenuButton:LanguageSettingsButton()];
+  [[EarlGrey selectElementWithMatcher:LanguageSettingsTableView()]
+      assertWithMatcher:grey_notNil()];
+  VerifyAccessibilityForCurrentScreen();
+
+  // Test accessibility on the Add Language page.
+  [[EarlGrey selectElementWithMatcher:AddLanguageButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:AddLanguageTableView()]
+      assertWithMatcher:grey_notNil()];
+  VerifyAccessibilityForCurrentScreen();
+
+  // Navigate back.
+  [[EarlGrey selectElementWithMatcher:NavigationBarLanguageSettingsButton()]
+      performAction:grey_tap()];
+
+  // Test accessibility on the Language Details page.
+  NSString* languageEntryLabel = [NSString
+      stringWithFormat:kLanguageEntryThreeLabelsTemplate, kEnglishLabel,
+                       kEnglishLabel, kNeverTranslateLabel];
+  [[EarlGrey selectElementWithMatcher:LanguageEntry(languageEntryLabel)]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:LanguageDetailsTableView()]
+      assertWithMatcher:grey_notNil()];
+  VerifyAccessibilityForCurrentScreen();
+
+  // Navigate back.
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+}
+
+// Tests that the Translate Switch enables/disables Translate and the UI gets
+// updated accordingly.
+- (void)testTransalteSwitch {
+  [ChromeEarlGreyUI openSettingsMenu];
+
+  // Go to the Language Settings page.
+  [ChromeEarlGreyUI tapSettingsMenuButton:LanguageSettingsButton()];
+
+  // Verify that the Translate switch is on and enabled. Toggle it off.
+  [[EarlGrey
+      selectElementWithMatcher:SettingsSwitchCell(
+                                   kTranslateSwitchAccessibilityIdentifier, YES,
+                                   YES)]
+      performAction:TurnSettingsSwitchOn(NO)];
+
+  // Verify the prefs are up-to-date.
+  GREYAssertFalse(translatePrefs_->IsOfferTranslateEnabled(),
+                  @"Translate is expected to be disabled.");
+
+  // Verify that "English (United States)" does not feature a label indicating
+  // it is Translate-blocked and it is not tappable.
+  NSString* languageEntryLabel =
+      [NSString stringWithFormat:kLanguageEntryTwoLabelsTemplate, kEnglishLabel,
+                                 kEnglishLabel];
+  [[EarlGrey selectElementWithMatcher:LanguageEntry(languageEntryLabel,
+                                                    /*tappable=*/NO)]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify that the Translate switch is off and enabled. Toggle it on.
+  [[EarlGrey
+      selectElementWithMatcher:SettingsSwitchCell(
+                                   kTranslateSwitchAccessibilityIdentifier, NO,
+                                   YES)]
+      performAction:TurnSettingsSwitchOn(YES)];
+
+  // Verify the prefs are up-to-date.
+  GREYAssertTrue(translatePrefs_->IsOfferTranslateEnabled(),
+                 @"Translate is expected to be enabled.");
+
+  // Verify that "English (United States)" features a label indicating it is
+  // Translate-blocked.
+  languageEntryLabel = [NSString
+      stringWithFormat:kLanguageEntryThreeLabelsTemplate, kEnglishLabel,
+                       kEnglishLabel, kNeverTranslateLabel];
+  [[EarlGrey selectElementWithMatcher:LanguageEntry(languageEntryLabel)]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that the Add Language page allows filtering languages and adding them
+// to the list of accept languages.
+- (void)testAddLanguage {
+  [ChromeEarlGreyUI openSettingsMenu];
+
+  // Go to the Language Settings page.
+  [ChromeEarlGreyUI tapSettingsMenuButton:LanguageSettingsButton()];
+
+  // Go to the Add Language page.
+  [[EarlGrey selectElementWithMatcher:AddLanguageButton()]
+      performAction:grey_tap()];
+
+  // Verify the "Turkish" language entry is not currently visible.
+  NSString* languageEntryLabel =
+      [NSString stringWithFormat:kLanguageEntryTwoLabelsTemplate, kTurkishLabel,
+                                 kTurkishNativeLabel];
+  [[EarlGrey selectElementWithMatcher:LanguageEntry(languageEntryLabel)]
+      assertWithMatcher:grey_nil()];
+
+  // Focus the search bar.
+  [[EarlGrey selectElementWithMatcher:SearchBar()] performAction:grey_tap()];
+
+  // Verify the scrim is visible when search bar is focused but not typed in.
+  [[EarlGrey selectElementWithMatcher:SearchBarScrim()]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify the cancel button is visible and unfocuses search bar when tapped.
+  [[EarlGrey selectElementWithMatcher:SearchBarCancelButton()]
+      performAction:grey_tap()];
+
+  // Verify languages are searchable using their name in the current locale.
+  [[EarlGrey selectElementWithMatcher:SearchBar()] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SearchBar()]
+      performAction:grey_replaceText(kTurkishLabel)];
+
+  // Verify that scrim is not visible anymore.
+  [[EarlGrey selectElementWithMatcher:SearchBarScrim()]
+      assertWithMatcher:grey_nil()];
+
+  // Verify the "Turkish" language entry is visible.
+  [[EarlGrey selectElementWithMatcher:LanguageEntry(languageEntryLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Clear the search.
+  [[EarlGrey selectElementWithMatcher:SearchBar()]
+      performAction:grey_replaceText(@"")];
+
+  // Verify the scrim is visible again.
+  [[EarlGrey selectElementWithMatcher:SearchBarScrim()]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify languages are searchable using their name in their native locale.
+  [[EarlGrey selectElementWithMatcher:SearchBar()]
+      performAction:grey_replaceText(kTurkishNativeLabel)];
+
+  // Verify the "Turkish" language entry is visible and tap it.
+  [[EarlGrey selectElementWithMatcher:LanguageEntry(languageEntryLabel)]
+      performAction:grey_tap()];
+
+  // Verify that we navigated back to the Language Settings's main page.
+  [[EarlGrey selectElementWithMatcher:LanguageSettingsTableView()]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify "Turkish" was added to the list of accept languages.
+  languageEntryLabel = [NSString
+      stringWithFormat:kLanguageEntryThreeLabelsTemplate, kTurkishLabel,
+                       kTurkishNativeLabel, kNeverTranslateLabel];
+  [[EarlGrey selectElementWithMatcher:LanguageEntry(languageEntryLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify the prefs are up-to-date.
+  ios::ChromeBrowserState* browserState = GetOriginalBrowserState();
+  GREYAssertEqual("en-US,tr",
+                  browserState->GetPrefs()->GetString(kAcceptLanguages),
+                  @"Unexpected value for kAcceptLanguages pref");
+}
+
+// Tests that the Language Details page allows blocking/unblocking languages.
+- (void)testLanguageDetails {
+  [ChromeEarlGreyUI openSettingsMenu];
+
+  // Add "Turkish" to the list of accept languages.
+  translatePrefs_->AddToLanguageList("tr", /*force_blocked=*/false);
+  // Verify the prefs are up-to-date.
+  GREYAssertTrue(translatePrefs_->IsBlockedLanguage("tr"),
+                 @"Turkish is expected to be Translate-blocked");
+
+  // Go to the Language Settings page.
+  [ChromeEarlGreyUI tapSettingsMenuButton:LanguageSettingsButton()];
+
+  // Go to the "Turkish" Language Details page.
+  NSString* languageEntryLabel = [NSString
+      stringWithFormat:kLanguageEntryThreeLabelsTemplate, kTurkishLabel,
+                       kTurkishNativeLabel, kNeverTranslateLabel];
+  [[EarlGrey selectElementWithMatcher:LanguageEntry(languageEntryLabel)]
+      performAction:grey_tap()];
+
+  // Verify both options are enabled and "Never Translate" is selected.
+  [[EarlGrey selectElementWithMatcher:NeverTranslateButton()]
+      assertWithMatcher:grey_allOf(grey_enabled(), ElementIsSelected(YES),
+                                   nil)];
+  [[EarlGrey selectElementWithMatcher:OfferToTranslateButton()]
+      assertWithMatcher:grey_allOf(grey_enabled(), ElementIsSelected(NO), nil)];
+
+  // Tap the "Offer to Translate" button.
+  [[EarlGrey selectElementWithMatcher:OfferToTranslateButton()]
+      performAction:grey_tap()];
+
+  // Verify that we navigated back to the Language Settings's main page.
+  [[EarlGrey selectElementWithMatcher:LanguageSettingsTableView()]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify that "Turkish" is unblocked.
+  languageEntryLabel = [NSString
+      stringWithFormat:kLanguageEntryThreeLabelsTemplate, kTurkishLabel,
+                       kTurkishNativeLabel, kOfferToTranslateLabel];
+  [[EarlGrey selectElementWithMatcher:LanguageEntry(languageEntryLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify the prefs are up-to-date.
+  GREYAssertFalse(translatePrefs_->IsBlockedLanguage("tr"),
+                  @"Turkish should not be Translate-blocked");
+
+  // Go to the "Turkish" Language Details page.
+  [[EarlGrey selectElementWithMatcher:LanguageEntry(languageEntryLabel)]
+      performAction:grey_tap()];
+
+  // Verify both options are enabled and "Offer to Translate" is selected.
+  [[EarlGrey selectElementWithMatcher:NeverTranslateButton()]
+      assertWithMatcher:grey_allOf(grey_enabled(), ElementIsSelected(NO), nil)];
+  [[EarlGrey selectElementWithMatcher:OfferToTranslateButton()]
+      assertWithMatcher:grey_allOf(grey_enabled(), ElementIsSelected(YES),
+                                   nil)];
+
+  // Tap the "Never Translate" button.
+  [[EarlGrey selectElementWithMatcher:NeverTranslateButton()]
+      performAction:grey_tap()];
+
+  // Verify that we navigated back to the Language Settings's main page.
+  [[EarlGrey selectElementWithMatcher:LanguageSettingsTableView()]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify "Turkish" is blocked.
+  languageEntryLabel = [NSString
+      stringWithFormat:kLanguageEntryThreeLabelsTemplate, kTurkishLabel,
+                       kTurkishNativeLabel, kNeverTranslateLabel];
+  [[EarlGrey selectElementWithMatcher:LanguageEntry(languageEntryLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify the prefs are up-to-date.
+  GREYAssertTrue(translatePrefs_->IsBlockedLanguage("tr"),
+                 @"Turkish is expected to be Translate-blocked");
+}
+
+@end
diff --git a/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm
index 3add4835..e02acba 100644
--- a/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm
@@ -17,6 +17,7 @@
 #import "ios/chrome/browser/ui/settings/language/language_settings_commands.h"
 #import "ios/chrome/browser/ui/settings/language/language_settings_data_source.h"
 #import "ios/chrome/browser/ui/settings/language/language_settings_histograms.h"
+#import "ios/chrome/browser/ui/settings/language/language_settings_ui_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h"
@@ -31,13 +32,6 @@
 
 namespace {
 
-NSString* const kLanguageSettingsTableViewAccessibilityIdentifier =
-    @"kLanguageSettingsTableViewAccessibilityIdentifier";
-NSString* const kAddLanguageButtonAccessibilityIdentifier =
-    @"kAddLanguageButtonAccessibilityIdentifier";
-NSString* const kTranslateSwitchAccessibilityIdentifier =
-    @"kTranslateSwitchAccessibilityIdentifier";
-
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
   SectionIdentifierLanguages = kSectionIdentifierEnumZero,
   SectionIdentifierTranslate,
@@ -427,8 +421,6 @@
   TableViewTextItem* addLanguageItem =
       [[TableViewTextItem alloc] initWithType:ItemTypeAddLanguage];
   self.addLanguageItem = addLanguageItem;
-  addLanguageItem.accessibilityIdentifier =
-      kAddLanguageButtonAccessibilityIdentifier;
   addLanguageItem.text = l10n_util::GetNSString(
       IDS_IOS_LANGUAGE_SETTINGS_ADD_LANGUAGE_BUTTON_TITLE);
   addLanguageItem.textColor = UIColorFromRGB(kTableViewTextLabelColorBlue);
diff --git a/ios/chrome/browser/ui/settings/language/language_settings_ui_constants.h b/ios/chrome/browser/ui/settings/language/language_settings_ui_constants.h
new file mode 100644
index 0000000..bf97af2
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/language/language_settings_ui_constants.h
@@ -0,0 +1,23 @@
+// 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 IOS_CHROME_BROWSER_UI_SETTINGS_LANGUAGE_LANGUAGE_SETTINGS_UI_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_LANGUAGE_LANGUAGE_SETTINGS_UI_CONSTANTS_H_
+
+#import <Foundation/Foundation.h>
+
+// Accessibility identifier of the search bar.
+extern NSString* const kAddLanguageSearchControllerAccessibilityIdentifier;
+// Accessibility identifier of the search bar's scrim over the table view.
+extern NSString* const kAddLanguageSearchScrimAccessibilityIdentifier;
+// Accessibility identifier of the Add Language page's table view.
+extern NSString* const kAddLanguageTableViewAccessibilityIdentifier;
+// Accessibility identifier of the Language Details page's table view.
+extern NSString* const kLanguageDetailsTableViewAccessibilityIdentifier;
+// Accessibility identifier of the Language Setting's main page's table view.
+extern NSString* const kLanguageSettingsTableViewAccessibilityIdentifier;
+// Accessibility identifier of the Translate switch.
+extern NSString* const kTranslateSwitchAccessibilityIdentifier;
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_LANGUAGE_LANGUAGE_SETTINGS_UI_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/settings/language/language_settings_ui_constants.mm b/ios/chrome/browser/ui/settings/language/language_settings_ui_constants.mm
new file mode 100644
index 0000000..af85012
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/language/language_settings_ui_constants.mm
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/settings/language/language_settings_ui_constants.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+NSString* const kAddLanguageSearchControllerAccessibilityIdentifier =
+    @"kAddLanguageSearchControllerAccessibilityIdentifier";
+NSString* const kAddLanguageSearchScrimAccessibilityIdentifier =
+    @"kAddLanguageSearchScrimAccessibilityIdentifier";
+NSString* const kAddLanguageTableViewAccessibilityIdentifier =
+    @"kAddLanguageTableViewAccessibilityIdentifier";
+NSString* const kLanguageDetailsTableViewAccessibilityIdentifier =
+    @"kLanguageDetailsTableViewAccessibilityIdentifier";
+NSString* const kLanguageSettingsTableViewAccessibilityIdentifier =
+    @"kLanguageSettingsTableViewAccessibilityIdentifier";
+NSString* const kTranslateSwitchAccessibilityIdentifier =
+    @"kTranslateSwitchAccessibilityIdentifier";
diff --git a/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm b/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm
index ff7c15d..42b441a1 100644
--- a/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm
@@ -231,9 +231,8 @@
     TableViewURLCell* urlCell =
         base::mac::ObjCCastStrict<TableViewURLCell>(cell);
 
-    FaviconAttributes* cachedAttributes = nil;
     if (item.type == ItemTypePrepopulatedEngine) {
-      cachedAttributes = _faviconLoader->FaviconForPageUrl(
+      _faviconLoader->FaviconForPageUrl(
           engineItem.URL, kFaviconDesiredSizeInPoint, kFaviconMinSizeInPoint,
           /*fallback_to_google_server=*/YES, ^(FaviconAttributes* attributes) {
             // Only set favicon if the cell hasn't been reused.
@@ -243,7 +242,7 @@
             }
           });
     } else {
-      cachedAttributes = _faviconLoader->FaviconForIconUrl(
+      _faviconLoader->FaviconForIconUrl(
           engineItem.URL, kFaviconDesiredSizeInPoint, kFaviconMinSizeInPoint,
           ^(FaviconAttributes* attributes) {
             // Only set favicon if the cell hasn't been reused.
@@ -253,8 +252,6 @@
             }
           });
     }
-    DCHECK(cachedAttributes);
-    [urlCell.faviconView configureWithAttributes:cachedAttributes];
   }
   return cell;
 }
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
index 6513dc4..80749d5 100644
--- a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
@@ -389,6 +389,15 @@
 
 - (void)removeItemWithID:(NSString*)removedItemID
           selectedItemID:(NSString*)selectedItemID {
+  // Disable the reordering recognizer to cancel any in-flight reordering.  The
+  // DCHECK below ensures that the gesture is re-enabled after being cancelled
+  // in |-handleItemReorderingWithGesture:|.
+  if (self.itemReorderRecognizer.state != UIGestureRecognizerStatePossible &&
+      self.itemReorderRecognizer.state != UIGestureRecognizerStateCancelled) {
+    self.itemReorderRecognizer.enabled = NO;
+    DCHECK(self.itemReorderRecognizer.enabled);
+  }
+
   NSUInteger index = [self indexOfItemWithID:removedItemID];
   auto modelUpdates = ^{
     [self.items removeObjectAtIndex:index];
diff --git a/ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h b/ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h
index dfd4a68..4cc2d46 100644
--- a/ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h
+++ b/ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h
@@ -19,8 +19,8 @@
 // returned synchronously and the actual favicon returned asynchronously. In
 // another example, the image returned synchronously may be the actual favicon,
 // so there is no need to call the completion block.
-- (FaviconAttributes*)faviconForURL:(const GURL&)URL
-                         completion:(void (^)(FaviconAttributes*))completion;
+- (void)faviconForURL:(const GURL&)URL
+           completion:(void (^)(FaviconAttributes*))completion;
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TABLE_VIEW_TABLE_VIEW_FAVICON_DATA_SOURCE_H_
diff --git a/ios/chrome/common/app_group/BUILD.gn b/ios/chrome/common/app_group/BUILD.gn
index 7b6f303..88962a9 100644
--- a/ios/chrome/common/app_group/BUILD.gn
+++ b/ios/chrome/common/app_group/BUILD.gn
@@ -39,6 +39,22 @@
   ]
 }
 
+# This target will be included into application extensions and the list
+# of its dependencies must be kept as short as possible.
+source_set("command") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "app_group_command.h",
+    "app_group_command.mm",
+  ]
+
+  deps = [
+    ":app_group",
+    ":client",
+    "//base",
+  ]
+}
+
 source_set("main_app") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
diff --git a/ios/chrome/common/app_group/app_group_command.h b/ios/chrome/common/app_group/app_group_command.h
new file mode 100644
index 0000000..04f3785
--- /dev/null
+++ b/ios/chrome/common/app_group/app_group_command.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 IOS_CHROME_COMMON_APP_GROUP_APP_GROUP_COMMAND_H_
+#define IOS_CHROME_COMMON_APP_GROUP_APP_GROUP_COMMAND_H_
+
+#import <UIKit/UIKit.h>
+
+typedef void (^URLOpenerBlock)(NSURL* URL);
+
+// This class contains helper functions to prepare dictionary commands, place
+// them in the shared NSUserDefault, and launch Chrome to execute them.
+@interface AppGroupCommand : NSObject
+- (instancetype)initWithSourceApp:(NSString*)sourceApp
+                   URLOpenerBlock:(URLOpenerBlock)opener;
+
+// Prepares a command without argument.
+- (void)prepareWithCommandID:(NSString*)commandID;
+
+// Prepares a command to open |URL|.
+- (void)prepareToOpenURL:(NSURL*)URL;
+
+// Prepares a command to open an item in a list.
+// |URL| is the URL in the item, and |index| is the index of the item in the
+// list.
+- (void)prepareToOpenItem:(NSURL*)URL index:(NSNumber*)index;
+
+// Prepares a command to search for |text|.
+- (void)prepareToSearchText:(NSString*)text;
+
+// Prepares a command to search for |image|.
+- (void)prepareToSearchImage:(UIImage*)image;
+
+// Launches the main app and execute the rceiver.
+- (void)executeInApp;
+
+@end
+
+#endif  // IOS_CHROME_COMMON_APP_GROUP_APP_GROUP_COMMAND_H_
diff --git a/ios/chrome/common/app_group/app_group_command.mm b/ios/chrome/common/app_group/app_group_command.mm
new file mode 100644
index 0000000..10e674f
--- /dev/null
+++ b/ios/chrome/common/app_group/app_group_command.mm
@@ -0,0 +1,130 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/common/app_group/app_group_command.h"
+
+#import "base/mac/foundation_util.h"
+#import "base/strings/sys_string_conversions.h"
+#import "ios/chrome/common/app_group/app_group_constants.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Using GURL in the extension is not wanted as it includes ICU which makes the
+// extension binary much larger; therefore, ios/chrome/common/x_callback_url.h
+// cannot be used. This class makes a very basic use of x-callback-url, so no
+// full implementation is required.
+NSString* const kXCallbackURLHost = @"x-callback-url";
+
+void PutCommandInNSUserDefault(NSDictionary* command) {
+  NSUserDefaults* shared_defaults = app_group::GetGroupUserDefaults();
+  NSString* defaults_key =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandPreference);
+
+  [shared_defaults setObject:command forKey:defaults_key];
+  [shared_defaults synchronize];
+}
+}  // namespace
+
+@interface AppGroupCommand ()
+// The identifier of the extension that sent the order.
+@property(nonatomic, strong) NSString* sourceApp;
+
+// A block that can be used to open a URL.
+@property(nonatomic, strong) URLOpenerBlock opener;
+@end
+
+@implementation AppGroupCommand
+- (instancetype)initWithSourceApp:(NSString*)sourceApp
+                   URLOpenerBlock:(URLOpenerBlock)opener {
+  self = [super init];
+  if (self) {
+    _sourceApp = sourceApp;
+    _opener = opener;
+  }
+  return self;
+}
+
+- (NSMutableDictionary*)baseCommandDictionary:(NSString*)command {
+  NSString* timePrefKey =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandTimePreference);
+  NSString* appPrefKey =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandAppPreference);
+  NSString* commandPrefKey = base::SysUTF8ToNSString(
+      app_group::kChromeAppGroupCommandCommandPreference);
+
+  return [NSMutableDictionary dictionaryWithDictionary:@{
+    timePrefKey : [NSDate date],
+    appPrefKey : _sourceApp,
+    commandPrefKey : command,
+  }];
+}
+
+- (void)prepareWithCommandID:(NSString*)commandID {
+  PutCommandInNSUserDefault([self baseCommandDictionary:commandID]);
+}
+
+- (void)prepareToOpenURL:(NSURL*)URL {
+  NSMutableDictionary* command = [self
+      baseCommandDictionary:base::SysUTF8ToNSString(
+                                app_group::kChromeAppGroupOpenURLCommand)];
+  NSString* textPrefKey =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandTextPreference);
+  command[textPrefKey] = URL.absoluteString;
+  PutCommandInNSUserDefault(command);
+}
+
+- (void)prepareToSearchText:(NSString*)text {
+  NSMutableDictionary* command = [self
+      baseCommandDictionary:base::SysUTF8ToNSString(
+                                app_group::kChromeAppGroupSearchTextCommand)];
+  NSString* textPrefKey =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandTextPreference);
+  command[textPrefKey] = text;
+  PutCommandInNSUserDefault(command);
+}
+
+- (void)prepareToSearchImage:(UIImage*)image {
+  NSMutableDictionary* command = [self
+      baseCommandDictionary:base::SysUTF8ToNSString(
+                                app_group::kChromeAppGroupSearchImageCommand)];
+  NSString* dataPrefKey =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandDataPreference);
+  command[dataPrefKey] = UIImageJPEGRepresentation(image, 1.0);
+  PutCommandInNSUserDefault(command);
+}
+
+- (void)prepareToOpenItem:(NSURL*)URL index:(NSNumber*)index {
+  NSMutableDictionary* command = [self
+      baseCommandDictionary:base::SysUTF8ToNSString(
+                                app_group::kChromeAppGroupOpenURLCommand)];
+  NSString* textPrefKey =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandTextPreference);
+  NSString* indexKey =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandIndexPreference);
+  command[textPrefKey] = URL.absoluteString;
+  command[indexKey] = index;
+  PutCommandInNSUserDefault(command);
+}
+
+- (void)executeInApp {
+  NSString* scheme = base::mac::ObjCCast<NSString>([[NSBundle mainBundle]
+      objectForInfoDictionaryKey:@"KSChannelChromeScheme"]);
+  if (!scheme)
+    return;
+
+  NSURLComponents* urlComponents = [NSURLComponents new];
+  urlComponents.scheme = scheme;
+  urlComponents.host = kXCallbackURLHost;
+  urlComponents.path = [@"/"
+      stringByAppendingString:base::SysUTF8ToNSString(
+                                  app_group::kChromeAppGroupXCallbackCommand)];
+
+  NSURL* openURL = [urlComponents URL];
+  _opener(openURL);
+}
+
+@end
diff --git a/ios/chrome/common/app_group/app_group_constants.h b/ios/chrome/common/app_group/app_group_constants.h
index f6045e7..e03ede1 100644
--- a/ios/chrome/common/app_group/app_group_constants.h
+++ b/ios/chrome/common/app_group/app_group_constants.h
@@ -24,6 +24,7 @@
 enum ShareExtensionItemType {
   READING_LIST_ITEM = 0,
   BOOKMARK_ITEM,
+  OPEN_IN_CHROME_ITEM
 };
 
 // The x-callback-url indicating that an application in the group requires a
@@ -116,6 +117,7 @@
 extern NSString* const kOpenCommandSourceTodayExtension;
 extern NSString* const kOpenCommandSourceContentExtension;
 extern NSString* const kOpenCommandSourceSearchExtension;
+extern NSString* const kOpenCommandSourceShareExtension;
 
 // The value of the key for the sharedDefaults used by the Content Widget.
 extern NSString* const kSuggestedItems;
diff --git a/ios/chrome/common/app_group/app_group_constants.mm b/ios/chrome/common/app_group/app_group_constants.mm
index 5ce9375..5b039eb 100644
--- a/ios/chrome/common/app_group/app_group_constants.mm
+++ b/ios/chrome/common/app_group/app_group_constants.mm
@@ -57,6 +57,7 @@
 NSString* const kOpenCommandSourceTodayExtension = @"ChromeTodayExtension";
 NSString* const kOpenCommandSourceContentExtension = @"ChromeContentExtension";
 NSString* const kOpenCommandSourceSearchExtension = @"ChromeSearchExtension";
+NSString* const kOpenCommandSourceShareExtension = @"ChromeShareExtension";
 
 NSString* const kSuggestedItems = @"SuggestedItems";
 
diff --git a/ios/chrome/content_widget_extension/BUILD.gn b/ios/chrome/content_widget_extension/BUILD.gn
index 6c7be6d..c718412 100644
--- a/ios/chrome/content_widget_extension/BUILD.gn
+++ b/ios/chrome/content_widget_extension/BUILD.gn
@@ -8,8 +8,8 @@
 import("//build/mac/tweak_info_plist.gni")
 import("//ios/build/chrome_build.gni")
 import("//ios/build/config.gni")
-import("//tools/grit/repack.gni")
 import("//ios/chrome/tools/strings/generate_localizable_strings.gni")
+import("//tools/grit/repack.gni")
 
 tweak_info_plist("tweak_info_plist") {
   info_plist = "Info.plist"
@@ -54,6 +54,7 @@
     "//base",
     "//ios/chrome/common:common_extension",
     "//ios/chrome/common/app_group",
+    "//ios/chrome/common/app_group:command",
     "//ios/chrome/common/favicon",
     "//ios/chrome/common/ntp_tile",
     "//ios/chrome/common/ui_util",
diff --git a/ios/chrome/content_widget_extension/content_widget_view_controller.mm b/ios/chrome/content_widget_extension/content_widget_view_controller.mm
index 15cd316..5b694c15 100644
--- a/ios/chrome/content_widget_extension/content_widget_view_controller.mm
+++ b/ios/chrome/content_widget_extension/content_widget_view_controller.mm
@@ -6,6 +6,7 @@
 
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
+#include "ios/chrome/common/app_group/app_group_command.h"
 #include "ios/chrome/common/app_group/app_group_constants.h"
 #include "ios/chrome/common/app_group/app_group_metrics.h"
 #import "ios/chrome/common/ntp_tile/ntp_tile.h"
@@ -164,47 +165,13 @@
 }
 
 - (void)openURL:(NSURL*)URL {
-  NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
-  NSString* defaultsKey =
-      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandPreference);
-
-  NSString* timePrefKey =
-      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandTimePreference);
-  NSString* appPrefKey =
-      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandAppPreference);
-  NSString* commandPrefKey = base::SysUTF8ToNSString(
-      app_group::kChromeAppGroupCommandCommandPreference);
-  NSString* textPrefKey =
-      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandTextPreference);
-  NSString* indexKey =
-      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandIndexPreference);
-
-  NSDictionary* commandDict = @{
-    timePrefKey : [NSDate date],
-    appPrefKey : app_group::kOpenCommandSourceContentExtension,
-    commandPrefKey :
-        base::SysUTF8ToNSString(app_group::kChromeAppGroupOpenURLCommand),
-    textPrefKey : URL.absoluteString,
-    indexKey : [NSNumber numberWithInt:[self.sites objectForKey:URL].position]
-  };
-
-  [sharedDefaults setObject:commandDict forKey:defaultsKey];
-  [sharedDefaults synchronize];
-
-  NSString* scheme = base::mac::ObjCCast<NSString>([[NSBundle mainBundle]
-      objectForInfoDictionaryKey:@"KSChannelChromeScheme"]);
-  if (!scheme)
-    return;
-
-  NSURLComponents* urlComponents = [NSURLComponents new];
-  urlComponents.scheme = scheme;
-  urlComponents.host = kXCallbackURLHost;
-  urlComponents.path = [@"/"
-      stringByAppendingString:base::SysUTF8ToNSString(
-                                  app_group::kChromeAppGroupXCallbackCommand)];
-
-  NSURL* openURL = [urlComponents URL];
-  [self.extensionContext openURL:openURL completionHandler:nil];
+  AppGroupCommand* command = [[AppGroupCommand alloc]
+      initWithSourceApp:app_group::kOpenCommandSourceContentExtension
+         URLOpenerBlock:^(NSURL* openURL) {
+           [self.extensionContext openURL:openURL completionHandler:nil];
+         }];
+  [command prepareToOpenURL:URL];
+  [command executeInApp];
 }
 
 @end
diff --git a/ios/chrome/search_widget_extension/BUILD.gn b/ios/chrome/search_widget_extension/BUILD.gn
index cb65f7d..84efa42 100644
--- a/ios/chrome/search_widget_extension/BUILD.gn
+++ b/ios/chrome/search_widget_extension/BUILD.gn
@@ -63,6 +63,7 @@
     "//components/open_from_clipboard:open_from_clipboard_impl",
     "//ios/chrome/common:common_extension",
     "//ios/chrome/common/app_group",
+    "//ios/chrome/common/app_group:command",
     "//ios/chrome/common/ui_util",
   ]
 
diff --git a/ios/chrome/search_widget_extension/search_widget_view_controller.mm b/ios/chrome/search_widget_extension/search_widget_view_controller.mm
index 548eaf3..4b3d469 100644
--- a/ios/chrome/search_widget_extension/search_widget_view_controller.mm
+++ b/ios/chrome/search_widget_extension/search_widget_view_controller.mm
@@ -7,6 +7,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/open_from_clipboard/clipboard_recent_content_impl_ios.h"
+#include "ios/chrome/common/app_group/app_group_command.h"
 #include "ios/chrome/common/app_group/app_group_constants.h"
 #include "ios/chrome/common/app_group/app_group_field_trial_version.h"
 #include "ios/chrome/common/app_group/app_group_metrics.h"
@@ -19,14 +20,6 @@
 #error "This file requires ARC support."
 #endif
 
-namespace {
-// Using GURL in the extension is not wanted as it includes ICU which makes the
-// extension binary much larger; therefore, ios/chrome/common/x_callback_url.h
-// cannot be used. This class makes a very basic use of x-callback-url, so no
-// full implementation is required.
-NSString* const kXCallbackURLHost = @"x-callback-url";
-}  // namespace
-
 @interface SearchWidgetViewController ()<SearchWidgetViewActionTarget>
 @property(nonatomic, weak) SearchWidgetView* widgetView;
 @property(nonatomic, strong, nullable) NSString* copiedText;
@@ -38,6 +31,7 @@
 // Whether the current default search engine supports search by image
 @property(nonatomic, assign) BOOL supportsSearchByImage;
 @property(nonatomic, readonly) BOOL copiedContentBehaviorEnabled;
+@property(nonatomic, strong) AppGroupCommand* command;
 
 @end
 
@@ -52,6 +46,11 @@
              userDefaults:app_group::GetGroupUserDefaults()
                  delegate:nil];
     _copiedContentType = CopiedContentTypeNone;
+    _command = [[AppGroupCommand alloc]
+        initWithSourceApp:app_group::kOpenCommandSourceSearchExtension
+           URLOpenerBlock:^(NSURL* openURL) {
+             [self.extensionContext openURL:openURL completionHandler:nil];
+           }];
   }
   return self;
 }
@@ -213,38 +212,32 @@
 
 - (void)openCopiedContent:(id)sender {
   DCHECK([self verifyCopiedContentType]);
-  NSString* command;
-  NSData* imageData;
   switch (self.copiedContentType) {
     case CopiedContentTypeURL:
-      command =
-          base::SysUTF8ToNSString(app_group::kChromeAppGroupOpenURLCommand);
+      [self.command prepareToOpenURL:[NSURL URLWithString:self.copiedText]];
       break;
     case CopiedContentTypeString:
-      command =
-          base::SysUTF8ToNSString(app_group::kChromeAppGroupSearchTextCommand);
+      [self.command prepareToSearchText:self.copiedText];
       break;
     case CopiedContentTypeImage: {
-      command =
-          base::SysUTF8ToNSString(app_group::kChromeAppGroupSearchImageCommand);
-
       // Resize image before converting to NSData so we can store less data.
       UIImage* resizedImage = ResizeImageForSearchByImage(self.copiedImage);
-      imageData = UIImageJPEGRepresentation(resizedImage, 1.0);
+      [self.command prepareToSearchImage:resizedImage];
       break;
     }
     case CopiedContentTypeNone:
       NOTREACHED();
       return;
   }
-  [self openAppWithCommand:command text:self.copiedText imageData:imageData];
+  [self.command executeInApp];
 }
 
 #pragma mark - internal
 
 // Opens the main application with the given |command|.
 - (void)openAppWithCommand:(NSString*)command {
-  return [self openAppWithCommand:command text:nil imageData:nil];
+  [self.command prepareWithCommandID:command];
+  [self.command executeInApp];
 }
 
 // Register a display of the widget in the app_group NSUserDefaults.
@@ -258,69 +251,6 @@
                       forKey:app_group::kSearchExtensionDisplayCount];
 }
 
-// Opens the main application with the given |command|, |text|, and |image|.
-- (void)openAppWithCommand:(NSString*)command
-                      text:(NSString*)text
-                 imageData:(NSData*)imageData {
-  NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
-  NSString* defaultsKey =
-      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandPreference);
-  [sharedDefaults
-      setObject:[SearchWidgetViewController dictForCommand:command
-                                                      text:text
-                                                 imageData:imageData]
-         forKey:defaultsKey];
-  [sharedDefaults synchronize];
-
-  NSString* scheme = base::mac::ObjCCast<NSString>([[NSBundle mainBundle]
-      objectForInfoDictionaryKey:@"KSChannelChromeScheme"]);
-  if (!scheme)
-    return;
-
-  NSURLComponents* urlComponents = [NSURLComponents new];
-  urlComponents.scheme = scheme;
-  urlComponents.host = kXCallbackURLHost;
-  urlComponents.path = [NSString
-      stringWithFormat:@"/%@", base::SysUTF8ToNSString(
-                                   app_group::kChromeAppGroupXCallbackCommand)];
-
-  NSURL* openURL = [urlComponents URL];
-  [self.extensionContext openURL:openURL completionHandler:nil];
-}
-
-// Returns the dictionary of commands to pass via user defaults to open the main
-// application for a given |command| and optional |text| and |image|.
-+ (NSDictionary*)dictForCommand:(NSString*)command
-                           text:(NSString*)text
-                      imageData:(NSData*)imageData {
-  NSString* timePrefKey =
-      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandTimePreference);
-  NSString* appPrefKey =
-      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandAppPreference);
-  NSString* commandPrefKey = base::SysUTF8ToNSString(
-      app_group::kChromeAppGroupCommandCommandPreference);
-
-  NSMutableDictionary* baseKeys = [@{
-    timePrefKey : [NSDate date],
-    appPrefKey : app_group::kOpenCommandSourceSearchExtension,
-    commandPrefKey : command,
-  } mutableCopy];
-
-  if (text) {
-    NSString* TextPrefKey = base::SysUTF8ToNSString(
-        app_group::kChromeAppGroupCommandTextPreference);
-    baseKeys[TextPrefKey] = text;
-  }
-
-  if (imageData) {
-    NSString* DataPrefKey = base::SysUTF8ToNSString(
-        app_group::kChromeAppGroupCommandDataPreference);
-    baseKeys[DataPrefKey] = imageData;
-  }
-
-  return baseKeys;
-}
-
 // Sets the copied content type. |copiedText| should be provided if the content
 // type requires textual data, otherwise it should be nil. Likewise,
 // |copiedImage| should be provided if the content type requires image data.
diff --git a/ios/chrome/share_extension/BUILD.gn b/ios/chrome/share_extension/BUILD.gn
index 3ea0d30..267003a 100644
--- a/ios/chrome/share_extension/BUILD.gn
+++ b/ios/chrome/share_extension/BUILD.gn
@@ -52,6 +52,7 @@
     "//base",
     "//ios/chrome/common/app_group",
     "//ios/chrome/common/app_group:client",
+    "//ios/chrome/common/app_group:command",
   ]
   bundle_deps_filter = [ "//third_party/icu:icudata" ]
 
diff --git a/ios/chrome/share_extension/share_extension_localize_strings_config.plist b/ios/chrome/share_extension/share_extension_localize_strings_config.plist
index 6fc22d7a..8213e4e 100644
--- a/ios/chrome/share_extension/share_extension_localize_strings_config.plist
+++ b/ios/chrome/share_extension/share_extension_localize_strings_config.plist
@@ -15,6 +15,7 @@
 			<array>
 				<string>IDS_IOS_ADD_READING_LIST_SHARE_EXTENSION</string>
 				<string>IDS_IOS_ADD_BOOKMARKS_SHARE_EXTENSION</string>
+				<string>IDS_IOS_OPEN_IN_CHROME_SHARE_EXTENSION</string>
 				<string>IDS_IOS_ADDED_ITEM_SHARE_EXTENSION</string>
 				<string>IDS_IOS_ERROR_MESSAGE_SHARE_EXTENSION</string>
 				<string>IDS_IOS_OK_BUTTON_SHARE_EXTENSION</string>
diff --git a/ios/chrome/share_extension/share_extension_view.h b/ios/chrome/share_extension/share_extension_view.h
index ce0f54e..6cd3a950 100644
--- a/ios/chrome/share_extension/share_extension_view.h
+++ b/ios/chrome/share_extension/share_extension_view.h
@@ -21,6 +21,10 @@
 
 // Called when the user presses the "Add to Reading List" button.
 - (void)shareExtensionViewDidSelectAddToReadingList:(id)sender;
+
+// Called when the user presses the "Open in Chrome" button.
+- (void)shareExtensionViewDidSelectOpenInChrome:(id)sender;
+
 @end
 
 // This is the view for the ShareExtensionController. It shows the shared
diff --git a/ios/chrome/share_extension/share_extension_view.mm b/ios/chrome/share_extension/share_extension_view.mm
index 3a0e146..86c31ae 100644
--- a/ios/chrome/share_extension/share_extension_view.mm
+++ b/ios/chrome/share_extension/share_extension_view.mm
@@ -146,11 +146,18 @@
         [self buttonWithTitle:addToBookmarksTitle
                      selector:@selector(addToBookmarksPressed:)];
 
+    NSString* openInChromeTitle = NSLocalizedString(
+        @"IDS_IOS_OPEN_IN_CHROME_SHARE_EXTENSION",
+        @"The Open in Chrome button text in share extension.");
+    UIButton* openButton =
+        [self buttonWithTitle:openInChromeTitle
+                     selector:@selector(openInChromePressed:)];
+
     UIStackView* contentStack = [[UIStackView alloc] initWithArrangedSubviews:@[
       [self navigationBar], [self dividerViewWithVibrancy:vibrancyEffect],
       [self sharedItemView], [self dividerViewWithVibrancy:vibrancyEffect],
       self.readingListButton, [self dividerViewWithVibrancy:vibrancyEffect],
-      bookmarksButton
+      bookmarksButton, [self dividerViewWithVibrancy:vibrancyEffect], openButton
     ]];
     [contentStack setAxis:UILayoutConstraintAxisVertical];
     [[blurringView contentView] addSubview:contentStack];
@@ -349,6 +356,14 @@
               }];
 }
 
+- (void)openInChromePressed:(UIButton*)sender {
+  if (_dismissed) {
+    return;
+  }
+  _dismissed = YES;
+  [_target shareExtensionViewDidSelectOpenInChrome:sender];
+}
+
 - (void)animateButtonPressed:(UIButton*)sender
               withCompletion:(void (^)(void))completion {
   NSString* addedString =
diff --git a/ios/chrome/share_extension/share_view_controller.mm b/ios/chrome/share_extension/share_view_controller.mm
index 9ac8f7ae..ae116d68 100644
--- a/ios/chrome/share_extension/share_view_controller.mm
+++ b/ios/chrome/share_extension/share_view_controller.mm
@@ -8,6 +8,7 @@
 
 #import "base/ios/block_types.h"
 #import "base/mac/foundation_util.h"
+#import "ios/chrome/common/app_group/app_group_command.h"
 #import "ios/chrome/common/app_group/app_group_constants.h"
 #import "ios/chrome/share_extension/share_extension_view.h"
 #import "ios/chrome/share_extension/ui_util.h"
@@ -348,6 +349,30 @@
                 }];
 }
 
+- (void)shareExtensionViewDidSelectOpenInChrome:(id)sender {
+  UIResponder* responder = self;
+  while ((responder = responder.nextResponder)) {
+    if ([responder respondsToSelector:@selector(openURL:)]) {
+      AppGroupCommand* command = [[AppGroupCommand alloc]
+          initWithSourceApp:app_group::kOpenCommandSourceShareExtension
+             URLOpenerBlock:^(NSURL* openURL) {
+               [responder performSelector:@selector(openURL:)
+                               withObject:openURL];
+             }];
+      [command prepareToOpenURL:_shareURL];
+      [command executeInApp];
+      break;
+    }
+  }
+  [self queueActionItemURL:_shareURL
+                     title:_shareTitle
+                    action:app_group::OPEN_IN_CHROME_ITEM
+                    cancel:NO
+                completion:^{
+                  [self dismissAndReturnItem:_shareItem];
+                }];
+}
+
 - (void)shareExtensionView:(id)sender
                typeChanged:(app_group::ShareExtensionItemType)type {
   [self setItemType:type];
diff --git a/ios/chrome/share_extension/strings/ios_share_extension_strings.grd b/ios/chrome/share_extension/strings/ios_share_extension_strings.grd
index a99a7dd..c52b27d 100644
--- a/ios/chrome/share_extension/strings/ios_share_extension_strings.grd
+++ b/ios/chrome/share_extension/strings/ios_share_extension_strings.grd
@@ -136,6 +136,9 @@
       <message name="IDS_IOS_ADD_BOOKMARKS_SHARE_EXTENSION" desc="Share extension button to add to bookmarks [Length: 30em]" meaning="Button title [Length: 30em]">
         Add to Bookmarks 
       </message>
+      <message name="IDS_IOS_OPEN_IN_CHROME_SHARE_EXTENSION" desc="Share extension button to open link in Chrome [Length: 30em]" meaning="Button title [Length: 30em]">
+        Open in Chrome
+      </message>
       <message name="IDS_IOS_ADDED_ITEM_SHARE_EXTENSION" desc="Label of the button 'add to' after it has been pressed confirming the action to the user.">
         Added
       </message>
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index ed64a50..515ca912 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -67,6 +67,7 @@
     "//ios/chrome/browser/ui/settings/autofill:eg_tests",
     "//ios/chrome/browser/ui/settings/clear_browsing_data:eg_tests",
     "//ios/chrome/browser/ui/settings/google_services:eg_tests",
+    "//ios/chrome/browser/ui/settings/language:eg_tests",
     "//ios/chrome/browser/ui/settings/password:eg_tests",
   ]
 }
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index 853ca8c..5a32a57 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -283,6 +283,9 @@
 
 // Returns a matcher for the Clear Browsing Data button in the History UI.
 id<GREYMatcher> HistoryClearBrowsingDataButton();
+
+// Returns a matcher for "Open In..." button.
+id<GREYMatcher> OpenInButton();
 }
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_MATCHERS_H_
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index 7fd791c..77cfc1d 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -361,4 +361,8 @@
   return [ChromeMatchersAppInterface historyClearBrowsingDataButton];
 }
 
+id<GREYMatcher> OpenInButton() {
+  return [ChromeMatchersAppInterface openInButton];
+}
+
 }  // namespace chrome_test_util
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
index 34b1a213..bc641181 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
@@ -11,6 +11,8 @@
 
 // Helper class to return matchers for EG tests.  These helpers are compiled
 // into the app binary and can be called from either app or test code.
+// All calls of grey_... involve the App process, so it's more efficient to
+// define the matchers in the app process.
 @interface ChromeMatchersAppInterface : NSObject
 
 // Matcher for element with accessibility label corresponding to |label| and
@@ -286,6 +288,9 @@
 // Returns a matcher for the Clear Browsing Data button in the History UI.
 + (id<GREYMatcher>)historyClearBrowsingDataButton;
 
+// Returns a matcher for "Open In..." button.
++ (id<GREYMatcher>)openInButton;
+
 @end
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_MATCHERS_APP_INTERFACE_H_
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index 56025be..9506310 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -588,4 +588,9 @@
   return grey_accessibilityID(kHistoryToolbarClearBrowsingButtonIdentifier);
 }
 
++ (id<GREYMatcher>)openInButton {
+  return [ChromeMatchersAppInterface
+      buttonWithAccessibilityLabelID:IDS_IOS_OPEN_IN];
+}
+
 @end
diff --git a/ios/chrome/test/earl_grey/chrome_test_case.mm b/ios/chrome/test/earl_grey/chrome_test_case.mm
index 3e44e21..9acca1e 100644
--- a/ios/chrome/test/earl_grey/chrome_test_case.mm
+++ b/ios/chrome/test/earl_grey/chrome_test_case.mm
@@ -16,6 +16,7 @@
 #import "ios/testing/earl_grey/coverage_utils.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 #import "ios/web/public/test/http_server/http_server.h"
+#include "net/test/embedded_test_server/default_handlers.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -218,8 +219,11 @@
 - (net::EmbeddedTestServer*)testServer {
   if (!_testServer) {
     _testServer = std::make_unique<net::EmbeddedTestServer>();
-    _testServer->AddDefaultHandlers(base::FilePath(
-        FILE_PATH_LITERAL("ios/testing/data/http_server_files/")));
+    NSString* bundlePath = [NSBundle bundleForClass:[self class]].resourcePath;
+    _testServer->ServeFilesFromDirectory(
+        base::FilePath(base::SysNSStringToUTF8(bundlePath))
+            .AppendASCII("ios/testing/data/http_server_files/"));
+    net::test_server::RegisterDefaultHandlers(_testServer.get());
   }
   return _testServer.get();
 }
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index d59c1ef..93e2180 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -41,6 +41,7 @@
   deps = [
     "//ios/chrome/browser/ui/activity_services:eg2_tests",
     "//ios/chrome/browser/ui/download:eg2_tests",
+    "//ios/chrome/browser/ui/open_in:eg2_tests",
     "//ios/chrome/browser/ui/toolbar:eg2_tests",
   ]
 }
diff --git a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
index a2ddfd5..95cd4cf 100644
--- a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
@@ -434,6 +434,9 @@
     private static MediaDrmBridge create(byte[] schemeUUID, String securityOrigin,
             String securityLevel, boolean requiresMediaCrypto, long nativeMediaDrmBridge,
             long nativeMediaDrmStorageBridge) {
+        Log.i(TAG, "Create MediaDrmBridge with level %s and origin %s", securityLevel,
+                securityOrigin);
+
         UUID cryptoScheme = getUUIDFromBytes(schemeUUID);
         if (cryptoScheme == null || !MediaDrm.isCryptoSchemeSupported(cryptoScheme)) {
             return null;
@@ -482,6 +485,7 @@
      */
     private boolean setOrigin(String origin) {
         assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
+        Log.i(TAG, "Set origin: %s", origin);
 
         if (!isWidevine()) {
             Log.d(TAG, "Property " + ORIGIN + " isn't supported");
diff --git a/media/base/android/media_drm_bridge.cc b/media/base/android/media_drm_bridge.cc
index 483ee888..ad799d0 100644
--- a/media/base/android/media_drm_bridge.cc
+++ b/media/base/android/media_drm_bridge.cc
@@ -888,9 +888,6 @@
       ConvertUTF8ToJavaString(env, security_level_str);
 
   bool use_origin_isolated_storage =
-      // TODO(yucliu): Remove the check once persistent storage is fully
-      // supported and check if origin is valid.
-      base::FeatureList::IsEnabled(kMediaDrmPersistentLicense) &&
       // Per-origin provisioning must be supported for origin isolated storage.
       IsPerOriginProvisioningSupported() &&
       // origin id can be empty when MediaDrmBridge is created by
diff --git a/media/base/android/media_drm_bridge_factory.cc b/media/base/android/media_drm_bridge_factory.cc
index e8ce4c9..5899aef 100644
--- a/media/base/android/media_drm_bridge_factory.cc
+++ b/media/base/android/media_drm_bridge_factory.cc
@@ -70,17 +70,9 @@
   // MediaDrmStorage may be lazy created in MediaDrmStorageBridge.
   storage_ = std::make_unique<MediaDrmStorageBridge>();
 
-  // IsPersistentLicenseTypeSupported() checks that per-origin provisioning is
-  // supported and that persistent licenses are supported. For WebView we
-  // disable persistent licenses as access to a profile is not connected, and
-  // thus persistent licenses can't be saved. As saving the origin IDs also
-  // requires data to be saved in the profile, using the persistent license
-  // check here to ensure data can be saved.
-  // TODO(crbug.com/493521). Once WebView has access to a profile to save data,
-  // switch this to calling IsPerOriginProvisioningSupported().
-  if (!MediaDrmBridge::IsPersistentLicenseTypeSupported(key_system)) {
-    // Per-origin provisioning isn't supported, so proceed without
-    // specifying an origin ID.
+  if (!MediaDrmBridge::IsPerOriginProvisioningSupported()) {
+    // Per-origin provisioning isn't supported, so proceed without specifying an
+    // origin ID.
     CreateMediaDrmBridge("");
     return;
   }
diff --git a/remoting/host/win/BUILD.gn b/remoting/host/win/BUILD.gn
index 3f0d71a..cccfd8dd 100644
--- a/remoting/host/win/BUILD.gn
+++ b/remoting/host/win/BUILD.gn
@@ -118,7 +118,7 @@
 
   if (is_clang) {
     # TODO(thakis): Remove this once midl.exe no longer produces nonstandard
-    # C++ (see the enums in com_imported_mstscax.tlh).
+    # C++ (see the enums in com_imported_mstscax.h).
     cflags = [ "-Wno-microsoft-enum-forward-reference" ]
   }
 
diff --git a/remoting/host/win/com_imported_mstscax.tlh b/remoting/host/win/com_imported_mstscax.h
similarity index 100%
rename from remoting/host/win/com_imported_mstscax.tlh
rename to remoting/host/win/com_imported_mstscax.h
diff --git a/remoting/host/win/rdp_client_window.h b/remoting/host/win/rdp_client_window.h
index 5d6e30c5..edbc284 100644
--- a/remoting/host/win/rdp_client_window.h
+++ b/remoting/host/win/rdp_client_window.h
@@ -19,7 +19,7 @@
 #include "remoting/host/screen_resolution.h"
 // The following header was generated by Visual Studio. We had to check it in
 // due to a bug in VS2013. See crbug.com/318952 for details.
-#include "remoting/host/win/com_imported_mstscax.tlh"
+#include "remoting/host/win/com_imported_mstscax.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
 
 namespace remoting {
diff --git a/services/viz/privileged/interfaces/compositing/display_private.mojom b/services/viz/privileged/interfaces/compositing/display_private.mojom
index deb327b..cd8dd80 100644
--- a/services/viz/privileged/interfaces/compositing/display_private.mojom
+++ b/services/viz/privileged/interfaces/compositing/display_private.mojom
@@ -94,10 +94,10 @@
   [EnableIf=use_x11]
   DidCompleteSwapWithNewSize(gfx.mojom.Size size);
 
-  // Notifies that context creation failed. On Android we can't fall back to
-  // SW in these cases, so we need to handle this specifically.
+  // Notifies the client of the result of context creation attempt. On Android we can't
+  // fall back to SW in failure cases, so we need to handle this specifically.
   [EnableIf=is_android]
-  OnFatalOrSurfaceContextCreationFailure(gpu.mojom.ContextResult result);
+  OnContextCreationResult(gpu.mojom.ContextResult result);
 
   [EnableIf=is_android]
   SetPreferredRefreshRate(float refresh_rate);
diff --git a/testing/buildbot/chromium.chrome.json b/testing/buildbot/chromium.chrome.json
index 34a9df4..f395ee1 100644
--- a/testing/buildbot/chromium.chrome.json
+++ b/testing/buildbot/chromium.chrome.json
@@ -2175,12 +2175,7 @@
         },
         "name": "chrome_sizes",
         "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "pool": "chrome.tests"
-            }
-          ]
+          "can_use_on_swarming_builders": false
         }
       }
     ]
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index f29d782b..bff487c 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -372,6 +372,17 @@
       }
     }
   },
+  'chrome_sizes': {
+    'modifications': {
+      # TODO(crbug.com/961548): Swarm this if/when there's available Mac
+      # capacity in chrome-swarming's general test pool.
+      'mac-google-rel': {
+        'swarming': {
+          'can_use_on_swarming_builders': False,
+        },
+      },
+    },
+  },
   'chromedriver_py_tests': {
     'remove_from': [
       # Timeout happens sometimes (crbug.com/951799).
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc b/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
index 096a4c1..df646b2 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
@@ -170,7 +170,8 @@
   // Visitor overrides.
   void Visit(const TraceWrapperV8Reference<v8::Value>&) final;
   void Visit(void*, TraceDescriptor) final;
-  void VisitBackingStoreStrongly(void* object,
+  void VisitBackingStoreStrongly(const char*,
+                                 void* object,
                                  void** object_slot,
                                  TraceDescriptor desc) final;
 
@@ -179,12 +180,13 @@
                  void** object_slot,
                  TraceDescriptor desc,
                  WeakCallback callback) final {}
-  void VisitBackingStoreWeakly(void*,
+  void VisitBackingStoreWeakly(const char*,
+                               void*,
                                void**,
                                TraceDescriptor,
                                WeakCallback,
                                void*) final {}
-  void VisitBackingStoreOnly(void*, void**) final {}
+  void VisitBackingStoreOnly(const char*, void*, void**) final {}
   void RegisterBackingStoreCallback(void**, MovingObjectCallback, void*) final {
   }
   void RegisterWeakCallback(void*, WeakCallback) final {}
@@ -558,7 +560,8 @@
   }
 }
 
-void V8EmbedderGraphBuilder::VisitBackingStoreStrongly(void* object,
+void V8EmbedderGraphBuilder::VisitBackingStoreStrongly(const char*,
+                                                       void* object,
                                                        void** object_slot,
                                                        TraceDescriptor desc) {
   if (!object)
diff --git a/third_party/blink/renderer/build/scripts/core/css/properties/templates/style_builder_functions.tmpl b/third_party/blink/renderer/build/scripts/core/css/properties/templates/style_builder_functions.tmpl
index 3fb3cbe..7252bec 100644
--- a/third_party/blink/renderer/build/scripts/core/css/properties/templates/style_builder_functions.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/properties/templates/style_builder_functions.tmpl
@@ -18,7 +18,7 @@
     {% if property.style_builder_inline == header and property.style_builder_generate_initial %}
 {{declare_initial(property.name.to_upper_camel_case() if not header)|indent(spaces, true)}} {
 {{(caller(property) ~ '}')|indent(spaces, true)}}
-    {% else %}
+    {% elif header %}
 {{declare_initial()|indent(spaces, true)-}};
     {% endif %}
   {% endif %}
@@ -30,7 +30,7 @@
     {% if property.style_builder_inline == header and property.style_builder_generate_inherit %}
 {{declare_inherit(property.name.to_upper_camel_case() if not header)|indent(spaces, true)}} {
 {{(caller(property) ~ '}')|indent(spaces, true)}}
-    {% else %}
+    {% elif header %}
 {{declare_inherit()|indent(spaces, true)-}};
     {% endif %}
   {% endif %}
@@ -42,7 +42,7 @@
     {% if property.style_builder_inline == header and property.style_builder_generate_value %}
 {{declare_value(property.name.to_upper_camel_case() if not header)|indent(spaces, true)}} {
 {{(caller(property) ~ '}')|indent(spaces, true)}}
-    {% else %}
+    {% elif header %}
 {{declare_value()|indent(spaces, true)-}};
     {% endif %}
   {% endif %}
@@ -374,12 +374,12 @@
     {% set is_visited = property.style_builder_template == 'visited_color' %}
     {% set main_getter = is_visited and property.unvisited_property.getter or property.getter %}
     {% call(property) apply_initial(property, header) %}
-  StyleColor color = {{initial_color}}();
+  auto color = {{initial_color}}();
   {{set_value(property)}}(color);
     {% endcall %}
 
     {% call(property) apply_inherit(property, header) %}
-  StyleColor color = state.ParentStyle()->{{main_getter}}();
+  auto color = state.ParentStyle()->{{main_getter}}();
   {{set_value(property)}}(color);
     {% endcall %}
 
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 080dc64..e1c5720 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -712,6 +712,14 @@
     "$blink_core_output_dir/css/properties/longhands/internal_visited_border_right_color.h",
     "$blink_core_output_dir/css/properties/longhands/internal_visited_border_top_color.cc",
     "$blink_core_output_dir/css/properties/longhands/internal_visited_border_top_color.h",
+    "$blink_core_output_dir/css/properties/longhands/internal_visited_color.cc",
+    "$blink_core_output_dir/css/properties/longhands/internal_visited_color.h",
+    "$blink_core_output_dir/css/properties/longhands/internal_visited_text_emphasis_color.cc",
+    "$blink_core_output_dir/css/properties/longhands/internal_visited_text_emphasis_color.h",
+    "$blink_core_output_dir/css/properties/longhands/internal_visited_text_fill_color.cc",
+    "$blink_core_output_dir/css/properties/longhands/internal_visited_text_fill_color.h",
+    "$blink_core_output_dir/css/properties/longhands/internal_visited_text_stroke_color.cc",
+    "$blink_core_output_dir/css/properties/longhands/internal_visited_text_stroke_color.h",
     "$blink_core_output_dir/css/properties/longhands/isolation.h",
     "$blink_core_output_dir/css/properties/longhands/justify_content.h",
     "$blink_core_output_dir/css/properties/longhands/justify_items.h",
diff --git a/third_party/blink/renderer/core/animation/color_property_functions.cc b/third_party/blink/renderer/core/animation/color_property_functions.cc
index b9be939c..8d9cb2cf4 100644
--- a/third_party/blink/renderer/core/animation/color_property_functions.cc
+++ b/third_party/blink/renderer/core/animation/color_property_functions.cc
@@ -80,17 +80,17 @@
         return StyleColor::CurrentColor();
       return style.VisitedLinkCaretColor().ToStyleColor();
     case CSSPropertyID::kColor:
-      return style.VisitedLinkColor();
+      return style.InternalVisitedColor();
     case CSSPropertyID::kOutlineColor:
       return style.VisitedLinkOutlineColor();
     case CSSPropertyID::kColumnRuleColor:
       return style.VisitedLinkColumnRuleColor();
     case CSSPropertyID::kWebkitTextEmphasisColor:
-      return style.VisitedLinkTextEmphasisColor();
+      return style.InternalVisitedTextEmphasisColor();
     case CSSPropertyID::kWebkitTextFillColor:
-      return style.VisitedLinkTextFillColor();
+      return style.InternalVisitedTextFillColor();
     case CSSPropertyID::kWebkitTextStrokeColor:
-      return style.VisitedLinkTextStrokeColor();
+      return style.InternalVisitedTextStrokeColor();
     case CSSPropertyID::kFloodColor:
       return style.FloodColor();
     case CSSPropertyID::kLightingColor:
@@ -180,7 +180,7 @@
     case CSSPropertyID::kCaretColor:
       return style.SetVisitedLinkCaretColor(color);
     case CSSPropertyID::kColor:
-      style.SetVisitedLinkColor(color);
+      style.SetInternalVisitedColor(color);
       return;
     case CSSPropertyID::kFloodColor:
       style.SetFloodColor(color);
@@ -201,7 +201,7 @@
       style.SetVisitedLinkColumnRuleColor(color);
       return;
     case CSSPropertyID::kWebkitTextStrokeColor:
-      style.SetVisitedLinkTextStrokeColor(color);
+      style.SetInternalVisitedTextStrokeColor(color);
       return;
     default:
       NOTREACHED();
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index b09dab9..d153e8d 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -3945,7 +3945,7 @@
       type_name: "Color",
       computed_style_custom_functions: ["getter", "setter"],
       converter: "ConvertStyleColor",
-      style_builder_template: "color",
+      style_builder_template: "color_only",
       valid_for_visited_link: true,
     },
     {
@@ -3976,7 +3976,7 @@
       type_name: "Color",
       computed_style_custom_functions: ["getter", "setter"],
       converter: "ConvertStyleColor",
-      style_builder_template: "color",
+      style_builder_template: "color_only",
       valid_for_visited_link: true,
     },
     {
@@ -4001,7 +4001,7 @@
       type_name: "Color",
       computed_style_custom_functions: ["getter", "setter"],
       converter: "ConvertStyleColor",
-      style_builder_template: "color",
+      style_builder_template: "color_only",
       valid_for_visited_link: true,
     },
     {
@@ -5144,6 +5144,24 @@
 
     // Visited properties.
     {
+      name: "-internal-visited-color",
+      visited_property_for: "color",
+      property_methods: ["ColorIncludingFallback"],
+      inherited: true,
+      field_group: "inherited",
+      field_template: "external",
+      include_paths: ["third_party/blink/renderer/platform/graphics/color.h"],
+      default_value: "Color::kBlack",
+      type_name: "Color",
+      computed_style_custom_functions: ["getter", "setter"],
+      style_builder_custom_functions: ['value'],
+      style_builder_template: "visited_color",
+      style_builder_template_args: {
+        initial_color: "ComputedStyleInitialValues::InitialColor",
+      },
+      priority: "High",
+    },
+    {
       name: "-internal-visited-background-color",
       visited_property_for: "background-color",
       property_methods: ["ColorIncludingFallback"],
@@ -5243,6 +5261,57 @@
         physical_group: "visited-border-color",
       },
     },
+    {
+      name: "-internal-visited-text-emphasis-color",
+      visited_property_for: "-webkit-text-emphasis-color",
+      property_methods: ["ColorIncludingFallback"],
+      inherited: true,
+      field_group: "*",
+      field_template: "external",
+      include_paths: ["third_party/blink/renderer/platform/graphics/color.h"],
+      default_value: "Color()",
+      type_name: "Color",
+      computed_style_custom_functions: ["getter", "setter"],
+      converter: "ConvertStyleColor",
+      style_builder_template: "visited_color",
+      style_builder_template_args: {
+        initial_color: "blink::Color",
+      },
+    },
+    {
+      name: "-internal-visited-text-fill-color",
+      visited_property_for: "-webkit-text-fill-color",
+      property_methods: ["ColorIncludingFallback"],
+      inherited: true,
+      field_group: "*",
+      field_template: "external",
+      include_paths: ["third_party/blink/renderer/platform/graphics/color.h"],
+      default_value: "Color()",
+      type_name: "Color",
+      computed_style_custom_functions: ["getter", "setter"],
+      converter: "ConvertStyleColor",
+      style_builder_template: "visited_color",
+      style_builder_template_args: {
+        initial_color: "blink::Color",
+      },
+    },
+    {
+      name: "-internal-visited-text-stroke-color",
+      visited_property_for: "-webkit-text-stroke-color",
+      property_methods: ["ColorIncludingFallback"],
+      inherited: true,
+      field_group: "*",
+      field_template: "external",
+      include_paths: ["third_party/blink/renderer/platform/graphics/color.h"],
+      default_value: "Color()",
+      type_name: "Color",
+      computed_style_custom_functions: ["getter", "setter"],
+      converter: "ConvertStyleColor",
+      style_builder_template: "visited_color",
+      style_builder_template_args: {
+        initial_color: "blink::Color",
+      },
+    },
 
     // Aliases; these map to the same CSSPropertyID
     {
diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc
index 5a3458e..145e2fae 100644
--- a/third_party/blink/renderer/core/css/css_property_equality.cc
+++ b/third_party/blink/renderer/core/css/css_property_equality.cc
@@ -137,7 +137,7 @@
       return a.Clip() == b.Clip();
     case CSSPropertyID::kColor:
       return a.GetColor() == b.GetColor() &&
-             a.VisitedLinkColor() == b.VisitedLinkColor();
+             a.InternalVisitedColor() == b.InternalVisitedColor();
     case CSSPropertyID::kFill: {
       const SVGComputedStyle& a_svg = a.SvgStyle();
       const SVGComputedStyle& b_svg = b.SvgStyle();
@@ -333,7 +333,8 @@
              a.PerspectiveOriginY() == b.PerspectiveOriginY();
     case CSSPropertyID::kWebkitTextStrokeColor:
       return a.TextStrokeColor() == b.TextStrokeColor() &&
-             a.VisitedLinkTextStrokeColor() == b.VisitedLinkTextStrokeColor();
+             a.InternalVisitedTextStrokeColor() ==
+                 b.InternalVisitedTextStrokeColor();
     case CSSPropertyID::kTransform:
       return a.Transform() == b.Transform();
     case CSSPropertyID::kTranslate:
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 3b32a8c..a722dfde 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -125,7 +125,7 @@
                         border_style == EBorderStyle::kRidge ||
                         border_style == EBorderStyle::kGroove))
     return blink::Color(238, 238, 238);
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  return visited_link ? style.InternalVisitedColor() : style.GetColor();
 }
 
 const CSSValue* ComputedStyleUtils::BackgroundImageOrWebkitMaskImage(
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 20ef3a0..bea66c6 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -178,6 +178,10 @@
 #include "third_party/blink/renderer/core/css/properties/longhands/internal_visited_border_left_color.h"
 #include "third_party/blink/renderer/core/css/properties/longhands/internal_visited_border_right_color.h"
 #include "third_party/blink/renderer/core/css/properties/longhands/internal_visited_border_top_color.h"
+#include "third_party/blink/renderer/core/css/properties/longhands/internal_visited_color.h"
+#include "third_party/blink/renderer/core/css/properties/longhands/internal_visited_text_emphasis_color.h"
+#include "third_party/blink/renderer/core/css/properties/longhands/internal_visited_text_fill_color.h"
+#include "third_party/blink/renderer/core/css/properties/longhands/internal_visited_text_stroke_color.h"
 #include "third_party/blink/renderer/core/css/properties/longhands/isolation.h"
 #include "third_party/blink/renderer/core/css/properties/longhands/justify_content.h"
 #include "third_party/blink/renderer/core/css/properties/longhands/justify_items.h"
@@ -1646,7 +1650,7 @@
                                                : auto_color.ToStyleColor();
   if (!result.IsCurrentColor())
     return result.GetColor();
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  return visited_link ? style.InternalVisitedColor() : style.GetColor();
 }
 
 const CSSValue* CaretColor::CSSValueFromComputedStyleInternal(
@@ -1818,10 +1822,8 @@
 const blink::Color Color::ColorIncludingFallback(
     bool visited_link,
     const ComputedStyle& style) const {
-  StyleColor result =
-      visited_link ? style.VisitedLinkColor() : style.GetColor();
-  ;
-  return result.GetColor();
+  DCHECK(!visited_link);
+  return style.GetColor();
 }
 
 const CSSValue* Color::CSSValueFromComputedStyleInternal(
@@ -1837,22 +1839,15 @@
 
 void Color::ApplyInitial(StyleResolverState& state) const {
   blink::Color color = ComputedStyleInitialValues::InitialColor();
-  if (state.ApplyPropertyToRegularStyle())
-    state.Style()->SetColor(color);
-  if (state.ApplyPropertyToVisitedLinkStyle())
-    state.Style()->SetVisitedLinkColor(color);
+  state.Style()->SetColor(color);
 }
 
 void Color::ApplyInherit(StyleResolverState& state) const {
   blink::Color color = state.ParentStyle()->GetColor();
-  if (state.ApplyPropertyToRegularStyle()) {
-    if (state.ParentStyle()->IsColorInternalText())
-      state.Style()->SetIsColorInternalText(true);
-    else
-      state.Style()->SetColor(color);
-  }
-  if (state.ApplyPropertyToVisitedLinkStyle())
-    state.Style()->SetVisitedLinkColor(color);
+  if (state.ParentStyle()->IsColorInternalText())
+    state.Style()->SetIsColorInternalText(true);
+  else
+    state.Style()->SetColor(color);
 }
 
 void Color::ApplyValue(StyleResolverState& state, const CSSValue& value) const {
@@ -1864,18 +1859,11 @@
     return;
   }
 
-  if (state.ApplyPropertyToRegularStyle()) {
-    if (identifier_value &&
-        identifier_value->GetValueID() == CSSValueID::kInternalRootColor) {
-      state.Style()->SetIsColorInternalText(true);
-    } else {
-      state.Style()->SetColor(
-          StyleBuilderConverter::ConvertColor(state, value));
-    }
-  }
-  if (state.ApplyPropertyToVisitedLinkStyle()) {
-    state.Style()->SetVisitedLinkColor(
-        StyleBuilderConverter::ConvertColor(state, value, true));
+  if (identifier_value &&
+      identifier_value->GetValueID() == CSSValueID::kInternalRootColor) {
+    state.Style()->SetIsColorInternalText(true);
+  } else {
+    state.Style()->SetColor(StyleBuilderConverter::ConvertColor(state, value));
   }
 }
 
@@ -2071,7 +2059,7 @@
                                    : style.ColumnRuleColor();
   if (!result.IsCurrentColor())
     return result.GetColor();
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  return visited_link ? style.InternalVisitedColor() : style.GetColor();
 }
 
 const CSSValue* ColumnRuleColor::CSSValueFromComputedStyleInternal(
@@ -2917,7 +2905,7 @@
   StyleColor result = style.FloodColor();
   if (!result.IsCurrentColor())
     return result.GetColor();
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  return visited_link ? style.InternalVisitedColor() : style.GetColor();
 }
 
 const CSSValue* FloodColor::CSSValueFromComputedStyleInternal(
@@ -3264,6 +3252,25 @@
   return CSSIdentifierValue::Create(style.ForcedColorAdjust());
 }
 
+void InternalVisitedColor::ApplyValue(StyleResolverState& state,
+                                      const CSSValue& value) const {
+  auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
+  if (identifier_value &&
+      identifier_value->GetValueID() == CSSValueID::kCurrentcolor) {
+    ApplyInherit(state);
+    return;
+  }
+  state.Style()->SetInternalVisitedColor(
+      StyleBuilderConverter::ConvertColor(state, value, true));
+}
+
+const blink::Color InternalVisitedColor::ColorIncludingFallback(
+    bool visited_link,
+    const ComputedStyle& style) const {
+  DCHECK(visited_link);
+  return style.InternalVisitedColor();
+}
+
 const CSSValue* GridAutoColumns::ParseSingleValue(
     CSSParserTokenRange& range,
     const CSSParserContext& context,
@@ -3689,8 +3696,8 @@
     const ComputedStyle& style) const {
   DCHECK(visited_link);
 
-  blink::Color color =
-      style.InternalVisitedBackgroundColor().Resolve(style.VisitedLinkColor());
+  blink::Color color = style.InternalVisitedBackgroundColor().Resolve(
+      style.InternalVisitedColor());
 
   // TODO: Technically someone could explicitly specify the color
   // transparent, but for now we'll just assume that if the background color
@@ -3710,7 +3717,7 @@
     const ComputedStyle& style) const {
   DCHECK(visited_link);
   return style.InternalVisitedBorderLeftColor().Resolve(
-      style.VisitedLinkColor());
+      style.InternalVisitedColor());
 }
 
 const blink::Color InternalVisitedBorderTopColor::ColorIncludingFallback(
@@ -3718,7 +3725,7 @@
     const ComputedStyle& style) const {
   DCHECK(visited_link);
   return style.InternalVisitedBorderTopColor().Resolve(
-      style.VisitedLinkColor());
+      style.InternalVisitedColor());
 }
 
 const blink::Color InternalVisitedBorderRightColor::ColorIncludingFallback(
@@ -3726,7 +3733,7 @@
     const ComputedStyle& style) const {
   DCHECK(visited_link);
   return style.InternalVisitedBorderRightColor().Resolve(
-      style.VisitedLinkColor());
+      style.InternalVisitedColor());
 }
 
 const blink::Color InternalVisitedBorderBottomColor::ColorIncludingFallback(
@@ -3734,7 +3741,31 @@
     const ComputedStyle& style) const {
   DCHECK(visited_link);
   return style.InternalVisitedBorderBottomColor().Resolve(
-      style.VisitedLinkColor());
+      style.InternalVisitedColor());
+}
+
+const blink::Color InternalVisitedTextEmphasisColor::ColorIncludingFallback(
+    bool visited_link,
+    const ComputedStyle& style) const {
+  DCHECK(visited_link);
+  return style.InternalVisitedTextEmphasisColor().Resolve(
+      style.InternalVisitedColor());
+}
+
+const blink::Color InternalVisitedTextFillColor::ColorIncludingFallback(
+    bool visited_link,
+    const ComputedStyle& style) const {
+  DCHECK(visited_link);
+  return style.InternalVisitedTextFillColor().Resolve(
+      style.InternalVisitedColor());
+}
+
+const blink::Color InternalVisitedTextStrokeColor::ColorIncludingFallback(
+    bool visited_link,
+    const ComputedStyle& style) const {
+  DCHECK(visited_link);
+  return style.InternalVisitedTextStrokeColor().Resolve(
+      style.InternalVisitedColor());
 }
 
 const CSSValue* Isolation::CSSValueFromComputedStyleInternal(
@@ -3889,7 +3920,7 @@
   StyleColor result = style.LightingColor();
   if (!result.IsCurrentColor())
     return result.GetColor();
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  return visited_link ? style.InternalVisitedColor() : style.GetColor();
 }
 
 const CSSValue* LightingColor::CSSValueFromComputedStyleInternal(
@@ -4583,7 +4614,7 @@
       visited_link ? style.VisitedLinkOutlineColor() : style.OutlineColor();
   if (!result.IsCurrentColor())
     return result.GetColor();
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  return visited_link ? style.InternalVisitedColor() : style.GetColor();
 }
 
 const CSSValue* OutlineColor::CSSValueFromComputedStyleInternal(
@@ -5927,7 +5958,7 @@
   StyleColor result = style.StopColor();
   if (!result.IsCurrentColor())
     return result.GetColor();
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  return visited_link ? style.InternalVisitedColor() : style.GetColor();
 }
 
 const CSSValue* StopColor::CSSValueFromComputedStyleInternal(
@@ -6207,7 +6238,7 @@
   StyleColor result = style.DecorationColorIncludingFallback(visited_link);
   if (!result.IsCurrentColor())
     return result.GetColor();
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  return visited_link ? style.InternalVisitedColor() : style.GetColor();
 }
 
 const CSSValue* TextDecorationColor::CSSValueFromComputedStyleInternal(
@@ -7645,7 +7676,7 @@
   StyleColor result = style.TapHighlightColor();
   if (!result.IsCurrentColor())
     return result.GetColor();
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  return visited_link ? style.InternalVisitedColor() : style.GetColor();
 }
 
 const CSSValue* WebkitTapHighlightColor::CSSValueFromComputedStyleInternal(
@@ -7697,11 +7728,8 @@
 const blink::Color WebkitTextEmphasisColor::ColorIncludingFallback(
     bool visited_link,
     const ComputedStyle& style) const {
-  StyleColor result = visited_link ? style.VisitedLinkTextEmphasisColor()
-                                   : style.TextEmphasisColor();
-  if (!result.IsCurrentColor())
-    return result.GetColor();
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  DCHECK(!visited_link);
+  return style.TextEmphasisColor().Resolve(style.GetColor());
 }
 
 const CSSValue* WebkitTextEmphasisColor::CSSValueFromComputedStyleInternal(
@@ -7928,11 +7956,8 @@
 const blink::Color WebkitTextFillColor::ColorIncludingFallback(
     bool visited_link,
     const ComputedStyle& style) const {
-  StyleColor result =
-      visited_link ? style.VisitedLinkTextFillColor() : style.TextFillColor();
-  if (!result.IsCurrentColor())
-    return result.GetColor();
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  DCHECK(!visited_link);
+  return style.TextFillColor().Resolve(style.GetColor());
 }
 
 const CSSValue* WebkitTextFillColor::CSSValueFromComputedStyleInternal(
@@ -7981,11 +8006,8 @@
 const blink::Color WebkitTextStrokeColor::ColorIncludingFallback(
     bool visited_link,
     const ComputedStyle& style) const {
-  StyleColor result = visited_link ? style.VisitedLinkTextStrokeColor()
-                                   : style.TextStrokeColor();
-  if (!result.IsCurrentColor())
-    return result.GetColor();
-  return visited_link ? style.VisitedLinkColor() : style.GetColor();
+  DCHECK(!visited_link);
+  return style.TextStrokeColor().Resolve(style.GetColor());
 }
 
 const CSSValue* WebkitTextStrokeColor::CSSValueFromComputedStyleInternal(
diff --git a/third_party/blink/renderer/core/css/resolver/css_property_priority.h b/third_party/blink/renderer/core/css/resolver/css_property_priority.h
index 00bae09..8fc3f10 100644
--- a/third_party/blink/renderer/core/css/resolver/css_property_priority.h
+++ b/third_party/blink/renderer/core/css/resolver/css_property_priority.h
@@ -87,7 +87,7 @@
 template <>
 inline CSSPropertyID CSSPropertyPriorityData<kHighPropertyPriority>::Last() {
   static_assert(static_cast<int>(CSSPropertyID::kZoom) ==
-                    static_cast<int>(CSSPropertyID::kColor) + 23,
+                    static_cast<int>(CSSPropertyID::kColor) + 24,
                 "CSSPropertyID::kZoom should be the end of the high priority "
                 "property range");
   static_assert(static_cast<int>(CSSPropertyID::kWritingMode) ==
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 8dde6ac7..3c776a8 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2229,15 +2229,12 @@
     return;
 
 #if DCHECK_IS_ON()
-  if (RuntimeEnabledFeatures::FastFlatTreeTraversalEnabled()) {
-    int assigned_nodes_in_slot_count = 0;
-    int nodes_which_have_assigned_slot_count = 0;
-    FlatTreeTraversal::AssertFlatTreeNodeDataUpdated(
-        *this, assigned_nodes_in_slot_count,
-        nodes_which_have_assigned_slot_count);
-    DCHECK_EQ(assigned_nodes_in_slot_count,
-              nodes_which_have_assigned_slot_count);
-  }
+  int assigned_nodes_in_slot_count = 0;
+  int nodes_which_have_assigned_slot_count = 0;
+  FlatTreeTraversal::AssertFlatTreeNodeDataUpdated(
+      *this, assigned_nodes_in_slot_count,
+      nodes_which_have_assigned_slot_count);
+  DCHECK_EQ(assigned_nodes_in_slot_count, nodes_which_have_assigned_slot_count);
 #endif
 
   // Entering here from inside layout, paint etc. would be catastrophic since
diff --git a/third_party/blink/renderer/core/dom/flat_tree_traversal.cc b/third_party/blink/renderer/core/dom/flat_tree_traversal.cc
index 99806c99..ee891dc 100644
--- a/third_party/blink/renderer/core/dom/flat_tree_traversal.cc
+++ b/third_party/blink/renderer/core/dom/flat_tree_traversal.cc
@@ -173,15 +173,6 @@
 Node* FlatTreeTraversal::TraverseSiblingsForV1HostChild(
     const Node& node,
     TraversalDirection direction) {
-  if (!RuntimeEnabledFeatures::FastFlatTreeTraversalEnabled()) {
-    HTMLSlotElement* slot = node.AssignedSlot();
-    if (!slot)
-      return nullptr;
-    return direction == kTraversalDirectionForward
-               ? slot->AssignedNodeNextTo(node)
-               : slot->AssignedNodePreviousTo(node);
-  }
-
   ShadowRoot* shadow_root = node.ParentElementShadowRoot();
   DCHECK(shadow_root);
   if (!shadow_root->HasSlotAssignment()) {
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index f6f42fe..25634af4 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -2809,10 +2809,6 @@
   ShadowRoot* root = V1ShadowRootOfParent();
   if (!root)
     return nullptr;
-  if (!RuntimeEnabledFeatures::FastFlatTreeTraversalEnabled()) {
-    // Don't recalc assignment in this case.
-    return root->AssignedSlotFor(*this);
-  }
 
   if (!root->HasSlotAssignment())
     return nullptr;
diff --git a/third_party/blink/renderer/core/dom/slot_assignment.cc b/third_party/blink/renderer/core/dom/slot_assignment.cc
index e838fa3..ddb5ccf 100644
--- a/third_party/blink/renderer/core/dom/slot_assignment.cc
+++ b/third_party/blink/renderer/core/dom/slot_assignment.cc
@@ -154,12 +154,10 @@
     if (FindHostChildBySlotName(slot_name)) {
       // |slot| lost assigned nodes
       if (slot_mutation_type == SlotMutationType::kRemoved) {
-        if (RuntimeEnabledFeatures::FastFlatTreeTraversalEnabled()) {
-          // |slot|'s previously assigned nodes' flat tree node data became
-          // dirty. Call SetNeedsAssignmentRecalc() to clear their flat tree
-          // node data surely in recalc timing.
-          SetNeedsAssignmentRecalc();
-        }
+        // |slot|'s previously assigned nodes' flat tree node data became
+        // dirty. Call SetNeedsAssignmentRecalc() to clear their flat tree
+        // node data surely in recalc timing.
+        SetNeedsAssignmentRecalc();
         slot.DidSlotChangeAfterRemovedFromShadowTree();
       } else {
         slot.DidSlotChangeAfterRenaming();
@@ -288,8 +286,7 @@
     if (slot) {
       slot->AppendAssignedNode(child);
     } else {
-      if (RuntimeEnabledFeatures::FastFlatTreeTraversalEnabled())
-        child.ClearFlatTreeNodeData();
+      child.ClearFlatTreeNodeData();
       child.RemovedFromFlatTree();
     }
   }
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5 b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
index 7e768dd..35e43a9 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
@@ -107,7 +107,6 @@
     {
       name: "DocumentDomain",
       feature_policy_name: "document-domain",
-      depends_on: ["ExperimentalProductivityFeatures"],
     },
     {
       name: "DocumentWrite",
diff --git a/third_party/blink/renderer/core/frame/use_counter_helper.cc b/third_party/blink/renderer/core/frame/use_counter_helper.cc
index 513b507..1d09515d 100644
--- a/third_party/blink/renderer/core/frame/use_counter_helper.cc
+++ b/third_party/blink/renderer/core/frame/use_counter_helper.cc
@@ -1259,6 +1259,10 @@
     case CSSPropertyID::kInternalVisitedBorderLeftColor:
     case CSSPropertyID::kInternalVisitedBorderRightColor:
     case CSSPropertyID::kInternalVisitedBorderTopColor:
+    case CSSPropertyID::kInternalVisitedColor:
+    case CSSPropertyID::kInternalVisitedTextEmphasisColor:
+    case CSSPropertyID::kInternalVisitedTextFillColor:
+    case CSSPropertyID::kInternalVisitedTextStrokeColor:
     case CSSPropertyID::kInvalid:
       NOTREACHED();
       return 0;
diff --git a/third_party/blink/renderer/core/html/forms/spin_button_element.h b/third_party/blink/renderer/core/html/forms/spin_button_element.h
index 6b1e0abc..fdbe80f8b 100644
--- a/third_party/blink/renderer/core/html/forms/spin_button_element.h
+++ b/third_party/blink/renderer/core/html/forms/spin_button_element.h
@@ -102,16 +102,11 @@
   TaskRunnerTimer<SpinButtonElement> repeating_timer_;
 };
 
-DEFINE_TYPE_CASTS(SpinButtonElement,
-                  Node,
-                  node,
-                  To<Element>(node)->IsSpinButtonElement(),
-                  To<Element>(node).IsSpinButtonElement());
-
 template <>
 struct DowncastTraits<SpinButtonElement> {
   static bool AllowFrom(const Node& node) {
-    return ToElement(node).IsSpinButtonElement();
+    auto* element = DynamicTo<Element>(node);
+    return element && element->IsSpinButtonElement();
   }
 };
 
diff --git a/third_party/blink/renderer/core/html/html_slot_element.cc b/third_party/blink/renderer/core/html/html_slot_element.cc
index e31c38e9..da4d9e0 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.cc
+++ b/third_party/blink/renderer/core/html/html_slot_element.cc
@@ -184,15 +184,11 @@
 
 void HTMLSlotElement::AppendAssignedNode(Node& host_child) {
   DCHECK(host_child.IsSlotable());
-  if (!RuntimeEnabledFeatures::FastFlatTreeTraversalEnabled())
-    assigned_nodes_index_.insert(&host_child, assigned_nodes_.size());
   assigned_nodes_.push_back(&host_child);
 }
 
 void HTMLSlotElement::ClearAssignedNodes() {
   assigned_nodes_.clear();
-  if (!RuntimeEnabledFeatures::FastFlatTreeTraversalEnabled())
-    assigned_nodes_index_.clear();
 }
 
 void HTMLSlotElement::ClearAssignedNodesAndFlatTreeChildren() {
@@ -249,32 +245,6 @@
   DispatchScopedEvent(*event);
 }
 
-Node* HTMLSlotElement::AssignedNodeNextTo(const Node& node) const {
-  DCHECK(!RuntimeEnabledFeatures::FastFlatTreeTraversalEnabled());
-  DCHECK(SupportsAssignment());
-  ContainingShadowRoot()->GetSlotAssignment().RecalcAssignment();
-
-  auto it = assigned_nodes_index_.find(&node);
-  DCHECK(it != assigned_nodes_index_.end());
-  unsigned index = it->value;
-  if (index + 1 == assigned_nodes_.size())
-    return nullptr;
-  return assigned_nodes_[index + 1].Get();
-}
-
-Node* HTMLSlotElement::AssignedNodePreviousTo(const Node& node) const {
-  DCHECK(!RuntimeEnabledFeatures::FastFlatTreeTraversalEnabled());
-  DCHECK(SupportsAssignment());
-  ContainingShadowRoot()->GetSlotAssignment().RecalcAssignment();
-
-  auto it = assigned_nodes_index_.find(&node);
-  DCHECK(it != assigned_nodes_index_.end());
-  unsigned index = it->value;
-  if (index == 0)
-    return nullptr;
-  return assigned_nodes_[index - 1].Get();
-}
-
 AtomicString HTMLSlotElement::GetName() const {
   return NormalizeSlotName(FastGetAttribute(kNameAttr));
 }
@@ -568,7 +538,6 @@
 
 void HTMLSlotElement::Trace(Visitor* visitor) {
   visitor->Trace(assigned_nodes_);
-  visitor->Trace(assigned_nodes_index_);
   visitor->Trace(flat_tree_children_);
   visitor->Trace(assigned_nodes_candidates_);
   HTMLElement::Trace(visitor);
diff --git a/third_party/blink/renderer/core/html/html_slot_element.h b/third_party/blink/renderer/core/html/html_slot_element.h
index a29e78a..50dde114 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.h
+++ b/third_party/blink/renderer/core/html/html_slot_element.h
@@ -73,8 +73,7 @@
 
   void WillRecalcAssignedNodes() { ClearAssignedNodes(); }
   void DidRecalcAssignedNodes() {
-    if (RuntimeEnabledFeatures::FastFlatTreeTraversalEnabled())
-      UpdateFlatTreeNodeDataForAssignedNodes();
+    UpdateFlatTreeNodeDataForAssignedNodes();
     RecalcFlatTreeChildren();
   }
 
@@ -143,7 +142,6 @@
   void ClearAssignedNodesAndFlatTreeChildren();
 
   HeapVector<Member<Node>> assigned_nodes_;
-  HeapHashMap<Member<const Node>, unsigned> assigned_nodes_index_;
   HeapVector<Member<Node>> flat_tree_children_;
 
   bool slotchange_event_enqueued_ = false;
diff --git a/third_party/blink/renderer/core/html/resources/html.css b/third_party/blink/renderer/core/html/resources/html.css
index 033f11e..07956cd 100644
--- a/third_party/blink/renderer/core/html/resources/html.css
+++ b/third_party/blink/renderer/core/html/resources/html.css
@@ -674,6 +674,9 @@
 
 area {
     display: inline;
+}
+
+area:-webkit-any-link {
     cursor: pointer;
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_theme.cc b/third_party/blink/renderer/core/layout/layout_theme.cc
index 50db8d9b..c827173 100644
--- a/third_party/blink/renderer/core/layout/layout_theme.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme.cc
@@ -378,7 +378,7 @@
   if (!node || !node->IsActive() || !node->IsElementNode() ||
       !ToElement(node)->IsSpinButtonElement())
     return false;
-  const SpinButtonElement* element = ToSpinButtonElement(node);
+  const auto* element = To<SpinButtonElement>(node);
   return element->GetUpDownState() == SpinButtonElement::kUp;
 }
 
@@ -395,7 +395,7 @@
     return false;
   if (!node->IsElementNode() || !ToElement(node)->IsSpinButtonElement())
     return node->IsHovered();
-  const SpinButtonElement* element = ToSpinButtonElement(node);
+  const auto* element = To<SpinButtonElement>(node);
   return element->IsHovered() &&
          element->GetUpDownState() != SpinButtonElement::kIndeterminate;
 }
@@ -404,7 +404,7 @@
   if (!node || !node->IsElementNode() ||
       !ToElement(node)->IsSpinButtonElement())
     return false;
-  const SpinButtonElement* element = ToSpinButtonElement(node);
+  const auto* element = To<SpinButtonElement>(node);
   return element->GetUpDownState() == SpinButtonElement::kUp;
 }
 
diff --git a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc
index 4f41fac..34e44534 100644
--- a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc
+++ b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc
@@ -234,7 +234,10 @@
 #if DCHECK_IS_ON()
   view->SetIsUpdatingDescendantDependentFlags(true);
 #endif
-  RootLayer()->UpdateDescendantDependentFlags();
+  {
+    TRACE_EVENT0("blink", "PaintLayer::UpdateDescendantDependentFlags");
+    RootLayer()->UpdateDescendantDependentFlags();
+  }
 #if DCHECK_IS_ON()
   view->SetIsUpdatingDescendantDependentFlags(false);
 #endif
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index b21f023..5e5a911 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -2037,13 +2037,13 @@
   if (TextStrokeWidth()) {
     // Prefer stroke color if possible, but not if it's fully transparent.
     StyleColor text_stroke_style_color =
-        visited_link ? VisitedLinkTextStrokeColor() : TextStrokeColor();
+        visited_link ? InternalVisitedTextStrokeColor() : TextStrokeColor();
     if (!text_stroke_style_color.IsCurrentColor() &&
         text_stroke_style_color.GetColor().Alpha())
       return text_stroke_style_color;
   }
 
-  return visited_link ? VisitedLinkTextFillColor() : TextFillColor();
+  return visited_link ? InternalVisitedTextFillColor() : TextFillColor();
 }
 
 Color ComputedStyle::VisitedDependentColor(
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 045da88d..3fa571ad 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -106,6 +106,10 @@
 class InternalVisitedBorderLeftColor;
 class InternalVisitedBorderRightColor;
 class InternalVisitedBorderTopColor;
+class InternalVisitedColor;
+class InternalVisitedTextEmphasisColor;
+class InternalVisitedTextFillColor;
+class InternalVisitedTextStrokeColor;
 class LightingColor;
 class OutlineColor;
 class StopColor;
@@ -199,6 +203,10 @@
   friend class css_longhand::InternalVisitedBorderLeftColor;
   friend class css_longhand::InternalVisitedBorderRightColor;
   friend class css_longhand::InternalVisitedBorderTopColor;
+  friend class css_longhand::InternalVisitedColor;
+  friend class css_longhand::InternalVisitedTextEmphasisColor;
+  friend class css_longhand::InternalVisitedTextFillColor;
+  friend class css_longhand::InternalVisitedTextStrokeColor;
   friend class css_longhand::LightingColor;
   friend class css_longhand::OutlineColor;
   friend class css_longhand::StopColor;
@@ -2352,6 +2360,9 @@
   }
 
  private:
+  void SetInternalVisitedColor(const Color& v) {
+    SetInternalVisitedColorInternal(v);
+  }
   void SetInternalVisitedBackgroundColor(const StyleColor& v) {
     SetInternalVisitedBackgroundColorInternal(v);
   }
@@ -2376,18 +2387,20 @@
   void SetVisitedLinkTextDecorationColor(const StyleColor& v) {
     SetVisitedLinkTextDecorationColorInternal(v);
   }
-  void SetVisitedLinkTextEmphasisColor(const StyleColor& color) {
-    SetVisitedLinkTextEmphasisColorInternal(color.Resolve(Color()));
-    SetVisitedLinkTextEmphasisColorIsCurrentColorInternal(
+  void SetInternalVisitedTextEmphasisColor(const StyleColor& color) {
+    SetInternalVisitedTextEmphasisColorInternal(color.Resolve(Color()));
+    SetInternalVisitedTextEmphasisColorIsCurrentColorInternal(
         color.IsCurrentColor());
   }
-  void SetVisitedLinkTextFillColor(const StyleColor& color) {
-    SetVisitedLinkTextFillColorInternal(color.Resolve(Color()));
-    SetVisitedLinkTextFillColorIsCurrentColorInternal(color.IsCurrentColor());
+  void SetInternalVisitedTextFillColor(const StyleColor& color) {
+    SetInternalVisitedTextFillColorInternal(color.Resolve(Color()));
+    SetInternalVisitedTextFillColorIsCurrentColorInternal(
+        color.IsCurrentColor());
   }
-  void SetVisitedLinkTextStrokeColor(const StyleColor& color) {
-    SetVisitedLinkTextStrokeColorInternal(color.Resolve(Color()));
-    SetVisitedLinkTextStrokeColorIsCurrentColorInternal(color.IsCurrentColor());
+  void SetInternalVisitedTextStrokeColor(const StyleColor& color) {
+    SetInternalVisitedTextStrokeColorInternal(color.Resolve(Color()));
+    SetInternalVisitedTextStrokeColorIsCurrentColorInternal(
+        color.IsCurrentColor());
   }
   void SetVisitedLinkCaretColor(const StyleAutoColor& color) {
     SetVisitedLinkCaretColorInternal(color.Resolve(Color()));
@@ -2496,6 +2509,7 @@
                ? StyleColor::CurrentColor()
                : StyleColor(TextStrokeColorInternal());
   }
+  Color InternalVisitedColor() const { return InternalVisitedColorInternal(); }
   StyleAutoColor VisitedLinkCaretColor() const {
     if (VisitedLinkCaretColorIsCurrentColorInternal())
       return StyleAutoColor::CurrentColor();
@@ -2555,20 +2569,20 @@
   StyleColor VisitedLinkTextDecorationColor() const {
     return VisitedLinkTextDecorationColorInternal();
   }
-  StyleColor VisitedLinkTextEmphasisColor() const {
-    return VisitedLinkTextEmphasisColorIsCurrentColorInternal()
+  StyleColor InternalVisitedTextEmphasisColor() const {
+    return InternalVisitedTextEmphasisColorIsCurrentColorInternal()
                ? StyleColor::CurrentColor()
-               : StyleColor(VisitedLinkTextEmphasisColorInternal());
+               : StyleColor(InternalVisitedTextEmphasisColorInternal());
   }
-  StyleColor VisitedLinkTextFillColor() const {
-    return VisitedLinkTextFillColorIsCurrentColorInternal()
+  StyleColor InternalVisitedTextFillColor() const {
+    return InternalVisitedTextFillColorIsCurrentColorInternal()
                ? StyleColor::CurrentColor()
-               : StyleColor(VisitedLinkTextFillColorInternal());
+               : StyleColor(InternalVisitedTextFillColorInternal());
   }
-  StyleColor VisitedLinkTextStrokeColor() const {
-    return VisitedLinkTextStrokeColorIsCurrentColorInternal()
+  StyleColor InternalVisitedTextStrokeColor() const {
+    return InternalVisitedTextStrokeColorIsCurrentColorInternal()
                ? StyleColor::CurrentColor()
-               : StyleColor(VisitedLinkTextStrokeColorInternal());
+               : StyleColor(InternalVisitedTextStrokeColorInternal());
   }
 
   StyleColor DecorationColorIncludingFallback(bool visited_link) const;
diff --git a/third_party/blink/renderer/core/style/computed_style_diff_functions.json5 b/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
index 876991a..fd6ab8c 100644
--- a/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
+++ b/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
@@ -412,7 +412,7 @@
     },
     {
         name: "UpdatePropertySpecificDifferencesTextDecorationOrColor",
-        fields_to_diff: ["color", "VisitedLinkColor", "text-decoration-line",
+        fields_to_diff: ["color", "-internal-visited-color", "text-decoration-line",
                 "text-decoration-style", "text-decoration-color",
                 "VisitedLinkTextDecorationColor", "TextEmphasisFill",
                 "text-underline-position", "text-decoration-skip-ink", "AppliedTextDecorations"],
@@ -434,16 +434,16 @@
             field_dependencies: ["-webkit-text-emphasis-color"]
           },
           {
-            method: "VisitedLinkTextFillColor()",
-            field_dependencies: ["VisitedLinkTextFillColor"]
+            method: "InternalVisitedTextFillColor()",
+            field_dependencies: ["-internal-visited-text-fill-color"]
           },
           {
-            method: "VisitedLinkTextStrokeColor()",
-            field_dependencies: ["VisitedLinkTextStrokeColor"]
+            method: "InternalVisitedTextStrokeColor()",
+            field_dependencies: ["-internal-visited-text-stroke-color"]
           },
           {
-            method: "VisitedLinkTextEmphasisColor()",
-            field_dependencies: ["VisitedLinkTextEmphasisColor"]
+            method: "InternalVisitedTextEmphasisColor()",
+            field_dependencies: ["-internal-visited-text-emphasis-color"]
           },
           {
             method: "CaretColor()",
diff --git a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
index c604314..33dfb38 100644
--- a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
+++ b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
@@ -277,13 +277,6 @@
       computed_style_custom_functions: ["getter", "setter"],
     },
     {
-      name: "VisitedLinkColor",
-      inherited: true,
-      field_template: "<color>",
-      field_group: "inherited",
-      default_value: "Color::kBlack",
-    },
-    {
       name: "TextAutosizingMultiplier",
       inherited: true,
       field_template: "primitive",
@@ -293,36 +286,6 @@
       computed_style_custom_functions: ["setter"],
     },
     {
-      name: "VisitedLinkTextStrokeColor",
-      inherited: true,
-      field_template: "external",
-      type_name: "Color",
-      include_paths: ["third_party/blink/renderer/platform/graphics/color.h"],
-      default_value: "Color()",
-      field_group: "*",
-      computed_style_custom_functions: ["getter", "setter"],
-    },
-    {
-      name: "VisitedLinkTextFillColor",
-      inherited: true,
-      field_template: "external",
-      type_name: "Color",
-      include_paths: ["third_party/blink/renderer/platform/graphics/color.h"],
-      default_value: "Color()",
-      field_group: "*",
-      computed_style_custom_functions: ["getter", "setter"],
-    },
-    {
-      name: "VisitedLinkTextEmphasisColor",
-      inherited: true,
-      field_template: "external",
-      type_name: "Color",
-      include_paths: ["third_party/blink/renderer/platform/graphics/color.h"],
-      default_value: "Color()",
-      field_group: "*",
-      computed_style_custom_functions: ["getter", "setter"],
-    },
-    {
       name: "VisitedLinkCaretColor",
       inherited: true,
       field_template: "external",
@@ -398,7 +361,7 @@
       computed_style_custom_functions: ["getter", "setter"],
     },
     {
-      name: "VisitedLinkTextStrokeColorIsCurrentColor",
+      name: "InternalVisitedTextStrokeColorIsCurrentColor",
       inherited: true,
       field_template: "primitive",
       type_name: "bool",
@@ -407,7 +370,7 @@
       computed_style_custom_functions: ["getter", "setter"],
     },
     {
-      name: "VisitedLinkTextFillColorIsCurrentColor",
+      name: "InternalVisitedTextFillColorIsCurrentColor",
       inherited: true,
       field_template: "primitive",
       type_name: "bool",
@@ -416,7 +379,7 @@
       computed_style_custom_functions: ["getter", "setter"],
     },
     {
-      name: "VisitedLinkTextEmphasisColorIsCurrentColor",
+      name: "InternalVisitedTextEmphasisColorIsCurrentColor",
       inherited: true,
       field_template: "primitive",
       type_name: "bool",
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index d89adc2..e84fb2b 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -742,8 +742,7 @@
     if (start_or_options.IsPerformanceMeasureOptions()) {
       // measure("name", {}, *)
       if (end) {
-        exception_state.ThrowDOMException(
-            DOMExceptionCode::kSyntaxError,
+        exception_state.ThrowTypeError(
             "If a PerformanceMeasureOptions object was passed, |end| must be "
             "null.");
         return nullptr;
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 3422407..3aaaf33 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1437,8 +1437,10 @@
   if (!IsAnchor() || !GetDocument())
     return AXObject::InPageLinkTarget();
 
-  Element* anchor = AnchorElement();
-  DCHECK(anchor);
+  const Element* anchor = AnchorElement();
+  if (!anchor)
+    return AXObject::InPageLinkTarget();
+
   KURL link_url = anchor->HrefURL();
   if (!link_url.IsValid())
     return AXObject::InPageLinkTarget();
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 18dead5..27b7b39 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -7485,9 +7485,7 @@
 }
 
 void WebGLRenderingContextBase::PrintWarningToConsole(const String& message) {
-  if (!canvas())
-    return;
-  canvas()->GetDocument().AddConsoleMessage(
+  Host()->GetTopExecutionContext()->AddConsoleMessage(
       ConsoleMessage::Create(mojom::ConsoleMessageSource::kRendering,
                              mojom::ConsoleMessageLevel::kWarning, message));
 }
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc
index 412d67c..4b7dc7d 100644
--- a/third_party/blink/renderer/platform/heap/heap.cc
+++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -202,8 +202,9 @@
   return compaction_.get();
 }
 
-void ThreadHeap::RegisterMovingObjectReference(MovableReference* slot) {
-  Compaction()->RegisterMovingObjectReference(slot);
+void ThreadHeap::RegisterMovingObjectReference(const char* name,
+                                               MovableReference* slot) {
+  Compaction()->RegisterMovingObjectReference(name, slot);
 }
 
 void ThreadHeap::RegisterMovingObjectCallback(MovableReference* slot,
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index aa009e32..05d3437 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -223,7 +223,7 @@
   //
   // When compaction moves the object pointed to by |*slot| to |newAddress|,
   // |*slot| must be updated to hold |newAddress| instead.
-  void RegisterMovingObjectReference(MovableReference*);
+  void RegisterMovingObjectReference(const char*, MovableReference*);
 
   // Register a callback to be invoked upon moving the object starting at
   // |reference|; see |MovingObjectCallback| documentation for details.
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc
index edeba918..d2595170 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -40,7 +40,7 @@
     relocatable_pages_.insert(page);
   }
 
-  void AddOrFilter(MovableReference* slot) {
+  void AddOrFilter(MovableReference* slot, const char* name) {
     MovableReference value = *slot;
     CHECK(value);
 
@@ -98,6 +98,7 @@
 
     // Add regular fixup.
     fixups_.insert({value, slot});
+    fixup_names_.insert({slot, name});
 
     // Check whether the slot itself resides on a page that is compacted.
     if (LIKELY(!relocatable_pages_.Contains(slot_page)))
@@ -177,6 +178,12 @@
     DebugSlotType slot_type = kNormalSlot;
     base::debug::Alias(&slot_type);
 
+    // TODO(918064): Remove this after getting the type of the object that
+    // crashes compaction.
+    constexpr size_t kMaxNameLen = 256;
+    char slot_container_name[kMaxNameLen];
+    base::debug::Alias(slot_container_name);
+
     // If the object is referenced by a slot that is contained on a compacted
     // area itself, check whether it can be updated already.
     MovableReference* slot = reinterpret_cast<MovableReference*>(it->second);
@@ -187,6 +194,12 @@
       if (!slot_location) {
         interior_it->second = to;
         slot_type = kInteriorSlotPreMove;
+        const char* name = fixup_names_.find(slot)->second;
+        size_t len = strlen(name);
+        if (len > kMaxNameLen)
+          len = kMaxNameLen;
+        strncpy(slot_container_name, name, len);
+        slot_container_name[len - 1] = 0;
       } else {
         LOG_HEAP_COMPACTION()
             << "Redirected slot: " << slot << " => " << slot_location;
@@ -254,6 +267,7 @@
   //
   // (TODO: consider in-place updating schemes.)
   std::unordered_map<MovableReference, MovableReference*> fixups_;
+  std::unordered_map<MovableReference*, const char*> fixup_names_;
 
   // Map from movable reference to callbacks that need to be invoked
   // when the object moves.
@@ -364,13 +378,15 @@
   force_for_next_gc_ = false;
 }
 
-void HeapCompact::RegisterMovingObjectReference(MovableReference* slot) {
+void HeapCompact::RegisterMovingObjectReference(const char* name,
+                                                MovableReference* slot) {
   CHECK(heap_->LookupPageForAddress(reinterpret_cast<Address>(slot)));
 
   if (!do_compact_)
     return;
 
   traced_slots_.insert(slot);
+  traced_slots_names_.insert(slot, name);
 }
 
 void HeapCompact::RegisterMovingObjectCallback(MovableReference* slot,
@@ -440,11 +456,12 @@
   last_fixup_count_for_testing_ = 0;
   for (auto** slot : traced_slots_) {
     if (*slot) {
-      Fixups().AddOrFilter(slot);
+      Fixups().AddOrFilter(slot, traced_slots_names_.find(slot)->value);
       last_fixup_count_for_testing_++;
     }
   }
   traced_slots_.clear();
+  traced_slots_names_.clear();
 }
 
 void HeapCompact::Finish() {
@@ -465,6 +482,7 @@
 
   last_fixup_count_for_testing_ = 0;
   traced_slots_.clear();
+  traced_slots_names_.clear();
   fixups_.reset();
   do_compact_ = false;
 }
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.h b/third_party/blink/renderer/platform/heap/heap_compact.h
index d1fee85..125fe85 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.h
+++ b/third_party/blink/renderer/platform/heap/heap_compact.h
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/platform/heap/blink_gc.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
 
@@ -63,7 +64,7 @@
   }
 
   // See |Heap::registerMovingObjectReference()| documentation.
-  void RegisterMovingObjectReference(MovableReference* slot);
+  void RegisterMovingObjectReference(const char* name, MovableReference* slot);
 
   // See |Heap::registerMovingObjectCallback()| documentation.
   void RegisterMovingObjectCallback(MovableReference*,
@@ -137,6 +138,7 @@
   // marking phases. The mapping between the slots and the backing stores are
   // created at the atomic pause phase.
   HashSet<MovableReference*> traced_slots_;
+  HashMap<MovableReference*, const char*> traced_slots_names_;
 
   // Set to |true| when a compacting sweep will go ahead.
   bool do_compact_ = false;
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index 090b9903..1c3c82d 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -56,15 +56,17 @@
                  void** object_slot,
                  TraceDescriptor desc,
                  WeakCallback callback) final {}
-  void VisitBackingStoreStrongly(void* object,
+  void VisitBackingStoreStrongly(const char*,
+                                 void* object,
                                  void** object_slot,
                                  TraceDescriptor desc) final {}
-  void VisitBackingStoreWeakly(void*,
+  void VisitBackingStoreWeakly(const char*,
+                               void*,
                                void**,
                                TraceDescriptor,
                                WeakCallback,
                                void*) final {}
-  void VisitBackingStoreOnly(void*, void**) final {}
+  void VisitBackingStoreOnly(const char*, void*, void**) final {}
   void RegisterBackingStoreCallback(void** slot,
                                     MovingObjectCallback,
                                     void* callback_data) final {}
diff --git a/third_party/blink/renderer/platform/heap/marking_verifier.h b/third_party/blink/renderer/platform/heap/marking_verifier.h
index f4095b9..567227b6 100644
--- a/third_party/blink/renderer/platform/heap/marking_verifier.h
+++ b/third_party/blink/renderer/platform/heap/marking_verifier.h
@@ -44,13 +44,17 @@
   }
 
   // Unused overrides.
-  void VisitBackingStoreStrongly(void*, void**, TraceDescriptor) final {}
-  void VisitBackingStoreWeakly(void*,
+  void VisitBackingStoreStrongly(const char*,
+                                 void*,
+                                 void**,
+                                 TraceDescriptor) final {}
+  void VisitBackingStoreWeakly(const char*,
+                               void*,
                                void**,
                                TraceDescriptor,
                                WeakCallback,
                                void*) final {}
-  void VisitBackingStoreOnly(void*, void**) final {}
+  void VisitBackingStoreOnly(const char*, void*, void**) final {}
   void RegisterBackingStoreCallback(void**, MovingObjectCallback, void*) final {
   }
   void RegisterWeakCallback(void*, WeakCallback) final {}
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.cc b/third_party/blink/renderer/platform/heap/marking_visitor.cc
index 1c9bfab..d26ca31 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -41,13 +41,14 @@
   weak_callback_worklist_.Push({object, callback});
 }
 
-void MarkingVisitorBase::RegisterBackingStoreReference(void** slot) {
+void MarkingVisitorBase::RegisterBackingStoreReference(const char* name,
+                                                       void** slot) {
   if (marking_mode_ != kGlobalMarkingWithCompaction)
     return;
   // TODO(mlippautz): Do not call into heap directly but rather use a Worklist
   // as temporary storage.
   Heap().RegisterMovingObjectReference(
-      reinterpret_cast<MovableReference*>(slot));
+      name, reinterpret_cast<MovableReference*>(slot));
 }
 
 void MarkingVisitorBase::RegisterBackingStoreCallback(
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.h b/third_party/blink/renderer/platform/heap/marking_visitor.h
index 3c4a767..9389088 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.h
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.h
@@ -62,22 +62,24 @@
     RegisterWeakCallback(object_slot, callback);
   }
 
-  void VisitBackingStoreStrongly(void* object,
+  void VisitBackingStoreStrongly(const char* name,
+                                 void* object,
                                  void** object_slot,
                                  TraceDescriptor desc) final {
-    RegisterBackingStoreReference(object_slot);
+    RegisterBackingStoreReference(name, object_slot);
     if (!object)
       return;
     Visit(object, desc);
   }
 
   // All work is registered through RegisterWeakCallback.
-  void VisitBackingStoreWeakly(void* object,
+  void VisitBackingStoreWeakly(const char* name,
+                               void* object,
                                void** object_slot,
                                TraceDescriptor desc,
                                WeakCallback callback,
                                void* parameter) final {
-    RegisterBackingStoreReference(object_slot);
+    RegisterBackingStoreReference(name, object_slot);
     if (!object)
       return;
     RegisterWeakCallback(parameter, callback);
@@ -86,8 +88,10 @@
   // Used to only mark the backing store when it has been registered for weak
   // processing. In this case, the contents are processed separately using
   // the corresponding traits but the backing store requires marking.
-  void VisitBackingStoreOnly(void* object, void** object_slot) final {
-    RegisterBackingStoreReference(object_slot);
+  void VisitBackingStoreOnly(const char* name,
+                             void* object,
+                             void** object_slot) final {
+    RegisterBackingStoreReference(name, object_slot);
     if (!object)
       return;
     MarkHeaderNoTracing(HeapObjectHeader::FromPayload(object));
@@ -120,7 +124,7 @@
   // transitioning an object from unmarked to marked state.
   ALWAYS_INLINE void AccountMarkedBytes(HeapObjectHeader*);
 
-  void RegisterBackingStoreReference(void** slot);
+  void RegisterBackingStoreReference(const char* name, void** slot);
 
   MarkingWorklist::View marking_worklist_;
   NotFullyConstructedWorklist::View not_fully_constructed_worklist_;
diff --git a/third_party/blink/renderer/platform/heap/visitor.h b/third_party/blink/renderer/platform/heap/visitor.h
index fd95a7c..e5a757d 100644
--- a/third_party/blink/renderer/platform/heap/visitor.h
+++ b/third_party/blink/renderer/platform/heap/visitor.h
@@ -120,7 +120,8 @@
     static_assert(IsGarbageCollectedType<T>::value,
                   "T needs to be a garbage collected object");
 
-    VisitBackingStoreStrongly(reinterpret_cast<void*>(backing_store),
+    VisitBackingStoreStrongly(__PRETTY_FUNCTION__,
+                              reinterpret_cast<void*>(backing_store),
                               reinterpret_cast<void**>(backing_store_slot),
                               TraceDescriptorFor(backing_store));
   }
@@ -134,7 +135,8 @@
     static_assert(IsGarbageCollectedType<T>::value,
                   "T needs to be a garbage collected object");
 
-    VisitBackingStoreWeakly(reinterpret_cast<void*>(backing_store),
+    VisitBackingStoreWeakly(__PRETTY_FUNCTION__,
+                            reinterpret_cast<void*>(backing_store),
                             reinterpret_cast<void**>(backing_store_slot),
                             TraceTrait<T>::GetTraceDescriptor(
                                 reinterpret_cast<void*>(backing_store)),
@@ -147,7 +149,8 @@
     static_assert(IsGarbageCollectedType<T>::value,
                   "T needs to be a garbage collected object");
 
-    VisitBackingStoreOnly(reinterpret_cast<void*>(backing_store),
+    VisitBackingStoreOnly(__PRETTY_FUNCTION__,
+                          reinterpret_cast<void*>(backing_store),
                           reinterpret_cast<void**>(backing_store_slot));
   }
 
@@ -217,13 +220,17 @@
   virtual void VisitWeak(void*, void**, TraceDescriptor, WeakCallback) = 0;
 
   // Visitors for collection backing stores.
-  virtual void VisitBackingStoreStrongly(void*, void**, TraceDescriptor) = 0;
-  virtual void VisitBackingStoreWeakly(void*,
+  virtual void VisitBackingStoreStrongly(const char*,
+                                         void*,
+                                         void**,
+                                         TraceDescriptor) = 0;
+  virtual void VisitBackingStoreWeakly(const char*,
+                                       void*,
                                        void**,
                                        TraceDescriptor,
                                        WeakCallback,
                                        void*) = 0;
-  virtual void VisitBackingStoreOnly(void*, void**) = 0;
+  virtual void VisitBackingStoreOnly(const char*, void*, void**) = 0;
 
   // Visits cross-component references to V8.
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index aee9e1c..bc0bf2a 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -568,10 +568,6 @@
       status: "experimental",
     },
     {
-      name: "FastFlatTreeTraversal",
-      status: "stable",
-    },
-    {
       name: "FastMobileScrolling",
       status: {"Android": "stable"},
     },
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 91d2c33..2fcedc1c 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -230,7 +230,7 @@
 crbug.com/451577 [ Linux ] http/tests/devtools/elements/user-properties.js [ Slow ]
 crbug.com/451577 [ Linux ] http/tests/devtools/layers/layer-canvas-log.js [ Slow ]
 
-crbug.com/793771 media/controls/modern/scrubbing.html [ Slow ]
+crbug.com/793771 media/controls/scrubbing.html [ Slow ]
 
 # These tests need to do many iterations and so can't be fast.
 crbug.com/748418 http/tests/streams/chromium/deep-recursion-getwriter.html [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 50d1e4e..c3f0f19 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1853,9 +1853,7 @@
 # expectation files. The following tests with [ Failure ] don't have failure
 # expectation files because they contain local path names.
 # Use crbug.com/490511 for untriaged failures.
-crbug.com/749492 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/008.html [ Skip ]
 crbug.com/749492 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/009.html [ Skip ]
-crbug.com/749492 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/010.html [ Skip ]
 crbug.com/490511 external/wpt/html/browsers/offline/application-cache-api/api_update.https.html [ Failure Pass ]
 crbug.com/108417 external/wpt/html/rendering/non-replaced-elements/tables/table-border-1.html [ Failure ]
 crbug.com/490511 external/wpt/html/rendering/non-replaced-elements/the-hr-element-0/color.html [ Failure ]
@@ -4825,19 +4823,19 @@
 
 # Double tap on modern media controls is a bit more complicated on Mac but
 # since we are not targeting Mac yet we can come back and fix this later.
-crbug.com/783154 [ Mac ] media/controls/modern/doubletap-to-jump-backwards.html [ Skip ]
-crbug.com/783154 [ Mac ] media/controls/modern/doubletap-to-jump-forwards.html [ Skip ]
-crbug.com/783154 [ Mac ] media/controls/modern/doubletap-to-jump-forwards-too-short.html [ Skip ]
-crbug.com/783154 [ Mac ] media/controls/modern/doubletap-on-play-button.html [ Skip ]
-crbug.com/783154 [ Mac ] media/controls/modern/doubletap-to-toggle-fullscreen.html [ Skip ]
+crbug.com/783154 [ Mac ] media/controls/doubletap-to-jump-backwards.html [ Skip ]
+crbug.com/783154 [ Mac ] media/controls/doubletap-to-jump-forwards.html [ Skip ]
+crbug.com/783154 [ Mac ] media/controls/doubletap-to-jump-forwards-too-short.html [ Skip ]
+crbug.com/783154 [ Mac ] media/controls/doubletap-on-play-button.html [ Skip ]
+crbug.com/783154 [ Mac ] media/controls/doubletap-to-toggle-fullscreen.html [ Skip ]
 crbug.com/783154 [ Mac ] media/controls/click-anywhere-to-play-pause.html [ Skip ]
 
 # Seen flaky on Linux, suppressing on Windows as well
-crbug.com/831720 [ Win ] media/controls/modern/doubletap-to-jump-forwards-too-short.html [ Pass Failure ]
-crbug.com/831720 [ Linux ] media/controls/modern/doubletap-to-jump-forwards-too-short.html [ Pass Failure ]
-crbug.com/831720 [ Mac ] media/controls/modern/tap-to-hide-controls.html [ Pass Failure ]
-crbug.com/831720 [ Win ] media/controls/modern/tap-to-hide-controls.html [ Pass Failure ]
-crbug.com/831720 [ Linux ] media/controls/modern/tap-to-hide-controls.html [ Pass Failure ]
+crbug.com/831720 [ Win ] media/controls/doubletap-to-jump-forwards-too-short.html [ Pass Failure ]
+crbug.com/831720 [ Linux ] media/controls/doubletap-to-jump-forwards-too-short.html [ Pass Failure ]
+crbug.com/831720 [ Mac ] media/controls/tap-to-hide-controls.html [ Pass Failure ]
+crbug.com/831720 [ Win ] media/controls/tap-to-hide-controls.html [ Pass Failure ]
+crbug.com/831720 [ Linux ] media/controls/tap-to-hide-controls.html [ Pass Failure ]
 
 crbug.com/802915 css3/blending/isolation-should-include-non-local-background.html [ Failure ]
 
@@ -4847,7 +4845,7 @@
 crbug.com/779087 external/wpt/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html [ Skip ]
 
 # Does not work on Mac
-crbug.com/793771 [ Mac ] media/controls/modern/scrubbing.html [ Skip ]
+crbug.com/793771 [ Mac ] media/controls/scrubbing.html [ Skip ]
 crbug.com/916902 [ Mac ] virtual/fractional_scrolling/fast/scrolling/overflow-scrollability.html [ Skip ]
 
 # Different paths may have different anti-aliasing pixels 2018-02-21
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/hyphens-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/hyphens-computed-expected.txt
new file mode 100644
index 0000000..0afda9c1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/hyphens-computed-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS Property hyphens value 'none' computes to 'none'
+PASS Property hyphens value 'manual' computes to 'manual'
+FAIL Property hyphens value 'auto' computes to 'auto' assert_equals: expected "auto" but got "manual"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/hyphens-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/hyphens-computed.html
new file mode 100644
index 0000000..61a3c14
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/hyphens-computed.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().hyphens</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-hyphens">
+<meta name="assert" content="hyphens computed value is specified keyword.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("hyphens", "none");
+test_computed_value("hyphens", "manual");
+test_computed_value("hyphens", "auto");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/letter-spacing-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/letter-spacing-computed-expected.txt
new file mode 100644
index 0000000..584cb53
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/letter-spacing-computed-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+FAIL Property letter-spacing value 'normal' computes to '0px' assert_equals: expected "0px" but got "normal"
+PASS Property letter-spacing value '10px' computes to '10px'
+PASS Property letter-spacing value '-20px' computes to '-20px'
+PASS Property letter-spacing value 'calc(10px - 0.5em)' computes to '-10px'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/letter-spacing-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/letter-spacing-computed.html
new file mode 100644
index 0000000..8a5bbb43
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/letter-spacing-computed.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().letterSpacing</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-letter-spacing">
+<meta name="assert" content="letter-spacing computed value is an absolute length.">
+<meta name="assert" content="'normal' computes to zero.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    font-size: 40px;
+  }
+</style>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("letter-spacing", "normal", "0px");
+
+test_computed_value("letter-spacing", "10px");
+test_computed_value("letter-spacing", "-20px");
+test_computed_value("letter-spacing", "calc(10px - 0.5em)", "-10px");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/line-break-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/line-break-computed.html
new file mode 100644
index 0000000..af06d821
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/line-break-computed.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().lineBreak</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-line-break">
+<meta name="assert" content="line-break computed value is specified keyword.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("line-break", "auto");
+test_computed_value("line-break", "loose");
+test_computed_value("line-break", "normal");
+test_computed_value("line-break", "strict");
+test_computed_value("line-break", "anywhere");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/overflow-wrap-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/overflow-wrap-computed-expected.txt
new file mode 100644
index 0000000..7983d76
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/overflow-wrap-computed-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS Property overflow-wrap value 'normal' computes to 'normal'
+PASS Property overflow-wrap value 'break-word' computes to 'break-word'
+FAIL Property overflow-wrap value 'anywhere' computes to 'anywhere' assert_equals: expected "anywhere" but got "normal"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/overflow-wrap-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/overflow-wrap-computed.html
new file mode 100644
index 0000000..469d1cc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/overflow-wrap-computed.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().overflowWrap</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-overflow-wrap">
+<meta name="assert" content="overflow-wrap computed value is specified keyword.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("overflow-wrap", "normal");
+test_computed_value("overflow-wrap", "break-word");
+test_computed_value("overflow-wrap", "anywhere");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/tab-size-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/tab-size-computed-expected.txt
new file mode 100644
index 0000000..d89a011
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/tab-size-computed-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+PASS Property tab-size value '0' computes to '0'
+PASS Property tab-size value '16' computes to '16'
+PASS Property tab-size value '4' computes to '4'
+FAIL Property tab-size value '2.5' computes to '2.5' assert_equals: expected "2.5" but got "8"
+PASS Property tab-size value '0px' computes to '0px'
+PASS Property tab-size value '10px' computes to '10px'
+PASS Property tab-size value 'calc(10px + 0.5em)' computes to '30px'
+PASS Property tab-size value 'calc(10px - 0.5em)' computes to '0px'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/tab-size-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/tab-size-computed.html
new file mode 100644
index 0000000..aff25ad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/tab-size-computed.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().tabSize</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-tab-size">
+<meta name="assert" content="tab-size computed value is the specified number or an absolute length.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    font-size: 40px;
+  }
+</style>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("tab-size", "0");
+test_computed_value("tab-size", "16");
+test_computed_value("tab-size", "4");
+test_computed_value("tab-size", "2.5");
+
+test_computed_value("tab-size", "0px");
+test_computed_value("tab-size", "10px");
+test_computed_value("tab-size", "calc(10px + 0.5em)", "30px");
+test_computed_value("tab-size", "calc(10px - 0.5em)", "0px");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-align-last-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-align-last-computed-expected.txt
new file mode 100644
index 0000000..ebc6e6c1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-align-last-computed-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+PASS Property text-align-last value 'auto' computes to 'auto'
+PASS Property text-align-last value 'start' computes to 'start'
+PASS Property text-align-last value 'end' computes to 'end'
+PASS Property text-align-last value 'left' computes to 'left'
+PASS Property text-align-last value 'right' computes to 'right'
+PASS Property text-align-last value 'center' computes to 'center'
+PASS Property text-align-last value 'justify' computes to 'justify'
+FAIL Property text-align-last value 'match-parent' computes to 'match-parent' assert_equals: expected "match-parent" but got "auto"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-align-last-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-align-last-computed.html
new file mode 100644
index 0000000..5853c221
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-align-last-computed.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().textAlignLast</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-text-align-last">
+<meta name="assert" content="text-align-last computed value is specified keyword.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("text-align-last", "auto");
+test_computed_value("text-align-last", "start");
+test_computed_value("text-align-last", "end");
+test_computed_value("text-align-last", "left");
+test_computed_value("text-align-last", "right");
+test_computed_value("text-align-last", "center");
+test_computed_value("text-align-last", "justify");
+test_computed_value("text-align-last", "match-parent");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-indent-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-indent-computed.html
new file mode 100644
index 0000000..f6da4964d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-indent-computed.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().textIndent</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-text-indent">
+<meta name="assert" content="text-indent computed value is computed <length-percentage> value, plus any specified keywords.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    font-size: 40px;
+  }
+</style>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("text-indent", "10px");
+test_computed_value("text-indent", "20%");
+test_computed_value("text-indent", "calc(50% + 60px)");
+test_computed_value("text-indent", "-30px");
+test_computed_value("text-indent", "-40%");
+test_computed_value("text-indent", "calc(10px - 0.5em)", "-10px");
+
+test_computed_value("text-indent", "10px hanging");
+test_computed_value("text-indent", "20% each-line");
+test_computed_value("text-indent", "calc(50% + 60px) hanging each-line");
+test_computed_value("text-indent", "each-line hanging calc(10px + 0.5em)", "30px hanging each-line");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-justify-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-justify-computed-expected.txt
new file mode 100644
index 0000000..4912130
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-justify-computed-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+PASS Property text-justify value 'auto' computes to 'auto'
+PASS Property text-justify value 'none' computes to 'none'
+PASS Property text-justify value 'inter-word' computes to 'inter-word'
+FAIL Property text-justify value 'inter-character' computes to 'inter-character' assert_equals: expected "inter-character" but got "auto"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-justify-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-justify-computed.html
new file mode 100644
index 0000000..b219305
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-justify-computed.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().textJustify</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-text-justify">
+<meta name="assert" content="text-justify computed value is specified keyword.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("text-justify", "auto");
+test_computed_value("text-justify", "none");
+test_computed_value("text-justify", "inter-word");
+test_computed_value("text-justify", "inter-character");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-transform-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-transform-computed-expected.txt
new file mode 100644
index 0000000..1eb332b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-transform-computed-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+PASS Property text-transform value 'none' computes to 'none'
+PASS Property text-transform value 'capitalize' computes to 'capitalize'
+PASS Property text-transform value 'uppercase' computes to 'uppercase'
+PASS Property text-transform value 'lowercase' computes to 'lowercase'
+FAIL Property text-transform value 'full-width' computes to 'full-width' assert_equals: expected "full-width" but got "none"
+FAIL Property text-transform value 'full-size-kana' computes to 'full-size-kana' assert_equals: expected "full-size-kana" but got "none"
+FAIL Property text-transform value 'capitalize full-width' computes to 'capitalize full-width' assert_equals: expected "capitalize full-width" but got "none"
+FAIL Property text-transform value 'full-width full-size-kana' computes to 'full-width full-size-kana' assert_equals: expected "full-width full-size-kana" but got "none"
+FAIL Property text-transform value 'uppercase full-width full-size-kana' computes to 'uppercase full-width full-size-kana' assert_equals: expected "uppercase full-width full-size-kana" but got "none"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-transform-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-transform-computed.html
new file mode 100644
index 0000000..68f2278
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/text-transform-computed.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().textTransform</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-text-transform">
+<meta name="assert" content="text-transform computed value is specified keywords.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("text-transform", "none");
+
+test_computed_value("text-transform", "capitalize");
+test_computed_value("text-transform", "uppercase");
+test_computed_value("text-transform", "lowercase");
+test_computed_value("text-transform", "full-width");
+test_computed_value("text-transform", "full-size-kana");
+
+test_computed_value("text-transform", "capitalize full-width");
+test_computed_value("text-transform", "full-width full-size-kana");
+
+test_computed_value("text-transform", "uppercase full-width full-size-kana");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/white-space-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/white-space-computed.html
new file mode 100644
index 0000000..047a8037
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/white-space-computed.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().whiteSpace</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-white-space">
+<meta name="assert" content="white-space computed value is specified keyword.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("white-space", "normal");
+test_computed_value("white-space", "pre");
+test_computed_value("white-space", "nowrap");
+test_computed_value("white-space", "pre-wrap");
+test_computed_value("white-space", "break-spaces");
+test_computed_value("white-space", "pre-line");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-break-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-break-computed.html
new file mode 100644
index 0000000..b12a108
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-break-computed.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().wordBreak</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-word-break">
+<meta name="assert" content="word-break computed value is specified keyword.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("word-break", "normal");
+test_computed_value("word-break", "keep-all");
+test_computed_value("word-break", "break-all");
+test_computed_value("word-break", "break-word");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-spacing-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-spacing-computed.html
new file mode 100644
index 0000000..095b83b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-spacing-computed.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().wordSpacing</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-word-spacing">
+<meta name="assert" content="word-spacing computed value is an absolute length.">
+<meta name="assert" content="'normal' computes to zero.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    font-size: 40px;
+  }
+</style>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("word-spacing", "normal", "0px");
+
+test_computed_value("word-spacing", "10px");
+test_computed_value("word-spacing", "-20px");
+test_computed_value("word-spacing", "calc(10px - 0.5em)", "-10px");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-wrap-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-wrap-computed-expected.txt
new file mode 100644
index 0000000..bd94d44
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-wrap-computed-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS Property word-wrap value 'normal' computes to 'normal'
+PASS Property word-wrap value 'break-word' computes to 'break-word'
+FAIL Property word-wrap value 'anywhere' computes to 'anywhere' assert_equals: expected "anywhere" but got "normal"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-wrap-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-wrap-computed.html
new file mode 100644
index 0000000..bb79ede
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-wrap-computed.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text: getComputedValue().wordWrap</title>
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#propdef-word-wrap">
+<meta name="assert" content="word-wrap computed value is specified keyword.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("word-wrap", "normal");
+test_computed_value("word-wrap", "break-word");
+test_computed_value("word-wrap", "anywhere");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/mouse-cursor-imagemap.html b/third_party/blink/web_tests/external/wpt/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/mouse-cursor-imagemap.html
new file mode 100644
index 0000000..78f69a2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/mouse-cursor-imagemap.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<img usemap="#map" src="data:image/gif;base64,R0lGODlhAQABAIAAAOTm7AAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" width="1000" height="1000" style="border: 1px solid black;">
+<map name="map">
+  <area id="clickable-area" shape="rect" coords="0,0,500,500" href="#" role="img">
+  <area id="nonclickable-area" shape="rect" coords="500,500,1000,1000" role="img"><!-- No href attribute.-->
+</map>
+
+<p>An unclickable (non-link) area should not show the link cursor when hovered.</p>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+  let clickable = window.getComputedStyle(document.getElementById('clickable-area'));
+  let nonclickable = window.getComputedStyle(document.getElementById('nonclickable-area'));
+  assert_equals(clickable.getPropertyValue('cursor'), 'pointer');
+  assert_not_equals(nonclickable.getPropertyValue('cursor'), 'pointer');
+}, 'Only clickable areas should show the link cursor.');
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/measure-with-dict.html b/third_party/blink/web_tests/external/wpt/user-timing/measure-with-dict.html
index e5869e1..1c46709 100644
--- a/third_party/blink/web_tests/external/wpt/user-timing/measure-with-dict.html
+++ b/third_party/blink/web_tests/external/wpt/user-timing/measure-with-dict.html
@@ -5,7 +5,13 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/user-timing-helper.js"></script>
 <script>
+  function cleanupPerformanceTimeline() {
+    performance.clearMarks();
+    performance.clearMeasures();
+  }
+
   async_test(function (t) {
+    this.add_cleanup(cleanupPerformanceTimeline);
     let measureEntries = [];
     const timeStamp1 = 784.4;
     const timeStamp2 = 1234.5;
@@ -40,8 +46,6 @@
           }
         })
       );
-    self.performance.clearMarks();
-    self.performance.clearMeasures();
     observer.observe({ entryTypes: ["measure"] });
     self.performance.mark("mark1", { detail: { randomInfo: 3 }, startTime: timeStamp1 });
     self.performance.mark("mark2", { startTime: timeStamp2 });
@@ -80,10 +84,16 @@
     checkEntries(returnedEntries, expectedEntries);
   }, "measure entries' detail and start/end are customizable");
 
-  async_test(function (t) {
-    assert_throws("SyntaxError", function() {
+  test(function () {
+    this.add_cleanup(cleanupPerformanceTimeline);
+    assert_throws(new TypeError(), function() {
       self.performance.measure("wrongUsage1", {}, 12);
-    });
-    t.done();
-  }, "measure should throw exception when passing option object and end at the same time");
+    }, "measure should throw a TypeError when passed an options object and an end time");
+    assert_throws(new TypeError(), function() {
+      self.performance.measure("wrongUsage2", {'startTime': 2}, 12);
+    }, "measure should throw a TypeError when passed an options object and an end time");
+    assert_throws(new TypeError(), function() {
+      self.performance.measure("wrongUsage3", {'startTime': 2}, 'mark1');
+    }, "measure should throw a TypeError when passed an options object and an end mark");
+  }, "measure should throw a TypeError when passed an invalid argument combination");
 </script>
diff --git a/third_party/blink/web_tests/media/controls/modern/add-poster-after-page-load.html b/third_party/blink/web_tests/media/controls/add-poster-after-page-load.html
similarity index 60%
rename from third_party/blink/web_tests/media/controls/modern/add-poster-after-page-load.html
rename to third_party/blink/web_tests/media/controls/add-poster-after-page-load.html
index 4e77795..4069ac9 100644
--- a/third_party/blink/web_tests/media/controls/modern/add-poster-after-page-load.html
+++ b/third_party/blink/web_tests/media/controls/add-poster-after-page-load.html
@@ -1,17 +1,17 @@
 <!DOCTYPE html>
 <html>
 <title>Test that adding a poster after page load shows the poster.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 preload=none src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 preload=none src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
 
   window.onload = t.step_func(() => {
     assert_true(showsDefaultPoster());
-    video.setAttribute('poster', '../../content/greenbox.png');
+    video.setAttribute('poster', '../content/greenbox.png');
     setTimeout(t.step_func_done(() => {
       assert_false(showsDefaultPoster());
     }));
diff --git a/third_party/blink/web_tests/media/controls/modern/dont-show-disabled-controls-in-overflow.html b/third_party/blink/web_tests/media/controls/dont-show-disabled-controls-in-overflow.html
similarity index 71%
rename from third_party/blink/web_tests/media/controls/modern/dont-show-disabled-controls-in-overflow.html
rename to third_party/blink/web_tests/media/controls/dont-show-disabled-controls-in-overflow.html
index 1790889..7beace0 100644
--- a/third_party/blink/web_tests/media/controls/modern/dont-show-disabled-controls-in-overflow.html
+++ b/third_party/blink/web_tests/media/controls/dont-show-disabled-controls-in-overflow.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that small videos don't show disabled mute/fullscreen buttons in the overflow menu.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=50 preload=none src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=50 preload=none src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/doubletap-on-play-button.html b/third_party/blink/web_tests/media/controls/doubletap-on-play-button.html
similarity index 75%
rename from third_party/blink/web_tests/media/controls/modern/doubletap-on-play-button.html
rename to third_party/blink/web_tests/media/controls/doubletap-on-play-button.html
index b35fead..20352d9 100644
--- a/third_party/blink/web_tests/media/controls/modern/doubletap-on-play-button.html
+++ b/third_party/blink/web_tests/media/controls/doubletap-on-play-button.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that player will play then pause if double tapped on the overlay play button.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<script src="../overlay-play-button.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<script src="overlay-play-button.js"></script>
 <body></body>
 <script>
 async_test(t => {
@@ -14,7 +14,7 @@
   const video = document.createElement('video');
   video.controls = true;
   video.width = 400;
-  video.src = '../../content/60_sec_video.webm';
+  video.src = '../content/60_sec_video.webm';
   document.body.appendChild(video);
   let didPause = false;
 
diff --git a/third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-backwards-at-start.html b/third_party/blink/web_tests/media/controls/doubletap-to-jump-backwards-at-start.html
similarity index 75%
rename from third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-backwards-at-start.html
rename to third_party/blink/web_tests/media/controls/doubletap-to-jump-backwards-at-start.html
index 1716a22e0..ff46cc9 100644
--- a/third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-backwards-at-start.html
+++ b/third_party/blink/web_tests/media/controls/doubletap-to-jump-backwards-at-start.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that player will jump to the beginning if it's in the first 10 seconds.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-backwards.html b/third_party/blink/web_tests/media/controls/doubletap-to-jump-backwards.html
similarity index 78%
rename from third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-backwards.html
rename to third_party/blink/web_tests/media/controls/doubletap-to-jump-backwards.html
index 095e947..0415ee02 100644
--- a/third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-backwards.html
+++ b/third_party/blink/web_tests/media/controls/doubletap-to-jump-backwards.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
 <title>Test that player will jump backwards 10 seconds if double tapped on the left hand side.</title>
-<script src="../../../resources/gesture-util.js"></script>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/gesture-util.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-forwards-too-short.html b/third_party/blink/web_tests/media/controls/doubletap-to-jump-forwards-too-short.html
similarity index 76%
rename from third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-forwards-too-short.html
rename to third_party/blink/web_tests/media/controls/doubletap-to-jump-forwards-too-short.html
index 6df970a..284058f 100644
--- a/third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-forwards-too-short.html
+++ b/third_party/blink/web_tests/media/controls/doubletap-to-jump-forwards-too-short.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that player will jump to the end if less than 10 seconds remaining.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-forwards.html b/third_party/blink/web_tests/media/controls/doubletap-to-jump-forwards.html
similarity index 75%
rename from third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-forwards.html
rename to third_party/blink/web_tests/media/controls/doubletap-to-jump-forwards.html
index 06eff61b..273b361 100644
--- a/third_party/blink/web_tests/media/controls/modern/doubletap-to-jump-forwards.html
+++ b/third_party/blink/web_tests/media/controls/doubletap-to-jump-forwards.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
 <title>Test that player will jump forwards 10 seconds if double tapped.</title>
-<script src="../../../resources/gesture-util.js"></script>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/gesture-util.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/doubletap-to-toggle-fullscreen.html b/third_party/blink/web_tests/media/controls/doubletap-to-toggle-fullscreen.html
similarity index 78%
rename from third_party/blink/web_tests/media/controls/modern/doubletap-to-toggle-fullscreen.html
rename to third_party/blink/web_tests/media/controls/doubletap-to-toggle-fullscreen.html
index fe295ff..5f0a1138 100644
--- a/third_party/blink/web_tests/media/controls/modern/doubletap-to-toggle-fullscreen.html
+++ b/third_party/blink/web_tests/media/controls/doubletap-to-toggle-fullscreen.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that player will enter fullscreen if double tapped.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/immersive-mode-adds-css-class.html b/third_party/blink/web_tests/media/controls/immersive-mode-adds-css-class.html
similarity index 88%
rename from third_party/blink/web_tests/media/controls/modern/immersive-mode-adds-css-class.html
rename to third_party/blink/web_tests/media/controls/immersive-mode-adds-css-class.html
index 9dd1f748..e4c4e5f 100644
--- a/third_party/blink/web_tests/media/controls/modern/immersive-mode-adds-css-class.html
+++ b/third_party/blink/web_tests/media/controls/immersive-mode-adds-css-class.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
 <title>Test that enabling immersive mode adds the immersive mode CSS class.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/run-after-layout-and-paint.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 preload=metadata src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 preload=metadata src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/overflow-button-disabled-no-source.html b/third_party/blink/web_tests/media/controls/overflow-button-disabled-no-source.html
similarity index 77%
rename from third_party/blink/web_tests/media/controls/modern/overflow-button-disabled-no-source.html
rename to third_party/blink/web_tests/media/controls/overflow-button-disabled-no-source.html
index 53d40dc9..1755c0d0 100644
--- a/third_party/blink/web_tests/media/controls/modern/overflow-button-disabled-no-source.html
+++ b/third_party/blink/web_tests/media/controls/overflow-button-disabled-no-source.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <html>
 <title>Test that the overflow menu is shown but disabled with no source.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
 <video controls width=400 controlsList=nodownload disablePictureInPicture></video>
 <script>
 async_test(t => {
@@ -14,7 +14,7 @@
   assert_equals(overflowButton(video).style.display, '');
 
   // Set the source and start playing.
-  video.src = '../../content/60_sec_video.webm';
+  video.src = '../content/60_sec_video.webm';
   video.play().then(t.step_func_done(() => {
     // Make sure the button has been hidden and is no longer disabled.
     assert_false(overflowButton(video).disabled);
diff --git a/third_party/blink/web_tests/media/controls/modern/overlay-play-button-tap-to-hide.html b/third_party/blink/web_tests/media/controls/overlay-play-button-tap-to-hide.html
similarity index 85%
rename from third_party/blink/web_tests/media/controls/modern/overlay-play-button-tap-to-hide.html
rename to third_party/blink/web_tests/media/controls/overlay-play-button-tap-to-hide.html
index 96e1e2e..0db4d93 100644
--- a/third_party/blink/web_tests/media/controls/modern/overlay-play-button-tap-to-hide.html
+++ b/third_party/blink/web_tests/media/controls/overlay-play-button-tap-to-hide.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <title>Hide the overlay play button on play, show it on tap</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<script src="../overlay-play-button.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<script src="overlay-play-button.js"></script>
 <body>
 <script>
 async_test((t) => {
@@ -46,6 +46,6 @@
     singleTapOnControl(button);
   }), { once: true });
 
-  video.src = '../../content/test.webm';
+  video.src = '../content/test.webm';
 });
 </script>
diff --git a/third_party/blink/web_tests/media/controls/modern/quirks-mode-timeline-is-not-hidden.html b/third_party/blink/web_tests/media/controls/quirks-mode-timeline-is-not-hidden.html
similarity index 70%
rename from third_party/blink/web_tests/media/controls/modern/quirks-mode-timeline-is-not-hidden.html
rename to third_party/blink/web_tests/media/controls/quirks-mode-timeline-is-not-hidden.html
index 8b177767..e33e3f9 100644
--- a/third_party/blink/web_tests/media/controls/modern/quirks-mode-timeline-is-not-hidden.html
+++ b/third_party/blink/web_tests/media/controls/quirks-mode-timeline-is-not-hidden.html
@@ -1,11 +1,11 @@
 <html>
 <!-- We don't declare a DOCTYPE in order to force quirks mode -->
 <title>Test that on quirks mode the timeline is not hidden.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
 <body>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
+<video controls width=400 src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/scrubbing-stops-when-controls-hidden.html b/third_party/blink/web_tests/media/controls/scrubbing-stops-when-controls-hidden.html
similarity index 89%
rename from third_party/blink/web_tests/media/controls/modern/scrubbing-stops-when-controls-hidden.html
rename to third_party/blink/web_tests/media/controls/scrubbing-stops-when-controls-hidden.html
index f7744c5..cf380c5 100644
--- a/third_party/blink/web_tests/media/controls/modern/scrubbing-stops-when-controls-hidden.html
+++ b/third_party/blink/web_tests/media/controls/scrubbing-stops-when-controls-hidden.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <title>Tests that scrubbing stops when the controls are hidden.</title>
-<script src='../../../resources/testharness.js'></script>
-<script src='../../../resources/testharnessreport.js'></script>
-<script src='../../media-controls.js'></script>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
+<script src='../../resources/testharness.js'></script>
+<script src='../../resources/testharnessreport.js'></script>
+<script src='../media-controls.js'></script>
+<video controls width=400 src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   let video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/scrubbing-touch.html b/third_party/blink/web_tests/media/controls/scrubbing-touch.html
similarity index 88%
rename from third_party/blink/web_tests/media/controls/modern/scrubbing-touch.html
rename to third_party/blink/web_tests/media/controls/scrubbing-touch.html
index fa95d18..4799f8a 100644
--- a/third_party/blink/web_tests/media/controls/modern/scrubbing-touch.html
+++ b/third_party/blink/web_tests/media/controls/scrubbing-touch.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that player will behave correctly when scrubbing with touch events.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/scrubbing.html b/third_party/blink/web_tests/media/controls/scrubbing.html
similarity index 87%
rename from third_party/blink/web_tests/media/controls/modern/scrubbing.html
rename to third_party/blink/web_tests/media/controls/scrubbing.html
index 1bddb1fb5..ef3bcde 100644
--- a/third_party/blink/web_tests/media/controls/modern/scrubbing.html
+++ b/third_party/blink/web_tests/media/controls/scrubbing.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that player will behave correctly when scrubbing.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/show-pause-icon-on-non-overlay-play-button.html b/third_party/blink/web_tests/media/controls/show-pause-icon-on-non-overlay-play-button.html
similarity index 79%
rename from third_party/blink/web_tests/media/controls/modern/show-pause-icon-on-non-overlay-play-button.html
rename to third_party/blink/web_tests/media/controls/show-pause-icon-on-non-overlay-play-button.html
index b2a194f..d5195f31 100644
--- a/third_party/blink/web_tests/media/controls/modern/show-pause-icon-on-non-overlay-play-button.html
+++ b/third_party/blink/web_tests/media/controls/show-pause-icon-on-non-overlay-play-button.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that playing a video shows a pause icon in the non-overlay play button.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=90 preload=metadata src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=90 preload=metadata src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/singletap-on-outside.html b/third_party/blink/web_tests/media/controls/singletap-on-outside.html
similarity index 75%
rename from third_party/blink/web_tests/media/controls/modern/singletap-on-outside.html
rename to third_party/blink/web_tests/media/controls/singletap-on-outside.html
index f253040..ab10b91 100644
--- a/third_party/blink/web_tests/media/controls/modern/singletap-on-outside.html
+++ b/third_party/blink/web_tests/media/controls/singletap-on-outside.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that the player pauses if single taped in the outer region.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<script src="../overlay-play-button.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<script src="overlay-play-button.js"></script>
 <body></body>
 <script>
 async_test(t => {
@@ -14,7 +14,7 @@
   const video = document.createElement('video');
   video.controls = true;
   video.width = 400;
-  video.src = '../../content/60_sec_video.webm';
+  video.src = '../content/60_sec_video.webm';
   document.body.appendChild(video);
 
   video.addEventListener('playing', t.step_func(() => {
diff --git a/third_party/blink/web_tests/media/controls/modern/singletap-on-overlay-closes-overflow-menu.html b/third_party/blink/web_tests/media/controls/singletap-on-overlay-closes-overflow-menu.html
similarity index 84%
rename from third_party/blink/web_tests/media/controls/modern/singletap-on-overlay-closes-overflow-menu.html
rename to third_party/blink/web_tests/media/controls/singletap-on-overlay-closes-overflow-menu.html
index 2d4e7c0..c942d39a6 100644
--- a/third_party/blink/web_tests/media/controls/modern/singletap-on-overlay-closes-overflow-menu.html
+++ b/third_party/blink/web_tests/media/controls/singletap-on-overlay-closes-overflow-menu.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that the overflow menu closes when the overlay play button area is tapped.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<script src="../overlay-play-button.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<script src="overlay-play-button.js"></script>
 <body></body>
 <script>
 async_test(t => {
@@ -14,7 +14,7 @@
   const video = document.createElement('video');
   video.controls = true;
   video.width = 400;
-  video.src='../../content/60_sec_video.webm';
+  video.src='../content/60_sec_video.webm';
   document.body.appendChild(video);
 
   var button = overflowButton(video);
diff --git a/third_party/blink/web_tests/media/controls/modern/singletap-on-play-button.html b/third_party/blink/web_tests/media/controls/singletap-on-play-button.html
similarity index 74%
rename from third_party/blink/web_tests/media/controls/modern/singletap-on-play-button.html
rename to third_party/blink/web_tests/media/controls/singletap-on-play-button.html
index b91556b..cb56b89a1 100644
--- a/third_party/blink/web_tests/media/controls/modern/singletap-on-play-button.html
+++ b/third_party/blink/web_tests/media/controls/singletap-on-play-button.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that the player pauses if single tapped on the play button.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<script src="../overlay-play-button.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<script src="overlay-play-button.js"></script>
 <body></body>
 <script>
 async_test(t => {
@@ -14,7 +14,7 @@
   const video = document.createElement('video');
   video.controls = true;
   video.width = 400;
-  video.src='../../content/60_sec_video.webm';
+  video.src='../content/60_sec_video.webm';
   document.body.appendChild(video);
 
   video.addEventListener('playing', t.step_func(() => {
diff --git a/third_party/blink/web_tests/media/controls/modern/singletouch-on-play-button.html b/third_party/blink/web_tests/media/controls/singletouch-on-play-button.html
similarity index 71%
rename from third_party/blink/web_tests/media/controls/modern/singletouch-on-play-button.html
rename to third_party/blink/web_tests/media/controls/singletouch-on-play-button.html
index e14c13a..f98feaf 100644
--- a/third_party/blink/web_tests/media/controls/modern/singletouch-on-play-button.html
+++ b/third_party/blink/web_tests/media/controls/singletouch-on-play-button.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
 <title>Test that the player pauses if single-touched on the play button.</title>
-<script src="../../../resources/gesture-util.js"></script>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<script src="../overlay-play-button.js"></script>
+<script src="../../resources/gesture-util.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<script src="overlay-play-button.js"></script>
 <body></body>
 <script>
 async_test(t => {
@@ -15,7 +15,7 @@
   const video = document.createElement('video');
   video.controls = true;
   video.width = 400;
-  video.src='../../content/60_sec_video.webm';
+  video.src='../content/60_sec_video.webm';
   document.body.appendChild(video);
 
   let tap_gesture;
diff --git a/third_party/blink/web_tests/media/controls/modern/slow-doubletap.html b/third_party/blink/web_tests/media/controls/slow-doubletap.html
similarity index 67%
rename from third_party/blink/web_tests/media/controls/modern/slow-doubletap.html
rename to third_party/blink/web_tests/media/controls/slow-doubletap.html
index 2d4366c..6cadec1 100644
--- a/third_party/blink/web_tests/media/controls/modern/slow-doubletap.html
+++ b/third_party/blink/web_tests/media/controls/slow-doubletap.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that player will not jump if the tap is too slow.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/tap-to-hide-controls.html b/third_party/blink/web_tests/media/controls/tap-to-hide-controls.html
similarity index 76%
rename from third_party/blink/web_tests/media/controls/modern/tap-to-hide-controls.html
rename to third_party/blink/web_tests/media/controls/tap-to-hide-controls.html
index 85f60c7..2b0105a 100644
--- a/third_party/blink/web_tests/media/controls/modern/tap-to-hide-controls.html
+++ b/third_party/blink/web_tests/media/controls/tap-to-hide-controls.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that the controls are hidden if they are tapped</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   // This number comes from MediaControlOverlayPlayButtonElement.cpp.
diff --git a/third_party/blink/web_tests/media/controls/modern/video-does-not-go-fullscreen-on-double-click-before-preload.html b/third_party/blink/web_tests/media/controls/video-does-not-go-fullscreen-on-double-click-before-preload.html
similarity index 75%
rename from third_party/blink/web_tests/media/controls/modern/video-does-not-go-fullscreen-on-double-click-before-preload.html
rename to third_party/blink/web_tests/media/controls/video-does-not-go-fullscreen-on-double-click-before-preload.html
index 34bb9fb..826e292 100644
--- a/third_party/blink/web_tests/media/controls/modern/video-does-not-go-fullscreen-on-double-click-before-preload.html
+++ b/third_party/blink/web_tests/media/controls/video-does-not-go-fullscreen-on-double-click-before-preload.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that videos do not fullscreen when double-clicked before preload.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<script src="../overlay-play-button.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<script src="overlay-play-button.js"></script>
 <body></body>
 <script>
 async_test(t => {
@@ -14,7 +14,7 @@
   const video = document.createElement('video');
   video.controls = true;
   video.width = 400;
-  video.src='../../content/60_sec_video.webm';
+  video.src='../content/60_sec_video.webm';
   document.body.appendChild(video);
   video.addEventListener("webkitfullscreenchange", t.unreached_func());
 
diff --git a/third_party/blink/web_tests/media/controls/modern/video-preload-none-has-disabled-controls.html b/third_party/blink/web_tests/media/controls/video-preload-none-has-disabled-controls.html
similarity index 78%
rename from third_party/blink/web_tests/media/controls/modern/video-preload-none-has-disabled-controls.html
rename to third_party/blink/web_tests/media/controls/video-preload-none-has-disabled-controls.html
index 434b542..c0405686 100644
--- a/third_party/blink/web_tests/media/controls/modern/video-preload-none-has-disabled-controls.html
+++ b/third_party/blink/web_tests/media/controls/video-preload-none-has-disabled-controls.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <title>Test that videos have disabled mute/fullscreen buttons before preload.</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 preload=none src="../../content/60_sec_video.webm"></video>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls width=400 preload=none src="../content/60_sec_video.webm"></video>
 <script>
 async_test(t => {
   const video = document.querySelector('video');
diff --git a/third_party/blink/web_tests/media/controls/modern/video-tag-with-only-audio-looks-like-audio-tag.html b/third_party/blink/web_tests/media/controls/video-tag-with-only-audio-looks-like-audio-tag.html
similarity index 86%
rename from third_party/blink/web_tests/media/controls/modern/video-tag-with-only-audio-looks-like-audio-tag.html
rename to third_party/blink/web_tests/media/controls/video-tag-with-only-audio-looks-like-audio-tag.html
index 4e65e0c..a415966 100644
--- a/third_party/blink/web_tests/media/controls/modern/video-tag-with-only-audio-looks-like-audio-tag.html
+++ b/third_party/blink/web_tests/media/controls/video-tag-with-only-audio-looks-like-audio-tag.html
@@ -1,21 +1,21 @@
 <!DOCTYPE html>
 <html>
 <title>Test video tag with only audio looks like audio tag</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
 <video id="with-video" controls preload=metadata>
-  <source src="../../content/60_sec_video.webm" />
+  <source src="../content/60_sec_video.webm" />
 </video>
 <video id="with-audio" controls preload=metadata>
-  <source src="../../content/test.oga" />
+  <source src="../content/test.oga" />
 </video>
 <script>
 async_test(t => {
   let videoWithVideo = document.getElementById('with-video');
   let videoWithAudio = document.getElementById('with-audio');
-  const videoURL = '../../content/60_sec_video.webm';
-  const audioURL = '../../content/test.oga';
+  const videoURL = '../content/60_sec_video.webm';
+  const audioURL = '../content/test.oga';
   let completedTests = 0;
 
   videoWithVideo.onloadedmetadata = t.step_func(() => {
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
index 6732d35..8b58bb8 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
@@ -5,6 +5,7 @@
 ambient-light-sensor
 autoplay
 camera
+document-domain
 encrypted-media
 fullscreen
 geolocation
diff --git a/third_party/blink/web_tests/virtual/user-timing-l2/external/wpt/user-timing/measure-with-dict-expected.txt b/third_party/blink/web_tests/virtual/user-timing-l2/external/wpt/user-timing/measure-with-dict-expected.txt
index f2933f82..94fdcd82 100644
--- a/third_party/blink/web_tests/virtual/user-timing-l2/external/wpt/user-timing/measure-with-dict-expected.txt
+++ b/third_party/blink/web_tests/virtual/user-timing-l2/external/wpt/user-timing/measure-with-dict-expected.txt
@@ -1,5 +1,7 @@
 This is a testharness.js-based test.
 FAIL measure entries' detail and start/end are customizable Failed to execute 'measure' on 'Performance': The mark '[object Object]' does not exist.
-PASS measure should throw exception when passing option object and end at the same time
+FAIL measure should throw a TypeError when passed an invalid argument combination assert_throws: measure should throw a TypeError when passed an options object and an end time function "function() {
+      self.performance.measure("wrongUsage1", {}, 12);
+    }" threw object "SyntaxError: Failed to execute 'measure' on 'Performance': The mark '[object Object]' does not exist." ("SyntaxError") expected object "TypeError" ("TypeError")
 Harness: the test ran to completion.
 
diff --git a/third_party/polymer/v1_0/components-chromium/paper-ripple/paper-ripple-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-ripple/paper-ripple-extracted.js
index 72abdd0..e232c78 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-ripple/paper-ripple-extracted.js
+++ b/third_party/polymer/v1_0/components-chromium/paper-ripple/paper-ripple-extracted.js
@@ -85,6 +85,9 @@
   },
 
   showAndHoldDown: function() {
+    if (this.holdDown) {
+      return;
+    }
     this.ripples.forEach(ripple => {
       ripple.remove();
     });
diff --git a/tools/android/asan/third_party/README.chromium b/tools/android/asan/third_party/README.chromium
index c35bff0..2d06990 100644
--- a/tools/android/asan/third_party/README.chromium
+++ b/tools/android/asan/third_party/README.chromium
@@ -1,6 +1,6 @@
 Name: asan_device_setup.sh
-License: Apache 2.0
-Version: 351636
+License: University of Illinois Open Source License.
+Version: 281410
 URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/scripts/asan_device_setup?view=co
 Security Critical: no
 
diff --git a/tools/android/asan/third_party/asan_device_setup.sh b/tools/android/asan/third_party/asan_device_setup.sh
index 041bf92..dee2ba2e 100755
--- a/tools/android/asan/third_party/asan_device_setup.sh
+++ b/tools/android/asan/third_party/asan_device_setup.sh
@@ -1,9 +1,10 @@
 #!/bin/bash
 #===- lib/asan/scripts/asan_device_setup -----------------------------------===#
 #
-# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
 #
 # Prepare Android device to run ASan applications.
 #
@@ -51,7 +52,7 @@
     local STORAGE=`$ADB shell mount | grep /system | cut -d ' ' -f1`
     if [ "$STORAGE" != "" ]; then
       echo Remounting $STORAGE at /system
-      $ADB shell su -c "mount -o rw,remount $STORAGE /system"
+      $ADB shell su -c "mount -o remount,rw $STORAGE /system"
     else
       echo Failed to get storage device name for "/system" mount point
     fi
@@ -94,7 +95,7 @@
     local _ARCH=
     local _ARCH64=
     if [[ $_ABI == x86* ]]; then
-        _ARCH=i386
+        _ARCH=i686
     elif [[ $_ABI == armeabi* ]]; then
         _ARCH=arm
     elif [[ $_ABI == arm64-v8a* ]]; then
@@ -180,17 +181,6 @@
   ASAN_RT64="libclang_rt.asan-$ARCH64-android.so"
 fi
 
-RELEASE=$(adb_shell getprop ro.build.version.release)
-PRE_L=0
-if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
-    PRE_L=1
-fi
-ANDROID_O=0
-if echo "$RELEASE" | grep '^8\.0\.' >&/dev/null; then
-    # 8.0.x is for Android O
-    ANDROID_O=1
-fi
-
 if [[ x$revert == xyes ]]; then
     echo '>> Uninstalling ASan'
 
@@ -212,10 +202,6 @@
       adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
     fi
 
-    if [[ ANDROID_O -eq 1 ]]; then
-      adb_shell mv /system/etc/ld.config.txt.saved /system/etc/ld.config.txt
-    fi
-
     echo '>> Restarting shell'
     adb_shell stop
     adb_shell start
@@ -265,6 +251,12 @@
 TMPDIR="$TMPDIRBASE/new"
 mkdir "$TMPDIROLD"
 
+RELEASE=$(adb_shell getprop ro.build.version.release)
+PRE_L=0
+if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
+    PRE_L=1
+fi
+
 if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
 
     if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then
@@ -308,22 +300,18 @@
   cp "$ASAN_RT_PATH/$ASAN_RT64" "$TMPDIR/"
 fi
 
-ASAN_OPTIONS=start_deactivated=1
+ASAN_OPTIONS=start_deactivated=1,malloc_context_size=0
 
-# The name of a symlink to libclang_rt.asan-$ARCH-android.so used in LD_PRELOAD.
-# The idea is to have the same name in lib and lib64 to keep it from falling
-# apart when a 64-bit process spawns a 32-bit one, inheriting the environment.
-ASAN_RT_SYMLINK=symlink-to-libclang_rt.asan
-
-function generate_zygote_wrapper { # from, to
+function generate_zygote_wrapper { # from, to, asan_rt
   local _from=$1
   local _to=$2
+  local _asan_rt=$3
   if [[ PRE_L -eq 0 ]]; then
     # LD_PRELOAD parsing is broken in N if it starts with ":". Luckily, it is
     # unset in the system environment since L.
-    local _ld_preload=$ASAN_RT_SYMLINK
+    local _ld_preload=$_asan_rt
   else
-    local _ld_preload=\$LD_PRELOAD:$ASAN_RT_SYMLINK
+    local _ld_preload=\$LD_PRELOAD:$_asan_rt
   fi
   cat <<EOF >"$TMPDIR/$_from"
 #!/system/bin/sh-from-zygote
@@ -336,8 +324,6 @@
 }
 
 # On Android-L not allowing user segv handler breaks some applications.
-# Since ~May 2017 this is the default setting; included for compatibility with
-# older library versions.
 if [[ PRE_L -eq 0 ]]; then
     ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1"
 fi
@@ -354,18 +340,18 @@
     mv "$TMPDIR/app_process32" "$TMPDIR/app_process32.real"
     mv "$TMPDIR/app_process64" "$TMPDIR/app_process64.real"
   fi
-  generate_zygote_wrapper "app_process32" "/system/bin/app_process32.real"
-  generate_zygote_wrapper "app_process64" "/system/bin/app_process64.real"
+  generate_zygote_wrapper "app_process32" "/system/bin/app_process32.real" "$ASAN_RT"
+  generate_zygote_wrapper "app_process64" "/system/bin/app_process64.real" "$ASAN_RT64"
 else
   # A 32-bit device.
-  generate_zygote_wrapper "app_process.wrap" "/system/bin/app_process32"
+  generate_zygote_wrapper "app_process.wrap" "/system/bin/app_process32" "$ASAN_RT"
 fi
 
 # General command-line tool wrapper (use for anything that's not started as
 # zygote).
 cat <<EOF >"$TMPDIR/asanwrapper"
 #!/system/bin/sh
-LD_PRELOAD=$ASAN_RT_SYMLINK \\
+LD_PRELOAD=$ASAN_RT \\
 exec \$@
 
 EOF
@@ -373,7 +359,7 @@
 if [[ -n "$ASAN_RT64" ]]; then
   cat <<EOF >"$TMPDIR/asanwrapper64"
 #!/system/bin/sh
-LD_PRELOAD=$ASAN_RT_SYMLINK \\
+LD_PRELOAD=$ASAN_RT64 \\
 exec \$@
 
 EOF
@@ -424,20 +410,12 @@
       install "$TMPDIR/app_process64.real" /system/bin 755 $CTX
       install "$TMPDIR/asanwrapper" /system/bin 755
       install "$TMPDIR/asanwrapper64" /system/bin 755
-
-      adb_shell rm -f /system/lib/$ASAN_RT_SYMLINK
-      adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
-      adb_shell rm -f /system/lib64/$ASAN_RT_SYMLINK
-      adb_shell ln -s $ASAN_RT64 /system/lib64/$ASAN_RT_SYMLINK
     else
       install "$TMPDIR/$ASAN_RT" /system/lib 644
       install "$TMPDIR/app_process32" /system/bin 755 $CTX
       install "$TMPDIR/app_process.wrap" /system/bin 755 $CTX
       install "$TMPDIR/asanwrapper" /system/bin 755 $CTX
 
-      adb_shell rm -f /system/lib/$ASAN_RT_SYMLINK
-      adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
-
       adb_shell rm /system/bin/app_process
       adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process
     fi
@@ -445,11 +423,6 @@
     adb_shell cp /system/bin/sh /system/bin/sh-from-zygote
     adb_shell chcon $CTX /system/bin/sh-from-zygote
 
-    if [[ ANDROID_O -eq 1 ]]; then
-      # For Android O, the linker namespace is temporarily disabled.
-      adb_shell mv /system/etc/ld.config.txt /system/etc/ld.config.txt.saved
-    fi
-
     if [ $ENFORCING == 1 ]; then
         adb_shell setenforce 1
     fi
@@ -463,4 +436,4 @@
     echo '>> Device is up to date'
 fi
 
-rm -r "$TMPDIRBASE"
+rm -r "$TMPDIRBASE"
\ No newline at end of file
diff --git a/tools/binary_size/sizes.gni b/tools/binary_size/sizes.gni
index c79366fa..246304e0 100644
--- a/tools/binary_size/sizes.gni
+++ b/tools/binary_size/sizes.gni
@@ -3,6 +3,9 @@
 # found in the LICENSE file.
 
 import("//build/util/generate_wrapper.gni")
+if (is_mac) {
+  import("//build/config/mac/mac_sdk.gni")
+}
 
 template("sizes_test") {
   generate_wrapper(target_name) {
@@ -25,5 +28,11 @@
     if (defined(invoker.executable_args)) {
       executable_args += invoker.executable_args
     }
+    if (is_mac) {
+      executable_args += [
+        "--size-path",
+        mac_bin_path + "size",
+      ]
+    }
   }
 }
diff --git a/tools/binary_size/sizes.py b/tools/binary_size/sizes.py
index 87f7393..d298b20 100755
--- a/tools/binary_size/sizes.py
+++ b/tools/binary_size/sizes.py
@@ -78,18 +78,12 @@
   return result, stdout
 
 
-def main_mac(output_directory, results_collector):
+def main_mac(output_directory, results_collector, size_path):
   """Print appropriate size information about built Mac targets.
 
   Returns the first non-zero exit status of any command it executes,
   or zero on success.
   """
-  # Set DEVELOPER_DIR to the hermetic Xcode.app so 'size' will work.
-  if not 'DEVELOPER_DIR' in os.environ:
-    xcode_path = os.path.join(SRC_DIR, 'build', 'mac_files', 'Xcode.app')
-    if os.path.exists(xcode_path):
-      os.environ['DEVELOPER_DIR'] = xcode_path
-
   result = 0
   # Work with either build type.
   base_names = ('Chromium', 'Google Chrome')
@@ -127,13 +121,13 @@
       }
 
       # Collect the segment info out of the App
-      result, stdout = run_process(result, ['size', chromium_executable])
+      result, stdout = run_process(result, [size_path, chromium_executable])
       print_dict['app_text'], print_dict['app_data'], print_dict['app_objc'] = \
           re.search(r'(\d+)\s+(\d+)\s+(\d+)', stdout).groups()
 
       # Collect the segment info out of the Framework
       result, stdout = run_process(result,
-                                   ['size', chromium_framework_executable])
+                                   [size_path, chromium_framework_executable])
       print_dict['framework_text'], print_dict['framework_data'], \
         print_dict['framework_objc'] = \
           re.search(r'(\d+)\s+(\d+)\s+(\d+)', stdout).groups()
@@ -226,12 +220,13 @@
   return result, sizes
 
 
-def main_linux(output_directory, results_collector):
+def main_linux(output_directory, results_collector, size_path):
   """Print appropriate size information about built Linux targets.
 
   Returns the first non-zero exit status of any command it executes,
   or zero on success.
   """
+  assert size_path is None
   binaries = [
       'chrome',
       'nacl_helper',
@@ -308,12 +303,13 @@
   return result
 
 
-def main_android_cronet(output_directory, results_collector):
+def main_android_cronet(output_directory, results_collector, size_path):
   """Print appropriate size information about Android Cronet targets.
 
   Returns the first non-zero exit status of any command it executes,
   or zero on success.
   """
+  assert size_path is None
   # Use version in binary file name, but not in printed output.
   binaries_with_paths = glob.glob(
       os.path.join(output_directory, 'libcronet.*.so'))
@@ -327,12 +323,13 @@
                                 binaries_to_print)
 
 
-def main_win(output_directory, results_collector):
+def main_win(output_directory, results_collector, size_path):
   """Print appropriate size information about built Windows targets.
 
   Returns the first non-zero exit status of any command it executes,
   or zero on success.
   """
+  assert size_path is None
   files = [
       'chrome.dll',
       'chrome.dll.pdb',
@@ -403,6 +400,7 @@
       default=default_platform,
       help='specify platform (%s) [default: %%(default)s]' %
       ', '.join(platforms))
+  parser.add_argument('--size-path', default=None, help='Path to size binary')
 
   # Accepted to conform to the isolated script interface, but ignored.
   parser.add_argument('--isolated-script-test-filter', help=argparse.SUPPRESS)
@@ -442,7 +440,7 @@
 
   results_collector = ResultsCollector()
   try:
-    rc = real_main(args.output_directory, results_collector)
+    rc = real_main(args.output_directory, results_collector, args.size_path)
     isolated_script_output = {
         'valid': True,
         'failures': [test_name] if rc else [],
diff --git a/tools/grit/grit_rule.gni b/tools/grit/grit_rule.gni
index 00918121..11d7c99 100644
--- a/tools/grit/grit_rule.gni
+++ b/tools/grit/grit_rule.gni
@@ -463,6 +463,7 @@
     set_sources_assignment_filter([
                                     "*.json",
                                     "*.pak",
+                                    "*.xml",
                                   ])
     sources = grit_outputs
     set_sources_assignment_filter(sources_assignment_filter)
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index be01f29..150c26a 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1251,6 +1251,7 @@
         './' + str(executable) + executable_suffix,
         '--test-launcher-bot-mode',
         '--asan=%d' % asan,
+        '--lsan=%d' % asan,  # Enable lsan when asan is enabled.
         '--msan=%d' % msan,
         '--tsan=%d' % tsan,
         '--cfi-diag=%d' % cfi_diag,
@@ -1261,6 +1262,7 @@
           './' + str(executable) + executable_suffix,
           '--test-launcher-bot-mode',
           '--asan=%d' % asan,
+          '--lsan=%d' % asan,  # Enable lsan when asan is enabled.
           '--msan=%d' % msan,
           '--tsan=%d' % tsan,
           '--cfi-diag=%d' % cfi_diag,
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index fe9f2c6d..e5fe18de 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -11242,7 +11242,7 @@
 </action>
 
 <action name="MobileBottomToolbarHomeButton">
-  <owner>amaralp@chromium.org</owner>
+  <owner>mdjones@chromium.org</owner>
   <owner>chrome-android-apps@chromium.org</owner>
   <description>
     The user tapped on the bottom toolbar's home button.
@@ -11250,7 +11250,7 @@
 </action>
 
 <action name="MobileBottomToolbarNewTabButton">
-  <owner>amaralp@chromium.org</owner>
+  <owner>mdjones@chromium.org</owner>
   <owner>chrome-android-apps@chromium.org</owner>
   <description>
     The user tapped on the bottom toolbar's new tab button.
@@ -11258,7 +11258,7 @@
 </action>
 
 <action name="MobileBottomToolbarShareButton">
-  <owner>amaralp@chromium.org</owner>
+  <owner>mdjones@chromium.org</owner>
   <owner>chrome-android-apps@chromium.org</owner>
   <description>
     The user tapped on the bottom toolbar's share button.
@@ -11266,13 +11266,13 @@
 </action>
 
 <action name="MobileBottomToolbarShowMenu">
-  <owner>amaralp@chromium.org</owner>
+  <owner>mdjones@chromium.org</owner>
   <owner>chrome-android-apps@chromium.org</owner>
   <description>The bottom toolbar's overflow menu was shown.</description>
 </action>
 
 <action name="MobileBottomToolbarTabSwitcherButtonInBrowsingView">
-  <owner>amaralp@chromium.org</owner>
+  <owner>mdjones@chromium.org</owner>
   <owner>chrome-android-apps@chromium.org</owner>
   <description>
     The user tapped on the bottom toolbar's tab switcher button.
@@ -12856,8 +12856,8 @@
 </action>
 
 <action name="MobileToolbarCloseAllIncognitoTabsButtonTap">
-  <owner>amaralp@chromium.org</owner>
   <owner>mdjones@chromium.org</owner>
+  <owner>chrome-android-apps@chromium.org</owner>
   <description>
     User tapped the close all incognito tabs button in the tab switcher bottom
     toolbar.
@@ -12865,8 +12865,8 @@
 </action>
 
 <action name="MobileToolbarCloseAllRegularTabsButtonTap">
-  <owner>amaralp@chromium.org</owner>
   <owner>mdjones@chromium.org</owner>
+  <owner>chrome-android-apps@chromium.org</owner>
   <description>
     User tapped the close all regular tabs button in the tab switcher bottom
     toolbar.
@@ -12893,7 +12893,7 @@
 
 <action name="MobileToolbarOmniboxAcceleratorTap">
   <owner>mdjones@chromium.org</owner>
-  <owner>amaralp@chromium.org</owner>
+  <owner>chrome-android-apps@chromium.org</owner>
   <description>
     User tapped the omnibox accelerator button in the bottom toolbar.
   </description>
@@ -12956,7 +12956,7 @@
 </action>
 
 <action name="MobileToolbarStackViewButtonInBrowsingView">
-  <owner>amaralp@chromium.org</owner>
+  <owner>mdjones@chromium.org</owner>
   <owner>chrome-android-apps@chromium.org</owner>
   <description>
     User in the Android browsing mode tapped the tab switcher button on the
@@ -12995,13 +12995,13 @@
 </action>
 
 <action name="MobileTopToolbarHomeButton">
-  <owner>amaralp@chromium.org</owner>
+  <owner>mdjones@chromium.org</owner>
   <owner>chrome-android-apps@chromium.org</owner>
   <description>The user tapped on the top toolbar's home button.</description>
 </action>
 
 <action name="MobileTopToolbarNewTabButton">
-  <owner>amaralp@chromium.org</owner>
+  <owner>mdjones@chromium.org</owner>
   <owner>chrome-android-apps@chromium.org</owner>
   <description>
     The user tapped on the top toolbar's new tab button.
@@ -13009,7 +13009,7 @@
 </action>
 
 <action name="MobileTopToolbarShowMenu">
-  <owner>amaralp@chromium.org</owner>
+  <owner>mdjones@chromium.org</owner>
   <owner>chrome-android-apps@chromium.org</owner>
   <description>
     Action indicating the top toolbar's overflow menu was shown.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 24ac3785..c8fe822 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -31184,6 +31184,7 @@
   <int value="1" label="Cancelled item"/>
   <int value="2" label="Reading list item"/>
   <int value="3" label="Bookmark item"/>
+  <int value="4" label="Open in Chrome item"/>
 </enum>
 
 <enum name="iOSSizeClassForReporting">
@@ -38973,6 +38974,7 @@
   <int value="12" label="Chrome Today Extension"/>
   <int value="13" label="Chrome Search Extension"/>
   <int value="14" label="Chrome Content Extension"/>
+  <int value="15" label="Chrome Share Extension"/>
 </enum>
 
 <enum name="MobileSessionShutdownType">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 3c8d4eb..161c7d2 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -78188,6 +78188,12 @@
   </summary>
 </histogram>
 
+<histogram name="NewTabPage.ContentSuggestions.ArtificialDelay" enum="Boolean">
+  <owner>skym@chromium.org</owner>
+  <owner>carlosk@chromium.org</owner>
+  <summary>Android: Loading of Zine was artifically delayed</summary>
+</histogram>
+
 <histogram name="NewTabPage.ContentSuggestions.BackgroundFetchTrigger"
     enum="BackgroundFetchTrigger">
   <owner>jkrcal@chromium.org</owner>
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc
index 1f79991b..cc1e7272 100644
--- a/ui/accessibility/ax_node.cc
+++ b/ui/accessibility/ax_node.cc
@@ -211,84 +211,83 @@
   return IsTableLike(data().role);
 }
 
-int32_t AXNode::GetTableColCount() const {
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
+base::Optional<int> AXNode::GetTableColCount() const {
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
-    return 0;
-
-  return int32_t{table_info->col_count};
+    return base::nullopt;
+  return int{table_info->col_count};
 }
 
-int32_t AXNode::GetTableRowCount() const {
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
+base::Optional<int> AXNode::GetTableRowCount() const {
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
-    return 0;
-
-  return int32_t{table_info->row_count};
+    return base::nullopt;
+  return int{table_info->row_count};
 }
 
-base::Optional<int32_t> AXNode::GetTableAriaColCount() const {
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
-  if (!table_info || !table_info->aria_col_count)
+base::Optional<int> AXNode::GetTableAriaColCount() const {
+  const AXTableInfo* table_info = GetAncestorTableInfo();
+  if (!table_info)
+    return base::nullopt;
+  return table_info->aria_col_count;
+}
+
+base::Optional<int> AXNode::GetTableAriaRowCount() const {
+  const AXTableInfo* table_info = GetAncestorTableInfo();
+  if (!table_info)
+    return base::nullopt;
+  return table_info->aria_row_count;
+}
+
+base::Optional<int> AXNode::GetTableCellCount() const {
+  const AXTableInfo* table_info = GetAncestorTableInfo();
+  if (!table_info)
     return base::nullopt;
 
-  return int32_t{table_info->aria_col_count.value()};
+  return static_cast<int>(table_info->unique_cell_ids.size());
 }
 
-base::Optional<int32_t> AXNode::GetTableAriaRowCount() const {
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
-  if (!table_info || !table_info->aria_row_count)
-    return base::nullopt;
-
-  return int32_t{table_info->aria_row_count.value()};
-}
-
-int32_t AXNode::GetTableCellCount() const {
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
-  if (!table_info)
-    return 0;
-
-  return static_cast<int32_t>(table_info->unique_cell_ids.size());
-}
-
-AXNode* AXNode::GetTableCellFromIndex(int32_t index) const {
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
+AXNode* AXNode::GetTableCellFromIndex(int index) const {
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
     return nullptr;
 
-  if (index < 0 || size_t{index} >= table_info->unique_cell_ids.size())
+  // There is a table but there is no cell with the given index.
+  if (index < 0 || size_t{index} >= table_info->unique_cell_ids.size()) {
     return nullptr;
+  }
 
   return tree_->GetFromId(table_info->unique_cell_ids[size_t{index}]);
 }
 
 AXNode* AXNode::GetTableCaption() const {
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
     return nullptr;
 
   return tree_->GetFromId(table_info->caption_id);
 }
 
-AXNode* AXNode::GetTableCellFromCoords(int32_t row_index,
-                                       int32_t col_index) const {
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
+AXNode* AXNode::GetTableCellFromCoords(int row_index, int col_index) const {
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
     return nullptr;
 
+  // There is a table but the given coordinates are outside the table.
   if (row_index < 0 || size_t{row_index} >= table_info->row_count ||
-      col_index < 0 || size_t{col_index} >= table_info->col_count)
+      col_index < 0 || size_t{col_index} >= table_info->col_count) {
     return nullptr;
+  }
 
   return tree_->GetFromId(
       table_info->cell_ids[size_t{row_index}][size_t{col_index}]);
 }
 
 void AXNode::GetTableColHeaderNodeIds(
-    int32_t col_index,
+    int col_index,
     std::vector<int32_t>* col_header_ids) const {
   DCHECK(col_header_ids);
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
     return;
 
@@ -300,10 +299,10 @@
 }
 
 void AXNode::GetTableRowHeaderNodeIds(
-    int32_t row_index,
+    int row_index,
     std::vector<int32_t>* row_header_ids) const {
   DCHECK(row_header_ids);
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
     return;
 
@@ -316,7 +315,7 @@
 
 void AXNode::GetTableUniqueCellIds(std::vector<int32_t>* cell_ids) const {
   DCHECK(cell_ids);
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
     return;
 
@@ -324,8 +323,9 @@
                    table_info->unique_cell_ids.end());
 }
 
-std::vector<AXNode*>* AXNode::GetExtraMacNodes() const {
-  AXTableInfo* table_info = tree_->GetTableInfo(this);
+const std::vector<AXNode*>* AXNode::GetExtraMacNodes() const {
+  // Should only be available on the table node itself, not any of its children.
+  const AXTableInfo* table_info = tree_->GetTableInfo(this);
   if (!table_info)
     return nullptr;
 
@@ -340,18 +340,18 @@
   return ui::IsTableRow(data().role);
 }
 
-int32_t AXNode::GetTableRowRowIndex() const {
+base::Optional<int> AXNode::GetTableRowRowIndex() const {
   if (!IsTableRow())
-    return 0;
+    return base::nullopt;
 
-  AXTableInfo* table_info = GetAncestorTableInfo();
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
-    return 0;
+    return base::nullopt;
 
   const auto& iter = table_info->row_id_to_index.find(id());
   if (iter != table_info->row_id_to_index.end())
-    return int32_t{iter->second};
-  return 0;
+    return int{iter->second};
+  return base::nullopt;
 }
 
 #if defined(OS_MACOSX)
@@ -364,15 +364,15 @@
   return ui::IsTableColumn(data().role);
 }
 
-int32_t AXNode::GetTableColColIndex() const {
+base::Optional<int> AXNode::GetTableColColIndex() const {
   if (!IsTableColumn())
-    return 0;
+    return base::nullopt;
 
-  AXTableInfo* table_info = GetAncestorTableInfo();
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
-    return 0;
+    return base::nullopt;
 
-  int32_t index = 0;
+  int index = 0;
   for (const AXNode* node : table_info->extra_mac_nodes) {
     if (node == this)
       break;
@@ -391,109 +391,108 @@
   return IsCellOrTableHeader(data().role);
 }
 
-int32_t AXNode::GetTableCellIndex() const {
+base::Optional<int> AXNode::GetTableCellIndex() const {
   if (!IsTableCellOrHeader())
-    return -1;
+    return base::nullopt;
 
-  AXTableInfo* table_info = GetAncestorTableInfo();
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
-    return -1;
+    return base::nullopt;
 
   const auto& iter = table_info->cell_id_to_index.find(id());
   if (iter != table_info->cell_id_to_index.end())
-    return int32_t{iter->second};
-
-  return -1;
+    return int{iter->second};
+  return base::nullopt;
 }
 
-int32_t AXNode::GetTableCellColIndex() const {
-  AXTableInfo* table_info = GetAncestorTableInfo();
+base::Optional<int> AXNode::GetTableCellColIndex() const {
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
-    return 0;
+    return base::nullopt;
 
-  int32_t index = GetTableCellIndex();
-  if (index == -1)
-    return 0;
+  base::Optional<int> index = GetTableCellIndex();
+  if (!index)
+    return base::nullopt;
 
-  return int32_t{table_info->cell_data_vector[index].col_index};
+  return int{table_info->cell_data_vector[*index].col_index};
 }
 
-int32_t AXNode::GetTableCellRowIndex() const {
-  AXTableInfo* table_info = GetAncestorTableInfo();
+base::Optional<int> AXNode::GetTableCellRowIndex() const {
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
-    return 0;
+    return base::nullopt;
 
-  int32_t index = GetTableCellIndex();
-  if (index == -1)
-    return 0;
+  base::Optional<int> index = GetTableCellIndex();
+  if (!index)
+    return base::nullopt;
 
-  return int32_t{table_info->cell_data_vector[index].row_index};
+  return int{table_info->cell_data_vector[*index].row_index};
 }
 
-int32_t AXNode::GetTableCellColSpan() const {
+base::Optional<int> AXNode::GetTableCellColSpan() const {
   // If it's not a table cell, don't return a col span.
   if (!IsTableCellOrHeader())
-    return 0;
+    return base::nullopt;
 
   // Otherwise, try to return a colspan, with 1 as the default if it's not
   // specified.
-  int32_t col_span = 1;
+  int col_span;
   if (GetIntAttribute(ax::mojom::IntAttribute::kTableCellColumnSpan, &col_span))
     return col_span;
-
   return 1;
 }
 
-int32_t AXNode::GetTableCellRowSpan() const {
+base::Optional<int> AXNode::GetTableCellRowSpan() const {
   // If it's not a table cell, don't return a row span.
   if (!IsTableCellOrHeader())
-    return 0;
+    return base::nullopt;
 
   // Otherwise, try to return a row span, with 1 as the default if it's not
   // specified.
-  int32_t row_span = 1;
+  int row_span;
   if (GetIntAttribute(ax::mojom::IntAttribute::kTableCellRowSpan, &row_span))
     return row_span;
   return 1;
 }
 
-int32_t AXNode::GetTableCellAriaColIndex() const {
-  AXTableInfo* table_info = GetAncestorTableInfo();
+base::Optional<int> AXNode::GetTableCellAriaColIndex() const {
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
-    return 0;
+    return base::nullopt;
 
-  int32_t index = GetTableCellIndex();
-  if (index == -1)
-    return 0;
+  base::Optional<int> index = GetTableCellIndex();
+  if (!index)
+    return base::nullopt;
 
-  return int32_t{table_info->cell_data_vector[index].aria_col_index};
+  return int{table_info->cell_data_vector[*index].aria_col_index};
 }
 
-int32_t AXNode::GetTableCellAriaRowIndex() const {
-  AXTableInfo* table_info = GetAncestorTableInfo();
+base::Optional<int> AXNode::GetTableCellAriaRowIndex() const {
+  const AXTableInfo* table_info = GetAncestorTableInfo();
   if (!table_info)
-    return -1;
+    return base::nullopt;
 
-  int32_t index = GetTableCellIndex();
-  if (index == -1)
-    return -1;
+  base::Optional<int> index = GetTableCellIndex();
+  if (!index)
+    return base::nullopt;
 
-  return int32_t{table_info->cell_data_vector[index].aria_row_index};
+  return int{table_info->cell_data_vector[*index].aria_row_index};
 }
 
 void AXNode::GetTableCellColHeaderNodeIds(
     std::vector<int32_t>* col_header_ids) const {
   DCHECK(col_header_ids);
-  AXTableInfo* table_info = GetAncestorTableInfo();
-  if (!table_info)
+  const AXTableInfo* table_info = GetAncestorTableInfo();
+  if (!table_info || table_info->col_count <= 0)
     return;
 
-  int32_t col_index = GetTableCellColIndex();
-  if (col_index < 0 || size_t{col_index} >= table_info->col_count)
-    return;
-
-  for (size_t i = 0; i < table_info->col_headers[size_t{col_index}].size(); i++)
-    col_header_ids->push_back(table_info->col_headers[size_t{col_index}][i]);
+  base::Optional<int> col_index = GetTableCellColIndex();
+  // If this node is not a cell, then return the headers for the first column.
+  for (size_t i = 0; i < table_info->col_headers[col_index.value_or(0)].size();
+       i++) {
+    col_header_ids->push_back(
+        table_info->col_headers[col_index.value_or(0)][i]);
+  }
 }
 
 void AXNode::GetTableCellColHeaders(std::vector<AXNode*>* col_headers) const {
@@ -507,16 +506,17 @@
 void AXNode::GetTableCellRowHeaderNodeIds(
     std::vector<int32_t>* row_header_ids) const {
   DCHECK(row_header_ids);
-  AXTableInfo* table_info = GetAncestorTableInfo();
-  if (!table_info)
+  const AXTableInfo* table_info = GetAncestorTableInfo();
+  if (!table_info || table_info->row_count <= 0)
     return;
 
-  int32_t row_index = GetTableCellRowIndex();
-  if (row_index < 0 || size_t{row_index} >= table_info->row_count)
-    return;
-
-  for (size_t i = 0; i < table_info->row_headers[size_t{row_index}].size(); i++)
-    row_header_ids->push_back(table_info->row_headers[size_t{row_index}][i]);
+  base::Optional<int> row_index = GetTableCellRowIndex();
+  // If this node is not a cell, then return the headers for the first row.
+  for (size_t i = 0; i < table_info->row_headers[row_index.value_or(0)].size();
+       i++) {
+    row_header_ids->push_back(
+        table_info->row_headers[row_index.value_or(0)][i]);
+  }
 }
 
 void AXNode::GetTableCellRowHeaders(std::vector<AXNode*>* row_headers) const {
@@ -572,43 +572,42 @@
   }
 }
 
-// Uses function in ax_role_properties to check if node is item-like.
 bool AXNode::IsOrderedSetItem() const {
   return ui::IsItemLike(data().role);
 }
-// Uses function in ax_role_properties to check if node is oredered-set-like.
+
 bool AXNode::IsOrderedSet() const {
   return ui::IsSetLike(data().role);
 }
 
 // pos_in_set and set_size related functions.
 // Uses AXTree's cache to calculate node's pos_in_set.
-int32_t AXNode::GetPosInSet() {
+base::Optional<int> AXNode::GetPosInSet() {
   // Only allow this to be called on nodes that can hold pos_in_set values,
   // which are defined in the ARIA spec.
   if (!IsOrderedSetItem()) {
-    return 0;
+    return base::nullopt;
   }
 
   const AXNode* ordered_set = GetOrderedSet();
   if (!ordered_set) {
-    return 0;
+    return base::nullopt;
   }
 
-  // If tree is being updated, return 0.
+  // If tree is being updated, return no value.
   if (tree()->GetTreeUpdateInProgressState())
-    return 0;
+    return base::nullopt;
 
   // See AXTree::GetPosInSet
   return tree_->GetPosInSet(*this, ordered_set);
 }
 
 // Uses AXTree's cache to calculate node's set_size.
-int32_t AXNode::GetSetSize() {
+base::Optional<int> AXNode::GetSetSize() {
   // Only allow this to be called on nodes that can hold set_size values, which
   // are defined in the ARIA spec.
   if (!(IsOrderedSetItem() || IsOrderedSet()))
-    return 0;
+    return base::nullopt;
 
   // If node is item-like, find its outerlying ordered set. Otherwise,
   // this node is the ordered set.
@@ -616,11 +615,11 @@
   if (IsItemLike(data().role))
     ordered_set = GetOrderedSet();
   if (!ordered_set)
-    return 0;
+    return base::nullopt;
 
-  // If tree is being updated, return 0.
+  // If tree is being updated, return no value.
   if (tree()->GetTreeUpdateInProgressState())
-    return 0;
+    return base::nullopt;
 
   // See AXTree::GetSetSize
   return tree_->GetSetSize(*this, ordered_set);
diff --git a/ui/accessibility/ax_node.h b/ui/accessibility/ax_node.h
index 68a05367..3f9ac74 100644
--- a/ui/accessibility/ax_node.h
+++ b/ui/accessibility/ax_node.h
@@ -12,6 +12,7 @@
 #include <string>
 #include <vector>
 
+#include "base/optional.h"
 #include "build/build_config.h"
 #include "ui/accessibility/ax_export.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -138,7 +139,7 @@
   bool HasIntAttribute(ax::mojom::IntAttribute attribute) const {
     return data().HasIntAttribute(attribute);
   }
-  int32_t GetIntAttribute(ax::mojom::IntAttribute attribute) const {
+  int GetIntAttribute(ax::mojom::IntAttribute attribute) const {
     return data().GetIntAttribute(attribute);
   }
   bool GetIntAttribute(ax::mojom::IntAttribute attribute, int* value) const {
@@ -200,8 +201,8 @@
   // PosInSet and SetSize public methods.
   bool IsOrderedSetItem() const;
   bool IsOrderedSet() const;
-  int32_t GetPosInSet();
-  int32_t GetSetSize();
+  base::Optional<int> GetPosInSet();
+  base::Optional<int> GetSetSize();
 
   // Helpers for GetPosInSet and GetSetSize.
   // Returns true if the role of ordered set matches the role of item.
@@ -238,44 +239,47 @@
   // ARIA indices are all 1-based. In other words, the top-left corner
   // of the table is row 0, column 0, cell index 0 - but that same cell
   // has a minimum ARIA row index of 1 and column index of 1.
+  //
+  // The below methods return base::nullopt if the AXNode they are called on is
+  // not inside a table.
   bool IsTable() const;
-  int32_t GetTableColCount() const;
-  int32_t GetTableRowCount() const;
-  base::Optional<int32_t> GetTableAriaColCount() const;
-  base::Optional<int32_t> GetTableAriaRowCount() const;
-  int32_t GetTableCellCount() const;
+  base::Optional<int> GetTableColCount() const;
+  base::Optional<int> GetTableRowCount() const;
+  base::Optional<int> GetTableAriaColCount() const;
+  base::Optional<int> GetTableAriaRowCount() const;
+  base::Optional<int> GetTableCellCount() const;
   AXNode* GetTableCaption() const;
-  AXNode* GetTableCellFromIndex(int32_t index) const;
-  AXNode* GetTableCellFromCoords(int32_t row_index, int32_t col_index) const;
-  void GetTableColHeaderNodeIds(int32_t col_index,
+  AXNode* GetTableCellFromIndex(int index) const;
+  AXNode* GetTableCellFromCoords(int row_index, int col_index) const;
+  void GetTableColHeaderNodeIds(int col_index,
                                 std::vector<int32_t>* col_header_ids) const;
-  void GetTableRowHeaderNodeIds(int32_t row_index,
+  void GetTableRowHeaderNodeIds(int row_index,
                                 std::vector<int32_t>* row_header_ids) const;
   void GetTableUniqueCellIds(std::vector<int32_t>* row_header_ids) const;
   // Extra computed nodes for the accessibility tree for macOS:
   // one column node for each table column, followed by one
   // table header container node, or nullptr if not applicable.
-  std::vector<AXNode*>* GetExtraMacNodes() const;
+  const std::vector<AXNode*>* GetExtraMacNodes() const;
 
   // Table row-like nodes.
   bool IsTableRow() const;
-  int32_t GetTableRowRowIndex() const;
+  base::Optional<int> GetTableRowRowIndex() const;
 
 #if defined(OS_MACOSX)
   // Table column-like nodes. These nodes are only present on macOS.
   bool IsTableColumn() const;
-  int32_t GetTableColColIndex() const;
+  base::Optional<int> GetTableColColIndex() const;
 #endif  // defined(OS_MACOSX)
 
   // Table cell-like nodes.
   bool IsTableCellOrHeader() const;
-  int32_t GetTableCellIndex() const;
-  int32_t GetTableCellColIndex() const;
-  int32_t GetTableCellRowIndex() const;
-  int32_t GetTableCellColSpan() const;
-  int32_t GetTableCellRowSpan() const;
-  int32_t GetTableCellAriaColIndex() const;
-  int32_t GetTableCellAriaRowIndex() const;
+  base::Optional<int> GetTableCellIndex() const;
+  base::Optional<int> GetTableCellColIndex() const;
+  base::Optional<int> GetTableCellRowIndex() const;
+  base::Optional<int> GetTableCellColSpan() const;
+  base::Optional<int> GetTableCellRowSpan() const;
+  base::Optional<int> GetTableCellAriaColIndex() const;
+  base::Optional<int> GetTableCellAriaRowIndex() const;
   void GetTableCellColHeaderNodeIds(std::vector<int32_t>* col_header_ids) const;
   void GetTableCellRowHeaderNodeIds(std::vector<int32_t>* row_header_ids) const;
   void GetTableCellColHeaders(std::vector<AXNode*>* col_headers) const;
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index ff9e226..e82402d 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -311,7 +311,7 @@
                                  int* value) const {
   auto iter = FindInVectorOfPairs(attribute, int_attributes);
   if (iter != int_attributes.end()) {
-    *value = iter->second;
+    *value = int{iter->second};
     return true;
   }
 
diff --git a/ui/accessibility/ax_table_info.cc b/ui/accessibility/ax_table_info.cc
index 5f97728..f703e25 100644
--- a/ui/accessibility/ax_table_info.cc
+++ b/ui/accessibility/ax_table_info.cc
@@ -110,17 +110,15 @@
   col_count = GetSizeTAttribute(*table_node_, IntAttribute::kTableColumnCount);
 
   int32_t aria_rows = table_node_->GetIntAttribute(IntAttribute::kAriaRowCount);
-  aria_row_count =
-      (aria_rows != ax::mojom::kUnknownAriaColumnOrRowCount)
-          ? base::make_optional(base::saturated_cast<size_t>(aria_rows))
-          : base::nullopt;
+  aria_row_count = (aria_rows != ax::mojom::kUnknownAriaColumnOrRowCount)
+                       ? base::make_optional(int{aria_rows})
+                       : base::nullopt;
 
   int32_t aria_cols =
       table_node_->GetIntAttribute(IntAttribute::kAriaColumnCount);
-  aria_col_count =
-      (aria_cols != ax::mojom::kUnknownAriaColumnOrRowCount)
-          ? base::make_optional(base::saturated_cast<size_t>(aria_cols))
-          : base::nullopt;
+  aria_col_count = (aria_cols != ax::mojom::kUnknownAriaColumnOrRowCount)
+                       ? base::make_optional(int{aria_cols})
+                       : base::nullopt;
 
   // Iterate over the cells and build up an array of CellData
   // entries, one for each cell. Compute the actual row and column
@@ -257,12 +255,14 @@
       row_count = std::max(row_count, cell_data.row_index + cell_data.row_span);
       col_count = std::max(col_count, cell_data.col_index + cell_data.col_span);
       if (aria_row_count) {
-        aria_row_count = std::max(
-            (*aria_row_count), current_aria_row_index + cell_data.row_span - 1);
+        aria_row_count =
+            std::max((*aria_row_count),
+                     int{current_aria_row_index + cell_data.row_span - 1});
       }
       if (aria_col_count) {
-        aria_col_count = std::max(
-            (*aria_col_count), current_aria_col_index + cell_data.col_span - 1);
+        aria_col_count =
+            std::max((*aria_col_count),
+                     int{current_aria_col_index + cell_data.col_span - 1});
       }
       // Update |current_col_index| to reflect the next available index after
       // this cell including its colspan. The next column index in this row
diff --git a/ui/accessibility/ax_table_info.h b/ui/accessibility/ax_table_info.h
index 3824cea..aafbc06 100644
--- a/ui/accessibility/ax_table_info.h
+++ b/ui/accessibility/ax_table_info.h
@@ -91,8 +91,8 @@
 
   // The ARIA row count and column count, if any ARIA table or grid
   // attributes are used in the table at all.
-  base::Optional<size_t> aria_row_count = 0;
-  base::Optional<size_t> aria_col_count = 0;
+  base::Optional<int> aria_row_count = 0;
+  base::Optional<int> aria_col_count = 0;
 
  private:
   AXTableInfo(AXTree* tree, AXNode* table_node);
diff --git a/ui/accessibility/ax_table_info_unittest.cc b/ui/accessibility/ax_table_info_unittest.cc
index 9c6c703..e4b990ae 100644
--- a/ui/accessibility/ax_table_info_unittest.cc
+++ b/ui/accessibility/ax_table_info_unittest.cc
@@ -66,6 +66,14 @@
 
 }  // namespace
 
+// A macro for testing that a base::Optional has both a value and that its value
+// is set to a particular expectation.
+#define EXPECT_OPTIONAL_EQ(expected, actual) \
+  EXPECT_TRUE(actual.has_value());           \
+  if (actual) {                              \
+    EXPECT_EQ(expected, actual.value());     \
+  }
+
 class AXTableInfoTest : public testing::Test {
  public:
   AXTableInfoTest() {}
@@ -147,9 +155,10 @@
   EXPECT_TRUE(table->IsTable());
   EXPECT_FALSE(table->IsTableRow());
   EXPECT_FALSE(table->IsTableCellOrHeader());
-  EXPECT_EQ(2, table->GetTableColCount());
-  EXPECT_EQ(2, table->GetTableRowCount());
+  EXPECT_OPTIONAL_EQ(2, table->GetTableColCount());
+  EXPECT_OPTIONAL_EQ(2, table->GetTableRowCount());
 
+  ASSERT_TRUE(table->GetTableCellFromCoords(0, 0));
   EXPECT_EQ(4, table->GetTableCellFromCoords(0, 0)->id());
   EXPECT_EQ(5, table->GetTableCellFromCoords(0, 1)->id());
   EXPECT_EQ(6, table->GetTableCellFromCoords(1, 0)->id());
@@ -168,33 +177,32 @@
   EXPECT_FALSE(row_0->IsTable());
   EXPECT_TRUE(row_0->IsTableRow());
   EXPECT_FALSE(row_0->IsTableCellOrHeader());
-  EXPECT_EQ(0, row_0->GetTableRowRowIndex());
+  EXPECT_OPTIONAL_EQ(0, row_0->GetTableRowRowIndex());
 
   AXNode* row_1 = tree.GetFromId(3);
   EXPECT_FALSE(row_1->IsTable());
   EXPECT_TRUE(row_1->IsTableRow());
   EXPECT_FALSE(row_1->IsTableCellOrHeader());
-  EXPECT_EQ(1, row_1->GetTableRowRowIndex());
+  EXPECT_OPTIONAL_EQ(1, row_1->GetTableRowRowIndex());
 
   AXNode* cell_0_0 = tree.GetFromId(4);
   EXPECT_FALSE(cell_0_0->IsTable());
   EXPECT_FALSE(cell_0_0->IsTableRow());
   EXPECT_TRUE(cell_0_0->IsTableCellOrHeader());
-  EXPECT_EQ(0, cell_0_0->GetTableCellIndex());
-  EXPECT_EQ(0, cell_0_0->GetTableCellColIndex());
-  EXPECT_EQ(0, cell_0_0->GetTableCellRowIndex());
-  EXPECT_EQ(1, cell_0_0->GetTableCellColSpan());
-  EXPECT_EQ(1, cell_0_0->GetTableCellRowSpan());
+  EXPECT_OPTIONAL_EQ(0, cell_0_0->GetTableCellIndex());
+  EXPECT_OPTIONAL_EQ(0, cell_0_0->GetTableCellColIndex());
+  EXPECT_OPTIONAL_EQ(0, cell_0_0->GetTableCellRowIndex());
+  EXPECT_OPTIONAL_EQ(1, cell_0_0->GetTableCellColSpan());
+  EXPECT_OPTIONAL_EQ(1, cell_0_0->GetTableCellRowSpan());
 
   AXNode* cell_1_1 = tree.GetFromId(7);
   EXPECT_FALSE(cell_1_1->IsTable());
   EXPECT_FALSE(cell_1_1->IsTableRow());
   EXPECT_TRUE(cell_1_1->IsTableCellOrHeader());
-  EXPECT_EQ(3, cell_1_1->GetTableCellIndex());
-  EXPECT_EQ(1, cell_1_1->GetTableCellColIndex());
-  EXPECT_EQ(1, cell_1_1->GetTableCellRowIndex());
-  EXPECT_EQ(1, cell_1_1->GetTableCellColSpan());
-  EXPECT_EQ(1, cell_1_1->GetTableCellRowSpan());
+  EXPECT_OPTIONAL_EQ(3, cell_1_1->GetTableCellIndex());
+  EXPECT_OPTIONAL_EQ(1, cell_1_1->GetTableCellRowIndex());
+  EXPECT_OPTIONAL_EQ(1, cell_1_1->GetTableCellColSpan());
+  EXPECT_OPTIONAL_EQ(1, cell_1_1->GetTableCellRowSpan());
 
   std::vector<AXNode*> col_headers;
   cell_1_1->GetTableCellColHeaders(&col_headers);
diff --git a/ui/accessibility/ax_tree_unittest.cc b/ui/accessibility/ax_tree_unittest.cc
index b9c07106..7d90bac 100644
--- a/ui/accessibility/ax_tree_unittest.cc
+++ b/ui/accessibility/ax_tree_unittest.cc
@@ -92,7 +92,7 @@
   void OnNodeChanged(AXTree* tree, AXNode* node) override {
     changed_ids_.push_back(node->id());
     if (call_posinset_and_setsize)
-      AssertPosinsetAndSetsizeZero(node);
+      AssertPosinsetAndSetsizeNotSet(node);
   }
 
   void OnAtomicUpdateFinished(AXTree* tree,
@@ -215,9 +215,9 @@
   }
 
   bool call_posinset_and_setsize = false;
-  void AssertPosinsetAndSetsizeZero(AXNode* node) {
-    ASSERT_EQ(0, node->GetPosInSet());
-    ASSERT_EQ(0, node->GetSetSize());
+  void AssertPosinsetAndSetsizeNotSet(AXNode* node) {
+    ASSERT_FALSE(node->GetPosInSet());
+    ASSERT_FALSE(node->GetSetSize());
   }
 
  private:
@@ -238,6 +238,14 @@
 
 }  // namespace
 
+// A macro for testing that a base::Optional has both a value and that its value
+// is set to a particular expectation.
+#define EXPECT_OPTIONAL_EQ(expected, actual) \
+  EXPECT_TRUE(actual.has_value());           \
+  if (actual) {                              \
+    EXPECT_EQ(expected, actual.value());     \
+  }
+
 TEST(AXTreeTest, SerializeSimpleAXTree) {
   AXNodeData root;
   root.id = 1;
@@ -1635,14 +1643,14 @@
   AXTree tree(tree_update);
 
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetPosInSet(), 2);
-  EXPECT_EQ(item1->GetSetSize(), 12);
+  EXPECT_OPTIONAL_EQ(2, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(12, item1->GetSetSize());
   AXNode* item2 = tree.GetFromId(3);
-  EXPECT_EQ(item2->GetPosInSet(), 5);
-  EXPECT_EQ(item2->GetSetSize(), 12);
+  EXPECT_OPTIONAL_EQ(5, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(12, item2->GetSetSize());
   AXNode* item3 = tree.GetFromId(4);
-  EXPECT_EQ(item3->GetPosInSet(), 9);
-  EXPECT_EQ(item3->GetSetSize(), 12);
+  EXPECT_OPTIONAL_EQ(9, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(12, item3->GetSetSize());
 }
 
 // Tests that pos_in_set and set_size can be calculated if not assigned.
@@ -1662,14 +1670,14 @@
   AXTree tree(tree_update);
 
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetPosInSet(), 1);
-  EXPECT_EQ(item1->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item1->GetSetSize());
   AXNode* item2 = tree.GetFromId(3);
-  EXPECT_EQ(item2->GetPosInSet(), 2);
-  EXPECT_EQ(item2->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item2->GetSetSize());
   AXNode* item3 = tree.GetFromId(4);
-  EXPECT_EQ(item3->GetPosInSet(), 3);
-  EXPECT_EQ(item3->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(3, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item3->GetSetSize());
 }
 
 // Tests pos_in_set can be calculated if unassigned, and set_size can be
@@ -1692,11 +1700,11 @@
 
   // Items should inherit set_size from ordered set if not specified.
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetSetSize(), 7);
+  EXPECT_OPTIONAL_EQ(7, item1->GetSetSize());
   AXNode* item2 = tree.GetFromId(3);
-  EXPECT_EQ(item2->GetSetSize(), 7);
+  EXPECT_OPTIONAL_EQ(7, item2->GetSetSize());
   AXNode* item3 = tree.GetFromId(4);
-  EXPECT_EQ(item3->GetSetSize(), 7);
+  EXPECT_OPTIONAL_EQ(7, item3->GetSetSize());
 }
 
 // Tests GetPosInSet and GetSetSize on a list containing various roles.
@@ -1724,20 +1732,20 @@
   // and kMenuItemRadio. For PosInSet and SetSize purposes, these items
   // are treated as the same role.
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetPosInSet(), 1);
-  EXPECT_EQ(item1->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, item1->GetSetSize());
   AXNode* checkbox = tree.GetFromId(3);
-  EXPECT_EQ(checkbox->GetPosInSet(), 2);
-  EXPECT_EQ(checkbox->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(2, checkbox->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, checkbox->GetSetSize());
   AXNode* radio = tree.GetFromId(4);
-  EXPECT_EQ(radio->GetPosInSet(), 3);
-  EXPECT_EQ(radio->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(3, radio->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, radio->GetSetSize());
   AXNode* item3 = tree.GetFromId(5);
-  EXPECT_EQ(item3->GetPosInSet(), 4);
-  EXPECT_EQ(item3->GetSetSize(), 4);
-  AXNode* image = tree.GetFromId(6);
-  EXPECT_EQ(image->GetPosInSet(), 0);
-  EXPECT_EQ(image->GetSetSize(), 0);
+  EXPECT_OPTIONAL_EQ(4, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, item3->GetSetSize());
+  AXNode* tab = tree.GetFromId(6);
+  EXPECT_OPTIONAL_EQ(0, tab->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(0, tab->GetSetSize());
 }
 
 // Tests GetPosInSet and GetSetSize on a nested list.
@@ -1764,22 +1772,22 @@
   AXTree tree(tree_update);
 
   AXNode* outer_item1 = tree.GetFromId(2);
-  EXPECT_EQ(outer_item1->GetPosInSet(), 1);
-  EXPECT_EQ(outer_item1->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(1, outer_item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, outer_item1->GetSetSize());
   AXNode* outer_item2 = tree.GetFromId(3);
-  EXPECT_EQ(outer_item2->GetPosInSet(), 2);
-  EXPECT_EQ(outer_item2->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(2, outer_item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, outer_item2->GetSetSize());
 
   AXNode* inner_item1 = tree.GetFromId(5);
-  EXPECT_EQ(inner_item1->GetPosInSet(), 1);
-  EXPECT_EQ(inner_item1->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(1, inner_item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, inner_item1->GetSetSize());
   AXNode* inner_item2 = tree.GetFromId(6);
-  EXPECT_EQ(inner_item2->GetPosInSet(), 2);
-  EXPECT_EQ(inner_item2->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(2, inner_item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, inner_item2->GetSetSize());
 
   AXNode* outer_item3 = tree.GetFromId(7);
-  EXPECT_EQ(outer_item3->GetPosInSet(), 3);
-  EXPECT_EQ(outer_item3->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(3, outer_item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, outer_item3->GetSetSize());
 }
 
 // Tests pos_in_set can be calculated if one item specifies pos_in_set, but
@@ -1803,18 +1811,18 @@
 
   // Item1 should have pos of 12, since item2 is assigned a pos of 13.
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetPosInSet(), 1);
-  EXPECT_EQ(item1->GetSetSize(), 20);
+  EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(20, item1->GetSetSize());
   AXNode* item2 = tree.GetFromId(3);
-  EXPECT_EQ(item2->GetPosInSet(), 13);
-  EXPECT_EQ(item2->GetSetSize(), 20);
+  EXPECT_OPTIONAL_EQ(13, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(20, item2->GetSetSize());
   // Item2 should have pos of 14, since item2 is assigned a pos of 13.
   AXNode* item3 = tree.GetFromId(4);
-  EXPECT_EQ(item3->GetPosInSet(), 14);
-  EXPECT_EQ(item3->GetSetSize(), 20);
+  EXPECT_OPTIONAL_EQ(14, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(20, item3->GetSetSize());
 }
 
-// A more difficult test that invovles missing pos_in_set and set_size values.
+// A more difficult test that involves missing pos_in_set and set_size values.
 TEST(AXTreeTest, TestSetSizePosInSetMissingDifficult) {
   AXTreeUpdate tree_update;
   tree_update.root_id = 1;
@@ -1839,20 +1847,20 @@
   AXTree tree(tree_update);
 
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetPosInSet(), 1);
-  EXPECT_EQ(item1->GetSetSize(), 11);
+  EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(11, item1->GetSetSize());
   AXNode* item2 = tree.GetFromId(3);
-  EXPECT_EQ(item2->GetPosInSet(), 5);
-  EXPECT_EQ(item2->GetSetSize(), 11);
+  EXPECT_OPTIONAL_EQ(5, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(11, item2->GetSetSize());
   AXNode* item3 = tree.GetFromId(4);
-  EXPECT_EQ(item3->GetPosInSet(), 6);
-  EXPECT_EQ(item3->GetSetSize(), 11);
+  EXPECT_OPTIONAL_EQ(6, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(11, item3->GetSetSize());
   AXNode* item4 = tree.GetFromId(5);
-  EXPECT_EQ(item4->GetPosInSet(), 10);
-  EXPECT_EQ(item4->GetSetSize(), 11);
+  EXPECT_OPTIONAL_EQ(10, item4->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(11, item4->GetSetSize());
   AXNode* item5 = tree.GetFromId(6);
-  EXPECT_EQ(item5->GetPosInSet(), 11);
-  EXPECT_EQ(item5->GetSetSize(), 11);
+  EXPECT_OPTIONAL_EQ(11, item5->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(11, item5->GetSetSize());
 }
 
 // Tests that code overwrites decreasing set_size assignments to largest of
@@ -1875,14 +1883,14 @@
   AXTree tree(tree_update);
 
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetPosInSet(), 1);
-  EXPECT_EQ(item1->GetSetSize(), 5);
+  EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(5, item1->GetSetSize());
   AXNode* item2 = tree.GetFromId(3);
-  EXPECT_EQ(item2->GetPosInSet(), 2);
-  EXPECT_EQ(item2->GetSetSize(), 5);
+  EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(5, item2->GetSetSize());
   AXNode* item3 = tree.GetFromId(4);
-  EXPECT_EQ(item3->GetPosInSet(), 3);
-  EXPECT_EQ(item3->GetSetSize(), 5);
+  EXPECT_OPTIONAL_EQ(3, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(5, item3->GetSetSize());
 }
 
 // Tests that code overwrites decreasing pos_in_set values.
@@ -1904,14 +1912,14 @@
   AXTree tree(tree_update);
 
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetPosInSet(), 1);
-  EXPECT_EQ(item1->GetSetSize(), 8);
+  EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(8, item1->GetSetSize());
   AXNode* item2 = tree.GetFromId(3);
-  EXPECT_EQ(item2->GetPosInSet(), 7);
-  EXPECT_EQ(item2->GetSetSize(), 8);
+  EXPECT_OPTIONAL_EQ(7, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(8, item2->GetSetSize());
   AXNode* item3 = tree.GetFromId(4);
-  EXPECT_EQ(item3->GetPosInSet(), 8);
-  EXPECT_EQ(item3->GetSetSize(), 8);
+  EXPECT_OPTIONAL_EQ(8, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(8, item3->GetSetSize());
 }
 
 // Tests that code overwrites duplicate pos_in_set values. Note this case is
@@ -1936,14 +1944,14 @@
   AXTree tree(tree_update);
 
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetPosInSet(), 6);
-  EXPECT_EQ(item1->GetSetSize(), 8);
+  EXPECT_OPTIONAL_EQ(6, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(8, item1->GetSetSize());
   AXNode* item2 = tree.GetFromId(3);
-  EXPECT_EQ(item2->GetPosInSet(), 7);
-  EXPECT_EQ(item2->GetSetSize(), 8);
+  EXPECT_OPTIONAL_EQ(7, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(8, item2->GetSetSize());
   AXNode* item3 = tree.GetFromId(4);
-  EXPECT_EQ(item3->GetPosInSet(), 8);
-  EXPECT_EQ(item3->GetSetSize(), 8);
+  EXPECT_OPTIONAL_EQ(8, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(8, item3->GetSetSize());
 }
 
 // Tests GetPosInSet and GetSetSize when some list items are nested in a generic
@@ -1972,23 +1980,23 @@
   AXTree tree(tree_update);
 
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetPosInSet(), 1);
-  EXPECT_EQ(item1->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, item1->GetSetSize());
   AXNode* g_container = tree.GetFromId(3);
-  EXPECT_EQ(g_container->GetPosInSet(), 0);
-  EXPECT_EQ(g_container->GetSetSize(), 0);
+  EXPECT_FALSE(g_container->GetPosInSet());
+  EXPECT_FALSE(g_container->GetSetSize());
   AXNode* item2 = tree.GetFromId(4);
-  EXPECT_EQ(item2->GetPosInSet(), 2);
-  EXPECT_EQ(item2->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, item2->GetSetSize());
   AXNode* ignored = tree.GetFromId(5);
-  EXPECT_EQ(ignored->GetPosInSet(), 0);
-  EXPECT_EQ(ignored->GetSetSize(), 0);
+  EXPECT_FALSE(ignored->GetPosInSet());
+  EXPECT_FALSE(ignored->GetSetSize());
   AXNode* item3 = tree.GetFromId(6);
-  EXPECT_EQ(item3->GetPosInSet(), 3);
-  EXPECT_EQ(item3->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(3, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, item3->GetSetSize());
   AXNode* item4 = tree.GetFromId(7);
-  EXPECT_EQ(item4->GetPosInSet(), 4);
-  EXPECT_EQ(item4->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(4, item4->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, item4->GetSetSize());
 }
 
 // Tests GetSetSize and GetPosInSet are correct, even when list items change.
@@ -2011,27 +2019,27 @@
   AXTree tree(initial_state);
 
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetPosInSet(), 1);
-  EXPECT_EQ(item1->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item1->GetSetSize());
   AXNode* item2 = tree.GetFromId(3);
-  EXPECT_EQ(item2->GetPosInSet(), 2);
-  EXPECT_EQ(item2->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item2->GetSetSize());
   AXNode* item3 = tree.GetFromId(4);
-  EXPECT_EQ(item3->GetPosInSet(), 3);
-  EXPECT_EQ(item3->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(3, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item3->GetSetSize());
 
   // TreeUpdates only need to describe what changed in tree.
   AXTreeUpdate update = initial_state;
   update.nodes.resize(1);
   update.nodes[0].child_ids = {2, 4};  // Delete item 2 of 3 from list.
-  EXPECT_TRUE(tree.Unserialize(update));
+  ASSERT_TRUE(tree.Unserialize(update));
 
   AXNode* new_item1 = tree.GetFromId(2);
-  EXPECT_EQ(new_item1->GetPosInSet(), 1);
-  EXPECT_EQ(new_item1->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(1, new_item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, new_item1->GetSetSize());
   AXNode* new_item2 = tree.GetFromId(4);
-  EXPECT_EQ(new_item2->GetPosInSet(), 2);
-  EXPECT_EQ(new_item2->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(2, new_item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, new_item2->GetSetSize());
 }
 
 // Tests GetSetSize and GetPosInSet are correct, even when list items change.
@@ -2054,36 +2062,36 @@
   AXTree tree(initial_state);
 
   AXNode* item1 = tree.GetFromId(2);
-  EXPECT_EQ(item1->GetPosInSet(), 1);
-  EXPECT_EQ(item1->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item1->GetSetSize());
   AXNode* item2 = tree.GetFromId(3);
-  EXPECT_EQ(item2->GetPosInSet(), 2);
-  EXPECT_EQ(item2->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item2->GetSetSize());
   AXNode* item3 = tree.GetFromId(4);
-  EXPECT_EQ(item3->GetPosInSet(), 3);
-  EXPECT_EQ(item3->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(3, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item3->GetSetSize());
 
-  // Insert item at beginning of list
+  // Insert an item at the beginning of the list.
   AXTreeUpdate update = initial_state;
   update.nodes.resize(2);
   update.nodes[0].id = 1;
   update.nodes[0].child_ids = {5, 2, 3, 4};
   update.nodes[1].id = 5;
   update.nodes[1].role = ax::mojom::Role::kListItem;
-  EXPECT_TRUE(tree.Unserialize(update));
+  ASSERT_TRUE(tree.Unserialize(update));
 
   AXNode* new_item1 = tree.GetFromId(5);
-  EXPECT_EQ(new_item1->GetPosInSet(), 1);
-  EXPECT_EQ(new_item1->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(1, new_item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, new_item1->GetSetSize());
   AXNode* new_item2 = tree.GetFromId(2);
-  EXPECT_EQ(new_item2->GetPosInSet(), 2);
-  EXPECT_EQ(new_item2->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(2, new_item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, new_item2->GetSetSize());
   AXNode* new_item3 = tree.GetFromId(3);
-  EXPECT_EQ(new_item3->GetPosInSet(), 3);
-  EXPECT_EQ(new_item3->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(3, new_item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, new_item3->GetSetSize());
   AXNode* new_item4 = tree.GetFromId(4);
-  EXPECT_EQ(new_item4->GetPosInSet(), 4);
-  EXPECT_EQ(new_item4->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(4, new_item4->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, new_item4->GetSetSize());
 }
 
 // Tests that the outerlying ordered set reports a set_size. Ordered sets
@@ -2126,48 +2134,48 @@
   AXTree tree(tree_update);
 
   AXNode* outer_list = tree.GetFromId(1);
-  EXPECT_EQ(outer_list->GetPosInSet(), 0);  // Ordered sets have pos of 0
-  EXPECT_EQ(outer_list->GetSetSize(), 3);
+  EXPECT_FALSE(outer_list->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, outer_list->GetSetSize());
   AXNode* outer_list_item1 = tree.GetFromId(2);
-  EXPECT_EQ(outer_list_item1->GetPosInSet(), 1);
-  EXPECT_EQ(outer_list_item1->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(1, outer_list_item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, outer_list_item1->GetSetSize());
   AXNode* outer_list_item2 = tree.GetFromId(3);
-  EXPECT_EQ(outer_list_item2->GetPosInSet(), 2);
-  EXPECT_EQ(outer_list_item2->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(2, outer_list_item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, outer_list_item2->GetSetSize());
   AXNode* outer_list_item3 = tree.GetFromId(7);
-  EXPECT_EQ(outer_list_item3->GetPosInSet(), 3);
-  EXPECT_EQ(outer_list_item3->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(3, outer_list_item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, outer_list_item3->GetSetSize());
 
   AXNode* inner_list1 = tree.GetFromId(4);
-  EXPECT_EQ(inner_list1->GetPosInSet(),
-            0);  // Ordered sets have pos of 0, even when nested
-  EXPECT_EQ(inner_list1->GetSetSize(), 2);
+  EXPECT_FALSE(inner_list1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, inner_list1->GetSetSize());
   AXNode* inner_list1_item1 = tree.GetFromId(5);
-  EXPECT_EQ(inner_list1_item1->GetPosInSet(), 1);
-  EXPECT_EQ(inner_list1_item1->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(1, inner_list1_item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, inner_list1_item1->GetSetSize());
   AXNode* inner_list1_item2 = tree.GetFromId(6);
-  EXPECT_EQ(inner_list1_item2->GetPosInSet(), 2);
-  EXPECT_EQ(inner_list1_item2->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(2, inner_list1_item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, inner_list1_item2->GetSetSize());
 
   AXNode* inner_list2 = tree.GetFromId(8);  // Empty list
-  EXPECT_EQ(inner_list2->GetPosInSet(), 0);
-  EXPECT_EQ(inner_list2->GetSetSize(), 0);
+  EXPECT_FALSE(inner_list2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(0, inner_list2->GetSetSize());
 
   AXNode* inner_list3 = tree.GetFromId(9);
-  EXPECT_EQ(inner_list3->GetPosInSet(), 0);
-  EXPECT_EQ(inner_list3->GetSetSize(), 1);  // Only 1 item whose role matches
+  EXPECT_FALSE(inner_list3->GetPosInSet());
+  // Only 1 item whose role matches.
+  EXPECT_OPTIONAL_EQ(1, inner_list3->GetSetSize());
   AXNode* inner_list3_article1 = tree.GetFromId(10);
-  EXPECT_EQ(inner_list3_article1->GetPosInSet(), 0);
-  EXPECT_EQ(inner_list3_article1->GetSetSize(), 0);
+  EXPECT_OPTIONAL_EQ(0, inner_list3_article1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(0, inner_list3_article1->GetSetSize());
   AXNode* inner_list3_item1 = tree.GetFromId(11);
-  EXPECT_EQ(inner_list3_item1->GetPosInSet(), 1);
-  EXPECT_EQ(inner_list3_item1->GetSetSize(), 1);
+  EXPECT_OPTIONAL_EQ(1, inner_list3_item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(1, inner_list3_item1->GetSetSize());
 
   AXNode* inner_list4 = tree.GetFromId(12);
-  EXPECT_EQ(inner_list4->GetPosInSet(), 0);
+  EXPECT_FALSE(inner_list4->GetPosInSet());
   // Even though list is empty, kSetSize attribute was set, so it takes
   // precedence
-  EXPECT_EQ(inner_list4->GetSetSize(), 5);
+  EXPECT_OPTIONAL_EQ(5, inner_list4->GetSetSize());
 }
 
 // Tests GetPosInSet and GetSetSize code on invalid input.
@@ -2187,14 +2195,14 @@
   AXTree tree(tree_update);
 
   AXNode* item1 = tree.GetFromId(1);
-  EXPECT_EQ(item1->GetPosInSet(), 0);
-  EXPECT_EQ(item1->GetSetSize(), 0);
+  EXPECT_FALSE(item1->GetPosInSet());
+  EXPECT_FALSE(item1->GetSetSize());
   AXNode* item2 = tree.GetFromId(2);
-  EXPECT_EQ(item2->GetPosInSet(), 0);
-  EXPECT_EQ(item2->GetSetSize(), 0);
+  EXPECT_OPTIONAL_EQ(0, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(0, item2->GetSetSize());
   AXNode* item3 = tree.GetFromId(3);
-  EXPECT_EQ(item3->GetPosInSet(), 0);
-  EXPECT_EQ(item3->GetSetSize(), 0);
+  EXPECT_OPTIONAL_EQ(0, item3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(0, item3->GetSetSize());
 }
 
 // Tests GetPosInSet and GetSetSize code on kRadioButtons. Radio buttons
@@ -2265,46 +2273,46 @@
   AXTree tree(tree_update);
 
   AXNode* sports_button1 = tree.GetFromId(2);
-  EXPECT_EQ(sports_button1->GetPosInSet(), 1);
-  EXPECT_EQ(sports_button1->GetSetSize(), 5);
+  EXPECT_OPTIONAL_EQ(1, sports_button1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(5, sports_button1->GetSetSize());
   AXNode* books_button = tree.GetFromId(3);
-  EXPECT_EQ(books_button->GetPosInSet(), 2);
-  EXPECT_EQ(books_button->GetSetSize(), 5);
+  EXPECT_OPTIONAL_EQ(2, books_button->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(5, books_button->GetSetSize());
 
   AXNode* radiogroup1 = tree.GetFromId(4);
-  EXPECT_EQ(radiogroup1->GetPosInSet(), 0);
-  EXPECT_EQ(radiogroup1->GetSetSize(), 4);
+  EXPECT_FALSE(radiogroup1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, radiogroup1->GetSetSize());
   AXNode* recipes_button1 = tree.GetFromId(5);
-  EXPECT_EQ(recipes_button1->GetPosInSet(), 1);
-  EXPECT_EQ(recipes_button1->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(1, recipes_button1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, recipes_button1->GetSetSize());
   AXNode* recipes_button2 = tree.GetFromId(6);
-  EXPECT_EQ(recipes_button2->GetPosInSet(), 2);
-  EXPECT_EQ(recipes_button2->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(2, recipes_button2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, recipes_button2->GetSetSize());
 
   AXNode* generic_container = tree.GetFromId(7);
-  EXPECT_EQ(generic_container->GetPosInSet(), 0);
-  EXPECT_EQ(generic_container->GetSetSize(), 0);
+  EXPECT_FALSE(generic_container->GetPosInSet());
+  EXPECT_FALSE(generic_container->GetSetSize());
   AXNode* recipes_button3 = tree.GetFromId(8);
-  EXPECT_EQ(recipes_button3->GetPosInSet(), 3);
-  EXPECT_EQ(recipes_button3->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(3, recipes_button3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, recipes_button3->GetSetSize());
   AXNode* recipes_button4 = tree.GetFromId(9);
-  EXPECT_EQ(recipes_button4->GetPosInSet(), 4);
-  EXPECT_EQ(recipes_button4->GetSetSize(), 4);
+  EXPECT_OPTIONAL_EQ(4, recipes_button4->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(4, recipes_button4->GetSetSize());
 
   // Elements with role kForm shouldn't report posinset or setsize
   AXNode* form = tree.GetFromId(10);
-  EXPECT_EQ(form->GetPosInSet(), 0);
-  EXPECT_EQ(form->GetSetSize(), 0);
+  EXPECT_FALSE(form->GetPosInSet());
+  EXPECT_FALSE(form->GetSetSize());
   AXNode* cities_button1 = tree.GetFromId(11);
-  EXPECT_EQ(cities_button1->GetPosInSet(), 1);
-  EXPECT_EQ(cities_button1->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(1, cities_button1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, cities_button1->GetSetSize());
   AXNode* cities_button2 = tree.GetFromId(12);
-  EXPECT_EQ(cities_button2->GetPosInSet(), 2);
-  EXPECT_EQ(cities_button2->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(2, cities_button2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, cities_button2->GetSetSize());
 
   AXNode* sports_button2 = tree.GetFromId(13);
-  EXPECT_EQ(sports_button2->GetPosInSet(), 4);
-  EXPECT_EQ(sports_button2->GetSetSize(), 5);
+  EXPECT_OPTIONAL_EQ(4, sports_button2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(5, sports_button2->GetSetSize());
 }
 
 // Tests GetPosInSet and GetSetSize on a list that includes radio buttons.
@@ -2332,29 +2340,29 @@
   AXTree tree(tree_update);
 
   AXNode* list = tree.GetFromId(1);
-  EXPECT_EQ(list->GetPosInSet(), 0);
-  EXPECT_EQ(list->GetSetSize(), 2);
+  EXPECT_FALSE(list->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, list->GetSetSize());
 
   AXNode* radiobutton1 = tree.GetFromId(2);
-  EXPECT_EQ(radiobutton1->GetPosInSet(), 1);
-  EXPECT_EQ(radiobutton1->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(1, radiobutton1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, radiobutton1->GetSetSize());
   AXNode* item1 = tree.GetFromId(3);
-  EXPECT_EQ(item1->GetPosInSet(), 1);
-  EXPECT_EQ(item1->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, item1->GetSetSize());
   AXNode* radiobutton2 = tree.GetFromId(4);
-  EXPECT_EQ(radiobutton2->GetPosInSet(), 2);
-  EXPECT_EQ(radiobutton2->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(2, radiobutton2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, radiobutton2->GetSetSize());
   AXNode* item2 = tree.GetFromId(5);
-  EXPECT_EQ(item2->GetPosInSet(), 2);
-  EXPECT_EQ(item2->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, item2->GetSetSize());
   AXNode* radiobutton3 = tree.GetFromId(6);
-  EXPECT_EQ(radiobutton3->GetPosInSet(), 3);
-  EXPECT_EQ(radiobutton3->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(3, radiobutton3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, radiobutton3->GetSetSize());
 
   // Ensure that the setsize of list was not modified after calling GetPosInSet
   // and GetSetSize on kRadioButtons.
-  EXPECT_EQ(list->GetPosInSet(), 0);
-  EXPECT_EQ(list->GetSetSize(), 2);
+  EXPECT_FALSE(list->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, list->GetSetSize());
 }
 
 // Tests GetPosInSet and GetSetSize on a flat tree representation. According
@@ -2383,14 +2391,14 @@
   AXTree tree(tree_update);
 
   AXNode* item1_level1 = tree.GetFromId(2);
-  EXPECT_EQ(item1_level1->GetPosInSet(), 1);
-  EXPECT_EQ(item1_level1->GetSetSize(), 1);
+  EXPECT_OPTIONAL_EQ(1, item1_level1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(1, item1_level1->GetSetSize());
   AXNode* item1_level2 = tree.GetFromId(3);
-  EXPECT_EQ(item1_level2->GetPosInSet(), 1);
-  EXPECT_EQ(item1_level2->GetSetSize(), 1);
+  EXPECT_OPTIONAL_EQ(1, item1_level2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(1, item1_level2->GetSetSize());
   AXNode* item1_level3 = tree.GetFromId(4);
-  EXPECT_EQ(item1_level3->GetPosInSet(), 1);
-  EXPECT_EQ(item1_level3->GetSetSize(), 1);
+  EXPECT_OPTIONAL_EQ(1, item1_level3->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(1, item1_level3->GetSetSize());
 }
 
 // Tests GetPosInSet and GetSetSize on a flat tree representation, where only
@@ -2438,31 +2446,31 @@
 
   // The order in which we query the nodes should not matter.
   AXNode* item3_level1 = tree.GetFromId(9);
-  EXPECT_EQ(item3_level1->GetPosInSet(), 3);
-  EXPECT_EQ(item3_level1->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(3, item3_level1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item3_level1->GetSetSize());
   AXNode* item3_level2a = tree.GetFromId(8);
-  EXPECT_EQ(item3_level2a->GetPosInSet(), 3);
-  EXPECT_EQ(item3_level2a->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(3, item3_level2a->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item3_level2a->GetSetSize());
   AXNode* item2_level2a = tree.GetFromId(7);
-  EXPECT_EQ(item2_level2a->GetPosInSet(), 2);
-  EXPECT_EQ(item2_level2a->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(2, item2_level2a->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item2_level2a->GetSetSize());
   AXNode* item1_level2a = tree.GetFromId(6);
-  EXPECT_EQ(item1_level2a->GetPosInSet(), 1);
-  EXPECT_EQ(item1_level2a->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(1, item1_level2a->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item1_level2a->GetSetSize());
   AXNode* item2_level1 = tree.GetFromId(5);
-  EXPECT_EQ(item2_level1->GetPosInSet(), 2);
-  EXPECT_EQ(item2_level1->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(2, item2_level1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item2_level1->GetSetSize());
   AXNode* item2_level2 = tree.GetFromId(4);
-  EXPECT_EQ(item2_level2->GetPosInSet(), 2);
-  EXPECT_EQ(item2_level2->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(2, item2_level2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, item2_level2->GetSetSize());
   AXNode* item1_level2 = tree.GetFromId(3);
-  EXPECT_EQ(item1_level2->GetPosInSet(), 1);
-  EXPECT_EQ(item1_level2->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(1, item1_level2->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, item1_level2->GetSetSize());
   AXNode* item1_level1 = tree.GetFromId(2);
-  EXPECT_EQ(item1_level1->GetPosInSet(), 1);
-  EXPECT_EQ(item1_level1->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(1, item1_level1->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(3, item1_level1->GetSetSize());
   AXNode* ordered_set = tree.GetFromId(1);
-  EXPECT_EQ(ordered_set->GetSetSize(), 3);
+  EXPECT_OPTIONAL_EQ(3, ordered_set->GetSetSize());
 }
 
 // Tests that GetPosInSet and GetSetSize work while a tree is being
@@ -2482,8 +2490,8 @@
 
   // This should work normally.
   AXNode* item = tree.GetFromId(3);
-  EXPECT_EQ(item->GetPosInSet(), 2);
-  EXPECT_EQ(item->GetSetSize(), 2);
+  EXPECT_OPTIONAL_EQ(2, item->GetPosInSet());
+  EXPECT_OPTIONAL_EQ(2, item->GetSetSize());
 
   // Use test observer to assert posinset and setsize are 0.
   TestAXTreeObserver test_observer(&tree);
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 6581901..faff790 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -1422,8 +1422,10 @@
 
 gint GetIndexAt(AtkTable* table, gint row, gint column) {
   if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(table))) {
-    if (auto* cell = obj->GetTableCell(row, column))
-      return cell->GetTableCellIndex();
+    if (const AXPlatformNodeBase* cell = obj->GetTableCell(row, column)) {
+      DCHECK(cell->GetTableCellIndex().has_value());
+      return cell->GetTableCellIndex().value();
+    }
   }
 
   return -1;
@@ -1431,8 +1433,10 @@
 
 gint GetColumnAtIndex(AtkTable* table, gint index) {
   if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(table))) {
-    if (auto* cell = obj->GetTableCell(index))
-      return cell->GetTableColumn();
+    if (const AXPlatformNodeBase* cell = obj->GetTableCell(index)) {
+      DCHECK(cell->GetTableColumn().has_value());
+      return cell->GetTableColumn().value();
+    }
   }
 
   return -1;
@@ -1440,31 +1444,39 @@
 
 gint GetRowAtIndex(AtkTable* table, gint index) {
   if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(table))) {
-    if (auto* cell = obj->GetTableCell(index))
-      return cell->GetTableRow();
+    if (const AXPlatformNodeBase* cell = obj->GetTableCell(index)) {
+      DCHECK(cell->GetTableRow().has_value());
+      return cell->GetTableRow().value();
+    }
   }
 
   return -1;
 }
 
 gint GetNColumns(AtkTable* table) {
-  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(table)))
-    return obj->GetTableColumnCount();
+  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(table))) {
+    // If the object is not a table, we return 0.
+    return obj->GetTableColumnCount().value_or(0);
+  }
 
   return 0;
 }
 
 gint GetNRows(AtkTable* table) {
-  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(table)))
-    return obj->GetTableRowCount();
+  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(table))) {
+    // If the object is not a table, we return 0.
+    return obj->GetTableRowCount().value_or(0);
+  }
 
   return 0;
 }
 
 gint GetColumnExtentAt(AtkTable* table, gint row, gint column) {
   if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(table))) {
-    if (auto* cell = obj->GetTableCell(row, column))
-      return cell->GetTableColumnSpan();
+    if (const AXPlatformNodeBase* cell = obj->GetTableCell(row, column)) {
+      DCHECK(cell->GetTableColumnSpan().has_value());
+      return cell->GetTableColumnSpan().value();
+    }
   }
 
   return 0;
@@ -1472,8 +1484,10 @@
 
 gint GetRowExtentAt(AtkTable* table, gint row, gint column) {
   if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(table))) {
-    if (auto* cell = obj->GetTableCell(row, column))
-      return cell->GetTableRowSpan();
+    if (const AXPlatformNodeBase* cell = obj->GetTableCell(row, column)) {
+      DCHECK(cell->GetTableRowSpan().has_value());
+      return cell->GetTableRowSpan().value();
+    }
   }
 
   return 0;
@@ -1575,8 +1589,11 @@
 namespace atk_table_cell {
 
 gint GetColumnSpan(AtkTableCell* cell) {
-  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell)))
-    return obj->GetTableColumnSpan();
+  if (const AXPlatformNodeBase* obj =
+          AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell))) {
+    // If the object is not a cell, we return 0.
+    return obj->GetTableColumnSpan().value_or(0);
+  }
 
   return 0;
 }
@@ -1591,17 +1608,19 @@
   // headers for non-header cells.
   if (atk_object_get_role(ATK_OBJECT(cell)) != ATK_ROLE_TABLE_CELL)
     return array;
+
   auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell));
   if (!obj)
     return array;
-  auto* table = obj->GetTable();
-  if (!table)
+
+  base::Optional<int> col_index = obj->GetTableColumn();
+  if (!col_index)
     return array;
 
-  std::vector<int32_t> ids =
-      table->GetDelegate()->GetColHeaderNodeIds(obj->GetTableColumn());
+  const std::vector<int32_t> ids =
+      obj->GetDelegate()->GetColHeaderNodeIds(*col_index);
   for (const auto& node_id : ids) {
-    if (AXPlatformNode* node = table->GetDelegate()->GetFromNodeID(node_id)) {
+    if (AXPlatformNode* node = obj->GetDelegate()->GetFromNodeID(node_id)) {
       if (AtkObject* atk_node = node->GetNativeViewAccessible()) {
         g_ptr_array_add(array, g_object_ref(atk_node));
       }
@@ -1613,8 +1632,13 @@
 
 gboolean GetCellPosition(AtkTableCell* cell, gint* row, gint* column) {
   if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell))) {
-    *row = obj->GetTableRow();
-    *column = obj->GetTableColumn();
+    base::Optional<int> row_index = obj->GetTableRow();
+    base::Optional<int> col_index = obj->GetTableColumn();
+    if (!row_index || !col_index)
+      return false;
+
+    *row = *row_index;
+    *column = *col_index;
     return true;
   }
 
@@ -1622,8 +1646,10 @@
 }
 
 gint GetRowSpan(AtkTableCell* cell) {
-  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell)))
-    return obj->GetTableRowSpan();
+  if (auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell))) {
+    // If the object is not a cell, we return 0.
+    return obj->GetTableRowSpan().value_or(0);
+  }
 
   return 0;
 }
@@ -1638,17 +1664,19 @@
   // headers for non-header cells.
   if (atk_object_get_role(ATK_OBJECT(cell)) != ATK_ROLE_TABLE_CELL)
     return array;
+
   auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(cell));
   if (!obj)
     return array;
-  auto* table = obj->GetTable();
-  if (!table)
+
+  base::Optional<int> row_index = obj->GetTableRow();
+  if (!row_index)
     return array;
 
-  std::vector<int32_t> ids =
-      table->GetDelegate()->GetRowHeaderNodeIds(obj->GetTableRow());
+  const std::vector<int32_t> ids =
+      obj->GetDelegate()->GetRowHeaderNodeIds(*row_index);
   for (const auto& node_id : ids) {
-    if (AXPlatformNode* node = table->GetDelegate()->GetFromNodeID(node_id)) {
+    if (AXPlatformNode* node = obj->GetDelegate()->GetFromNodeID(node_id)) {
       if (AtkObject* atk_node = node->GetNativeViewAccessible()) {
         g_ptr_array_add(array, g_object_ref(atk_node));
       }
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
index 590cd64..faab37bb 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
@@ -1024,6 +1024,8 @@
   verify_text(nullptr, -1, -1);
   verify_text(nullptr, 5, 2);
   verify_text(nullptr, 10, 20);
+
+  g_object_unref(root_obj);
 }
 
 TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextCharacterGranularity) {
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index 01275b3b..e99c109 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -556,8 +556,12 @@
     return nullptr;
 
   DCHECK(table->delegate_);
+  base::Optional<int32_t> cell_id = table->delegate_->CellIndexToId(index);
+  if (!cell_id)
+    return nullptr;
+
   return static_cast<AXPlatformNodeBase*>(
-      table->delegate_->GetFromNodeID(table->delegate_->CellIndexToId(index)));
+      table->delegate_->GetFromNodeID(*cell_id));
 }
 
 AXPlatformNodeBase* AXPlatformNodeBase::GetTableCell(int row,
@@ -565,46 +569,49 @@
   if (!IsTableLike(GetData().role) && !IsCellOrTableHeader(GetData().role))
     return nullptr;
 
-  if (row < 0 || row >= GetTableRowCount() || column < 0 ||
-      column >= GetTableColumnCount()) {
+  AXPlatformNodeBase* table = GetTable();
+  if (!table || !GetTableRowCount() || !GetTableColumnCount())
+    return nullptr;
+
+  if (row < 0 || row >= *GetTableRowCount() || column < 0 ||
+      column >= *GetTableColumnCount()) {
     return nullptr;
   }
 
-  AXPlatformNodeBase* table = GetTable();
-  if (!table)
+  DCHECK(table->delegate_);
+  base::Optional<int32_t> cell_id = table->delegate_->GetCellId(row, column);
+  if (!cell_id)
     return nullptr;
 
-  DCHECK(table->delegate_);
-  int32_t cell_id = table->delegate_->GetCellId(row, column);
   return static_cast<AXPlatformNodeBase*>(
-      table->delegate_->GetFromNodeID(cell_id));
+      table->delegate_->GetFromNodeID(*cell_id));
 }
 
-int AXPlatformNodeBase::GetTableCellIndex() const {
+base::Optional<int> AXPlatformNodeBase::GetTableCellIndex() const {
   if (!delegate_)
-    return 0;
-  return int{delegate_->GetTableCellIndex()};
+    return base::nullopt;
+  return delegate_->GetTableCellIndex();
 }
 
-int AXPlatformNodeBase::GetTableColumn() const {
+base::Optional<int> AXPlatformNodeBase::GetTableColumn() const {
   if (!delegate_)
-    return 0;
-  return int{delegate_->GetTableCellColIndex()};
+    return base::nullopt;
+  return delegate_->GetTableCellColIndex();
 }
 
-int AXPlatformNodeBase::GetTableColumnCount() const {
+base::Optional<int> AXPlatformNodeBase::GetTableColumnCount() const {
   if (!delegate_)
-    return 0;
+    return base::nullopt;
 
   AXPlatformNodeBase* table = GetTable();
   if (!table)
-    return 0;
+    return base::nullopt;
 
   DCHECK(table->delegate_);
-  return int{table->delegate_->GetTableColCount()};
+  return table->delegate_->GetTableColCount();
 }
 
-base::Optional<int32_t> AXPlatformNodeBase::GetTableAriaColumnCount() const {
+base::Optional<int> AXPlatformNodeBase::GetTableAriaColumnCount() const {
   if (!delegate_)
     return base::nullopt;
 
@@ -616,35 +623,35 @@
   return table->delegate_->GetTableAriaColCount();
 }
 
-int AXPlatformNodeBase::GetTableColumnSpan() const {
+base::Optional<int> AXPlatformNodeBase::GetTableColumnSpan() const {
   if (!delegate_)
-    return 1;
-  return int{delegate_->GetTableCellColSpan()};
+    return base::nullopt;
+  return delegate_->GetTableCellColSpan();
 }
 
-int AXPlatformNodeBase::GetTableRow() const {
+base::Optional<int> AXPlatformNodeBase::GetTableRow() const {
   if (!delegate_)
-    return 0;
+    return base::nullopt;
   if (delegate_->IsTableRow())
-    return int{delegate_->GetTableRowRowIndex()};
+    return delegate_->GetTableRowRowIndex();
   if (delegate_->IsTableCellOrHeader())
-    return int{delegate_->GetTableCellRowIndex()};
-  return 0;
+    return delegate_->GetTableCellRowIndex();
+  return base::nullopt;
 }
 
-int AXPlatformNodeBase::GetTableRowCount() const {
+base::Optional<int> AXPlatformNodeBase::GetTableRowCount() const {
   if (!delegate_)
-    return 0;
+    return base::nullopt;
 
   AXPlatformNodeBase* table = GetTable();
   if (!table)
-    return 0;
+    return base::nullopt;
 
   DCHECK(table->delegate_);
-  return int{table->delegate_->GetTableRowCount()};
+  return table->delegate_->GetTableRowCount();
 }
 
-base::Optional<int32_t> AXPlatformNodeBase::GetTableAriaRowCount() const {
+base::Optional<int> AXPlatformNodeBase::GetTableAriaRowCount() const {
   if (!delegate_)
     return base::nullopt;
 
@@ -656,10 +663,10 @@
   return table->delegate_->GetTableAriaRowCount();
 }
 
-int AXPlatformNodeBase::GetTableRowSpan() const {
+base::Optional<int> AXPlatformNodeBase::GetTableRowSpan() const {
   if (!delegate_)
-    return 1;
-  return int{delegate_->GetTableCellRowSpan()};
+    return base::nullopt;
+  return delegate_->GetTableCellRowSpan();
 }
 
 bool AXPlatformNodeBase::HasCaret() {
@@ -925,9 +932,9 @@
 
   // Expose table cell index.
   if (IsCellOrTableHeader(GetData().role)) {
-    int32_t index = delegate_->GetTableCellIndex();
-    if (index >= 0) {
-      std::string str_index(base::NumberToString(index));
+    base::Optional<int> index = delegate_->GetTableCellIndex();
+    if (index) {
+      std::string str_index(base::NumberToString(*index));
       AddAttributeToList("table-cell-index", str_index, attributes);
     }
   }
@@ -1170,11 +1177,15 @@
                                             PlatformAttributeList* attributes) {
 }
 
-int32_t AXPlatformNodeBase::GetPosInSet() const {
+base::Optional<int> AXPlatformNodeBase::GetPosInSet() const {
+  if (!delegate_)
+    return base::nullopt;
   return delegate_->GetPosInSet();
 }
 
-int32_t AXPlatformNodeBase::GetSetSize() const {
+base::Optional<int> AXPlatformNodeBase::GetSetSize() const {
+  if (!delegate_)
+    return base::nullopt;
   return delegate_->GetSetSize();
 }
 
diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h
index abfb229..bc54f877 100644
--- a/ui/accessibility/platform/ax_platform_node_base.h
+++ b/ui/accessibility/platform/ax_platform_node_base.h
@@ -125,58 +125,66 @@
   AXPlatformNodeBase* GetTable() const;
 
   // If inside an HTML or ARIA table, returns the object containing the caption.
+  // Returns nullptr if not inside a table, or if there is no
+  // caption.
   AXPlatformNodeBase* GetTableCaption() const;
 
   // If inside a table or ARIA grid, returns the cell found at the given index.
   // Indices are in row major order and each cell is counted once regardless of
-  // its span.
+  // its span. Returns nullptr if the cell is not found or if not inside a
+  // table.
   AXPlatformNodeBase* GetTableCell(int index) const;
 
   // If inside a table or ARIA grid, returns the cell at the given row and
   // column (0-based). Works correctly with cells that span multiple rows or
-  // columns.
+  // columns. Returns nullptr if the cell is not found or if not inside a
+  // table.
   AXPlatformNodeBase* GetTableCell(int row, int column) const;
 
   // If inside a table or ARIA grid, returns the zero-based index of the cell.
   // Indices are in row major order and each cell is counted once regardless of
-  // its span. Returns -1 if the cell is not found or if not inside a table.
-  int GetTableCellIndex() const;
+  // its span. Returns base::nullopt if not a cell or if not inside a table.
+  base::Optional<int> GetTableCellIndex() const;
 
   // If inside a table or ARIA grid, returns the physical column number for the
   // current cell. In contrast to logical columns, physical columns always start
   // from 0 and have no gaps in their numbering. Logical columns can be set
-  // using aria-colindex.
-  int GetTableColumn() const;
+  // using aria-colindex. Returns base::nullopt if not a cell or if not inside a
+  // table.
+  base::Optional<int> GetTableColumn() const;
 
-  // If inside a table or ARIA grid, returns the number of physical columns,
-  // otherwise returns 0.
-  int GetTableColumnCount() const;
+  // If inside a table or ARIA grid, returns the number of physical columns.
+  // Returns base::nullopt if not inside a table.
+  base::Optional<int> GetTableColumnCount() const;
 
-  // If inside a table or ARIA grid, returns the number of ARIA columns,
-  // otherwise returns nullopt.
-  base::Optional<int32_t> GetTableAriaColumnCount() const;
+  // If inside a table or ARIA grid, returns the number of ARIA columns.
+  // Returns base::nullopt if not inside a table.
+  base::Optional<int> GetTableAriaColumnCount() const;
 
   // If inside a table or ARIA grid, returns the number of physical columns that
-  // this cell spans. If not a cell, returns 0.
-  int GetTableColumnSpan() const;
+  // this cell spans. Returns base::nullopt if not a cell or if not inside a
+  // table.
+  base::Optional<int> GetTableColumnSpan() const;
 
   // If inside a table or ARIA grid, returns the physical row number for the
   // current cell. In contrast to logical rows, physical rows always start from
   // 0 and have no gaps in their numbering. Logical rows can be set using
-  // aria-rowindex.
-  int GetTableRow() const;
+  // aria-rowindex. Returns base::nullopt if not a cell or if not inside a
+  // table.
+  base::Optional<int> GetTableRow() const;
 
-  // If inside a table or ARIA grid, returns the number of physical rows,
-  // otherwise returns 0.
-  int GetTableRowCount() const;
+  // If inside a table or ARIA grid, returns the number of physical rows.
+  // Returns base::nullopt if not inside a table.
+  base::Optional<int> GetTableRowCount() const;
 
-  // If inside a table or ARIA grid, returns the number of ARIA rows,
-  // otherwise returns nullopt.
-  base::Optional<int32_t> GetTableAriaRowCount() const;
+  // If inside a table or ARIA grid, returns the number of ARIA rows.
+  // Returns base::nullopt if not inside a table.
+  base::Optional<int> GetTableAriaRowCount() const;
 
   // If inside a table or ARIA grid, returns the number of physical rows that
-  // this cell spans. If not a cell, returns 0.
-  int GetTableRowSpan() const;
+  // this cell spans. Returns base::nullopt if not a cell or if not inside a
+  // table.
+  base::Optional<int> GetTableRowSpan() const;
 
   // Returns true if either a descendant has selection (sel_focus_object_id) or
   // if this node is a simple text element and has text selection attributes.
@@ -392,8 +400,9 @@
                                           size_t* start,
                                           size_t* old_len,
                                           size_t* new_len);
-  int32_t GetPosInSet() const;
-  int32_t GetSetSize() const;
+
+  base::Optional<int> GetPosInSet() const;
+  base::Optional<int> GetSetSize() const;
 
   AXHypertext hypertext_;
 
diff --git a/ui/accessibility/platform/ax_platform_node_delegate.h b/ui/accessibility/platform/ax_platform_node_delegate.h
index df4bc673..47f7b60 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate.h
@@ -5,10 +5,15 @@
 #ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_DELEGATE_H_
 #define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_DELEGATE_H_
 
+#include <stdint.h>
+
+#include <memory>
+#include <new>
 #include <set>
 #include <utility>
 #include <vector>
 
+#include "base/optional.h"
 #include "ui/accessibility/ax_clipping_behavior.h"
 #include "ui/accessibility/ax_coordinate_system.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -207,37 +212,36 @@
 
   //
   // Tables. All of these should be called on a node that's a table-like
-  // role.
+  // role, otherwise they return nullopt.
   //
   virtual bool IsTable() const = 0;
-  virtual int32_t GetTableColCount() const = 0;
-  virtual int32_t GetTableRowCount() const = 0;
-  virtual base::Optional<int32_t> GetTableAriaColCount() const = 0;
-  virtual base::Optional<int32_t> GetTableAriaRowCount() const = 0;
-  virtual int32_t GetTableCellCount() const = 0;
-  virtual const std::vector<int32_t> GetColHeaderNodeIds() const = 0;
-  virtual const std::vector<int32_t> GetColHeaderNodeIds(
-      int32_t col_index) const = 0;
-  virtual const std::vector<int32_t> GetRowHeaderNodeIds() const = 0;
-  virtual const std::vector<int32_t> GetRowHeaderNodeIds(
-      int32_t row_index) const = 0;
-  virtual AXPlatformNode* GetTableCaption() = 0;
+  virtual base::Optional<int> GetTableColCount() const = 0;
+  virtual base::Optional<int> GetTableRowCount() const = 0;
+  virtual base::Optional<int> GetTableAriaColCount() const = 0;
+  virtual base::Optional<int> GetTableAriaRowCount() const = 0;
+  virtual base::Optional<int> GetTableCellCount() const = 0;
+  virtual std::vector<int32_t> GetColHeaderNodeIds() const = 0;
+  virtual std::vector<int32_t> GetColHeaderNodeIds(int col_index) const = 0;
+  virtual std::vector<int32_t> GetRowHeaderNodeIds() const = 0;
+  virtual std::vector<int32_t> GetRowHeaderNodeIds(int row_index) const = 0;
+  virtual AXPlatformNode* GetTableCaption() const = 0;
 
   // Table row-like nodes.
   virtual bool IsTableRow() const = 0;
-  virtual int32_t GetTableRowRowIndex() const = 0;
+  virtual base::Optional<int> GetTableRowRowIndex() const = 0;
 
   // Table cell-like nodes.
   virtual bool IsTableCellOrHeader() const = 0;
-  virtual int32_t GetTableCellIndex() const = 0;
-  virtual int32_t GetTableCellColIndex() const = 0;
-  virtual int32_t GetTableCellRowIndex() const = 0;
-  virtual int32_t GetTableCellColSpan() const = 0;
-  virtual int32_t GetTableCellRowSpan() const = 0;
-  virtual int32_t GetTableCellAriaColIndex() const = 0;
-  virtual int32_t GetTableCellAriaRowIndex() const = 0;
-  virtual int32_t GetCellId(int32_t row_index, int32_t col_index) const = 0;
-  virtual int32_t CellIndexToId(int32_t cell_index) const = 0;
+  virtual base::Optional<int> GetTableCellIndex() const = 0;
+  virtual base::Optional<int> GetTableCellColIndex() const = 0;
+  virtual base::Optional<int> GetTableCellRowIndex() const = 0;
+  virtual base::Optional<int> GetTableCellColSpan() const = 0;
+  virtual base::Optional<int> GetTableCellRowSpan() const = 0;
+  virtual base::Optional<int> GetTableCellAriaColIndex() const = 0;
+  virtual base::Optional<int> GetTableCellAriaRowIndex() const = 0;
+  virtual base::Optional<int32_t> GetCellId(int row_index,
+                                            int col_index) const = 0;
+  virtual base::Optional<int32_t> CellIndexToId(int cell_index) const = 0;
 
   // Helper methods to check if a cell is an ARIA-1.1+ 'cell' or 'gridcell'
   virtual bool IsCellOrHeaderOfARIATable() const = 0;
@@ -246,8 +250,8 @@
   // Ordered-set-like and item-like nodes.
   virtual bool IsOrderedSetItem() const = 0;
   virtual bool IsOrderedSet() const = 0;
-  virtual int32_t GetPosInSet() const = 0;
-  virtual int32_t GetSetSize() const = 0;
+  virtual base::Optional<int> GetPosInSet() const = 0;
+  virtual base::Optional<int> GetSetSize() const = 0;
 
   //
   // Events.
@@ -283,7 +287,7 @@
   // Accessibility objects can have the "hot tracked" state set when
   // the mouse is hovering over them, but this makes tests flaky because
   // the test behaves differently when the mouse happens to be over an
-  // element. The default value should be falses if not in testing mode.
+  // element. The default value should be false if not in testing mode.
   virtual bool ShouldIgnoreHoveredStateForTesting() = 0;
 
  protected:
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.cc b/ui/accessibility/platform/ax_platform_node_delegate_base.cc
index a65c2b15..6e142f0b 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_delegate_base.cc
@@ -133,57 +133,53 @@
   return ui::IsTableLike(GetData().role);
 }
 
-int AXPlatformNodeDelegateBase::GetTableRowCount() const {
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableRowCount() const {
   return GetData().GetIntAttribute(ax::mojom::IntAttribute::kTableRowCount);
 }
 
-int AXPlatformNodeDelegateBase::GetTableColCount() const {
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableColCount() const {
   return GetData().GetIntAttribute(ax::mojom::IntAttribute::kTableColumnCount);
 }
 
-base::Optional<int32_t> AXPlatformNodeDelegateBase::GetTableAriaColCount()
-    const {
-  int32_t aria_column_count =
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableAriaColCount() const {
+  int aria_column_count =
       GetData().GetIntAttribute(ax::mojom::IntAttribute::kAriaColumnCount);
   if (aria_column_count == ax::mojom::kUnknownAriaColumnOrRowCount)
     return base::nullopt;
   return aria_column_count;
 }
 
-base::Optional<int32_t> AXPlatformNodeDelegateBase::GetTableAriaRowCount()
-    const {
-  int32_t aria_row_count =
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableAriaRowCount() const {
+  int aria_row_count =
       GetData().GetIntAttribute(ax::mojom::IntAttribute::kAriaRowCount);
   if (aria_row_count == ax::mojom::kUnknownAriaColumnOrRowCount)
     return base::nullopt;
   return aria_row_count;
 }
 
-int32_t AXPlatformNodeDelegateBase::GetTableCellCount() const {
-  return 0;
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableCellCount() const {
+  return base::nullopt;
 }
 
-const std::vector<int32_t> AXPlatformNodeDelegateBase::GetColHeaderNodeIds()
-    const {
+std::vector<int32_t> AXPlatformNodeDelegateBase::GetColHeaderNodeIds() const {
   return {};
 }
 
-const std::vector<int32_t> AXPlatformNodeDelegateBase::GetColHeaderNodeIds(
-    int32_t col_index) const {
+std::vector<int32_t> AXPlatformNodeDelegateBase::GetColHeaderNodeIds(
+    int col_index) const {
   return {};
 }
 
-const std::vector<int32_t> AXPlatformNodeDelegateBase::GetRowHeaderNodeIds()
-    const {
+std::vector<int32_t> AXPlatformNodeDelegateBase::GetRowHeaderNodeIds() const {
   return {};
 }
 
-const std::vector<int32_t> AXPlatformNodeDelegateBase::GetRowHeaderNodeIds(
-    int32_t row_index) const {
+std::vector<int32_t> AXPlatformNodeDelegateBase::GetRowHeaderNodeIds(
+    int row_index) const {
   return {};
 }
 
-AXPlatformNode* AXPlatformNodeDelegateBase::GetTableCaption() {
+AXPlatformNode* AXPlatformNodeDelegateBase::GetTableCaption() const {
   return nullptr;
 }
 
@@ -191,7 +187,7 @@
   return ui::IsTableRow(GetData().role);
 }
 
-int32_t AXPlatformNodeDelegateBase::GetTableRowRowIndex() const {
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableRowRowIndex() const {
   return GetData().GetIntAttribute(ax::mojom::IntAttribute::kTableRowIndex);
 }
 
@@ -199,44 +195,48 @@
   return ui::IsCellOrTableHeader(GetData().role);
 }
 
-int32_t AXPlatformNodeDelegateBase::GetTableCellColIndex() const {
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableCellColIndex() const {
   return GetData().GetIntAttribute(
       ax::mojom::IntAttribute::kTableCellColumnIndex);
 }
 
-int32_t AXPlatformNodeDelegateBase::GetTableCellRowIndex() const {
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableCellRowIndex() const {
   return GetData().GetIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex);
 }
 
-int32_t AXPlatformNodeDelegateBase::GetTableCellColSpan() const {
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableCellColSpan() const {
   return GetData().GetIntAttribute(
       ax::mojom::IntAttribute::kTableCellColumnSpan);
 }
 
-int32_t AXPlatformNodeDelegateBase::GetTableCellRowSpan() const {
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableCellRowSpan() const {
   return GetData().GetIntAttribute(ax::mojom::IntAttribute::kTableCellRowSpan);
 }
 
-int32_t AXPlatformNodeDelegateBase::GetTableCellAriaColIndex() const {
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableCellAriaColIndex()
+    const {
   return GetData().GetIntAttribute(
       ax::mojom::IntAttribute::kAriaCellColumnIndex);
 }
 
-int32_t AXPlatformNodeDelegateBase::GetTableCellAriaRowIndex() const {
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableCellAriaRowIndex()
+    const {
   return GetData().GetIntAttribute(ax::mojom::IntAttribute::kAriaCellRowIndex);
 }
 
-int32_t AXPlatformNodeDelegateBase::GetCellId(int32_t row_index,
-                                              int32_t col_index) const {
-  return -1;
+base::Optional<int32_t> AXPlatformNodeDelegateBase::GetCellId(
+    int row_index,
+    int col_index) const {
+  return base::nullopt;
 }
 
-int32_t AXPlatformNodeDelegateBase::GetTableCellIndex() const {
-  return -1;
+base::Optional<int> AXPlatformNodeDelegateBase::GetTableCellIndex() const {
+  return base::nullopt;
 }
 
-int32_t AXPlatformNodeDelegateBase::CellIndexToId(int32_t cell_index) const {
-  return -1;
+base::Optional<int32_t> AXPlatformNodeDelegateBase::CellIndexToId(
+    int cell_index) const {
+  return base::nullopt;
 }
 
 bool AXPlatformNodeDelegateBase::IsCellOrHeaderOfARIATable() const {
@@ -247,6 +247,22 @@
   return false;
 }
 
+bool AXPlatformNodeDelegateBase::IsOrderedSetItem() const {
+  return false;
+}
+
+bool AXPlatformNodeDelegateBase::IsOrderedSet() const {
+  return false;
+}
+
+base::Optional<int> AXPlatformNodeDelegateBase::GetPosInSet() const {
+  return base::nullopt;
+}
+
+base::Optional<int> AXPlatformNodeDelegateBase::GetSetSize() const {
+  return base::nullopt;
+}
+
 bool AXPlatformNodeDelegateBase::AccessibilityPerformAction(
     const ui::AXActionData& data) {
   return false;
@@ -355,20 +371,4 @@
   return {};
 }
 
-bool AXPlatformNodeDelegateBase::IsOrderedSetItem() const {
-  return false;
-}
-
-bool AXPlatformNodeDelegateBase::IsOrderedSet() const {
-  return false;
-}
-
-int32_t AXPlatformNodeDelegateBase::GetPosInSet() const {
-  return 0;
-}
-
-int32_t AXPlatformNodeDelegateBase::GetSetSize() const {
-  return 0;
-}
-
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.h b/ui/accessibility/platform/ax_platform_node_delegate_base.h
index 1cf86c94..cffd385 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate_base.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate_base.h
@@ -7,9 +7,13 @@
 
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
 
+#include <stdint.h>
+
 #include <set>
 #include <vector>
 
+#include "base/optional.h"
+
 namespace ui {
 
 // Base implementation of AXPlatformNodeDelegate where all functions
@@ -147,37 +151,36 @@
 
   //
   // Tables. All of these should be called on a node that's a table-like
-  // role.
+  // role, otherwise they return nullopt.
   //
   bool IsTable() const override;
-  int32_t GetTableColCount() const override;
-  int32_t GetTableRowCount() const override;
-  base::Optional<int32_t> GetTableAriaColCount() const override;
-  base::Optional<int32_t> GetTableAriaRowCount() const override;
-  int32_t GetTableCellCount() const override;
-  const std::vector<int32_t> GetColHeaderNodeIds() const override;
-  const std::vector<int32_t> GetColHeaderNodeIds(
-      int32_t col_index) const override;
-  const std::vector<int32_t> GetRowHeaderNodeIds() const override;
-  const std::vector<int32_t> GetRowHeaderNodeIds(
-      int32_t row_index) const override;
-  AXPlatformNode* GetTableCaption() override;
+  base::Optional<int> GetTableColCount() const override;
+  base::Optional<int> GetTableRowCount() const override;
+  base::Optional<int> GetTableAriaColCount() const override;
+  base::Optional<int> GetTableAriaRowCount() const override;
+  base::Optional<int> GetTableCellCount() const override;
+  std::vector<int32_t> GetColHeaderNodeIds() const override;
+  std::vector<int32_t> GetColHeaderNodeIds(int col_index) const override;
+  std::vector<int32_t> GetRowHeaderNodeIds() const override;
+  std::vector<int32_t> GetRowHeaderNodeIds(int row_index) const override;
+  AXPlatformNode* GetTableCaption() const override;
 
   // Table row-like nodes.
   bool IsTableRow() const override;
-  int32_t GetTableRowRowIndex() const override;
+  base::Optional<int> GetTableRowRowIndex() const override;
 
   // Table cell-like nodes.
   bool IsTableCellOrHeader() const override;
-  int32_t GetTableCellIndex() const override;
-  int32_t GetTableCellColIndex() const override;
-  int32_t GetTableCellRowIndex() const override;
-  int32_t GetTableCellColSpan() const override;
-  int32_t GetTableCellRowSpan() const override;
-  int32_t GetTableCellAriaColIndex() const override;
-  int32_t GetTableCellAriaRowIndex() const override;
-  int32_t GetCellId(int32_t row_index, int32_t col_index) const override;
-  int32_t CellIndexToId(int32_t cell_index) const override;
+  base::Optional<int> GetTableCellIndex() const override;
+  base::Optional<int> GetTableCellColIndex() const override;
+  base::Optional<int> GetTableCellRowIndex() const override;
+  base::Optional<int> GetTableCellColSpan() const override;
+  base::Optional<int> GetTableCellRowSpan() const override;
+  base::Optional<int> GetTableCellAriaColIndex() const override;
+  base::Optional<int> GetTableCellAriaRowIndex() const override;
+  base::Optional<int32_t> GetCellId(int row_index,
+                                    int col_index) const override;
+  base::Optional<int32_t> CellIndexToId(int cell_index) const override;
 
   // Helper methods to check if a cell is an ARIA-1.1+ 'cell' or 'gridcell'
   bool IsCellOrHeaderOfARIATable() const override;
@@ -186,8 +189,8 @@
   // Ordered-set-like and item-like nodes.
   bool IsOrderedSetItem() const override;
   bool IsOrderedSet() const override;
-  int32_t GetPosInSet() const override;
-  int32_t GetSetSize() const override;
+  base::Optional<int> GetPosInSet() const override;
+  base::Optional<int> GetSetSize() const override;
 
   //
   // Events.
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index a96cc539..413c495 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -97,6 +97,24 @@
   if (!arg4)                                               \
     return E_INVALIDARG;                                   \
   *arg4 = {};
+#define COM_OBJECT_VALIDATE_5_ARGS(arg1, arg2, arg3, arg4, arg5) \
+  if (!GetDelegate())                                            \
+    return E_FAIL;                                               \
+  if (!arg1)                                                     \
+    return E_INVALIDARG;                                         \
+  *arg1 = {};                                                    \
+  if (!arg2)                                                     \
+    return E_INVALIDARG;                                         \
+  *arg2 = {};                                                    \
+  if (!arg3)                                                     \
+    return E_INVALIDARG;                                         \
+  *arg3 = {};                                                    \
+  if (!arg4)                                                     \
+    return E_INVALIDARG;                                         \
+  *arg4 = {};                                                    \
+  if (!arg5)                                                     \
+    return E_INVALIDARG;                                         \
+  *arg5 = {};
 #define COM_OBJECT_VALIDATE_VAR_ID_AND_GET_TARGET(var_id, target) \
   if (!GetDelegate())                                             \
     return E_FAIL;                                                \
@@ -804,11 +822,11 @@
 
     case NAVDIR_DOWN: {
       // This direction is not implemented except in tables.
-      if (!IsTableLike(GetData().role) && !IsCellOrTableHeader(GetData().role))
+      if (!GetTableRow() || !GetTableRowSpan() || !GetTableColumn())
         return E_NOTIMPL;
 
       AXPlatformNodeBase* next = target->GetTableCell(
-          GetTableRow() + GetTableRowSpan(), GetTableColumn());
+          *GetTableRow() + *GetTableRowSpan(), *GetTableColumn());
       if (!next)
         return S_OK;
 
@@ -818,11 +836,11 @@
 
     case NAVDIR_UP: {
       // This direction is not implemented except in tables.
-      if (!IsTableLike(GetData().role) && !IsCellOrTableHeader(GetData().role))
+      if (!GetTableRow() || !GetTableColumn())
         return E_NOTIMPL;
 
       AXPlatformNodeBase* next =
-          target->GetTableCell(GetTableRow() - 1, GetTableColumn());
+          target->GetTableCell(*GetTableRow() - 1, *GetTableColumn());
       if (!next)
         return S_OK;
 
@@ -832,11 +850,11 @@
 
     case NAVDIR_LEFT: {
       // This direction is not implemented except in tables.
-      if (!IsTableLike(GetData().role) && !IsCellOrTableHeader(GetData().role))
+      if (!GetTableRow() || !GetTableColumn())
         return E_NOTIMPL;
 
       AXPlatformNodeBase* next =
-          target->GetTableCell(GetTableRow(), GetTableColumn() - 1);
+          target->GetTableCell(*GetTableRow(), *GetTableColumn() - 1);
       if (!next)
         return S_OK;
 
@@ -847,11 +865,11 @@
     case NAVDIR_RIGHT: {
       // This direction is not implemented except in tables.
 
-      if (!IsTableLike(GetData().role) && !IsCellOrTableHeader(GetData().role))
+      if (!GetTableRow() || !GetTableColumn() || !GetTableColumnSpan())
         return E_NOTIMPL;
 
       AXPlatformNodeBase* next = target->GetTableCell(
-          GetTableRow(), GetTableColumn() + GetTableColumnSpan());
+          *GetTableRow(), *GetTableColumn() + *GetTableColumnSpan());
       if (!next)
         return S_OK;
 
@@ -1365,8 +1383,8 @@
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
   *group_level = GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel);
-  *similar_items_in_group = GetSetSize();
-  *position_in_group = GetPosInSet();
+  *similar_items_in_group = GetSetSize().value_or(0);
+  *position_in_group = GetPosInSet().value_or(0);
 
   if (!*group_level && !*similar_items_in_group && !*position_in_group)
     return S_FALSE;
@@ -1696,14 +1714,20 @@
 IFACEMETHODIMP AXPlatformNodeWin::get_Column(int* result) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GRIDITEM_GET_COLUMN);
   UIA_VALIDATE_CALL_1_ARG(result);
-  *result = GetTableColumn();
+  base::Optional<int> column = GetTableColumn();
+  if (!column)
+    return E_FAIL;
+  *result = *column;
   return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_ColumnSpan(int* result) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GRIDITEM_GET_COLUMNSPAN);
   UIA_VALIDATE_CALL_1_ARG(result);
-  *result = GetTableColumnSpan();
+  base::Optional<int> column_span = GetTableColumnSpan();
+  if (!column_span)
+    return E_FAIL;
+  *result = *column_span;
   return S_OK;
 }
 
@@ -1725,14 +1749,20 @@
 IFACEMETHODIMP AXPlatformNodeWin::get_Row(int* result) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GRIDITEM_GET_ROW);
   UIA_VALIDATE_CALL_1_ARG(result);
-  *result = GetTableRow();
+  base::Optional<int> row = GetTableRow();
+  if (!row)
+    return E_FAIL;
+  *result = *row;
   return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_RowSpan(int* result) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GRIDITEM_GET_ROWSPAN);
   UIA_VALIDATE_CALL_1_ARG(result);
-  *result = GetTableRowSpan();
+  base::Optional<int> row_span = GetTableRowSpan();
+  if (!row_span)
+    return E_FAIL;
+  *result = *row_span;
   return S_OK;
 }
 
@@ -1760,10 +1790,9 @@
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GRID_GET_ROWCOUNT);
   UIA_VALIDATE_CALL_1_ARG(result);
 
-  base::Optional<int32_t> row_count = GetTableAriaRowCount();
+  base::Optional<int> row_count = GetTableAriaRowCount();
   if (!row_count)
     return E_UNEXPECTED;
-
   *result = *row_count;
   return S_OK;
 }
@@ -1772,10 +1801,9 @@
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GRID_GET_COLUMNCOUNT);
   UIA_VALIDATE_CALL_1_ARG(result);
 
-  base::Optional<int32_t> column_count = GetTableAriaColumnCount();
+  base::Optional<int> column_count = GetTableAriaColumnCount();
   if (!column_count)
     return E_UNEXPECTED;
-
   *result = *column_count;
   return S_OK;
 }
@@ -2083,12 +2111,13 @@
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TABLEITEM_GETCOLUMNHEADERITEMS);
   UIA_VALIDATE_CALL_1_ARG(result);
 
-  if (!IsCellOrTableHeader(GetData().role) || !GetTable())
+  base::Optional<int> column = GetTableColumn();
+  if (!column)
     return E_FAIL;
 
   std::vector<int32_t> column_header_ids =
-      GetTable()->GetDelegate()->GetColHeaderNodeIds(GetTableColumn());
-  base::EraseIf(column_header_ids, [&](int32_t node_id) {
+      GetDelegate()->GetColHeaderNodeIds(*column);
+  base::EraseIf(column_header_ids, [this](int32_t node_id) {
     return !IsValidUiaRelationTarget(GetDelegate()->GetFromNodeID(node_id));
   });
   *result = CreateUIAElementsArrayFromIdVector(column_header_ids);
@@ -2099,12 +2128,13 @@
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TABLEITEM_GETROWHEADERITEMS);
   UIA_VALIDATE_CALL_1_ARG(result);
 
-  if (!IsCellOrTableHeader(GetData().role) || !GetTable())
+  base::Optional<int> row = GetTableRow();
+  if (!row)
     return E_FAIL;
 
   std::vector<int32_t> row_header_ids =
-      GetTable()->GetDelegate()->GetRowHeaderNodeIds(GetTableRow());
-  base::EraseIf(row_header_ids, [&](int32_t node_id) {
+      GetDelegate()->GetRowHeaderNodeIds(*row);
+  base::EraseIf(row_header_ids, [this](int32_t node_id) {
     return !IsValidUiaRelationTarget(GetDelegate()->GetFromNodeID(node_id));
   });
   *result = CreateUIAElementsArrayFromIdVector(row_header_ids);
@@ -2119,11 +2149,8 @@
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TABLE_GETCOLUMNHEADERS);
   UIA_VALIDATE_CALL_1_ARG(result);
 
-  if (!GetTable())
-    return E_FAIL;
-
   std::vector<int32_t> column_header_ids = GetDelegate()->GetColHeaderNodeIds();
-  base::EraseIf(column_header_ids, [&](int32_t node_id) {
+  base::EraseIf(column_header_ids, [this](int32_t node_id) {
     return !IsValidUiaRelationTarget(GetDelegate()->GetFromNodeID(node_id));
   });
   *result = CreateUIAElementsArrayFromIdVector(column_header_ids);
@@ -2134,11 +2161,8 @@
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TABLE_GETROWHEADERS);
   UIA_VALIDATE_CALL_1_ARG(result);
 
-  if (!GetTable())
-    return E_FAIL;
-
   std::vector<int32_t> row_header_ids = GetDelegate()->GetRowHeaderNodeIds();
-  base::EraseIf(row_header_ids, [&](int32_t node_id) {
+  base::EraseIf(row_header_ids, [this](int32_t node_id) {
     return !IsValidUiaRelationTarget(GetDelegate()->GetFromNodeID(node_id));
   });
   *result = CreateUIAElementsArrayFromIdVector(row_header_ids);
@@ -2376,95 +2400,85 @@
                                                    LONG column,
                                                    IUnknown** accessible) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACCESSIBLE_AT);
+  COM_OBJECT_VALIDATE_1_ARG(accessible);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!accessible)
+  AXPlatformNodeBase* cell = GetTableCell(int{row}, int{column});
+  if (!cell)
     return E_INVALIDARG;
 
-  AXPlatformNodeBase* cell =
-      GetTableCell(static_cast<int>(row), static_cast<int>(column));
-  if (cell) {
-    auto* node_win = static_cast<AXPlatformNodeWin*>(cell);
-    node_win->AddRef();
-
-    *accessible = static_cast<IAccessible*>(node_win);
-    return S_OK;
-  }
-
-  *accessible = nullptr;
-  return E_INVALIDARG;
+  auto* node_win = static_cast<AXPlatformNodeWin*>(cell);
+  return node_win->QueryInterface(IID_PPV_ARGS(accessible));
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_caption(IUnknown** accessible) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CAPTION);
+  COM_OBJECT_VALIDATE_1_ARG(accessible);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!accessible)
-    return E_INVALIDARG;
+  AXPlatformNodeBase* caption = GetTableCaption();
+  if (!caption)
+    return S_FALSE;
 
-  // TODO(dmazzoni): implement
-  *accessible = nullptr;
-  return S_FALSE;
+  auto* node_win = static_cast<AXPlatformNodeWin*>(caption);
+  return node_win->QueryInterface(IID_PPV_ARGS(accessible));
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_childIndex(LONG row,
                                                  LONG column,
                                                  LONG* cell_index) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CHILD_INDEX);
+  COM_OBJECT_VALIDATE_1_ARG(cell_index);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!cell_index)
+  AXPlatformNodeBase* cell = GetTableCell(int{row}, int{column});
+  if (!cell)
     return E_INVALIDARG;
 
-  AXPlatformNodeBase* cell =
-      GetTableCell(static_cast<int>(row), static_cast<int>(column));
-  if (cell) {
-    *cell_index = static_cast<LONG>(cell->GetTableCellIndex());
-    return S_OK;
-  }
+  base::Optional<int> index = cell->GetTableCellIndex();
+  if (!index)
+    return E_FAIL;
 
-  *cell_index = 0;
-  return E_INVALIDARG;
+  *cell_index = LONG{*index};
+  return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_columnDescription(LONG column,
                                                         BSTR* description) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_DESCRIPTION);
+  COM_OBJECT_VALIDATE_1_ARG(description);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!description)
+  base::Optional<int> columns = GetTableColumnCount();
+  if (!columns)
+    return E_FAIL;
+
+  if (column < 0 || column >= *columns)
     return E_INVALIDARG;
 
-  int columns = GetTableColumnCount();
-  if (column < 0 || column >= columns)
-    return E_INVALIDARG;
+  std::vector<int32_t> column_header_ids =
+      GetDelegate()->GetColHeaderNodeIds(int{column});
+  for (int32_t node_id : column_header_ids) {
+    AXPlatformNodeWin* cell =
+        static_cast<AXPlatformNodeWin*>(GetDelegate()->GetFromNodeID(node_id));
+    if (!cell)
+      continue;
 
-  int rows = GetTableRowCount();
-  if (rows <= 0) {
-    *description = nullptr;
-    return S_FALSE;
-  }
+    base::string16 cell_name =
+        cell->GetString16Attribute(ax::mojom::StringAttribute::kName);
+    if (!cell_name.empty()) {
+      *description = SysAllocString(cell_name.c_str());
+      return S_OK;
+    }
 
-  for (int r = 0; r < rows; ++r) {
-    AXPlatformNodeBase* cell = GetTableCell(r, column);
-    if (cell && cell->GetData().role == ax::mojom::Role::kColumnHeader) {
-      base::string16 cell_name =
-          cell->GetString16Attribute(ax::mojom::StringAttribute::kName);
-      if (cell_name.size() > 0) {
-        *description = SysAllocString(cell_name.c_str());
-        return S_OK;
-      }
-
-      cell_name =
-          cell->GetString16Attribute(ax::mojom::StringAttribute::kDescription);
-      if (cell_name.size() > 0) {
-        *description = SysAllocString(cell_name.c_str());
-        return S_OK;
-      }
+    cell_name =
+        cell->GetString16Attribute(ax::mojom::StringAttribute::kDescription);
+    if (!cell_name.empty()) {
+      *description = SysAllocString(cell_name.c_str());
+      return S_OK;
     }
   }
 
-  *description = nullptr;
   return S_FALSE;
 }
 
@@ -2472,17 +2486,17 @@
                                                      LONG column,
                                                      LONG* n_columns_spanned) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_EXTENT_AT);
+  COM_OBJECT_VALIDATE_1_ARG(n_columns_spanned);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!n_columns_spanned)
-    return E_INVALIDARG;
-
-  AXPlatformNodeBase* cell =
-      GetTableCell(static_cast<int>(row), static_cast<int>(column));
+  AXPlatformNodeBase* cell = GetTableCell(int{row}, int{column});
   if (!cell)
     return E_INVALIDARG;
 
-  *n_columns_spanned = cell->GetTableColumnSpan();
+  base::Optional<int> column_span = cell->GetTableColumnSpan();
+  if (!column_span)
+    return E_FAIL;
+  *n_columns_spanned = LONG{*column_span};
   return S_OK;
 }
 
@@ -2490,6 +2504,7 @@
     IAccessibleTable** accessible_table,
     LONG* starting_row_index) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_HEADER);
+  COM_OBJECT_VALIDATE_2_ARGS(accessible_table, starting_row_index);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
   // TODO(dmazzoni): implement
@@ -2499,60 +2514,59 @@
 IFACEMETHODIMP AXPlatformNodeWin::get_columnIndex(LONG cell_index,
                                                   LONG* column_index) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_INDEX);
+  COM_OBJECT_VALIDATE_1_ARG(column_index);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!column_index)
-    return E_INVALIDARG;
-
   AXPlatformNodeBase* cell = GetTableCell(cell_index);
   if (!cell)
     return E_INVALIDARG;
-  *column_index = cell->GetTableColumn();
+
+  base::Optional<int> cell_column = cell->GetTableColumn();
+  if (!cell_column)
+    return E_FAIL;
+  *column_index = LONG{*cell_column};
   return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_nColumns(LONG* column_count) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_COLUMNS);
+  COM_OBJECT_VALIDATE_1_ARG(column_count);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
-
-  if (!column_count)
-    return E_INVALIDARG;
-
-  *column_count = GetTableColumnCount();
+  base::Optional<int> columns = GetTableColumnCount();
+  if (!columns)
+    return E_FAIL;
+  *column_count = LONG{*columns};
   return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_nRows(LONG* row_count) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_ROWS);
+  COM_OBJECT_VALIDATE_1_ARG(row_count);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
-
-  if (!row_count)
-    return E_INVALIDARG;
-
-  *row_count = GetTableRowCount();
+  base::Optional<int> rows = GetTableRowCount();
+  if (!rows)
+    return E_FAIL;
+  *row_count = LONG{*rows};
   return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_nSelectedChildren(LONG* cell_count) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTED_CHILDREN);
+  COM_OBJECT_VALIDATE_1_ARG(cell_count);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!cell_count)
-    return E_INVALIDARG;
-  *cell_count = 0;
-
-  int columns = GetTableColumnCount();
-  int rows = GetTableRowCount();
-  if (columns <= 0 || rows <= 0)
-    return S_FALSE;
+  base::Optional<int> columns = GetTableColumnCount();
+  base::Optional<int> rows = GetTableRowCount();
+  if (!columns || !rows)
+    return E_FAIL;
 
   LONG result = 0;
-  for (int r = 0; r < rows; ++r) {
-    for (int c = 0; c < columns; ++c) {
+  for (int r = 0; r < *rows; ++r) {
+    for (int c = 0; c < *columns; ++c) {
       AXPlatformNodeBase* cell = GetTableCell(r, c);
       if (cell &&
           cell->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
-        result++;
+        ++result;
     }
   }
   *cell_count = result;
@@ -2561,29 +2575,26 @@
 
 IFACEMETHODIMP AXPlatformNodeWin::get_nSelectedColumns(LONG* column_count) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTED_COLUMNS);
+  COM_OBJECT_VALIDATE_1_ARG(column_count);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!column_count)
-    return E_INVALIDARG;
-  *column_count = 0;
-
-  int columns = GetTableColumnCount();
-  int rows = GetTableRowCount();
-  if (columns <= 0 || rows <= 0)
-    return S_FALSE;
+  base::Optional<int> columns = GetTableColumnCount();
+  base::Optional<int> rows = GetTableRowCount();
+  if (!columns || !rows)
+    return E_FAIL;
 
   // If every cell in a column is selected, then that column is selected.
   LONG result = 0;
-  for (int c = 0; c < columns; ++c) {
+  for (int c = 0; c < *columns; ++c) {
     bool selected = true;
-    for (int r = 0; r < rows && selected == true; ++r) {
-      AXPlatformNodeBase* cell = GetTableCell(r, c);
+    for (int r = 0; r < *rows && selected == true; ++r) {
+      const AXPlatformNodeBase* cell = GetTableCell(r, c);
       if (!cell || !(cell->GetData().GetBoolAttribute(
                        ax::mojom::BoolAttribute::kSelected)))
         selected = false;
     }
     if (selected)
-      result++;
+      ++result;
   }
 
   *column_count = result;
@@ -2592,29 +2603,26 @@
 
 IFACEMETHODIMP AXPlatformNodeWin::get_nSelectedRows(LONG* row_count) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTED_ROWS);
+  COM_OBJECT_VALIDATE_1_ARG(row_count);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!row_count)
-    return E_INVALIDARG;
-  *row_count = 0;
-
-  int columns = GetTableColumnCount();
-  int rows = GetTableRowCount();
-  if (columns <= 0 || rows <= 0)
-    return S_FALSE;
+  base::Optional<int> columns = GetTableColumnCount();
+  base::Optional<int> rows = GetTableRowCount();
+  if (!columns || !rows)
+    return E_FAIL;
 
   // If every cell in a row is selected, then that row is selected.
   LONG result = 0;
-  for (int r = 0; r < rows; ++r) {
+  for (int r = 0; r < *rows; ++r) {
     bool selected = true;
-    for (int c = 0; c < columns && selected == true; ++c) {
-      AXPlatformNodeBase* cell = GetTableCell(r, c);
+    for (int c = 0; c < *columns && selected == true; ++c) {
+      const AXPlatformNodeBase* cell = GetTableCell(r, c);
       if (!cell || !(cell->GetData().GetBoolAttribute(
                        ax::mojom::BoolAttribute::kSelected)))
         selected = false;
     }
     if (selected)
-      result++;
+      ++result;
   }
 
   *row_count = result;
@@ -2624,39 +2632,39 @@
 IFACEMETHODIMP AXPlatformNodeWin::get_rowDescription(LONG row,
                                                      BSTR* description) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_DESCRIPTION);
+  COM_OBJECT_VALIDATE_1_ARG(description);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!description)
+  base::Optional<int> rows = GetTableRowCount();
+  if (!rows)
+    return E_FAIL;
+
+  if (row < 0 || row >= *rows)
     return E_INVALIDARG;
 
-  if (row < 0 || row >= GetTableRowCount())
-    return E_INVALIDARG;
+  std::vector<int32_t> row_header_ids =
+      GetDelegate()->GetRowHeaderNodeIds(int{row});
+  for (int32_t node_id : row_header_ids) {
+    AXPlatformNodeWin* cell =
+        static_cast<AXPlatformNodeWin*>(GetDelegate()->GetFromNodeID(node_id));
+    if (!cell)
+      continue;
 
-  int columns = GetTableColumnCount();
-  if (columns <= 0) {
-    *description = nullptr;
-    return S_FALSE;
-  }
+    base::string16 cell_name =
+        cell->GetString16Attribute(ax::mojom::StringAttribute::kName);
+    if (!cell_name.empty()) {
+      *description = SysAllocString(cell_name.c_str());
+      return S_OK;
+    }
 
-  for (int c = 0; c < columns; ++c) {
-    AXPlatformNodeBase* cell = GetTableCell(row, c);
-    if (cell && cell->GetData().role == ax::mojom::Role::kRowHeader) {
-      base::string16 cell_name =
-          cell->GetString16Attribute(ax::mojom::StringAttribute::kName);
-      if (cell_name.size() > 0) {
-        *description = SysAllocString(cell_name.c_str());
-        return S_OK;
-      }
-      cell_name =
-          cell->GetString16Attribute(ax::mojom::StringAttribute::kDescription);
-      if (cell_name.size() > 0) {
-        *description = SysAllocString(cell_name.c_str());
-        return S_OK;
-      }
+    cell_name =
+        cell->GetString16Attribute(ax::mojom::StringAttribute::kDescription);
+    if (!cell_name.empty()) {
+      *description = SysAllocString(cell_name.c_str());
+      return S_OK;
     }
   }
 
-  *description = nullptr;
   return S_FALSE;
 }
 
@@ -2664,16 +2672,17 @@
                                                   LONG column,
                                                   LONG* n_rows_spanned) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_EXTENT_AT);
+  COM_OBJECT_VALIDATE_1_ARG(n_rows_spanned);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!n_rows_spanned)
-    return E_INVALIDARG;
-
-  AXPlatformNodeBase* cell = GetTableCell(row, column);
+  const AXPlatformNodeBase* cell = GetTableCell(int{row}, int{column});
   if (!cell)
     return E_INVALIDARG;
 
-  *n_rows_spanned = GetTableRowSpan();
+  base::Optional<int> cell_row_span = cell->GetTableRowSpan();
+  if (!cell_row_span)
+    return E_FAIL;
+  *n_rows_spanned = LONG{*cell_row_span};
   return S_OK;
 }
 
@@ -2681,6 +2690,7 @@
     IAccessibleTable** accessible_table,
     LONG* starting_column_index) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_HEADER);
+  COM_OBJECT_VALIDATE_2_ARGS(accessible_table, starting_column_index);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
   // TODO(dmazzoni): implement
@@ -2690,16 +2700,17 @@
 IFACEMETHODIMP AXPlatformNodeWin::get_rowIndex(LONG cell_index,
                                                LONG* row_index) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(row_index);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!row_index)
-    return E_INVALIDARG;
-
-  AXPlatformNodeBase* cell = GetTableCell(cell_index);
+  const AXPlatformNodeBase* cell = GetTableCell(cell_index);
   if (!cell)
     return E_INVALIDARG;
 
-  *row_index = cell->GetTableRow();
+  base::Optional<int> cell_row = cell->GetTableRow();
+  if (!cell_row)
+    return E_FAIL;
+  *row_index = LONG{*cell_row};
   return S_OK;
 }
 
@@ -2707,24 +2718,29 @@
                                                        LONG** children,
                                                        LONG* n_children) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_2_ARGS(children, n_children);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!children || !n_children || max_children <= 0)
+  if (max_children <= 0)
     return E_INVALIDARG;
 
-  int columns = GetTableColumnCount();
-  int rows = GetTableRowCount();
-  if (columns <= 0 || rows <= 0)
-    return S_FALSE;
+  base::Optional<int> columns = GetTableColumnCount();
+  base::Optional<int> rows = GetTableRowCount();
+  if (!columns || !rows)
+    return E_FAIL;
 
   std::vector<LONG> results;
-  for (int r = 0; r < rows; ++r) {
-    for (int c = 0; c < columns; ++c) {
-      AXPlatformNodeBase* cell = GetTableCell(r, c);
-      if (cell &&
-          cell->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
-        // index is row index * column count + column index.
-        results.push_back(r * columns + c);
+  for (int r = 0; r < *rows; ++r) {
+    for (int c = 0; c < *columns; ++c) {
+      const AXPlatformNodeBase* cell = GetTableCell(r, c);
+      if (cell && cell->GetData().GetBoolAttribute(
+                      ax::mojom::BoolAttribute::kSelected)) {
+        base::Optional<int> cell_index = cell->GetTableCellIndex();
+        if (!cell_index)
+          return E_FAIL;
+
+        results.push_back(*cell_index);
+      }
     }
   }
 
@@ -2736,21 +2752,22 @@
                                                       LONG** columns,
                                                       LONG* n_columns) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_2_ARGS(columns, n_columns);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!columns || !n_columns || max_columns <= 0)
+  if (max_columns <= 0)
     return E_INVALIDARG;
 
-  int column_count = GetTableColumnCount();
-  int row_count = GetTableRowCount();
-  if (column_count <= 0 || row_count <= 0)
-    return S_FALSE;
+  base::Optional<int> column_count = GetTableColumnCount();
+  base::Optional<int> row_count = GetTableRowCount();
+  if (!column_count || !row_count)
+    return E_FAIL;
 
   std::vector<LONG> results;
-  for (int c = 0; c < column_count; ++c) {
+  for (int c = 0; c < *column_count; ++c) {
     bool selected = true;
-    for (int r = 0; r < row_count && selected == true; ++r) {
-      AXPlatformNodeBase* cell = GetTableCell(r, c);
+    for (int r = 0; r < *row_count && selected == true; ++r) {
+      const AXPlatformNodeBase* cell = GetTableCell(r, c);
       if (!cell || !(cell->GetData().GetBoolAttribute(
                        ax::mojom::BoolAttribute::kSelected)))
         selected = false;
@@ -2766,20 +2783,22 @@
                                                    LONG** rows,
                                                    LONG* n_rows) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_2_ARGS(rows, n_rows);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
-  if (!rows || !n_rows || max_rows <= 0)
+
+  if (max_rows <= 0)
     return E_INVALIDARG;
 
-  int column_count = GetTableColumnCount();
-  int row_count = GetTableRowCount();
-  if (column_count <= 0 || row_count <= 0)
-    return S_FALSE;
+  base::Optional<int> column_count = GetTableColumnCount();
+  base::Optional<int> row_count = GetTableRowCount();
+  if (!column_count || !row_count)
+    return E_FAIL;
 
   std::vector<LONG> results;
-  for (int r = 0; r < row_count; ++r) {
+  for (int r = 0; r < *row_count; ++r) {
     bool selected = true;
-    for (int c = 0; c < column_count && selected == true; ++c) {
-      AXPlatformNodeBase* cell = GetTableCell(r, c);
+    for (int c = 0; c < *column_count && selected == true; ++c) {
+      const AXPlatformNodeBase* cell = GetTableCell(r, c);
       if (!cell || !(cell->GetData().GetBoolAttribute(
                        ax::mojom::BoolAttribute::kSelected)))
         selected = false;
@@ -2793,31 +2812,29 @@
 
 IFACEMETHODIMP AXPlatformNodeWin::get_summary(IUnknown** accessible) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(accessible);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!accessible)
-    return E_INVALIDARG;
-
-  // TODO(dmazzoni): implement
-  *accessible = nullptr;
-  return S_FALSE;
+  // TODO(dmazzoni): implement.
+  return E_NOTIMPL;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_isColumnSelected(LONG column,
                                                        boolean* is_selected) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(is_selected);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
-  if (!is_selected)
+
+  base::Optional<int> columns = GetTableColumnCount();
+  base::Optional<int> rows = GetTableRowCount();
+  if (!columns || !rows)
+    return E_FAIL;
+
+  if (column < 0 || column >= *columns)
     return E_INVALIDARG;
-  *is_selected = false;
 
-  int columns = GetTableColumnCount();
-  int rows = GetTableRowCount();
-  if (columns <= 0 || rows <= 0 || column >= columns || column < 0)
-    return S_FALSE;
-
-  for (int r = 0; r < rows; ++r) {
-    AXPlatformNodeBase* cell = GetTableCell(r, column);
+  for (int r = 0; r < *rows; ++r) {
+    const AXPlatformNodeBase* cell = GetTableCell(r, column);
     if (!cell || !(cell->GetData().GetBoolAttribute(
                      ax::mojom::BoolAttribute::kSelected)))
       return S_OK;
@@ -2830,18 +2847,19 @@
 IFACEMETHODIMP AXPlatformNodeWin::get_isRowSelected(LONG row,
                                                     boolean* is_selected) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(is_selected);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
-  if (!is_selected)
+
+  base::Optional<int> columns = GetTableColumnCount();
+  base::Optional<int> rows = GetTableRowCount();
+  if (!columns || !rows)
+    return E_FAIL;
+
+  if (row < 0 || row >= *rows)
     return E_INVALIDARG;
-  *is_selected = false;
 
-  int columns = GetTableColumnCount();
-  int rows = GetTableRowCount();
-  if (columns <= 0 || rows <= 0 || row >= rows || row < 0)
-    return S_FALSE;
-
-  for (int c = 0; c < columns; ++c) {
-    AXPlatformNodeBase* cell = GetTableCell(row, c);
+  for (int c = 0; c < *columns; ++c) {
+    const AXPlatformNodeBase* cell = GetTableCell(row, c);
     if (!cell || !(cell->GetData().GetBoolAttribute(
                      ax::mojom::BoolAttribute::kSelected)))
       return S_OK;
@@ -2855,20 +2873,19 @@
                                                  LONG column,
                                                  boolean* is_selected) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(is_selected);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
-  if (!is_selected)
+
+  base::Optional<int> columns = GetTableColumnCount();
+  base::Optional<int> rows = GetTableRowCount();
+  if (!columns || !rows)
+    return E_FAIL;
+
+  const AXPlatformNodeBase* cell = GetTableCell(int{row}, int{column});
+  if (!cell)
     return E_INVALIDARG;
-  *is_selected = false;
 
-  int columns = GetTableColumnCount();
-  int rows = GetTableRowCount();
-  if (columns <= 0 || rows <= 0 || row >= rows || row < 0 ||
-      column >= columns || column < 0)
-    return S_FALSE;
-
-  AXPlatformNodeBase* cell = GetTableCell(row, column);
-  if (cell &&
-      cell->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
+  if (cell->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
     *is_selected = true;
 
   return S_OK;
@@ -2882,21 +2899,27 @@
     LONG* column_extents,
     boolean* is_selected) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_5_ARGS(row, column, row_extents, column_extents,
+                             is_selected);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!row || !column || !row_extents || !column_extents || !is_selected)
-    return E_INVALIDARG;
-
-  AXPlatformNodeBase* cell = GetTableCell(index);
+  const AXPlatformNodeBase* cell = GetTableCell(index);
   if (!cell)
     return E_INVALIDARG;
 
-  *row = cell->GetTableRow();
-  *column = cell->GetTableColumn();
-  *row_extents = GetTableRowSpan();
-  *column_extents = GetTableColumnSpan();
-  *is_selected = false;  // Not supported.
+  base::Optional<int> row_index = cell->GetTableRow();
+  base::Optional<int> column_index = cell->GetTableColumn();
+  base::Optional<int> row_span = cell->GetTableRowSpan();
+  base::Optional<int> column_span = cell->GetTableColumnSpan();
+  if (!row_index || !column_index || !row_span || !column_span)
+    return E_FAIL;
 
+  *row = LONG{*row_index};
+  *column = LONG{*column_index};
+  *row_extents = LONG{*row_span};
+  *column_extents = LONG{*column_span};
+  if (cell->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
+    *is_selected = true;
   return S_OK;
 }
 
@@ -2904,6 +2927,13 @@
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
+  base::Optional<int> rows = GetTableRowCount();
+  if (!rows)
+    return E_FAIL;
+
+  if (row < 0 || row >= *rows)
+    return E_INVALIDARG;
+
   return E_NOTIMPL;
 }
 
@@ -2911,6 +2941,13 @@
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
+  base::Optional<int> columns = GetTableColumnCount();
+  if (!columns)
+    return E_FAIL;
+
+  if (column < 0 || column >= *columns)
+    return E_INVALIDARG;
+
   return E_NOTIMPL;
 }
 
@@ -2918,6 +2955,13 @@
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
+  base::Optional<int> rows = GetTableRowCount();
+  if (!rows)
+    return E_FAIL;
+
+  if (row < 0 || row >= *rows)
+    return E_INVALIDARG;
+
   return E_NOTIMPL;
 }
 
@@ -2925,12 +2969,20 @@
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
+  base::Optional<int> columns = GetTableColumnCount();
+  if (!columns)
+    return E_FAIL;
+
+  if (column < 0 || column >= *columns)
+    return E_INVALIDARG;
+
   return E_NOTIMPL;
 }
 
-IFACEMETHODIMP
-AXPlatformNodeWin::get_modelChange(IA2TableModelChange* model_change) {
+IFACEMETHODIMP AXPlatformNodeWin::get_modelChange(
+    IA2TableModelChange* model_change) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(model_change);
   return E_NOTIMPL;
 }
 
@@ -2942,21 +2994,15 @@
                                              LONG column,
                                              IUnknown** cell) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(cell);
   AXPlatformNode::NotifyAddAXModeFlags(AXMode::kScreenReader);
-  if (!cell)
+
+  AXPlatformNodeBase* table_cell = GetTableCell(int{row}, int{column});
+  if (!table_cell)
     return E_INVALIDARG;
 
-  AXPlatformNodeBase* table_cell =
-      GetTableCell(static_cast<int>(row), static_cast<int>(column));
-  if (table_cell) {
-    auto* node_win = static_cast<AXPlatformNodeWin*>(table_cell);
-    node_win->AddRef();
-    *cell = static_cast<IAccessible*>(node_win);
-    return S_OK;
-  }
-
-  *cell = nullptr;
-  return E_INVALIDARG;
+  auto* node_win = static_cast<AXPlatformNodeWin*>(table_cell);
+  return node_win->QueryInterface(IID_PPV_ARGS(cell));
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_nSelectedCells(LONG* cell_count) {
@@ -2969,21 +3015,17 @@
 IFACEMETHODIMP AXPlatformNodeWin::get_selectedCells(IUnknown*** cells,
                                                     LONG* n_selected_cells) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_2_ARGS(cells, n_selected_cells);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
-  if (!cells || !n_selected_cells)
-    return E_INVALIDARG;
 
-  *cells = nullptr;
-  *n_selected_cells = 0;
-
-  int columns = GetTableColumnCount();
-  int rows = GetTableRowCount();
-  if (columns <= 0 || rows <= 0)
-    return S_FALSE;
+  base::Optional<int> columns = GetTableColumnCount();
+  base::Optional<int> rows = GetTableRowCount();
+  if (!columns || !rows)
+    return E_FAIL;
 
   std::vector<AXPlatformNodeBase*> selected;
-  for (int r = 0; r < rows; ++r) {
-    for (int c = 0; c < columns; ++c) {
+  for (int r = 0; r < *rows; ++r) {
+    for (int c = 0; c < *columns; ++c) {
       AXPlatformNodeBase* cell = GetTableCell(r, c);
       if (cell &&
           cell->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
@@ -2993,22 +3035,23 @@
 
   *n_selected_cells = static_cast<LONG>(selected.size());
   *cells = static_cast<IUnknown**>(
-      CoTaskMemAlloc((*n_selected_cells) * sizeof(cells[0])));
+      CoTaskMemAlloc(selected.size() * sizeof(IUnknown*)));
 
   for (size_t i = 0; i < selected.size(); ++i) {
     auto* node_win = static_cast<AXPlatformNodeWin*>(selected[i]);
-    node_win->AddRef();
-    (*cells)[i] = static_cast<IAccessible*>(node_win);
+    node_win->QueryInterface(IID_PPV_ARGS(&(*cells)[i]));
   }
   return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_selectedColumns(LONG** columns,
                                                       LONG* n_columns) {
+  // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
   return get_selectedColumns(INT_MAX, columns, n_columns);
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_selectedRows(LONG** rows, LONG* n_rows) {
+  // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
   return get_selectedRows(INT_MAX, rows, n_rows);
 }
 
@@ -3018,11 +3061,13 @@
 
 IFACEMETHODIMP AXPlatformNodeWin::get_columnExtent(LONG* n_columns_spanned) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(n_columns_spanned);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
-  if (!n_columns_spanned)
-    return E_INVALIDARG;
 
-  *n_columns_spanned = GetTableColumnSpan();
+  base::Optional<int> column_span = GetTableColumnSpan();
+  if (!column_span)
+    return E_FAIL;
+  *n_columns_spanned = LONG{*column_span};
   return S_OK;
 }
 
@@ -3030,66 +3075,52 @@
     IUnknown*** cell_accessibles,
     LONG* n_column_header_cells) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_2_ARGS(cell_accessibles, n_column_header_cells);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
-  if (!cell_accessibles || !n_column_header_cells)
-    return E_INVALIDARG;
 
-  *n_column_header_cells = 0;
-  if (!IsCellOrTableHeader(GetData().role))
-    return S_FALSE;
+  base::Optional<int> column = GetTableColumn();
+  if (!column)
+    return E_FAIL;
 
-  AXPlatformNodeBase* table = GetTable();
-  if (!table)
-    return S_FALSE;
-
-  int column = GetTableColumn();
-  int columns = GetTableColumnCount();
-  int rows = GetTableRowCount();
-  if (columns <= 0 || rows <= 0 || column < 0 || column >= columns)
-    return S_FALSE;
-
-  for (int r = 0; r < rows; ++r) {
-    AXPlatformNodeBase* cell = GetTableCell(r, column);
-    if (cell && cell->GetData().role == ax::mojom::Role::kColumnHeader)
-      (*n_column_header_cells)++;
-  }
-
+  std::vector<int32_t> column_header_ids =
+      GetDelegate()->GetColHeaderNodeIds(*column);
   *cell_accessibles = static_cast<IUnknown**>(
-      CoTaskMemAlloc((*n_column_header_cells) * sizeof(cell_accessibles[0])));
+      CoTaskMemAlloc(column_header_ids.size() * sizeof(IUnknown*)));
   int index = 0;
-  for (int r = 0; r < rows; ++r) {
-    AXPlatformNodeBase* cell = GetTableCell(r, column);
-    if (cell && cell->GetData().role == ax::mojom::Role::kColumnHeader) {
-      auto* node_win = static_cast<AXPlatformNodeWin*>(cell);
-      node_win->AddRef();
-
-      (*cell_accessibles)[index] = static_cast<IAccessible*>(node_win);
+  for (int32_t node_id : column_header_ids) {
+    AXPlatformNodeWin* node_win =
+        static_cast<AXPlatformNodeWin*>(GetDelegate()->GetFromNodeID(node_id));
+    if (node_win) {
+      node_win->QueryInterface(IID_PPV_ARGS(&(*cell_accessibles)[index]));
       ++index;
     }
   }
 
+  *n_column_header_cells = LONG{column_header_ids.size()};
   return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_columnIndex(LONG* column_index) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(column_index);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!column_index)
-    return E_INVALIDARG;
-
-  *column_index = GetTableColumn();
+  base::Optional<int> column = GetTableColumn();
+  if (!column)
+    return E_FAIL;
+  *column_index = LONG{*column};
   return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_rowExtent(LONG* n_rows_spanned) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(n_rows_spanned);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!n_rows_spanned)
-    return E_INVALIDARG;
-
-  *n_rows_spanned = GetTableRowSpan();
+  base::Optional<int> row_span = GetTableRowSpan();
+  if (!row_span)
+    return E_FAIL;
+  *n_rows_spanned = LONG{*row_span};
   return S_OK;
 }
 
@@ -3097,67 +3128,50 @@
     IUnknown*** cell_accessibles,
     LONG* n_row_header_cells) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_2_ARGS(cell_accessibles, n_row_header_cells);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!cell_accessibles || !n_row_header_cells)
-    return E_INVALIDARG;
+  base::Optional<int> row = GetTableRow();
+  if (!row)
+    return E_FAIL;
 
-  *n_row_header_cells = 0;
-  if (!IsCellOrTableHeader(GetData().role))
-    return S_FALSE;
-
-  AXPlatformNodeBase* table = GetTable();
-  if (!table)
-    return S_FALSE;
-
-  int row = GetTableRow();
-  int columns = GetTableColumnCount();
-  int rows = GetTableRowCount();
-  if (columns <= 0 || rows <= 0 || row < 0 || row >= rows)
-    return S_FALSE;
-
-  for (int c = 0; c < columns; ++c) {
-    AXPlatformNodeBase* cell = GetTableCell(row, c);
-    if (cell && cell->GetData().role == ax::mojom::Role::kRowHeader)
-      (*n_row_header_cells)++;
-  }
-
+  std::vector<int32_t> row_header_ids =
+      GetDelegate()->GetRowHeaderNodeIds(*row);
   *cell_accessibles = static_cast<IUnknown**>(
-      CoTaskMemAlloc((*n_row_header_cells) * sizeof(cell_accessibles[0])));
+      CoTaskMemAlloc(row_header_ids.size() * sizeof(IUnknown*)));
   int index = 0;
-  for (int c = 0; c < columns; ++c) {
-    AXPlatformNodeBase* cell = GetTableCell(row, c);
-    if (cell && cell->GetData().role == ax::mojom::Role::kRowHeader) {
-      auto* node_win = static_cast<AXPlatformNodeWin*>(cell);
-      node_win->AddRef();
-
-      (*cell_accessibles)[index] = static_cast<IAccessible*>(node_win);
+  for (int32_t node_id : row_header_ids) {
+    AXPlatformNodeWin* node_win =
+        static_cast<AXPlatformNodeWin*>(GetDelegate()->GetFromNodeID(node_id));
+    if (node_win) {
+      node_win->QueryInterface(IID_PPV_ARGS(&(*cell_accessibles)[index]));
       ++index;
     }
   }
 
+  *n_row_header_cells = LONG{row_header_ids.size()};
   return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_rowIndex(LONG* row_index) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(row_index);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!row_index)
-    return E_INVALIDARG;
-
-  *row_index = GetTableRow();
+  base::Optional<int> row = GetTableRow();
+  if (!row)
+    return E_FAIL;
+  *row_index = LONG{*row};
   return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_isSelected(boolean* is_selected) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(is_selected);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!is_selected)
-    return E_INVALIDARG;
-
-  *is_selected = false;
+  if (GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
+    *is_selected = true;
   return S_OK;
 }
 
@@ -3167,42 +3181,37 @@
                                                        LONG* column_extents,
                                                        boolean* is_selected) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_5_ARGS(row_index, column_index, row_extents,
+                             column_extents, is_selected);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!row_index || !column_index || !row_extents || !column_extents ||
-      !is_selected) {
-    return E_INVALIDARG;
-  }
+  base::Optional<int> row = GetTableRow();
+  base::Optional<int> column = GetTableColumn();
+  base::Optional<int> row_span = GetTableRowSpan();
+  base::Optional<int> column_span = GetTableColumnSpan();
+  if (!row || !column || !row_span || !column_span)
+    return E_FAIL;
 
-  *row_index = GetTableRow();
-  *column_index = GetTableColumn();
-  *row_extents = GetTableRowSpan();
-  *column_extents = GetTableColumnSpan();
-  *is_selected = false;  // Not supported.
-
+  *row_index = LONG{*row};
+  *column_index = LONG{*column};
+  *row_extents = LONG{*row_span};
+  *column_extents = LONG{*column_span};
+  if (GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
+    *is_selected = true;
   return S_OK;
 }
 
 IFACEMETHODIMP AXPlatformNodeWin::get_table(IUnknown** table) {
   // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM?
+  COM_OBJECT_VALIDATE_1_ARG(table);
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!table)
-    return E_INVALIDARG;
+  AXPlatformNodeBase* table_node = GetTable();
+  if (!table_node)
+    return E_FAIL;
 
-  AXPlatformNodeBase* find_table = GetTable();
-  if (!find_table) {
-    *table = nullptr;
-    return S_FALSE;
-  }
-
-  // The IAccessibleTable interface is still on the AXPlatformNodeWin
-  // class.
-  auto* node_win = static_cast<AXPlatformNodeWin*>(find_table);
-  node_win->AddRef();
-
-  *table = static_cast<IAccessibleTable*>(node_win);
-  return S_OK;
+  auto* node_win = static_cast<AXPlatformNodeWin*>(table_node);
+  return node_win->QueryInterface(IID_PPV_ARGS(table));
 }
 
 //
@@ -3963,12 +3972,13 @@
       result->boolVal = VARIANT_FALSE;
       break;
 
-    case UIA_PositionInSetPropertyId:
-      if (GetDelegate()->IsOrderedSetItem()) {
+    case UIA_PositionInSetPropertyId: {
+      base::Optional<int> pos_in_set = GetPosInSet();
+      if (pos_in_set) {
         result->vt = VT_I4;
-        result->intVal = GetDelegate()->GetPosInSet();
+        result->intVal = *pos_in_set;
       }
-      break;
+    } break;
 
     case UIA_ScrollHorizontalScrollPercentPropertyId: {
       V_VT(result) = VT_R8;
@@ -3982,13 +3992,13 @@
       break;
     }
 
-    case UIA_SizeOfSetPropertyId:
-      if (data.GetIntAttribute(ax::mojom::IntAttribute::kSetSize,
-                               &int_attribute)) {
+    case UIA_SizeOfSetPropertyId: {
+      base::Optional<int> set_size = GetSetSize();
+      if (set_size) {
         result->vt = VT_I4;
-        result->intVal = int_attribute;
+        result->intVal = *set_size;
       }
-      break;
+    } break;
 
     case UIA_LandmarkTypePropertyId: {
       base::Optional<LONG> landmark_type = ComputeUIALandmarkType();
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index 9144c40..cf83d12 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -1504,6 +1504,7 @@
     ScopedBstr name;
     EXPECT_EQ(S_FALSE, result->get_rowDescription(0, name.Receive()));
   }
+
   {
     ScopedBstr name;
     EXPECT_EQ(S_OK, result->get_rowDescription(1, name.Receive()));
@@ -1530,7 +1531,7 @@
 
   LONG rows_spanned;
   EXPECT_EQ(S_OK, result->get_rowExtentAt(0, 1, &rows_spanned));
-  EXPECT_EQ(rows_spanned, 0);
+  EXPECT_EQ(1, rows_spanned);
 
   EXPECT_EQ(E_INVALIDARG, result->get_columnExtentAt(-1, -1, &rows_spanned));
 }
@@ -1546,9 +1547,9 @@
 
   LONG index;
   EXPECT_EQ(S_OK, result->get_rowIndex(2, &index));
-  EXPECT_EQ(index, 0);
+  EXPECT_EQ(0, index);
   EXPECT_EQ(S_OK, result->get_rowIndex(3, &index));
-  EXPECT_EQ(index, 1);
+  EXPECT_EQ(1, index);
 
   EXPECT_EQ(E_INVALIDARG, result->get_rowIndex(-1, &index));
 }
@@ -1563,15 +1564,16 @@
   ASSERT_NE(nullptr, result.Get());
 
   LONG row, column, row_extents, column_extents;
-  boolean is_selected;
+  BOOLEAN is_selected = false;
   EXPECT_EQ(S_OK,
             result->get_rowColumnExtentsAtIndex(0, &row, &column, &row_extents,
                                                 &column_extents, &is_selected));
 
-  EXPECT_EQ(row, 0);
-  EXPECT_EQ(column, 0);
-  EXPECT_EQ(row_extents, 0);
-  EXPECT_EQ(column_extents, 0);
+  EXPECT_EQ(0, row);
+  EXPECT_EQ(0, column);
+  EXPECT_EQ(1, row_extents);
+  EXPECT_EQ(1, column_extents);
+  EXPECT_FALSE(is_selected);
 
   EXPECT_EQ(E_INVALIDARG,
             result->get_rowColumnExtentsAtIndex(-1, &row, &column, &row_extents,
@@ -1607,7 +1609,7 @@
 
   LONG column_spanned;
   EXPECT_EQ(S_OK, cell->get_columnExtent(&column_spanned));
-  EXPECT_EQ(column_spanned, 1);
+  EXPECT_EQ(1, column_spanned);
 }
 
 TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetColumnHeaderCells) {
@@ -1621,7 +1623,7 @@
   LONG number_cells;
   EXPECT_EQ(S_OK,
             cell->get_columnHeaderCells(&cell_accessibles, &number_cells));
-  EXPECT_EQ(number_cells, 1);
+  EXPECT_EQ(1, number_cells);
 }
 
 TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetColumnIndex) {
@@ -1677,13 +1679,14 @@
   ASSERT_NE(nullptr, cell.Get());
 
   LONG row, column, row_extents, column_extents;
-  boolean is_selected;
+  BOOLEAN is_selected = false;
   EXPECT_EQ(S_OK, cell->get_rowColumnExtents(&row, &column, &row_extents,
                                              &column_extents, &is_selected));
-  EXPECT_EQ(row, 1);
-  EXPECT_EQ(column, 1);
-  EXPECT_EQ(row_extents, 1);
-  EXPECT_EQ(column_extents, 1);
+  EXPECT_EQ(1, row);
+  EXPECT_EQ(1, column);
+  EXPECT_EQ(1, row_extents);
+  EXPECT_EQ(1, column_extents);
+  EXPECT_FALSE(is_selected);
 }
 
 TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetTable) {
@@ -2393,7 +2396,7 @@
   table.CopyTo(result.GetAddressOf());
   ASSERT_NE(nullptr, result.Get());
 
-  boolean selected;
+  BOOLEAN selected = false;
   EXPECT_EQ(S_OK, result->get_isColumnSelected(0, &selected));
   EXPECT_FALSE(selected);
 
@@ -2403,8 +2406,8 @@
   EXPECT_EQ(S_OK, result->get_isColumnSelected(2, &selected));
   EXPECT_FALSE(selected);
 
-  EXPECT_EQ(S_FALSE, result->get_isColumnSelected(3, &selected));
-  EXPECT_EQ(S_FALSE, result->get_isColumnSelected(-3, &selected));
+  EXPECT_EQ(E_INVALIDARG, result->get_isColumnSelected(3, &selected));
+  EXPECT_EQ(E_INVALIDARG, result->get_isColumnSelected(-3, &selected));
 }
 
 TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableIsRowSelected) {
@@ -2429,7 +2432,7 @@
   table.CopyTo(result.GetAddressOf());
   ASSERT_NE(nullptr, result.Get());
 
-  boolean selected;
+  BOOLEAN selected;
   EXPECT_EQ(S_OK, result->get_isRowSelected(0, &selected));
   EXPECT_FALSE(selected);
 
@@ -2439,8 +2442,8 @@
   EXPECT_EQ(S_OK, result->get_isRowSelected(2, &selected));
   EXPECT_FALSE(selected);
 
-  EXPECT_EQ(S_FALSE, result->get_isRowSelected(3, &selected));
-  EXPECT_EQ(S_FALSE, result->get_isRowSelected(-3, &selected));
+  EXPECT_EQ(E_INVALIDARG, result->get_isRowSelected(3, &selected));
+  EXPECT_EQ(E_INVALIDARG, result->get_isRowSelected(-3, &selected));
 }
 
 TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableIsSelected) {
@@ -2465,8 +2468,7 @@
   table.CopyTo(result.GetAddressOf());
   ASSERT_NE(nullptr, result.Get());
 
-  boolean selected;
-
+  BOOLEAN selected = false;
   EXPECT_EQ(S_OK, result->get_isSelected(0, 0, &selected));
   EXPECT_FALSE(selected);
 
@@ -2476,7 +2478,7 @@
   EXPECT_EQ(S_OK, result->get_isSelected(0, 2, &selected));
   EXPECT_FALSE(selected);
 
-  EXPECT_EQ(S_FALSE, result->get_isSelected(0, 4, &selected));
+  EXPECT_EQ(E_INVALIDARG, result->get_isSelected(0, 4, &selected));
 
   EXPECT_EQ(S_OK, result->get_isSelected(1, 0, &selected));
   EXPECT_TRUE(selected);
@@ -2487,7 +2489,7 @@
   EXPECT_EQ(S_OK, result->get_isSelected(1, 2, &selected));
   EXPECT_TRUE(selected);
 
-  EXPECT_EQ(S_FALSE, result->get_isSelected(1, 4, &selected));
+  EXPECT_EQ(E_INVALIDARG, result->get_isSelected(1, 4, &selected));
 }
 
 TEST_F(AXPlatformNodeWinTest, TestIAccessibleTable2GetSelectedChildrenZero) {
diff --git a/ui/accessibility/platform/compute_attributes.cc b/ui/accessibility/platform/compute_attributes.cc
index 1b04e0e..b5adf3d 100644
--- a/ui/accessibility/platform/compute_attributes.cc
+++ b/ui/accessibility/platform/compute_attributes.cc
@@ -61,30 +61,25 @@
   }
 }
 
-base::Optional<int32_t> GetOrderedSetItemAttribute(
+base::Optional<int> GetOrderedSetItemAttribute(
     const ui::AXPlatformNodeDelegate* delegate,
     ax::mojom::IntAttribute attribute) {
-  int value = 0;
   switch (attribute) {
     case ax::mojom::IntAttribute::kPosInSet:
-      value = delegate->GetPosInSet();
-      return value;
+      return delegate->GetPosInSet();
     case ax::mojom::IntAttribute::kSetSize:
-      value = delegate->GetSetSize();
-      return value;
+      return delegate->GetSetSize();
     default:
       return base::nullopt;
   }
 }
 
-base::Optional<int32_t> GetOrderedSetAttribute(
+base::Optional<int> GetOrderedSetAttribute(
     const ui::AXPlatformNodeDelegate* delegate,
     ax::mojom::IntAttribute attribute) {
-  int value = 0;
   switch (attribute) {
     case ax::mojom::IntAttribute::kSetSize:
-      value = delegate->GetSetSize();
-      return value;
+      return delegate->GetSetSize();
     default:
       return base::nullopt;
   }
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.cc b/ui/accessibility/platform/test_ax_node_wrapper.cc
index 5c24558..a8fddfc 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.cc
+++ b/ui/accessibility/platform/test_ax_node_wrapper.cc
@@ -352,47 +352,47 @@
   return node_->IsTable();
 }
 
-int TestAXNodeWrapper::GetTableRowCount() const {
+base::Optional<int> TestAXNodeWrapper::GetTableRowCount() const {
   return node_->GetTableRowCount();
 }
 
-int TestAXNodeWrapper::GetTableColCount() const {
+base::Optional<int> TestAXNodeWrapper::GetTableColCount() const {
   return node_->GetTableColCount();
 }
 
-base::Optional<int32_t> TestAXNodeWrapper::GetTableAriaRowCount() const {
+base::Optional<int> TestAXNodeWrapper::GetTableAriaRowCount() const {
   return node_->GetTableAriaRowCount();
 }
 
-base::Optional<int32_t> TestAXNodeWrapper::GetTableAriaColCount() const {
+base::Optional<int> TestAXNodeWrapper::GetTableAriaColCount() const {
   return node_->GetTableAriaColCount();
 }
 
-int TestAXNodeWrapper::GetTableCellCount() const {
+base::Optional<int> TestAXNodeWrapper::GetTableCellCount() const {
   return node_->GetTableCellCount();
 }
 
-const std::vector<int32_t> TestAXNodeWrapper::GetColHeaderNodeIds() const {
+std::vector<int32_t> TestAXNodeWrapper::GetColHeaderNodeIds() const {
   std::vector<int32_t> header_ids;
   node_->GetTableCellColHeaderNodeIds(&header_ids);
   return header_ids;
 }
 
-const std::vector<int32_t> TestAXNodeWrapper::GetColHeaderNodeIds(
-    int32_t col_index) const {
+std::vector<int32_t> TestAXNodeWrapper::GetColHeaderNodeIds(
+    int col_index) const {
   std::vector<int32_t> header_ids;
   node_->GetTableColHeaderNodeIds(col_index, &header_ids);
   return header_ids;
 }
 
-const std::vector<int32_t> TestAXNodeWrapper::GetRowHeaderNodeIds() const {
+std::vector<int32_t> TestAXNodeWrapper::GetRowHeaderNodeIds() const {
   std::vector<int32_t> header_ids;
   node_->GetTableCellRowHeaderNodeIds(&header_ids);
   return header_ids;
 }
 
-const std::vector<int32_t> TestAXNodeWrapper::GetRowHeaderNodeIds(
-    int32_t row_index) const {
+std::vector<int32_t> TestAXNodeWrapper::GetRowHeaderNodeIds(
+    int row_index) const {
   std::vector<int32_t> header_ids;
   node_->GetTableRowHeaderNodeIds(row_index, &header_ids);
   return header_ids;
@@ -402,7 +402,7 @@
   return node_->IsTableRow();
 }
 
-int TestAXNodeWrapper::GetTableRowRowIndex() const {
+base::Optional<int> TestAXNodeWrapper::GetTableRowRowIndex() const {
   return node_->GetTableRowRowIndex();
 }
 
@@ -410,41 +410,40 @@
   return node_->IsTableCellOrHeader();
 }
 
-int TestAXNodeWrapper::GetTableCellIndex() const {
+base::Optional<int> TestAXNodeWrapper::GetTableCellIndex() const {
   return node_->GetTableCellIndex();
 }
 
-int TestAXNodeWrapper::GetTableCellColIndex() const {
+base::Optional<int> TestAXNodeWrapper::GetTableCellColIndex() const {
   return node_->GetTableCellColIndex();
 }
 
-int TestAXNodeWrapper::GetTableCellRowIndex() const {
+base::Optional<int> TestAXNodeWrapper::GetTableCellRowIndex() const {
   return node_->GetTableCellRowIndex();
 }
 
-int TestAXNodeWrapper::GetTableCellColSpan() const {
+base::Optional<int> TestAXNodeWrapper::GetTableCellColSpan() const {
   return node_->GetTableCellColSpan();
 }
 
-int TestAXNodeWrapper::GetTableCellRowSpan() const {
+base::Optional<int> TestAXNodeWrapper::GetTableCellRowSpan() const {
   return node_->GetTableCellRowSpan();
 }
 
-int TestAXNodeWrapper::GetTableCellAriaColIndex() const {
+base::Optional<int> TestAXNodeWrapper::GetTableCellAriaColIndex() const {
   return node_->GetTableCellAriaColIndex();
 }
 
-int TestAXNodeWrapper::GetTableCellAriaRowIndex() const {
+base::Optional<int> TestAXNodeWrapper::GetTableCellAriaRowIndex() const {
   return node_->GetTableCellAriaRowIndex();
 }
 
-int32_t TestAXNodeWrapper::GetCellId(int32_t row_index,
-                                     int32_t col_index) const {
-  ui::AXNode* cell = node_->GetTableCellFromCoords(row_index, col_index);
-  if (cell)
-    return cell->id();
-
-  return -1;
+base::Optional<int32_t> TestAXNodeWrapper::GetCellId(int row_index,
+                                                     int col_index) const {
+  AXNode* cell = node_->GetTableCellFromCoords(row_index, col_index);
+  if (!cell)
+    return base::nullopt;
+  return cell->id();
 }
 
 gfx::AcceleratedWidget
@@ -452,11 +451,11 @@
   return native_event_target_;
 }
 
-int32_t TestAXNodeWrapper::CellIndexToId(int32_t cell_index) const {
-  ui::AXNode* cell = node_->GetTableCellFromIndex(cell_index);
-  if (cell)
-    return cell->id();
-  return -1;
+base::Optional<int32_t> TestAXNodeWrapper::CellIndexToId(int cell_index) const {
+  AXNode* cell = node_->GetTableCellFromIndex(cell_index);
+  if (!cell)
+    return base::nullopt;
+  return cell->id();
 }
 
 bool TestAXNodeWrapper::IsCellOrHeaderOfARIATable() const {
@@ -629,11 +628,11 @@
   return node_->IsOrderedSet();
 }
 
-int32_t TestAXNodeWrapper::GetPosInSet() const {
+base::Optional<int> TestAXNodeWrapper::GetPosInSet() const {
   return node_->GetPosInSet();
 }
 
-int32_t TestAXNodeWrapper::GetSetSize() const {
+base::Optional<int> TestAXNodeWrapper::GetSetSize() const {
   return node_->GetSetSize();
 }
 
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.h b/ui/accessibility/platform/test_ax_node_wrapper.h
index adfa923..e8305947f 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.h
+++ b/ui/accessibility/platform/test_ax_node_wrapper.h
@@ -80,29 +80,28 @@
   AXPlatformNode* GetFromNodeID(int32_t id) override;
   int GetIndexInParent() const override;
   bool IsTable() const override;
-  int GetTableRowCount() const override;
-  int GetTableColCount() const override;
-  base::Optional<int32_t> GetTableAriaColCount() const override;
-  base::Optional<int32_t> GetTableAriaRowCount() const override;
-  int GetTableCellCount() const override;
-  const std::vector<int32_t> GetColHeaderNodeIds() const override;
-  const std::vector<int32_t> GetColHeaderNodeIds(
-      int32_t col_index) const override;
-  const std::vector<int32_t> GetRowHeaderNodeIds() const override;
-  const std::vector<int32_t> GetRowHeaderNodeIds(
-      int32_t row_index) const override;
+  base::Optional<int> GetTableRowCount() const override;
+  base::Optional<int> GetTableColCount() const override;
+  base::Optional<int> GetTableAriaColCount() const override;
+  base::Optional<int> GetTableAriaRowCount() const override;
+  base::Optional<int> GetTableCellCount() const override;
+  std::vector<int32_t> GetColHeaderNodeIds() const override;
+  std::vector<int32_t> GetColHeaderNodeIds(int col_index) const override;
+  std::vector<int32_t> GetRowHeaderNodeIds() const override;
+  std::vector<int32_t> GetRowHeaderNodeIds(int row_index) const override;
   bool IsTableRow() const override;
-  int GetTableRowRowIndex() const override;
+  base::Optional<int> GetTableRowRowIndex() const override;
   bool IsTableCellOrHeader() const override;
-  int GetTableCellIndex() const override;
-  int GetTableCellColIndex() const override;
-  int GetTableCellRowIndex() const override;
-  int GetTableCellColSpan() const override;
-  int GetTableCellRowSpan() const override;
-  int GetTableCellAriaColIndex() const override;
-  int GetTableCellAriaRowIndex() const override;
-  int32_t GetCellId(int32_t row_index, int32_t col_index) const override;
-  int32_t CellIndexToId(int32_t cell_index) const override;
+  base::Optional<int> GetTableCellIndex() const override;
+  base::Optional<int> GetTableCellColIndex() const override;
+  base::Optional<int> GetTableCellRowIndex() const override;
+  base::Optional<int> GetTableCellColSpan() const override;
+  base::Optional<int> GetTableCellRowSpan() const override;
+  base::Optional<int> GetTableCellAriaColIndex() const override;
+  base::Optional<int> GetTableCellAriaRowIndex() const override;
+  base::Optional<int32_t> GetCellId(int row_index,
+                                    int col_index) const override;
+  base::Optional<int32_t> CellIndexToId(int cell_index) const override;
   bool IsCellOrHeaderOfARIATable() const override;
   bool IsCellOrHeaderOfARIAGrid() const override;
   gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
@@ -120,8 +119,8 @@
       ax::mojom::IntListAttribute attr) override;
   bool IsOrderedSetItem() const override;
   bool IsOrderedSet() const override;
-  int32_t GetPosInSet() const override;
-  int32_t GetSetSize() const override;
+  base::Optional<int> GetPosInSet() const override;
+  base::Optional<int> GetSetSize() const override;
   const std::vector<gfx::NativeViewAccessible> GetDescendants() const override;
   gfx::RectF GetLocation() const;
   int InternalChildCount() const;
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index 56d1c23..ca673b2c 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -1395,6 +1395,9 @@
   <message name="IDS_AUDIO_PLAYER_OPEN_PLAY_LIST_BUTTON_LABEL" desc="Label for a button which is used to open play list in audio player.">
     Open play list
   </message>
+  <message name="IDS_AUDIO_PLAYER_ARTWORK_EXPAND_BUTTON_LABEL" desc="Label for an expand button which is used to show/hide artwork.">
+    Artwork
+  </message>
 
 <!-- Video Player -->
   <message name="IDS_VIDEO_PLAYER_PLAY_THIS_COMPUTER" desc="In the video player app, message of menu item which is shown at the top of the list of Chromecasts. This menuitem is to play the video on this computer (locally), intead of on TVs with Chromecast.">
diff --git a/ui/file_manager/audio_player/elements/audio_player.html b/ui/file_manager/audio_player/elements/audio_player.html
index 45bccf9..ef31a78e 100644
--- a/ui/file_manager/audio_player/elements/audio_player.html
+++ b/ui/file_manager/audio_player/elements/audio_player.html
@@ -12,8 +12,9 @@
 <dom-module id="audio-player">
   <link rel="import" type="css" href="audio_player.css">
   <template>
-    <track-info-panel id="trackInfo"
-        expanded="{{trackInfoExpanded}}"></track-info-panel>
+    <track-info-panel id="trackInfo" expanded="{{trackInfoExpanded}}"
+        aria-expand-artwork-label="[[ariaExpandArtworkLabel]]">
+    </track-info-panel>
     <track-list id="trackList"
         expanded$="[[playlistExpanded]]"
         shuffle="[[shuffle]]"
diff --git a/ui/file_manager/audio_player/elements/audio_player.js b/ui/file_manager/audio_player/elements/audio_player.js
index bbb307a..b6928251 100644
--- a/ui/file_manager/audio_player/elements/audio_player.js
+++ b/ui/file_manager/audio_player/elements/audio_player.js
@@ -100,6 +100,8 @@
 
     /** @type {AriaLabels} */
     ariaLabels: Object,
+
+    ariaExpandArtworkLabel: String,
   },
 
   /**
diff --git a/ui/file_manager/audio_player/elements/track_info_panel.css b/ui/file_manager/audio_player/elements/track_info_panel.css
index 56a0ea1e..3bb5812 100644
--- a/ui/file_manager/audio_player/elements/track_info_panel.css
+++ b/ui/file_manager/audio_player/elements/track_info_panel.css
@@ -23,7 +23,7 @@
 
 :host([expanded]) .track {
   background-color: rgba(255, 255, 255, 0.9);
-  bottom: 0px;
+  bottom: 0;
   position: absolute;
 }
 
@@ -93,13 +93,22 @@
 
 /* expand icon. */
 .track .expand {
+  --ink-color: rgb(51, 51, 51);
+  background-color: transparent;
   background-position: center;
   background-repeat: no-repeat;
+  border: none;
+  border-radius: 0;
+  box-shadow: none;
   flex: none;
   height: 48px;
   width: 48px;
 }
 
+:host-context(.focus-outline-visible) .track .expand:focus {
+  background-color: rgba(153, 153, 153, .2);
+}
+
 :host(:not([expanded])) .track .expand {
   background-image: -webkit-image-set(
       url(../assets/100/player_cover_open.png) 1x,
@@ -125,8 +134,8 @@
 .track .data .data-artist {
   flex: auto;
   overflow: hidden;
-  text-overflow: ellipsis;
   text-align: center;
+  text-overflow: ellipsis;
   white-space: nowrap;
 }
 
diff --git a/ui/file_manager/audio_player/elements/track_info_panel.html b/ui/file_manager/audio_player/elements/track_info_panel.html
index e290095..ca15439 100644
--- a/ui/file_manager/audio_player/elements/track_info_panel.html
+++ b/ui/file_manager/audio_player/elements/track_info_panel.html
@@ -5,7 +5,7 @@
   -->
 
 <link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/font-roboto/roboto.html">
 
 <dom-module id="track-info-panel">
@@ -22,11 +22,9 @@
           <div class="data-title">[[track.title]]</div>
           <div class="data-artist">[[track.artist]]</div>
         </div>
-        <paper-button toggles
-            id="expand"
-            class="expand"
-            active="{{expanded}}">
-        </paper-button>
+        <cr-button id="expand" class="expand" on-click="onExpandClick_"
+            aria-expanded="false" aria-label$="[[ariaExpandArtworkLabel]]">
+        </cr-button>
       </div>
     </div>
   </template>
diff --git a/ui/file_manager/audio_player/elements/track_info_panel.js b/ui/file_manager/audio_player/elements/track_info_panel.js
index ea3d9a1..cd769b8 100644
--- a/ui/file_manager/audio_player/elements/track_info_panel.js
+++ b/ui/file_manager/audio_player/elements/track_info_panel.js
@@ -11,21 +11,34 @@
     properties: {
       track: {
         type: Object,
-        value: null
+        value: null,
       },
 
       expanded: {
         type: Boolean,
         value: false,
         notify: true,
-        reflectToAttribute: true
+        reflectToAttribute: true,
+        observer: 'onExpandedChanged_',
       },
 
       artworkAvailable: {
         type: Boolean,
         value: false,
-        reflectToAttribute: true
-      }
+        reflectToAttribute: true,
+      },
+
+      ariaExpandArtworkLabel: String,
+    },
+
+    /** @private */
+    onExpandClick_: function() {
+      this.expanded = !this.expanded;
+    },
+
+    /** @private */
+    onExpandedChanged_: function() {
+      this.$.expand.setAttribute('aria-expanded', Boolean(this.expanded));
     },
   });
 })();  // Anonymous closure
diff --git a/ui/file_manager/audio_player/js/audio_player.js b/ui/file_manager/audio_player/js/audio_player.js
index 9ee818a6..37bf415 100644
--- a/ui/file_manager/audio_player/js/audio_player.js
+++ b/ui/file_manager/audio_player/js/audio_player.js
@@ -123,6 +123,8 @@
         unmute: strings['MEDIA_PLAYER_UNMUTE_BUTTON_LABEL'],
         volumeSlider: strings['MEDIA_PLAYER_VOLUME_SLIDER_LABEL']
       };
+      this.player_.ariaExpandArtworkLabel =
+          strings['AUDIO_PLAYER_ARTWORK_EXPAND_BUTTON_LABEL'];
     }.bind(this));
 
     this.volumeManager_.addEventListener('externally-unmounted',
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index 0f20c64f..dd9fc92c 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -280,7 +280,7 @@
   flex: auto;
 }
 
-.dialog-header paper-button,
+.dialog-header cr-button,
 .dialog-header button {
   border-radius: 2px;
   height: 32px;
@@ -333,7 +333,7 @@
   width: 16px;
 }
 
-.dialog-header paper-button {
+.dialog-header cr-button {
   line-height: 32px;
   padding: 0 8px;
 }
@@ -575,18 +575,23 @@
   width: 0;
 }
 
-#cloud-import-details paper-button {
+#cloud-import-details cr-button {
+  --ink-color: white;
   align-self: flex-end;
+  border: none;
+  box-shadow: none;
   color: white;
   font-weight: 500;
   margin-top: 20px;
+  padding: 0;
+  text-transform: uppercase;
 }
 
-#cloud-import-details paper-button.import {
+#cloud-import-details cr-button.import {
   background-color: rgb(51, 103, 214);
 }
 
-#cloud-import-details paper-button.cancel {
+#cloud-import-details cr-button.cancel {
   background-color: rgb(219, 68, 55);
 }
 
@@ -601,8 +606,16 @@
 }
 
 #cancel-selection-button {
+  --ink-color: currentColor;
+  background-color: transparent;
+  border: none;
+  box-shadow: none;
+  color: currentColor;
   display: flex;
-  text-transform: none;
+}
+
+#cancel-selection-button:focus:not([tabindex='-1']):not(:active) {
+  background-color: rgba(153, 153, 153, .2);
 }
 
 #cancel-selection-button > span {
@@ -780,15 +793,15 @@
               0 2px 4px -1px rgba(0, 0, 0, 0.4);
 }
 
-.dialog-footer paper-button,
+.dialog-footer cr-button,
 .dialog-footer button {
   height: 32px;
   margin: 0 4px;
   min-width: 92px;
 }
 
-.dialog-footer paper-button[disabled],
-.dialog-footer paper-button[disabled]:hover,
+.dialog-footer cr-button[disabled],
+.dialog-footer cr-button[disabled]:hover,
 .dialog-footer button[disabled],
 .dialog-footer button[disabled]:hover {
   background-color: rgb(234, 234, 234);
@@ -837,7 +850,7 @@
 .dialog-footer .primary:focus:not(:active),
 .dialog-footer .secondary:focus:not(:active),
 .dialog-footer select:focus:not(:active),
-#cloud-import-details paper-button.keyboard-focus {
+html.focus-outline-visible #cloud-import-details cr-button:focus {
   box-shadow: 0 0 0 2px rgba(51, 103, 214, 0.5);
 }
 
diff --git a/ui/file_manager/file_manager/foreground/elements/elements_bundle.html b/ui/file_manager/file_manager/foreground/elements/elements_bundle.html
index caf8ed7..e45b8e3 100644
--- a/ui/file_manager/file_manager/foreground/elements/elements_bundle.html
+++ b/ui/file_manager/file_manager/foreground/elements/elements_bundle.html
@@ -3,9 +3,9 @@
   -- found in the LICENSE file.
   -->
 
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html">
 <link rel="import" href="files_ripple.html">
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
index 3eff63f..cdd0828a 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
@@ -43,8 +43,9 @@
   max-width: 100%;
 }
 
-paper-button:focus:not(:active) {
+cr-button:focus:not(:active) {
   box-shadow: 0 0 0 1px rgba(66, 133, 244, 0.5);
+  font-weight: bold;
 }
 
 #audio-artwork {
@@ -150,13 +151,17 @@
   };
 }
 
-paper-button,
+cr-button,
 files-icon-button {
+  --ink-color: currentColor;
+  border: none;
   border-radius: 2px;
+  color: currentColor;
   height: 32px;
   margin: 0 8px;
   min-width: 32px;
   padding: 0;
+  text-transform: uppercase;
 }
 
 iron-icon {
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
index 3b40174..553784a1 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
@@ -4,8 +4,8 @@
 -->
 
 <link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="files_icon_button.html">
 <link rel="import" href="files_metadata_box.html">
 <link rel="import" href="files_safe_media.html">
@@ -18,15 +18,15 @@
     <dialog id="dialog" on-close="onDialogClose_">
       <div id="toolbar">
         <div class="buttons-group">
-           <paper-button on-tap="close" aria-label="$i18n{QUICK_VIEW_CLOSE_BUTTON_LABEL}" tabindex="0" has-tooltip>
+           <cr-button on-click="close" aria-label="$i18n{QUICK_VIEW_CLOSE_BUTTON_LABEL}" has-tooltip>
              <iron-icon icon="files:arrow-back"></iron-icon>
-           </paper-button>
+           </cr-button>
         </div>
         <div id="file-path">[[filePath]]</div>
         <div class="buttons-group">
-           <paper-button id="open-button" on-tap="onOpenInNewButtonTap" hidden$="[[!hasTask]]" aria-label="$i18n{QUICK_VIEW_OPEN_IN_NEW_BUTTON_LABEL}" tabindex="0">
+           <cr-button id="open-button" on-click="onOpenInNewButtonTap" hidden$="[[!hasTask]]" aria-label="$i18n{QUICK_VIEW_OPEN_IN_NEW_BUTTON_LABEL}">
              <span>$i18n{QUICK_VIEW_OPEN_IN_NEW_BUTTON_LABEL}</span>
-           </paper-button>
+           </cr-button>
            <files-icon-button toggles id="metadata-button" on-tap="onMetadataButtonTap_" active="{{metadataBoxActive}}" aria-label="$i18n{QUICK_VIEW_TOGGLE_METADATA_BOX_BUTTON_LABEL}" tabindex="0" has-tooltip>
            </files-icon-button>
         </div>
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller.js b/ui/file_manager/file_manager/foreground/js/import_controller.js
index b7a2339..2e21b37 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/import_controller.js
@@ -573,13 +573,13 @@
 
     /** @private @const {!HTMLElement} */
     this.importButton_ =
-        queryRequiredElement('#cloud-import-details paper-button.import');
+        queryRequiredElement('#cloud-import-details cr-button.import');
     this.importButton_.onclick =
         this.onButtonClicked_.bind(this, importer.ClickSource.IMPORT);
 
     /** @private @const {!HTMLElement} */
     this.cancelButton_ =
-        queryRequiredElement('#cloud-import-details paper-button.cancel');
+        queryRequiredElement('#cloud-import-details cr-button.cancel');
     this.cancelButton_.onclick =
         this.onButtonClicked_.bind(this, importer.ClickSource.CANCEL);
 
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index b70c7c8..93bc1bee 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -360,11 +360,11 @@
     <div class="dialog-header" role="contentinfo">
       <div id="location-breadcrumbs" class="breadcrumbs"></div>
       <div id="cancel-selection-button-wrapper">
-        <paper-button id="cancel-selection-button" class="menu-button" tabindex="8"
+        <cr-button id="cancel-selection-button" class="menu-button" tabindex="8"
               aria-label="$i18n{CANCEL_SELECTION_BUTTON_LABEL}" has-tooltip>
           <span class="icon-arrow-back"></span>
           <span id="cancel-selection-label">$i18n{CANCEL_SELECTION_BUTTON_LABEL}</span>
-        </paper-button>
+        </cr-button>
       </div>
       <div id="files-selected-label"></div>
       <div class="spacer"></div>
@@ -475,12 +475,12 @@
           <div class="content"></div>
         </div>
         <div class="progress"><div class="value"></div></div>
-        <paper-button class="import" tabindex="16">
+        <cr-button class="import" tabindex="16">
           <span>$i18n{CLOUD_IMPORT_COMMAND}</span>
-        </paper-button>
-        <paper-button class="cancel" tabindex="16">
+        </cr-button>
+        <cr-button class="cancel" tabindex="16">
           <span>$i18n{CLOUD_IMPORT_CANCEL_COMMAND}</span>
-        </paper-button>
+        </cr-button>
       </div>
     </div>
 
diff --git a/ui/file_manager/file_manager/test/scripts/create_test_main.py b/ui/file_manager/file_manager/test/scripts/create_test_main.py
index ed3dc4f..e89604f 100755
--- a/ui/file_manager/file_manager/test/scripts/create_test_main.py
+++ b/ui/file_manager/file_manager/test/scripts/create_test_main.py
@@ -243,6 +243,8 @@
   buf = buf.replace('chrome://resources/polymer/v1_0/', '../../cc/')
   buf = buf.replace('<link rel="import" href="chrome://resources/cr_elements/'
                     'cr_input/cr_input.html">', '')
+  buf = buf.replace('<link rel="import" href="chrome://resources/cr_elements/'
+                    'cr_button/cr_button.html">', '')
   buf = buf.replace('src="files_', 'src="' + elements_path('files_'))
   for old, new in substitutions:
     buf = buf.replace(old, new)
diff --git a/ui/gfx/platform_font_win.cc b/ui/gfx/platform_font_win.cc
index 478e45e..a1d6f94a 100644
--- a/ui/gfx/platform_font_win.cc
+++ b/ui/gfx/platform_font_win.cc
@@ -17,6 +17,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/debug/alias.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
@@ -44,6 +45,19 @@
 
 namespace {
 
+// Tracking of HFontRef allocations (see: http://crbug/972689). The following
+// code is temporary. It will be used to track and fix incorrect usage of
+// GDI handles.
+
+// Amount of HFontRef objects currently allocated.
+int g_hfontref_total = 0;
+
+// Max amount of HFontRef object before reporting a crash.
+constexpr int kMaxHFontRefObjects = 1024;
+
+// Whether a report already got sent.
+bool g_hfontref_dump_already_sent = false;
+
 // Enable the use of PlatformFontSkia instead of PlatformFontWin.
 const base::Feature kPlatformFontSkiaOnWindows{
     "PlatformFontSkiaOnWindows", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -580,6 +594,18 @@
       requested_font_size_(font_size) {
   DLOG_ASSERT(hfont);
 
+  // We are seeing cases in the field where the amount of HFontRef is above
+  // 10k objects. Each object keeps an HFONT alive, counting towards out 10K GDI
+  // handle limit. This code will help track the source of these issues
+  // (see crbug.com/972689) (todo: etienneb: remove this code when the
+  // experiment is done).
+  ++g_hfontref_total;
+  if (g_hfontref_total > kMaxHFontRefObjects &&
+      !g_hfontref_dump_already_sent) {
+    g_hfontref_dump_already_sent = true;
+    base::debug::DumpWithoutCrashing();
+  }
+
   LOGFONT font_info;
   GetObject(hfont_, sizeof(LOGFONT), &font_info);
   font_name_ = base::UTF16ToUTF8(base::string16(font_info.lfFaceName));
@@ -623,6 +649,7 @@
 }
 
 PlatformFontWin::HFontRef::~HFontRef() {
+  --g_hfontref_total;
   DeleteObject(hfont_);
 }
 
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index b8cbbc3..71dccfc0 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -1073,8 +1073,36 @@
 }
 
 bool RenderText::IsNewlineSegment(const internal::LineSegment& segment) const {
-  DCHECK_LT(segment.char_range.start(), text_.length());
-  return text_[segment.char_range.start()] == '\n';
+  return IsNewlineSegment(text_, segment);
+}
+
+bool RenderText::IsNewlineSegment(const base::string16& text,
+                                  const internal::LineSegment& segment) const {
+  DCHECK_LT(segment.char_range.start(), text.length());
+  return text[segment.char_range.start()] == '\n';
+}
+
+Range RenderText::GetLineRange(const base::string16& text,
+                               const internal::Line& line) const {
+  // This will find the logical start and end indices of the given line.
+  size_t max_index = 0;
+  size_t min_index = text.length();
+  for (const auto& segment : line.segments) {
+    min_index = std::min<size_t>(min_index, segment.char_range.GetMin());
+    max_index = std::max<size_t>(max_index, segment.char_range.GetMax());
+  }
+
+  // Do not include the newline character, as that could be considered leading
+  // into the next line. Note that the newline character is always the last
+  // character of the line regardless of the text direction, so decrease the
+  // |max_index|.
+  if (!line.segments.empty() &&
+      (IsNewlineSegment(text, line.segments.back()) ||
+       IsNewlineSegment(text, line.segments.front()))) {
+    --max_index;
+  }
+
+  return Range(min_index, max_index);
 }
 
 RenderText::RenderText()
@@ -1162,27 +1190,14 @@
   }
 
   DCHECK_GT(GetNumLines(), 1U);
-  size_t max_index = 0;
-  size_t min_index = text().length();
-  for (const auto& segment : line.segments) {
-    min_index = std::min<size_t>(min_index, segment.char_range.GetMin());
-    max_index = std::max<size_t>(max_index, segment.char_range.GetMax());
-  }
-
-  // Do not select the newline character to preserve the line number of the
-  // cursor. Note that the newline character is always the last character of the
-  // line regardless of the text direction, so decrease the |max_index|.
-  if (!line.segments.empty() && (IsNewlineSegment(line.segments.back()) ||
-                                 IsNewlineSegment(line.segments.front()))) {
-    --max_index;
-  }
+  Range line_range = GetLineRange(text(), line);
 
   // Cursor affinity should be the opposite of visual direction to preserve the
   // line number of the cursor in multiline text.
   return direction == GetVisualDirectionOfLogicalEnd()
-             ? SelectionModel(DisplayIndexToTextIndex(max_index),
+             ? SelectionModel(DisplayIndexToTextIndex(line_range.end()),
                               CURSOR_BACKWARD)
-             : SelectionModel(DisplayIndexToTextIndex(min_index),
+             : SelectionModel(DisplayIndexToTextIndex(line_range.start()),
                               CURSOR_FORWARD);
 }
 
@@ -1227,14 +1242,15 @@
     render_text->EnsureLayout();
 
     if (render_text->lines_.size() > max_lines_) {
-      // Find the start index of the line to be elided.
-      size_t start_of_elision = layout_text_.length();
-      for (const auto& segment : render_text->lines_[max_lines_ - 1].segments) {
-        uint32_t segment_start = segment.char_range.GetMin();
-        start_of_elision = std::min<size_t>(start_of_elision, segment_start);
-      }
-      base::string16 text_to_elide = layout_text_.substr(start_of_elision);
-      display_text_.assign(layout_text_.substr(0, start_of_elision) +
+      // Find the start and end index of the line to be elided.
+      Range line_range =
+          GetLineRange(layout_text_, render_text->lines_[max_lines_ - 1]);
+      // Add an ellipsis character in case the last line is short enough to fit
+      // on a single line. Otherwise that character will be elided anyway.
+      base::string16 text_to_elide =
+          layout_text_.substr(line_range.start(), line_range.length()) +
+          base::string16(kEllipsisUTF16);
+      display_text_.assign(layout_text_.substr(0, line_range.start()) +
                            Elide(text_to_elide, 0,
                                  static_cast<float>(display_rect_.width()),
                                  ELIDE_TAIL));
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index 5c39851..5a422c0 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -566,9 +566,6 @@
 
   void set_strike_thickness_factor(SkScalar f) { strike_thickness_factor_ = f; }
 
-  // Whether |segment| corresponds to the newline character.
-  bool IsNewlineSegment(const internal::LineSegment& segment) const;
-
   // Return the line index that contains the argument; or the index of the last
   // line if the |caret| exceeds the text length.
   virtual size_t GetLineContainingCaret(const SelectionModel& caret) = 0;
@@ -576,6 +573,19 @@
  protected:
   RenderText();
 
+  // Whether |segment| corresponds to the newline character. This uses |text_|
+  // to look up the corresponding character.
+  bool IsNewlineSegment(const internal::LineSegment& segment) const;
+
+  // Whether |segment| corresponds to the newline character inside |text|.
+  bool IsNewlineSegment(const base::string16& text,
+                        const internal::LineSegment& segment) const;
+
+  // Returns the character range of segments in |line| excluding the trailing
+  // newline segment.
+  Range GetLineRange(const base::string16& text,
+                     const internal::Line& line) const;
+
   // NOTE: The value of these accessors may be stale. Please make sure
   // that these fields are up to date before accessing them.
   const base::string16& layout_text() const { return layout_text_; }
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index 9d7b23d..86f9f71 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -348,7 +348,7 @@
       segment.char_range = run.range;
       segment.x_range = RangeF(SkScalarToFloat(text_x_),
                                SkScalarToFloat(text_x_) + run.shape.width);
-      AddLineSegment(segment);
+      AddLineSegment(segment, false);
     }
   }
 
@@ -451,7 +451,7 @@
       if (IsNewlineSegment(text_, segment) ||
           segment.width() <= available_width_ ||
           word_wrap_behavior_ == IGNORE_LONG_WORDS) {
-        AddLineSegment(segment);
+        AddLineSegment(segment, true);
       } else {
         DCHECK(word_wrap_behavior_ == TRUNCATE_LONG_WORDS ||
                word_wrap_behavior_ == WRAP_LONG_WORDS);
@@ -470,7 +470,7 @@
                 Range(remaining_segment.char_range.start(), cutoff_pos);
             cut_segment.x_range = RangeF(SkScalarToFloat(text_x_),
                                          SkScalarToFloat(text_x_ + width));
-            AddLineSegment(cut_segment);
+            AddLineSegment(cut_segment, true);
             // Updates old segment range.
             remaining_segment.char_range.set_start(cutoff_pos);
             remaining_segment.x_range.set_start(SkScalarToFloat(text_x_));
@@ -487,7 +487,7 @@
   // Add a line segment to the current line. Note that, in order to keep the
   // visual order correct for ltr and rtl language, we need to merge segments
   // that belong to the same run.
-  void AddLineSegment(const internal::LineSegment& segment) {
+  void AddLineSegment(const internal::LineSegment& segment, bool multiline) {
     DCHECK(!lines_.empty());
     internal::Line* line = &lines_.back();
     const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]);
@@ -515,19 +515,22 @@
     line->segments.push_back(segment);
     line->size.set_width(line->size.width() + segment.width());
 
-    SkFont font(run.font_params.skia_face, run.font_params.font_size);
-    font.setEdging(run.font_params.render_params.antialiasing
-                       ? SkFont::Edging::kAntiAlias
-                       : SkFont::Edging::kAlias);
-    SkFontMetrics metrics;
-    font.getMetrics(&metrics);
+    // Newline characters are not drawn for multi-line, ignore their metrics.
+    if (!multiline || !IsNewlineSegment(text_, segment)) {
+      SkFont font(run.font_params.skia_face, run.font_params.font_size);
+      font.setEdging(run.font_params.render_params.antialiasing
+                         ? SkFont::Edging::kAntiAlias
+                         : SkFont::Edging::kAlias);
+      SkFontMetrics metrics;
+      font.getMetrics(&metrics);
 
-    // max_descent_ is y-down, fDescent is y-down, baseline_offset is y-down
-    max_descent_ = std::max(max_descent_,
-                            metrics.fDescent + run.font_params.baseline_offset);
-    // max_ascent_ is y-up, fAscent is y-down, baseline_offset is y-down
-    max_ascent_ = std::max(
-        max_ascent_, -(metrics.fAscent + run.font_params.baseline_offset));
+      // max_descent_ is y-down, fDescent is y-down, baseline_offset is y-down
+      max_descent_ = std::max(
+          max_descent_, metrics.fDescent + run.font_params.baseline_offset);
+      // max_ascent_ is y-up, fAscent is y-down, baseline_offset is y-down
+      max_ascent_ = std::max(
+          max_ascent_, -(metrics.fAscent + run.font_params.baseline_offset));
+    }
 
     if (run.font_params.is_rtl) {
       rtl_segments_.push_back(
@@ -1700,13 +1703,14 @@
   ApplyCompositionAndSelectionStyles();
 
   internal::TextRunList* run_list = GetRunList();
+  const base::string16& display_text = GetDisplayText();
   for (size_t i = 0; i < lines().size(); ++i) {
     const internal::Line& line = lines()[i];
     const Vector2d origin = GetLineOffset(i) + Vector2d(0, line.baseline);
     SkScalar preceding_segment_widths = 0;
     for (const internal::LineSegment& segment : line.segments) {
       // Don't draw the newline glyph (crbug.com/680430).
-      if (IsNewlineSegment(segment))
+      if (IsNewlineSegment(display_text, segment))
         continue;
 
       const internal::TextRunHarfBuzz& run = *run_list->runs()[segment.run];
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index dc3b0ad0..6ea37e64 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -1148,6 +1148,42 @@
   EXPECT_EQ(render_text->GetNumLines(), 1U);
 }
 
+TEST_F(RenderTextTest, MultilineElideBiDi) {
+  RenderText* render_text = GetRenderText();
+  SetGlyphWidth(5);
+
+  base::string16 input_text(UTF8ToUTF16("אa\nbcdבגדהefg\nhו"));
+  render_text->SetText(input_text);
+  render_text->SetCursorEnabled(false);
+  render_text->SetMultiline(true);
+  render_text->SetMaxLines(2);
+  render_text->SetElideBehavior(ELIDE_TAIL);
+  render_text->SetDisplayRect(Rect(30, 0));
+  render_text->GetStringSize();
+
+  EXPECT_EQ(render_text->GetDisplayText(),
+            UTF8ToUTF16("אa\nbcdבג") + base::string16(kEllipsisUTF16));
+  EXPECT_EQ(render_text->GetNumLines(), 2U);
+}
+
+TEST_F(RenderTextTest, MultilineElideLinebreak) {
+  RenderText* render_text = GetRenderText();
+  SetGlyphWidth(5);
+
+  base::string16 input_text(UTF8ToUTF16("hello\nworld"));
+  render_text->SetText(input_text);
+  render_text->SetCursorEnabled(false);
+  render_text->SetMultiline(true);
+  render_text->SetMaxLines(1);
+  render_text->SetElideBehavior(ELIDE_TAIL);
+  render_text->SetDisplayRect(Rect(100, 0));
+  render_text->GetStringSize();
+
+  EXPECT_EQ(render_text->GetDisplayText(),
+            input_text.substr(0, 5) + base::string16(kEllipsisUTF16));
+  EXPECT_EQ(render_text->GetNumLines(), 1U);
+}
+
 TEST_F(RenderTextTest, ElidedStyledTextRtl) {
   static const char* kInputTexts[] = {
       "http://ar.wikipedia.com/فحص",
@@ -2958,6 +2994,10 @@
       25, render_text->GetLineSize(SelectionModel(6, CURSOR_FORWARD)).width());
   // |GetStringSize()| of multi-line text does not include newline character.
   EXPECT_EQ(25, render_text->GetStringSize().width());
+  // Expect height to be 2 times the font height. This assumes simple strings
+  // that do not have special metrics.
+  int font_height = render_text->font_list().GetHeight();
+  EXPECT_EQ(font_height * 2, render_text->GetStringSize().height());
 }
 
 TEST_F(RenderTextTest, MinLineHeight) {
diff --git a/ui/message_center/views/message_popup_collection.cc b/ui/message_center/views/message_popup_collection.cc
index 67b55fd..5f0be7d2 100644
--- a/ui/message_center/views/message_popup_collection.cc
+++ b/ui/message_center/views/message_popup_collection.cc
@@ -239,18 +239,15 @@
     // If the animation is finished, transition to IDLE.
     state_ = State::IDLE;
   } else if (state_ == State::FADE_OUT && !popup_items_.empty()) {
-    if ((HasAddedPopup() && CollapseAllPopups()) || !inverse_) {
-      // If FADE_OUT animation is finished and we still have remaining popups,
-      // we have to MOVE_DOWN them.
       // If we're going to add a new popup after this MOVE_DOWN, do the collapse
       // animation at the same time. Otherwise it will take another MOVE_DOWN.
+      if (HasAddedPopup())
+        CollapseAllPopups();
+
+      // If FADE_OUT animation is finished and we still have remaining popups,
+      // we have to MOVE_DOWN them.
       state_ = State::MOVE_DOWN;
       MoveDownPopups();
-    } else {
-      // If there's no collapsable popups and |inverse_| is on, there's nothing
-      // to do after FADE_OUT.
-      state_ = State::IDLE;
-    }
   } else if (state_ == State::MOVE_UP_FOR_INVERSE) {
     for (auto& item : popup_items_)
       item.is_animating = item.will_fade_in;
diff --git a/ui/message_center/views/message_popup_collection_unittest.cc b/ui/message_center/views/message_popup_collection_unittest.cc
index b412236..5feb66d 100644
--- a/ui/message_center/views/message_popup_collection_unittest.cc
+++ b/ui/message_center/views/message_popup_collection_unittest.cc
@@ -585,6 +585,9 @@
   EXPECT_EQ(ids[1], GetPopup(ids[1])->id());
   EXPECT_TRUE(IsAnimating());
 
+  // MOVE_DOWN
+  AnimateToEnd();
+
   gfx::Rect before = GetPopup(ids[1])->GetBoundsInScreen();
 
   // MOVE_UP_FOR_INVERSE
@@ -610,6 +613,44 @@
   EXPECT_FALSE(IsAnimating());
 }
 
+TEST_F(MessagePopupCollectionTest, NotificationsMoveDownForEmptySpace) {
+  popup_collection()->set_inverse();
+
+  std::vector<std::string> ids;
+  for (size_t i = 0; i < kMaxVisiblePopupNotifications; ++i)
+    ids.push_back(AddNotification());
+
+  AnimateUntilIdle();
+
+  EXPECT_EQ(kMaxVisiblePopupNotifications, GetPopupCounts());
+  EXPECT_FALSE(IsAnimating());
+
+  // Dismiss bottom most notification
+  gfx::Rect dismissed = GetPopup(ids.back())->GetBoundsInScreen();
+  MessageCenter::Get()->MarkSinglePopupAsShown(ids.back(), false);
+
+  // FADE_OUT
+  AnimateToMiddle();
+  EXPECT_GT(1.0f, GetPopup(ids.back())->GetOpacity());
+  EXPECT_EQ(ids[2], GetPopup(ids.back())->id());
+
+  AnimateToEnd();
+  EXPECT_EQ(ids[1], GetPopup(ids[1])->id());
+
+  gfx::Rect before = GetPopup(ids[1])->GetBoundsInScreen();
+
+  // MOVE_DOWN
+  AnimateToMiddle();
+  gfx::Rect moving = GetPopup(ids[1])->GetBoundsInScreen();
+  EXPECT_GT(moving.bottom(), before.bottom());
+  EXPECT_GT(dismissed.bottom(), moving.bottom());
+
+  AnimateToEnd();
+  gfx::Rect after = GetPopup(ids[1])->GetBoundsInScreen();
+  EXPECT_EQ(after, dismissed);
+  EXPECT_FALSE(IsAnimating());
+}
+
 TEST_F(MessagePopupCollectionTest, PopupResized) {
   std::vector<std::string> ids;
   for (size_t i = 0; i < kMaxVisiblePopupNotifications; ++i)
diff --git a/ui/resources/ui_resources.grd b/ui/resources/ui_resources.grd
index b32d036..7bbae80 100644
--- a/ui/resources/ui_resources.grd
+++ b/ui/resources/ui_resources.grd
@@ -78,43 +78,47 @@
         <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_ZOOM_IN" file="common/pointers/zoom_in.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_ZOOM_OUT" file="common/pointers/zoom_out.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_2" file="close_2.png" />
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_2_H" file="close_2_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_2_MASK" file="close_2_mask.png" />
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_2_P" file="close_2_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_3_MASK" file="common/close_3_mask.png" />
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_DIALOG" file="close_dialog.png" />
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_DIALOG_H" file="close_dialog_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_CLOSE_DIALOG_P" file="close_dialog_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_DISABLE" file="disable.png" />
-      <structure type="chrome_scaled_image" name="IDR_DISABLE_H" file="disable_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_DISABLE_P" file="disable_pressed.png" />
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_2" file="close_2.png" />
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_2_H" file="close_2_hover.png" />
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_2_MASK" file="close_2_mask.png" />
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_2_P" file="close_2_pressed.png" />
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_3_MASK" file="common/close_3_mask.png" />
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_DIALOG" file="close_dialog.png" />
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_DIALOG_H" file="close_dialog_hover.png" />
+        <structure type="chrome_scaled_image" name="IDR_CLOSE_DIALOG_P" file="close_dialog_pressed.png" />
+        <structure type="chrome_scaled_image" name="IDR_DISABLE" file="disable.png" />
+        <structure type="chrome_scaled_image" name="IDR_DISABLE_H" file="disable_hover.png" />
+        <structure type="chrome_scaled_image" name="IDR_DISABLE_P" file="disable_pressed.png" />
+      </if>
       <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON" file="common/default_favicon.png" />
       <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON_DARK" file="common/default_favicon_dark.png" />
-      <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON_32" file="common/default_favicon_32.png" />
-      <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON_DARK_32" file="common/default_favicon_dark_32.png" />
-      <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON_64" file="common/default_favicon_64.png" />
-      <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON_DARK_64" file="common/default_favicon_dark_64.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_HARDLOCKED" file="common/easy_unlock_hardlocked.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_HARDLOCKED_HOVER" file="common/easy_unlock_hardlocked_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_HARDLOCKED_PRESSED" file="common/easy_unlock_hardlocked_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED" file="common/easy_unlock_locked.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_HOVER" file="common/easy_unlock_locked_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_PRESSED" file="common/easy_unlock_locked_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED" file="common/easy_unlock_locked_to_be_activated.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED_HOVER" file="common/easy_unlock_locked_to_be_activated_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED_PRESSED" file="common/easy_unlock_locked_to_be_activated_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT" file="common/easy_unlock_locked_with_proximity_hint.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT_HOVER" file="common/easy_unlock_locked_with_proximity_hint_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT_PRESSED" file="common/easy_unlock_locked_with_proximity_hint_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_SPINNER" file="common/easy_unlock_spinner.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED" file="common/easy_unlock_unlocked.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED_HOVER" file="common/easy_unlock_unlocked_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED_PRESSED" file="common/easy_unlock_unlocked_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_FOLDER_CLOSED" file="common/folder_closed.png" />
-      <structure type="chrome_scaled_image" name="IDR_FOLDER_CLOSED_RTL" file="common/folder_closed_rtl.png" />
-      <structure type="chrome_scaled_image" name="IDR_SEND_TAB_TO_SELF_ICON_LIGHT" file="common/send_tab_to_self_light_mode_icon.png" />
-      <structure type="chrome_scaled_image" name="IDR_SEND_TAB_TO_SELF_ICON_DARK" file="common/send_tab_to_self_dark_mode_icon.png" />
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON_32" file="common/default_favicon_32.png" />
+        <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON_DARK_32" file="common/default_favicon_dark_32.png" />
+        <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON_64" file="common/default_favicon_64.png" />
+        <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON_DARK_64" file="common/default_favicon_dark_64.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_HARDLOCKED" file="common/easy_unlock_hardlocked.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_HARDLOCKED_HOVER" file="common/easy_unlock_hardlocked_hover.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_HARDLOCKED_PRESSED" file="common/easy_unlock_hardlocked_pressed.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED" file="common/easy_unlock_locked.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_HOVER" file="common/easy_unlock_locked_hover.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_PRESSED" file="common/easy_unlock_locked_pressed.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED" file="common/easy_unlock_locked_to_be_activated.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED_HOVER" file="common/easy_unlock_locked_to_be_activated_hover.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED_PRESSED" file="common/easy_unlock_locked_to_be_activated_pressed.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT" file="common/easy_unlock_locked_with_proximity_hint.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT_HOVER" file="common/easy_unlock_locked_with_proximity_hint_hover.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT_PRESSED" file="common/easy_unlock_locked_with_proximity_hint_pressed.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_SPINNER" file="common/easy_unlock_spinner.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED" file="common/easy_unlock_unlocked.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED_HOVER" file="common/easy_unlock_unlocked_hover.png" />
+        <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED_PRESSED" file="common/easy_unlock_unlocked_pressed.png" />
+        <structure type="chrome_scaled_image" name="IDR_FOLDER_CLOSED" file="common/folder_closed.png" />
+        <structure type="chrome_scaled_image" name="IDR_FOLDER_CLOSED_RTL" file="common/folder_closed_rtl.png" />
+        <structure type="chrome_scaled_image" name="IDR_SEND_TAB_TO_SELF_ICON_LIGHT" file="common/send_tab_to_self_light_mode_icon.png" />
+        <structure type="chrome_scaled_image" name="IDR_SEND_TAB_TO_SELF_ICON_DARK" file="common/send_tab_to_self_dark_mode_icon.png" />
+      </if>
       <if expr="toolkit_views and not is_macosx">
         <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_SETTINGS" file="common/notification_settings.png"/>
       </if>
diff --git a/ui/views/color_chooser/color_chooser_view.cc b/ui/views/color_chooser/color_chooser_view.cc
index 6bdd26a..1a4bb49 100644
--- a/ui/views/color_chooser/color_chooser_view.cc
+++ b/ui/views/color_chooser/color_chooser_view.cc
@@ -390,14 +390,14 @@
   columns->AddColumn(
       GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0);
   layout->StartRow(0, 0);
-  textfield_ = new Textfield();
-  textfield_->set_controller(this);
-  textfield_->SetDefaultWidthInChars(kTextfieldLengthInChars);
-  textfield_->SetAccessibleName(
+  auto textfield = std::make_unique<Textfield>();
+  textfield->set_controller(this);
+  textfield->SetDefaultWidthInChars(kTextfieldLengthInChars);
+  textfield->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_COLOR_CHOOSER_HEX_INPUT));
-  layout->AddView(textfield_);
-  selected_color_patch_ = new SelectedColorPatchView();
-  layout->AddView(selected_color_patch_);
+  textfield_ = layout->AddView(std::move(textfield));
+  selected_color_patch_ =
+      layout->AddView(std::make_unique<SelectedColorPatchView>());
   AddChildView(std::move(container2));
 
   OnColorChanged(initial_color);
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc
index 494d79a0..f661662 100644
--- a/ui/views/controls/scroll_view.cc
+++ b/ui/views/controls/scroll_view.cc
@@ -275,9 +275,12 @@
 }
 
 void ScrollView::SetBackgroundColor(SkColor color) {
+  if (background_color_data_.color == color)
+    return;
   background_color_data_.color = color;
   use_color_id_ = false;
   UpdateBackground();
+  OnPropertyChanged(&background_color_data_, kPropertyEffectsPaint);
 }
 
 void ScrollView::SetBackgroundThemeColorId(ui::NativeTheme::ColorId color_id) {
@@ -294,6 +297,20 @@
                    contents_viewport_->height());
 }
 
+void ScrollView::SetHideHorizontalScrollBar(bool visible) {
+  if (hide_horizontal_scrollbar_ == visible)
+    return;
+  hide_horizontal_scrollbar_ = visible;
+  OnPropertyChanged(&hide_horizontal_scrollbar_, kPropertyEffectsPaint);
+}
+
+void ScrollView::SetDrawOverflowIndicator(bool draw_overflow_indicator) {
+  if (draw_overflow_indicator_ == draw_overflow_indicator)
+    return;
+  draw_overflow_indicator_ = draw_overflow_indicator;
+  OnPropertyChanged(&draw_overflow_indicator, kPropertyEffectsPaint);
+}
+
 void ScrollView::ClipHeightTo(int min_height, int max_height) {
   min_height_ = min_height;
   max_height_ = max_height;
@@ -332,6 +349,7 @@
 
     focus_ring_->SchedulePaint();
   SchedulePaint();
+  OnPropertyChanged(&has_focus_indicator, kPropertyEffectsPaint);
 }
 
 gfx::Size ScrollView::CalculatePreferredSize() const {
@@ -947,6 +965,12 @@
 
 BEGIN_METADATA(ScrollView)
 METADATA_PARENT_CLASS(View)
+ADD_READONLY_PROPERTY_METADATA(ScrollView, int, MinHeight)
+ADD_READONLY_PROPERTY_METADATA(ScrollView, int, MaxHeight)
+ADD_PROPERTY_METADATA(ScrollView, SkColor, BackgroundColor)
+ADD_PROPERTY_METADATA(ScrollView, bool, DrawOverflowIndicator)
+ADD_PROPERTY_METADATA(ScrollView, bool, HasFocusIndicator)
+ADD_PROPERTY_METADATA(ScrollView, bool, HideHorizontalScrollBar)
 END_METADATA()
 
 // VariableRowHeightScrollHelper ----------------------------------------------
diff --git a/ui/views/controls/scroll_view.h b/ui/views/controls/scroll_view.h
index c26cbc3..cebafcd 100644
--- a/ui/views/controls/scroll_view.h
+++ b/ui/views/controls/scroll_view.h
@@ -77,6 +77,10 @@
   }
   void SetHeader(std::nullptr_t);
 
+  int GetMaxHeight() const { return max_height_; }
+
+  int GetMinHeight() const { return min_height_; }
+
   // The background color can be configured in two distinct ways:
   // . By way of SetBackgroundThemeColorId(). This is the default and when
   //   called the background color comes from the theme (and changes if the
@@ -84,19 +88,21 @@
   // . By way of setting an explicit color, i.e. SetBackgroundColor(). Use
   //   SK_ColorTRANSPARENT if you don't want any color, but be warned this
   //   produces awful results when layers are used with subpixel rendering.
+  SkColor GetBackgroundColor() const;
   void SetBackgroundColor(SkColor color);
+
   void SetBackgroundThemeColorId(ui::NativeTheme::ColorId color_id);
 
   // Returns the visible region of the content View.
   gfx::Rect GetVisibleRect() const;
 
-  void set_hide_horizontal_scrollbar(bool visible) {
-    hide_horizontal_scrollbar_ = visible;
-  }
+  bool GetUseColorId() const { return use_color_id_; }
 
-  void set_draw_overflow_indicator(bool draw_overflow_indicator) {
-    draw_overflow_indicator_ = draw_overflow_indicator;
-  }
+  bool GetHideHorizontalScrollBar() const { return hide_horizontal_scrollbar_; }
+  void SetHideHorizontalScrollBar(bool visible);
+
+  bool GetDrawOverflowIndicator() const { return draw_overflow_indicator_; }
+  void SetDrawOverflowIndicator(bool draw_overflow_indicator);
 
   // Turns this scroll view into a bounded scroll view, with a fixed height.
   // By default, a ScrollView will stretch to fill its outer container.
@@ -119,7 +125,8 @@
   void SetHorizontalScrollBar(ScrollBar* horiz_sb);
   void SetVerticalScrollBar(ScrollBar* vert_sb);
 
-  // Sets whether this ScrollView has a focus indicator or not.
+  // Gets/Sets whether this ScrollView has a focus indicator or not.
+  bool GetHasFocusIndicator() const { return draw_focus_indicator_; }
   void SetHasFocusIndicator(bool has_focus_indicator);
 
   // View overrides:
@@ -207,7 +214,6 @@
   void UpdateBorder();
 
   void UpdateBackground();
-  SkColor GetBackgroundColor() const;
 
   // Positions each overflow indicator against their respective content edge.
   void PositionOverflowIndicators();
diff --git a/ui/views/controls/scroll_view_unittest.cc b/ui/views/controls/scroll_view_unittest.cc
index 9a2125e0..685261b 100644
--- a/ui/views/controls/scroll_view_unittest.cc
+++ b/ui/views/controls/scroll_view_unittest.cc
@@ -1514,7 +1514,7 @@
       /* horizontal */ false, /* overlaps_content */ false, kThickness));
 
   // Also, let's turn off horizontal scroll bar.
-  scroll_view_->set_hide_horizontal_scrollbar(true);
+  scroll_view_->SetHideHorizontalScrollBar(true);
 
   View* contents = InstallContents();
   contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
diff --git a/ui/views/examples/button_sticker_sheet.cc b/ui/views/examples/button_sticker_sheet.cc
index fb603a1..b264debb 100644
--- a/ui/views/examples/button_sticker_sheet.cc
+++ b/ui/views/examples/button_sticker_sheet.cc
@@ -4,6 +4,10 @@
 
 #include "ui/views/examples/button_sticker_sheet.h"
 
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/md_text_button.h"
@@ -50,24 +54,24 @@
 // Add a row containing a label whose text is |label_text| and then all the
 // views in |views| to the supplied GridLayout, with padding between rows.
 template <typename T>
-void AddLabelledRowToGridLayout(GridLayout* layout,
-                                const std::string& label_text,
-                                std::vector<std::unique_ptr<T>> views) {
+void AddLabeledRowToGridLayout(GridLayout* layout,
+                               const std::string& label_text,
+                               std::vector<std::unique_ptr<T>> views) {
   const float kRowDoesNotResizeVertically = 0.0;
   const int kPaddingRowHeight = 8;
   layout->StartRow(kRowDoesNotResizeVertically, kStretchyGridColumnSetId);
-  layout->AddView(MakePlainLabel(label_text).release());
+  layout->AddView(MakePlainLabel(label_text));
   for (auto& view : views)
-    layout->AddView(view.release());
+    layout->AddView(std::move(view));
   // This gets added extraneously after the last row, but it doesn't hurt and
   // means there's no need to keep track of whether to add it or not.
   layout->AddPaddingRow(kRowDoesNotResizeVertically, kPaddingRowHeight);
 }
 
 // Constructs a pair of MdTextButtons in the specified |state| with the
-// specified |listener|, and returns them in |primary| and |secondary|. The
-// button in |primary| is a call-to-action button, and the button in
-// |secondary| is a regular button.
+// specified |listener|, and returns them in |*primary| and |*secondary|. The
+// button in |*primary| is a call-to-action button, and the button in
+// |*secondary| is a regular button.
 std::vector<std::unique_ptr<MdTextButton>> MakeButtonsInState(
     ButtonListener* listener,
     Button::ButtonState state) {
@@ -98,18 +102,18 @@
   std::vector<std::unique_ptr<View>> plainLabel;
   plainLabel.push_back(MakePlainLabel("Primary"));
   plainLabel.push_back(MakePlainLabel("Secondary"));
-  AddLabelledRowToGridLayout(layout, std::string(), std::move(plainLabel));
+  AddLabeledRowToGridLayout(layout, std::string(), std::move(plainLabel));
 
-  AddLabelledRowToGridLayout(layout, "Default",
-                             MakeButtonsInState(this, Button::STATE_NORMAL));
-  AddLabelledRowToGridLayout(layout, "Normal",
-                             MakeButtonsInState(this, Button::STATE_NORMAL));
-  AddLabelledRowToGridLayout(layout, "Hovered",
-                             MakeButtonsInState(this, Button::STATE_HOVERED));
-  AddLabelledRowToGridLayout(layout, "Pressed",
-                             MakeButtonsInState(this, Button::STATE_PRESSED));
-  AddLabelledRowToGridLayout(layout, "Disabled",
-                             MakeButtonsInState(this, Button::STATE_DISABLED));
+  AddLabeledRowToGridLayout(layout, "Default",
+                            MakeButtonsInState(this, Button::STATE_NORMAL));
+  AddLabeledRowToGridLayout(layout, "Normal",
+                            MakeButtonsInState(this, Button::STATE_NORMAL));
+  AddLabeledRowToGridLayout(layout, "Hovered",
+                            MakeButtonsInState(this, Button::STATE_HOVERED));
+  AddLabeledRowToGridLayout(layout, "Pressed",
+                            MakeButtonsInState(this, Button::STATE_PRESSED));
+  AddLabeledRowToGridLayout(layout, "Disabled",
+                            MakeButtonsInState(this, Button::STATE_DISABLED));
 }
 
 void ButtonStickerSheet::ButtonPressed(Button* button, const ui::Event& event) {
diff --git a/ui/views/examples/dialog_example.cc b/ui/views/examples/dialog_example.cc
index e812aea..6c1797f9 100644
--- a/ui/views/examples/dialog_example.cc
+++ b/ui/views/examples/dialog_example.cc
@@ -59,14 +59,12 @@
     return parent_->title_->text();
   }
 
-  // TODO(crbug.com/961660): CreateExtraView should return std::unique_ptr<View>
-  // DialogDelegate:
-  View* CreateExtraView() override {
+  std::unique_ptr<View> CreateExtraView() override {
     if (!parent_->has_extra_button_->GetChecked())
       return nullptr;
     auto view = MdTextButton::CreateSecondaryUiButton(
         nullptr, parent_->extra_button_label_->text());
-    return view.release();
+    return view;
   }
 
   bool Cancel() override { return parent_->AllowDialogClose(false); }
@@ -160,10 +158,9 @@
   AddCheckbox(layout, &has_extra_button_);
 
   StartRowWithLabel(layout, "Modal Type");
-  mode_ = new Combobox(&mode_model_);
+  mode_ = layout->AddView(std::make_unique<Combobox>(&mode_model_));
   mode_->set_listener(this);
   mode_->SetSelectedIndex(ui::MODAL_TYPE_CHILD);
-  layout->AddView(mode_);
 
   StartRowWithLabel(layout, "Bubble");
   AddCheckbox(layout, &bubble_);
@@ -177,11 +174,8 @@
       kFixed, kButtonsColumnId, kFixed,
       provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL));
 
-  // TODO(crbug.com/943560): Avoid this release().
-  show_ =
-      MdTextButton::CreateSecondaryUiButton(this, base::ASCIIToUTF16("Show"))
-          .release();
-  layout->AddView(show_);
+  show_ = layout->AddView(
+      MdTextButton::CreateSecondaryUiButton(this, base::ASCIIToUTF16("Show")));
 }
 
 void DialogExample::StartRowWithLabel(GridLayout* layout, const char* label) {
@@ -190,7 +184,7 @@
                               kFixedVerticalResize,
                               views::LayoutProvider::Get()->GetDistanceMetric(
                                   views::DISTANCE_RELATED_CONTROL_VERTICAL));
-  layout->AddView(new Label(base::ASCIIToUTF16(label)));
+  layout->AddView(std::make_unique<Label>(base::ASCIIToUTF16(label)));
 }
 
 void DialogExample::StartTextfieldRow(GridLayout* layout,
@@ -198,18 +192,16 @@
                                       const char* label,
                                       const char* value) {
   StartRowWithLabel(layout, label);
-  Textfield* textfield = new Textfield();
-  layout->AddView(textfield);
+  auto textfield = std::make_unique<Textfield>();
   textfield->set_controller(this);
   textfield->SetText(base::ASCIIToUTF16(value));
-  *member = textfield;
+  *member = layout->AddView(std::move(textfield));
 }
 
 void DialogExample::AddCheckbox(GridLayout* layout, Checkbox** member) {
-  Checkbox* checkbox = new Checkbox(base::string16(), this);
+  auto checkbox = std::make_unique<Checkbox>(base::string16(), this);
   checkbox->SetChecked(true);
-  layout->AddView(checkbox);
-  *member = checkbox;
+  *member = layout->AddView(std::move(checkbox));
 }
 
 ui::ModalType DialogExample::GetModalType() const {
diff --git a/ui/views/examples/examples_window.cc b/ui/views/examples/examples_window.cc
index f771a183..1ae43c9 100644
--- a/ui/views/examples/examples_window.cc
+++ b/ui/views/examples/examples_window.cc
@@ -142,16 +142,14 @@
                                public ComboboxListener {
  public:
   ExamplesWindowContents(base::OnceClosure on_close, ExampleVector examples)
-      : example_shown_(new View),
-        status_label_(new Label),
-        on_close_(std::move(on_close)) {
+      : on_close_(std::move(on_close)) {
     auto combobox_model = std::make_unique<ComboboxModelExampleList>();
     combobox_model_ = combobox_model.get();
     combobox_model_->SetExamples(std::move(examples));
-    combobox_ = new Combobox(std::move(combobox_model));
+    auto combobox = std::make_unique<Combobox>(std::move(combobox_model));
 
     instance_ = this;
-    combobox_->set_listener(this);
+    combobox->set_listener(this);
 
     SetBackground(CreateThemedSolidBackground(
         this, ui::NativeTheme::kColorId_DialogBackground));
@@ -164,17 +162,18 @@
     column_set->AddPaddingColumn(0, 5);
     layout->AddPaddingRow(0, 5);
     layout->StartRow(0 /* no expand */, 0);
-    layout->AddView(combobox_);
+    combobox_ = layout->AddView(std::move(combobox));
 
     if (combobox_model_->GetItemCount() > 0) {
       layout->StartRow(1, 0);
-      example_shown_->SetLayoutManager(std::make_unique<FillLayout>());
-      example_shown_->AddChildView(combobox_model_->GetItemViewAt(0));
-      layout->AddView(example_shown_);
+      auto example_shown = std::make_unique<View>();
+      example_shown->SetLayoutManager(std::make_unique<FillLayout>());
+      example_shown->AddChildView(combobox_model_->GetItemViewAt(0));
+      example_shown_ = layout->AddView(std::move(example_shown));
     }
 
     layout->StartRow(0 /* no expand */, 0);
-    layout->AddView(status_label_);
+    status_label_ = layout->AddView(std::make_unique<Label>());
     layout->AddPaddingRow(0, 5);
   }
 
@@ -223,8 +222,8 @@
   }
 
   static ExamplesWindowContents* instance_;
-  View* example_shown_;
-  Label* status_label_;
+  View* example_shown_ = nullptr;
+  Label* status_label_ = nullptr;
   base::OnceClosure on_close_;
   Combobox* combobox_ = nullptr;
   // Owned by |combobox_|.
diff --git a/ui/views/examples/label_example.cc b/ui/views/examples/label_example.cc
index 4e48df0a..289ed46e 100644
--- a/ui/views/examples/label_example.cc
+++ b/ui/views/examples/label_example.cc
@@ -156,11 +156,11 @@
 }
 
 void LabelExample::AddCustomLabel(View* container) {
-  View* control_container = new View();
+  std::unique_ptr<View> control_container = std::make_unique<View>();
   control_container->SetBorder(CreateSolidBorder(2, SK_ColorGRAY));
   control_container->SetBackground(CreateSolidBackground(SK_ColorLTGRAY));
   GridLayout* layout = control_container->SetLayoutManager(
-      std::make_unique<views::GridLayout>(control_container));
+      std::make_unique<views::GridLayout>(control_container.get()));
 
   ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL,
@@ -169,13 +169,14 @@
                         1.0f, GridLayout::USE_PREF, 0, 0);
 
   layout->StartRow(0, 0);
-  layout->AddView(new Label(ASCIIToUTF16("Content: ")));
-  textfield_ = new Textfield();
-  textfield_->SetText(ASCIIToUTF16("Use the provided controls to configure the "
-      "content and presentation of this custom label."));
-  textfield_->SetEditableSelectionRange(gfx::Range());
-  textfield_->set_controller(this);
-  layout->AddView(textfield_);
+  layout->AddView(std::make_unique<Label>(ASCIIToUTF16("Content: ")));
+  auto textfield = std::make_unique<Textfield>();
+  textfield->SetText(
+      ASCIIToUTF16("Use the provided controls to configure the "
+                   "content and presentation of this custom label."));
+  textfield->SetEditableSelectionRange(gfx::Range());
+  textfield->set_controller(this);
+  textfield_ = layout->AddView(std::move(textfield));
 
   alignment_ =
       AddCombobox(layout, "Alignment: ", kAlignments, base::size(kAlignments));
@@ -191,29 +192,29 @@
   column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
                         GridLayout::USE_PREF, 0, 0);
   layout->StartRow(0, 1);
-  multiline_ = new Checkbox(base::ASCIIToUTF16("Multiline"), this);
-  layout->AddView(multiline_);
-  shadows_ = new Checkbox(base::ASCIIToUTF16("Shadows"), this);
-  layout->AddView(shadows_);
-  selectable_ = new Checkbox(base::ASCIIToUTF16("Selectable"), this);
-  layout->AddView(selectable_);
+  multiline_ = layout->AddView(
+      std::make_unique<Checkbox>(base::ASCIIToUTF16("Multiline"), this));
+  shadows_ = layout->AddView(
+      std::make_unique<Checkbox>(base::ASCIIToUTF16("Shadows"), this));
+  selectable_ = layout->AddView(
+      std::make_unique<Checkbox>(base::ASCIIToUTF16("Selectable"), this));
   layout->AddPaddingRow(0, 8);
 
   column_set = layout->AddColumnSet(2);
   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL,
                         1, GridLayout::USE_PREF, 0, 0);
   layout->StartRow(0, 2);
-  custom_label_ = new ExamplePreferredSizeLabel();
-  custom_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  custom_label_->SetElideBehavior(gfx::NO_ELIDE);
-  custom_label_->SetText(textfield_->text());
-  layout->AddView(custom_label_);
+  auto custom_label = std::make_unique<ExamplePreferredSizeLabel>();
+  custom_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  custom_label->SetElideBehavior(gfx::NO_ELIDE);
+  custom_label->SetText(textfield_->text());
+  custom_label_ = layout->AddView(std::move(custom_label));
 
   // Disable the text selection checkbox if |custom_label_| does not support
   // text selection.
   selectable_->SetEnabled(custom_label_->IsSelectionSupported());
 
-  container->AddChildView(control_container);
+  container->AddChildView(std::move(control_container));
 }
 
 Combobox* LabelExample::AddCombobox(GridLayout* layout,
@@ -221,13 +222,12 @@
                                     const char** strings,
                                     int count) {
   layout->StartRow(0, 0);
-  layout->AddView(new Label(base::ASCIIToUTF16(name)));
-  Combobox* combobox =
-      new Combobox(std::make_unique<ExampleComboboxModel>(strings, count));
+  layout->AddView(std::make_unique<Label>(base::ASCIIToUTF16(name)));
+  auto combobox = std::make_unique<Combobox>(
+      std::make_unique<ExampleComboboxModel>(strings, count));
   combobox->SetSelectedIndex(0);
   combobox->set_listener(this);
-  layout->AddView(combobox);
-  return combobox;
+  return layout->AddView(std::move(combobox));
 }
 
 }  // namespace examples
diff --git a/ui/views/examples/message_box_example.cc b/ui/views/examples/message_box_example.cc
index d1837ff..b21e9e1 100644
--- a/ui/views/examples/message_box_example.cc
+++ b/ui/views/examples/message_box_example.cc
@@ -4,6 +4,9 @@
 
 #include "ui/views/examples/message_box_example.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/strings/utf_string_conversions.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/message_box_view.h"
@@ -21,22 +24,19 @@
 MessageBoxExample::~MessageBoxExample() = default;
 
 void MessageBoxExample::CreateExampleView(View* container) {
-  message_box_view_ = new MessageBoxView(
-      MessageBoxView::InitParams(ASCIIToUTF16("Hello, world!")));
-  status_ = new LabelButton(this, ASCIIToUTF16("Show Status"));
-  toggle_ = new LabelButton(this, ASCIIToUTF16("Toggle Checkbox"));
-
   GridLayout* layout = container->SetLayoutManager(
       std::make_unique<views::GridLayout>(container));
 
-  message_box_view_->SetCheckBoxLabel(ASCIIToUTF16("Check Box"));
+  auto message_box_view = std::make_unique<MessageBoxView>(
+      MessageBoxView::InitParams(ASCIIToUTF16("Hello, world!")));
+  message_box_view->SetCheckBoxLabel(ASCIIToUTF16("Check Box"));
 
   const int message_box_column = 0;
   ColumnSet* column_set = layout->AddColumnSet(message_box_column);
   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
                         GridLayout::USE_PREF, 0, 0);
   layout->StartRow(1 /* expand */, message_box_column);
-  layout->AddView(message_box_view_);
+  message_box_view_ = layout->AddView(std::move(message_box_view));
 
   const int button_column = 1;
   column_set = layout->AddColumnSet(button_column);
@@ -47,8 +47,10 @@
 
   layout->StartRow(0 /* no expand */, button_column);
 
-  layout->AddView(status_);
-  layout->AddView(toggle_);
+  status_ = layout->AddView(
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Show Status")));
+  toggle_ = layout->AddView(
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Toggle Checkbox")));
 }
 
 void MessageBoxExample::ButtonPressed(Button* sender, const ui::Event& event) {
diff --git a/ui/views/examples/multiline_example.cc b/ui/views/examples/multiline_example.cc
index e05d81d..a990d89 100644
--- a/ui/views/examples/multiline_example.cc
+++ b/ui/views/examples/multiline_example.cc
@@ -130,25 +130,27 @@
       L"\x627\x644\x631\x626\x64A\x633\x64A\x629"
       L"asdfgh");
 
-  render_text_view_ = new RenderTextView();
-  render_text_view_->SetText(kTestString);
+  auto render_text_view = std::make_unique<RenderTextView>();
+  render_text_view->SetText(kTestString);
 
-  label_ = new PreferredSizeLabel();
-  label_->SetText(kTestString);
-  label_->SetMultiLine(true);
-  label_->SetBorder(CreateSolidBorder(2, SK_ColorCYAN));
+  auto label = std::make_unique<PreferredSizeLabel>();
+  label->SetText(kTestString);
+  label->SetMultiLine(true);
+  label->SetBorder(CreateSolidBorder(2, SK_ColorCYAN));
 
-  label_checkbox_ = new Checkbox(ASCIIToUTF16("views::Label:"), this);
-  label_checkbox_->SetChecked(true);
-  label_checkbox_->set_request_focus_on_press(false);
+  auto label_checkbox =
+      std::make_unique<Checkbox>(ASCIIToUTF16("views::Label:"), this);
+  label_checkbox->SetChecked(true);
+  label_checkbox->set_request_focus_on_press(false);
 
-  elision_checkbox_ = new Checkbox(ASCIIToUTF16("elide text?"), this);
-  elision_checkbox_->SetChecked(false);
-  elision_checkbox_->set_request_focus_on_press(false);
+  auto elision_checkbox =
+      std::make_unique<Checkbox>(ASCIIToUTF16("elide text?"), this);
+  elision_checkbox->SetChecked(false);
+  elision_checkbox->set_request_focus_on_press(false);
 
-  textfield_ = new Textfield();
-  textfield_->set_controller(this);
-  textfield_->SetText(kTestString);
+  auto textfield = std::make_unique<Textfield>();
+  textfield->set_controller(this);
+  textfield->SetText(kTestString);
 
   GridLayout* layout = container->SetLayoutManager(
       std::make_unique<views::GridLayout>(container));
@@ -160,19 +162,19 @@
       1.0f, GridLayout::FIXED, 0, 0);
 
   layout->StartRow(0, 0);
-  layout->AddView(new Label(ASCIIToUTF16("gfx::RenderText:")));
-  layout->AddView(render_text_view_);
+  layout->AddView(std::make_unique<Label>(ASCIIToUTF16("gfx::RenderText:")));
+  render_text_view_ = layout->AddView(std::move(render_text_view));
 
   layout->StartRow(0, 0);
-  layout->AddView(label_checkbox_);
-  layout->AddView(label_);
+  label_checkbox_ = layout->AddView(std::move(label_checkbox));
+  label_ = layout->AddView(std::move(label));
 
   layout->StartRow(0, 0);
-  layout->AddView(elision_checkbox_);
+  elision_checkbox_ = layout->AddView(std::move(elision_checkbox));
 
   layout->StartRow(0, 0);
-  layout->AddView(new Label(ASCIIToUTF16("Sample Text:")));
-  layout->AddView(textfield_);
+  layout->AddView(std::make_unique<Label>(ASCIIToUTF16("Sample Text:")));
+  textfield_ = layout->AddView(std::move(textfield));
 }
 
 void MultilineExample::ContentsChanged(Textfield* sender,
diff --git a/ui/views/examples/native_theme_example.cc b/ui/views/examples/native_theme_example.cc
index c5d1fbb..80387bd 100644
--- a/ui/views/examples/native_theme_example.cc
+++ b/ui/views/examples/native_theme_example.cc
@@ -56,8 +56,8 @@
   color_view->SetSelectable(true);
 
   layout->StartRow(GridLayout::kFixedSize, 0);
-  layout->AddView(label_view.release());
-  layout->AddView(color_view.release());
+  layout->AddView(std::move(label_view));
+  layout->AddView(std::move(color_view));
 }
 
 // Returns a view of two columns where the first contains the identifier names
diff --git a/ui/views/examples/progress_bar_example.cc b/ui/views/examples/progress_bar_example.cc
index 1f640e2..e932f4fa 100644
--- a/ui/views/examples/progress_bar_example.cc
+++ b/ui/views/examples/progress_bar_example.cc
@@ -44,24 +44,25 @@
                         GridLayout::USE_PREF, 0, 0);
 
   layout->StartRow(0, 0);
-  minus_button_ = MdTextButton::Create(this, base::ASCIIToUTF16("-")).release();
-  layout->AddView(minus_button_);
-  progress_bar_ = new ProgressBar();
-  layout->AddView(progress_bar_);
-  plus_button_ = MdTextButton::Create(this, base::ASCIIToUTF16("+")).release();
-  layout->AddView(plus_button_);
-  layout->StartRowWithPadding(0, 0, 0, 10);
-  layout->AddView(new Label(base::ASCIIToUTF16("Infinite loader:")));
-  ProgressBar* infinite_bar = new ProgressBar();
-  infinite_bar->SetValue(-1);
-  layout->AddView(infinite_bar);
+  minus_button_ =
+      layout->AddView(MdTextButton::Create(this, base::ASCIIToUTF16("-")));
+  progress_bar_ = layout->AddView(std::make_unique<ProgressBar>());
+  plus_button_ =
+      layout->AddView(MdTextButton::Create(this, base::ASCIIToUTF16("+")));
 
   layout->StartRowWithPadding(0, 0, 0, 10);
   layout->AddView(
-      new Label(base::ASCIIToUTF16("Infinite loader (very short):")));
-  ProgressBar* shorter_bar = new ProgressBar(2);
+      std::make_unique<Label>(base::ASCIIToUTF16("Infinite loader:")));
+  auto infinite_bar = std::make_unique<ProgressBar>();
+  infinite_bar->SetValue(-1);
+  layout->AddView(std::move(infinite_bar));
+
+  layout->StartRowWithPadding(0, 0, 0, 10);
+  layout->AddView(std::make_unique<Label>(
+      base::ASCIIToUTF16("Infinite loader (very short):")));
+  auto shorter_bar = std::make_unique<ProgressBar>(2);
   shorter_bar->SetValue(-1);
-  layout->AddView(shorter_bar);
+  layout->AddView(std::move(shorter_bar));
 }
 
 void ProgressBarExample::ButtonPressed(Button* sender, const ui::Event& event) {
diff --git a/ui/views/examples/radio_button_example.cc b/ui/views/examples/radio_button_example.cc
index d1e25be..d6d9f0a23 100644
--- a/ui/views/examples/radio_button_example.cc
+++ b/ui/views/examples/radio_button_example.cc
@@ -35,21 +35,21 @@
   ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL,
                         1.0f, GridLayout::USE_PREF, 0, 0);
-
+  const int group = 1;
   for (size_t i = 0; i < 3; ++i) {
     layout->StartRow(0, 0);
-    radio_buttons_.push_back(new RadioButton(
-        base::ASCIIToUTF16("Radio ") + base::NumberToString16(i), 1));
-    layout->AddView(radio_buttons_.back());
+    radio_buttons_.push_back(layout->AddView(std::make_unique<RadioButton>(
+        base::UTF8ToUTF16(base::StringPrintf("Radio %d in group %d",
+                                             static_cast<int>(i) + 1, group)),
+        group)));
   }
 
   layout->StartRow(0, 0);
-  select_ = new LabelButton(this, base::ASCIIToUTF16("Select"));
-  layout->AddView(select_);
-
+  select_ = layout->AddView(
+      std::make_unique<LabelButton>(this, base::ASCIIToUTF16("Select")));
   layout->StartRow(0, 0);
-  status_ = new LabelButton(this, base::ASCIIToUTF16("Show Status"));
-  layout->AddView(status_);
+  status_ = layout->AddView(
+      std::make_unique<LabelButton>(this, base::ASCIIToUTF16("Show Status")));
 }
 
 void RadioButtonExample::ButtonPressed(Button* sender, const ui::Event& event) {
diff --git a/ui/views/examples/scroll_view_example.cc b/ui/views/examples/scroll_view_example.cc
index a5d6c18..567d4960 100644
--- a/ui/views/examples/scroll_view_example.cc
+++ b/ui/views/examples/scroll_view_example.cc
@@ -75,13 +75,8 @@
 ScrollViewExample::~ScrollViewExample() = default;
 
 void ScrollViewExample::CreateExampleView(View* container) {
-  wide_ = new LabelButton(this, ASCIIToUTF16("Wide"));
-  tall_ = new LabelButton(this, ASCIIToUTF16("Tall"));
-  big_square_ = new LabelButton(this, ASCIIToUTF16("Big Square"));
-  small_square_ = new LabelButton(this, ASCIIToUTF16("Small Square"));
-  scroll_to_ = new LabelButton(this, ASCIIToUTF16("Scroll to"));
-  scroll_view_ = new ScrollView();
-  scrollable_ = scroll_view_->SetContents(std::make_unique<ScrollableView>());
+  auto scroll_view = std::make_unique<ScrollView>();
+  scrollable_ = scroll_view->SetContents(std::make_unique<ScrollableView>());
   scrollable_->SetBounds(0, 0, 1000, 100);
   scrollable_->SetColor(SK_ColorYELLOW, SK_ColorCYAN);
 
@@ -93,7 +88,7 @@
   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
                         GridLayout::USE_PREF, 0, 0);
   layout->StartRow(1, 0);
-  layout->AddView(scroll_view_);
+  scroll_view_ = layout->AddView(std::move(scroll_view));
 
   // Add control buttons.
   column_set = layout->AddColumnSet(1);
@@ -102,11 +97,16 @@
                           GridLayout::USE_PREF, 0, 0);
   }
   layout->StartRow(0, 1);
-  layout->AddView(wide_);
-  layout->AddView(tall_);
-  layout->AddView(big_square_);
-  layout->AddView(small_square_);
-  layout->AddView(scroll_to_);
+  wide_ = layout->AddView(
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Wide")));
+  tall_ = layout->AddView(
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Tall")));
+  big_square_ = layout->AddView(
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Big Square")));
+  small_square_ = layout->AddView(
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Small Square")));
+  scroll_to_ = layout->AddView(
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Scroll to")));
 }
 
 void ScrollViewExample::ButtonPressed(Button* sender, const ui::Event& event) {
diff --git a/ui/views/examples/tabbed_pane_example.cc b/ui/views/examples/tabbed_pane_example.cc
index c4b0f6c..4003b26 100644
--- a/ui/views/examples/tabbed_pane_example.cc
+++ b/ui/views/examples/tabbed_pane_example.cc
@@ -20,11 +20,12 @@
 TabbedPaneExample::~TabbedPaneExample() = default;
 
 void TabbedPaneExample::CreateExampleView(View* container) {
-  tabbed_pane_ = new TabbedPane();
-  tabbed_pane_->set_listener(this);
-  add_ = new LabelButton(this, ASCIIToUTF16("Add"));
-  add_at_ = new LabelButton(this, ASCIIToUTF16("Add At 1"));
-  select_at_ = new LabelButton(this, ASCIIToUTF16("Select At 1"));
+  auto tabbed_pane = std::make_unique<TabbedPane>();
+  tabbed_pane->set_listener(this);
+  auto add = std::make_unique<LabelButton>(this, ASCIIToUTF16("Add"));
+  auto add_at = std::make_unique<LabelButton>(this, ASCIIToUTF16("Add At 1"));
+  auto select_at =
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Select At 1"));
 
   GridLayout* layout = container->SetLayoutManager(
       std::make_unique<views::GridLayout>(container));
@@ -34,7 +35,7 @@
   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL,
                         1.0f, GridLayout::USE_PREF, 0, 0);
   layout->StartRow(1 /* expand */, tabbed_pane_column);
-  layout->AddView(tabbed_pane_);
+  tabbed_pane_ = layout->AddView(std::move(tabbed_pane));
 
   // Create a few tabs with a button first.
   AddButton("Tab 1");
@@ -50,9 +51,9 @@
   }
 
   layout->StartRow(0 /* no expand */, button_column);
-  layout->AddView(add_);
-  layout->AddView(add_at_);
-  layout->AddView(select_at_);
+  add_ = layout->AddView(std::move(add));
+  add_at_ = layout->AddView(std::move(add_at));
+  select_at_ = layout->AddView(std::move(select_at));
 }
 
 void TabbedPaneExample::ButtonPressed(Button* sender, const ui::Event& event) {
diff --git a/ui/views/examples/table_example.cc b/ui/views/examples/table_example.cc
index fc264a8..c0636e3 100644
--- a/ui/views/examples/table_example.cc
+++ b/ui/views/examples/table_example.cc
@@ -41,19 +41,6 @@
 }
 
 void TableExample::CreateExampleView(View* container) {
-  column1_visible_checkbox_ =
-      new Checkbox(ASCIIToUTF16("Fruit column visible"), this);
-  column1_visible_checkbox_->SetChecked(true);
-  column2_visible_checkbox_ =
-      new Checkbox(ASCIIToUTF16("Color column visible"), this);
-  column2_visible_checkbox_->SetChecked(true);
-  column3_visible_checkbox_ =
-      new Checkbox(ASCIIToUTF16("Origin column visible"), this);
-  column3_visible_checkbox_->SetChecked(true);
-  column4_visible_checkbox_ =
-      new Checkbox(ASCIIToUTF16("Price column visible"), this);
-  column4_visible_checkbox_->SetChecked(true);
-
   GridLayout* layout = container->SetLayoutManager(
       std::make_unique<views::GridLayout>(container));
 
@@ -80,8 +67,7 @@
                         GridLayout::USE_PREF, 0, 0);
   layout->StartRow(1 /* expand */, 0);
   table_ = table.get();
-  layout->AddView(
-      TableView::CreateScrollViewWithTable(std::move(table)).release());
+  layout->AddView(TableView::CreateScrollViewWithTable(std::move(table)));
 
   column_set = layout->AddColumnSet(1);
   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL,
@@ -95,10 +81,21 @@
 
   layout->StartRow(0 /* no expand */, 1);
 
-  layout->AddView(column1_visible_checkbox_);
-  layout->AddView(column2_visible_checkbox_);
-  layout->AddView(column3_visible_checkbox_);
-  layout->AddView(column4_visible_checkbox_);
+  auto make_checkbox =
+      [this](base::string16 text) -> std::unique_ptr<Checkbox> {
+    auto result = std::make_unique<Checkbox>(text, this);
+    result->SetChecked(true);
+    return result;
+  };
+
+  column1_visible_checkbox_ =
+      layout->AddView(make_checkbox(ASCIIToUTF16("Fruit column visible")));
+  column2_visible_checkbox_ =
+      layout->AddView(make_checkbox(ASCIIToUTF16("Color column visible")));
+  column3_visible_checkbox_ =
+      layout->AddView(make_checkbox(ASCIIToUTF16("Origin column visible")));
+  column4_visible_checkbox_ =
+      layout->AddView(make_checkbox(ASCIIToUTF16("Price column visible")));
 }
 
 int TableExample::RowCount() {
diff --git a/ui/views/examples/text_example.cc b/ui/views/examples/text_example.cc
index 48f7115..24c9986 100644
--- a/ui/views/examples/text_example.cc
+++ b/ui/views/examples/text_example.cc
@@ -130,9 +130,8 @@
 TextExample::~TextExample() = default;
 
 Checkbox* TextExample::AddCheckbox(GridLayout* layout, const char* name) {
-  Checkbox* checkbox = new Checkbox(base::ASCIIToUTF16(name), this);
-  layout->AddView(checkbox);
-  return checkbox;
+  return layout->AddView(
+      std::make_unique<Checkbox>(base::ASCIIToUTF16(name), this));
 }
 
 Combobox* TextExample::AddCombobox(GridLayout* layout,
@@ -140,18 +139,17 @@
                                    const char* const* strings,
                                    int count) {
   layout->StartRow(0, 0);
-  layout->AddView(new Label(base::ASCIIToUTF16(name)));
-  Combobox* combobox =
-      new Combobox(std::make_unique<ExampleComboboxModel>(strings, count));
+  layout->AddView(std::make_unique<Label>(base::ASCIIToUTF16(name)));
+  auto combobox = std::make_unique<Combobox>(
+      std::make_unique<ExampleComboboxModel>(strings, count));
   combobox->SetSelectedIndex(0);
   combobox->set_listener(this);
-  layout->AddView(combobox, kNumColumns - 1, 1);
-  return combobox;
+  return layout->AddView(std::move(combobox), kNumColumns - 1, 1);
 }
 
 void TextExample::CreateExampleView(View* container) {
-  text_view_ = new TextExampleView;
-  text_view_->SetBorder(CreateSolidBorder(1, SK_ColorGRAY));
+  auto text_view = std::make_unique<TextExampleView>();
+  text_view->SetBorder(CreateSolidBorder(1, SK_ColorGRAY));
   GridLayout* layout = container->SetLayoutManager(
       std::make_unique<views::GridLayout>(container));
   layout->AddPaddingRow(0, 8);
@@ -190,7 +188,7 @@
                         1, GridLayout::USE_PREF, 0, 0);
   column_set->AddPaddingColumn(0, 16);
   layout->StartRow(1, 1);
-  layout->AddView(text_view_);
+  text_view_ = layout->AddView(std::move(text_view));
   layout->AddPaddingRow(0, 8);
 }
 
diff --git a/ui/views/examples/textfield_example.cc b/ui/views/examples/textfield_example.cc
index b7a0290..7a7a9d50 100644
--- a/ui/views/examples/textfield_example.cc
+++ b/ui/views/examples/textfield_example.cc
@@ -23,34 +23,41 @@
 namespace views {
 namespace examples {
 
+namespace {
+
+template <class K, class T>
+T* MakeRow(GridLayout* layout,
+           std::unique_ptr<K> view1,
+           std::unique_ptr<T> view2) {
+  layout->StartRowWithPadding(0, 0, 0, 5);
+  if (view1)
+    layout->AddView(std::move(view1));
+  return layout->AddView(std::move(view2));
+}
+
+}  // namespace
+
 TextfieldExample::TextfieldExample() : ExampleBase("Textfield") {}
 
 TextfieldExample::~TextfieldExample() = default;
 
 void TextfieldExample::CreateExampleView(View* container) {
-  name_ = new Textfield();
-  password_ = new Textfield();
-  password_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
-  password_->set_placeholder_text(ASCIIToUTF16("password"));
-  disabled_ = new Textfield();
-  disabled_->SetEnabled(false);
-  disabled_->SetText(ASCIIToUTF16("disabled"));
-  read_only_ = new Textfield();
-  read_only_->SetReadOnly(true);
-  read_only_->SetText(ASCIIToUTF16("read only"));
-  invalid_ = new Textfield();
-  invalid_->SetInvalid(true);
-  rtl_ = new Textfield();
-  rtl_->ChangeTextDirectionAndLayoutAlignment(base::i18n::RIGHT_TO_LEFT);
-  show_password_ = new LabelButton(this, ASCIIToUTF16("Show password"));
-  set_background_ =
-      new LabelButton(this, ASCIIToUTF16("Set non-default background"));
-  clear_all_ = new LabelButton(this, ASCIIToUTF16("Clear All"));
-  append_ = new LabelButton(this, ASCIIToUTF16("Append"));
-  set_ = new LabelButton(this, ASCIIToUTF16("Set"));
-  set_style_ = new LabelButton(this, ASCIIToUTF16("Set Styles"));
-  name_->set_controller(this);
-  password_->set_controller(this);
+  auto name = std::make_unique<Textfield>();
+  name->set_controller(this);
+  auto password = std::make_unique<Textfield>();
+  password->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
+  password->set_placeholder_text(ASCIIToUTF16("password"));
+  password->set_controller(this);
+  auto disabled = std::make_unique<Textfield>();
+  disabled->SetEnabled(false);
+  disabled->SetText(ASCIIToUTF16("disabled"));
+  auto read_only = std::make_unique<Textfield>();
+  read_only->SetReadOnly(true);
+  read_only->SetText(ASCIIToUTF16("read only"));
+  auto invalid = std::make_unique<Textfield>();
+  invalid->SetInvalid(true);
+  auto rtl = std::make_unique<Textfield>();
+  rtl->ChangeTextDirectionAndLayoutAlignment(base::i18n::RIGHT_TO_LEFT);
 
   GridLayout* layout = container->SetLayoutManager(
       std::make_unique<views::GridLayout>(container));
@@ -61,25 +68,42 @@
   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL,
                         0.8f, GridLayout::USE_PREF, 0, 0);
 
-  auto MakeRow = [layout](View* view1, View* view2) {
-    layout->StartRowWithPadding(0, 0, 0, 5);
-    layout->AddView(view1);
-    if (view2)
-      layout->AddView(view2);
-  };
-  MakeRow(new Label(ASCIIToUTF16("Name:")), name_);
-  MakeRow(new Label(ASCIIToUTF16("Password:")), password_);
-  MakeRow(new Label(ASCIIToUTF16("Disabled:")), disabled_);
-  MakeRow(new Label(ASCIIToUTF16("Read Only:")), read_only_);
-  MakeRow(new Label(ASCIIToUTF16("Invalid:")), invalid_);
-  MakeRow(new Label(ASCIIToUTF16("RTL:")), rtl_);
-  MakeRow(new Label(ASCIIToUTF16("Name:")), nullptr);
-  MakeRow(show_password_, nullptr);
-  MakeRow(set_background_, nullptr);
-  MakeRow(clear_all_, nullptr);
-  MakeRow(append_, nullptr);
-  MakeRow(set_, nullptr);
-  MakeRow(set_style_, nullptr);
+  name_ = MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("Name:")),
+                  std::move(name));
+  password_ =
+      MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("Password:")),
+              std::move(password));
+  disabled_ =
+      MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("Disabled:")),
+              std::move(disabled));
+  read_only_ =
+      MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("Read Only:")),
+              std::move(read_only));
+  invalid_ = MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("Invalid:")),
+                     std::move(invalid));
+  rtl_ = MakeRow(layout, std::make_unique<Label>(ASCIIToUTF16("RTL:")),
+                 std::move(rtl));
+  MakeRow<View, Label>(layout, nullptr,
+                       std::make_unique<Label>(ASCIIToUTF16("Name:")));
+  show_password_ = MakeRow<View, LabelButton>(
+      layout, nullptr,
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Show password")));
+  set_background_ = MakeRow<View, LabelButton>(
+      layout, nullptr,
+      std::make_unique<LabelButton>(
+          this, ASCIIToUTF16("Set non-default background")));
+  clear_all_ = MakeRow<View, LabelButton>(
+      layout, nullptr,
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Clear All")));
+  append_ = MakeRow<View, LabelButton>(
+      layout, nullptr,
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Append")));
+  set_ = MakeRow<View, LabelButton>(
+      layout, nullptr,
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Set")));
+  set_style_ = MakeRow<View, LabelButton>(
+      layout, nullptr,
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Set Styles")));
 }
 
 void TextfieldExample::ContentsChanged(Textfield* sender,
diff --git a/ui/views/examples/tree_view_example.cc b/ui/views/examples/tree_view_example.cc
index 55e0bda..f652d46 100644
--- a/ui/views/examples/tree_view_example.cc
+++ b/ui/views/examples/tree_view_example.cc
@@ -73,15 +73,16 @@
   tree_view->SetController(this);
   tree_view->SetDrawingProvider(
       std::make_unique<ExampleTreeViewDrawingProvider>());
-  add_ = new LabelButton(this, ASCIIToUTF16("Add"));
-  add_->SetFocusForPlatform();
-  add_->set_request_focus_on_press(true);
-  remove_ = new LabelButton(this, ASCIIToUTF16("Remove"));
-  remove_->SetFocusForPlatform();
-  remove_->set_request_focus_on_press(true);
-  change_title_ = new LabelButton(this, ASCIIToUTF16("Change Title"));
-  change_title_->SetFocusForPlatform();
-  change_title_->set_request_focus_on_press(true);
+  auto add = std::make_unique<LabelButton>(this, ASCIIToUTF16("Add"));
+  add->SetFocusForPlatform();
+  add->set_request_focus_on_press(true);
+  auto remove = std::make_unique<LabelButton>(this, ASCIIToUTF16("Remove"));
+  remove->SetFocusForPlatform();
+  remove->set_request_focus_on_press(true);
+  auto change_title =
+      std::make_unique<LabelButton>(this, ASCIIToUTF16("Change Title"));
+  change_title->SetFocusForPlatform();
+  change_title->set_request_focus_on_press(true);
 
   GridLayout* layout = container->SetLayoutManager(
       std::make_unique<views::GridLayout>(container));
@@ -92,8 +93,7 @@
                         1.0f, GridLayout::USE_PREF, 0, 0);
   layout->StartRow(1 /* expand */, tree_view_column);
   tree_view_ = tree_view.get();
-  layout->AddView(
-      TreeView::CreateScrollViewWithTree(std::move(tree_view)).release());
+  layout->AddView(TreeView::CreateScrollViewWithTree(std::move(tree_view)));
 
   // Add control buttons horizontally.
   const int button_column = 1;
@@ -104,9 +104,9 @@
   }
 
   layout->StartRow(0 /* no expand */, button_column);
-  layout->AddView(add_);
-  layout->AddView(remove_);
-  layout->AddView(change_title_);
+  add_ = layout->AddView(std::move(add));
+  remove_ = layout->AddView(std::move(remove));
+  change_title_ = layout->AddView(std::move(change_title));
 }
 
 void TreeViewExample::AddNewNode() {
diff --git a/ui/views/examples/widget_example.cc b/ui/views/examples/widget_example.cc
index 891c4e5e..2ee82e2 100644
--- a/ui/views/examples/widget_example.cc
+++ b/ui/views/examples/widget_example.cc
@@ -28,7 +28,7 @@
   WidgetDialogExample();
   ~WidgetDialogExample() override;
   base::string16 GetWindowTitle() const override;
-  View* CreateExtraView() override;
+  std::unique_ptr<View> CreateExtraView() override;
   View* CreateFootnoteView() override;
 };
 
@@ -56,11 +56,10 @@
   return ASCIIToUTF16("Dialog Widget Example");
 }
 
-// TODO(crbug.com/961660): CreateExtraView should return std::unique_ptr<View>
-View* WidgetDialogExample::CreateExtraView() {
+std::unique_ptr<View> WidgetDialogExample::CreateExtraView() {
   auto view = MdTextButton::CreateSecondaryUiButton(
       nullptr, ASCIIToUTF16("Extra button!"));
-  return view.release();
+  return view;
 }
 
 View* WidgetDialogExample::CreateFootnoteView() {
diff --git a/ui/views/layout/grid_layout.cc b/ui/views/layout/grid_layout.cc
index fdd98437..5859715 100644
--- a/ui/views/layout/grid_layout.cc
+++ b/ui/views/layout/grid_layout.cc
@@ -818,10 +818,6 @@
   SkipPaddingColumns();
 }
 
-void GridLayout::AddView(View* view) {
-  AddView(view, 1, 1);
-}
-
 void GridLayout::AddView(View* view, int col_span, int row_span) {
   DCHECK(current_row_col_set_ &&
          next_column_ < current_row_col_set_->num_columns());
@@ -829,19 +825,24 @@
   AddView(view, col_span, row_span, column->h_align(), column->v_align());
 }
 
-void GridLayout::AddView(View* view, int col_span, int row_span,
-                         Alignment h_align, Alignment v_align) {
-  AddView(view, col_span, row_span, h_align, v_align, 0, 0);
-}
-
-void GridLayout::AddView(View* view, int col_span, int row_span,
-                         Alignment h_align, Alignment v_align,
-                         int pref_width, int pref_height) {
+void GridLayout::AddView(View* view,
+                         int col_span,
+                         int row_span,
+                         Alignment h_align,
+                         Alignment v_align,
+                         int pref_width,
+                         int pref_height) {
   DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 &&
          (next_column_ + col_span) <= current_row_col_set_->num_columns());
   // We don't support baseline alignment of views spanning rows. Please add if
   // you need it.
   DCHECK(v_align != BASELINE || row_span == 1);
+  DCHECK(view && (view->parent() == nullptr || view->parent() == host_));
+  if (!view->parent()) {
+    adding_view_ = true;
+    host_->AddChildView(view);
+    adding_view_ = false;
+  }
   AddViewState(std::make_unique<ViewState>(
       current_row_col_set_, view, next_column_, current_row_, col_span,
       row_span, h_align, v_align, pref_width, pref_height));
@@ -1046,14 +1047,39 @@
   }
 }
 
+void GridLayout::AddViewImpl(std::unique_ptr<View> view,
+                             int col_span,
+                             int row_span) {
+  DCHECK(current_row_col_set_ &&
+         next_column_ < current_row_col_set_->num_columns());
+  Column* column = current_row_col_set_->columns_[next_column_].get();
+  AddViewImpl(std::move(view), col_span, row_span, column->h_align(),
+              column->v_align(), 0, 0);
+}
+
+void GridLayout::AddViewImpl(std::unique_ptr<View> view,
+                             int col_span,
+                             int row_span,
+                             Alignment h_align,
+                             Alignment v_align,
+                             int pref_width,
+                             int pref_height) {
+  DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 &&
+         (next_column_ + col_span) <= current_row_col_set_->num_columns());
+  // We don't support baseline alignment of views spanning rows. Please add if
+  // you need it.
+  DCHECK(v_align != BASELINE || row_span == 1);
+  DCHECK(view && view->parent() == nullptr);
+  adding_view_ = true;
+  View* view_ptr = host_->AddChildView(std::move(view));
+  adding_view_ = false;
+  AddViewState(std::make_unique<ViewState>(
+      current_row_col_set_, view_ptr, next_column_, current_row_, col_span,
+      row_span, h_align, v_align, pref_width, pref_height));
+}
+
 void GridLayout::AddViewState(std::unique_ptr<ViewState> view_state) {
-  DCHECK(view_state->view && (view_state->view->parent() == nullptr ||
-                              view_state->view->parent() == host_));
-  if (!view_state->view->parent()) {
-    adding_view_ = true;
-    host_->AddChildView(view_state->view);
-    adding_view_ = false;
-  }
+  DCHECK(view_state->view && view_state->view->parent() == host_);
   remaining_row_span_ = std::max(remaining_row_span_, view_state->row_span);
   next_column_ += view_state->col_span;
   current_row_col_set_->AddViewState(view_state.get());
diff --git a/ui/views/layout/grid_layout.h b/ui/views/layout/grid_layout.h
index cd046c7..791dbcb 100644
--- a/ui/views/layout/grid_layout.h
+++ b/ui/views/layout/grid_layout.h
@@ -145,31 +145,50 @@
   // contain any views.
   void SkipColumns(int col_count);
 
-  // Adds a view using the default alignment from the column. The added
-  // view has a column and row span of 1.
-  // As a convenience this adds the view to the host. The view becomes owned
-  // by the host, and NOT this GridLayout.
-  void AddView(View* view);
-
   // Adds a view using the default alignment from the column.
   // As a convenience this adds the view to the host. The view becomes owned
   // by the host, and NOT this GridLayout.
-  void AddView(View* view, int col_span, int row_span);
+  template <typename T>
+  T* AddView(std::unique_ptr<T> view, int col_span = 1, int row_span = 1) {
+    T* result = view.get();
+    AddViewImpl(std::move(view), col_span, row_span);
+    return result;
+  }
 
-  // Adds a view with the specified alignment and spans.
-  // As a convenience this adds the view to the host. The view becomes owned
-  // by the host, and NOT this GridLayout.
-  void AddView(View* view, int col_span, int row_span, Alignment h_align,
-               Alignment v_align);
+  // Adds a view to the layout using the default alignment from the column.
+  void AddView(View* view, int col_span = 1, int row_span = 1);
 
   // Adds a view with the specified alignment and spans. If
   // pref_width/pref_height is > 0 then the preferred width/height of the view
   // is fixed to the specified value.
   // As a convenience this adds the view to the host. The view becomes owned
   // by the host, and NOT this GridLayout.
-  void AddView(View* view, int col_span, int row_span,
-               Alignment h_align, Alignment v_align,
-               int pref_width, int pref_height);
+  template <typename T>
+  T* AddView(std::unique_ptr<T> view,
+             int col_span,
+             int row_span,
+             Alignment h_align,
+             Alignment v_align,
+             int pref_width = 0,
+             int pref_height = 0) {
+    T* result = view.get();
+    AddViewImpl(std::move(view), col_span, row_span, h_align, v_align,
+                pref_width, pref_height);
+    return result;
+  }
+
+  // Adds a view to the layout with the specified alignment and spans. If
+  // pref_width/pref_height is > 0 then the preferred width/height of the view
+  // is fixed to the specified value.
+  // TODO: Rename to AddViewToLayout() once call sites are refactored to prefer
+  //       the AddView<T> version above.
+  void AddView(View* view,
+               int col_span,
+               int row_span,
+               Alignment h_align,
+               Alignment v_align,
+               int pref_width = 0,
+               int pref_height = 0);
 
   // Notification we've been installed on a particular host. Checks that host
   // is the same as the View supplied in the constructor.
@@ -205,8 +224,20 @@
   // a description of what a master column is.
   void CalculateMasterColumnsIfNecessary() const;
 
-  // This is called internally from AddView. It adds the ViewState to the
-  // appropriate structures, and updates internal fields such as next_column_.
+  // These are called internally from AddView<T>.
+  void AddViewImpl(std::unique_ptr<View> view, int col_span, int row_span);
+
+  void AddViewImpl(std::unique_ptr<View> view,
+                   int col_span,
+                   int row_span,
+                   Alignment h_align,
+                   Alignment v_align,
+                   int pref_width,
+                   int pref_height);
+
+  // This is called internally from AddView & AddViewState above. It adds the
+  // ViewState to the appropriate structures and updates the internal fields
+  // such as next_column_.
   void AddViewState(std::unique_ptr<ViewState> view_state);
 
   // Adds the Row to rows_, as well as updating next_column_,
diff --git a/ui/views/layout/grid_layout_unittest.cc b/ui/views/layout/grid_layout_unittest.cc
index b35944a..ad1d50a 100644
--- a/ui/views/layout/grid_layout_unittest.cc
+++ b/ui/views/layout/grid_layout_unittest.cc
@@ -4,6 +4,9 @@
 
 #include "ui/views/layout/grid_layout.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/compiler_specific.h"
 #include "base/test/gtest_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -21,8 +24,8 @@
   EXPECT_EQ(h, view->height());
 }
 
-View* CreateSizedView(const gfx::Size& size) {
-  auto* view = new View();
+std::unique_ptr<View> CreateSizedView(const gfx::Size& size) {
+  auto view = std::make_unique<View>();
   view->SetPreferredSize(size);
   return view;
 }
@@ -42,8 +45,9 @@
   DISALLOW_COPY_AND_ASSIGN(MinSizeView);
 };
 
-View* CreateViewWithMinAndPref(const gfx::Size& min, const gfx::Size& pref) {
-  MinSizeView* view = new MinSizeView(min);
+std::unique_ptr<MinSizeView> CreateViewWithMinAndPref(const gfx::Size& min,
+                                                      const gfx::Size& pref) {
+  auto view = std::make_unique<MinSizeView>(min);
   view->SetPreferredSize(pref);
   return view;
 }
@@ -99,52 +103,49 @@
 
 class GridLayoutTest : public testing::Test {
  public:
-  GridLayoutTest() {
-    layout_ =
-        host_.SetLayoutManager(std::make_unique<views::GridLayout>(&host_));
+  GridLayoutTest() : host_(std::make_unique<View>()) {
+    layout_ = host_->SetLayoutManager(
+        std::make_unique<views::GridLayout>(host_.get()));
   }
 
-  void RemoveAll() { host_.RemoveAllChildViews(false); }
+  gfx::Size GetPreferredSize() {
+    return layout_->GetPreferredSize(host_.get());
+  }
 
-  gfx::Size GetPreferredSize() { return layout_->GetPreferredSize(&host_); }
-
-  View& host() { return host_; }
+  View* host() { return host_.get(); }
   GridLayout* layout() { return layout_; }
 
  private:
-  View host_;
+  std::unique_ptr<View> host_;
   GridLayout* layout_;
 };
 
 class GridLayoutAlignmentTest : public testing::Test {
  public:
-  GridLayoutAlignmentTest() {
-    layout_ =
-        host_.SetLayoutManager(std::make_unique<views::GridLayout>(&host_));
-    v1_.SetPreferredSize(gfx::Size(10, 20));
+  GridLayoutAlignmentTest() : host_(std::make_unique<View>()) {
+    layout_ = host_->SetLayoutManager(
+        std::make_unique<views::GridLayout>(host_.get()));
   }
 
-  void RemoveAll() { host_.RemoveAllChildViews(false); }
-
   void TestAlignment(GridLayout::Alignment alignment, gfx::Rect* bounds) {
+    auto v1 = std::make_unique<View>();
+    v1->SetPreferredSize(gfx::Size(10, 20));
     ColumnSet* c1 = layout_->AddColumnSet(0);
     c1->AddColumn(alignment, alignment, 1, GridLayout::USE_PREF, 0, 0);
     layout_->StartRow(1, 0);
-    layout_->AddView(&v1_);
-    gfx::Size pref = layout_->GetPreferredSize(&host_);
+    auto* v1_ptr = layout_->AddView(std::move(v1));
+    gfx::Size pref = layout_->GetPreferredSize(host_.get());
     EXPECT_EQ(gfx::Size(10, 20), pref);
-    host_.SetBounds(0, 0, 100, 100);
-    layout_->Layout(&host_);
-    *bounds = v1_.bounds();
-    RemoveAll();
+    host_->SetBounds(0, 0, 100, 100);
+    layout_->Layout(host_.get());
+    *bounds = v1_ptr->bounds();
   }
 
-  View& host() { return host_; }
+  View* host() { return host_.get(); }
   GridLayout* layout() { return layout_; }
 
  private:
-  View host_;
-  View v1_;
+  std::unique_ptr<View> host_;
   GridLayout* layout_;
 };
 
@@ -173,38 +174,28 @@
 }
 
 TEST_F(GridLayoutTest, TwoColumns) {
-  View v1;
-  v1.SetPreferredSize(gfx::Size(10, 20));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(20, 20));
+  auto v1 = CreateSizedView(gfx::Size(10, 20));
+  auto v2 = CreateSizedView(gfx::Size(20, 20));
   ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1);
-  layout()->AddView(&v2);
+  auto* v1_ptr = layout()->AddView(std::move(v1));
+  auto* v2_ptr = layout()->AddView(std::move(v2));
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(30, 20), pref);
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 10, 20, &v1);
-  ExpectViewBoundsEquals(10, 0, 20, 20, &v2);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 10, 20, v1_ptr);
+  ExpectViewBoundsEquals(10, 0, 20, 20, v2_ptr);
 }
 
 // Test linked column sizes, and the column size limit.
 TEST_F(GridLayoutTest, LinkedSizes) {
-  View v1;
-  v1.SetPreferredSize(gfx::Size(10, 20));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(20, 20));
-  View v3;
-  v3.SetPreferredSize(gfx::Size(0, 20));
   ColumnSet* c1 = layout()->AddColumnSet(0);
 
   // Fill widths.
@@ -216,9 +207,9 @@
                 0, 0);
 
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1);
-  layout()->AddView(&v2);
-  layout()->AddView(&v3);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
+  auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(20, 20)));
+  auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(0, 20)));
 
   // Link all the columns.
   c1->LinkColumnSizes(0, 1, 2, -1);
@@ -227,130 +218,108 @@
   // |v1| and |v3| should obtain the same width as |v2|, since |v2| is largest.
   pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(20 + 20 + 20, 20), pref);
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 20, 20, &v1);
-  ExpectViewBoundsEquals(20, 0, 20, 20, &v2);
-  ExpectViewBoundsEquals(40, 0, 20, 20, &v3);
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 20, 20, v1);
+  ExpectViewBoundsEquals(20, 0, 20, 20, v2);
+  ExpectViewBoundsEquals(40, 0, 20, 20, v3);
 
   // If the limit is zero, behaves as though the columns are not linked.
   c1->set_linked_column_size_limit(0);
   pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(10 + 20 + 0, 20), pref);
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 10, 20, &v1);
-  ExpectViewBoundsEquals(10, 0, 20, 20, &v2);
-  ExpectViewBoundsEquals(30, 0, 0, 20, &v3);
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 10, 20, v1);
+  ExpectViewBoundsEquals(10, 0, 20, 20, v2);
+  ExpectViewBoundsEquals(30, 0, 0, 20, v3);
 
   // Set a size limit.
   c1->set_linked_column_size_limit(40);
-  v1.SetPreferredSize(gfx::Size(35, 20));
+  v1->SetPreferredSize(gfx::Size(35, 20));
 
   // |v1| now dominates, but it is still below the limit.
   pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(35 + 35 + 35, 20), pref);
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 35, 20, &v1);
-  ExpectViewBoundsEquals(35, 0, 35, 20, &v2);
-  ExpectViewBoundsEquals(70, 0, 35, 20, &v3);
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 35, 20, v1);
+  ExpectViewBoundsEquals(35, 0, 35, 20, v2);
+  ExpectViewBoundsEquals(70, 0, 35, 20, v3);
 
   // Go over the limit. |v1| shouldn't influence size at all, but the others
   // should still be linked to the next largest width.
-  v1.SetPreferredSize(gfx::Size(45, 20));
+  v1->SetPreferredSize(gfx::Size(45, 20));
   pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(45 + 20 + 20, 20), pref);
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 45, 20, &v1);
-  ExpectViewBoundsEquals(45, 0, 20, 20, &v2);
-  ExpectViewBoundsEquals(65, 0, 20, 20, &v3);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 45, 20, v1);
+  ExpectViewBoundsEquals(45, 0, 20, 20, v2);
+  ExpectViewBoundsEquals(65, 0, 20, 20, v3);
 }
 
 TEST_F(GridLayoutTest, ColSpan1) {
-  View v1;
-  v1.SetPreferredSize(gfx::Size(100, 20));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(10, 40));
   ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1, 2, 1);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v2);
+  auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 40)));
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(100, 60), pref);
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
-  ExpectViewBoundsEquals(0, 20, 10, 40, &v2);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 100, 20, v1);
+  ExpectViewBoundsEquals(0, 20, 10, 40, v2);
 }
 
 TEST_F(GridLayoutTest, ColSpan2) {
-  View v1;
-  v1.SetPreferredSize(gfx::Size(100, 20));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(10, 20));
   ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1, 2, 1);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1);
   layout()->StartRow(0, 0);
   layout()->SkipColumns(1);
-  layout()->AddView(&v2);
+  auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(100, 40), pref);
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
-  ExpectViewBoundsEquals(90, 20, 10, 20, &v2);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 100, 20, v1);
+  ExpectViewBoundsEquals(90, 20, 10, 20, v2);
 }
 
 TEST_F(GridLayoutTest, ColSpan3) {
-  View v1;
-  v1.SetPreferredSize(gfx::Size(100, 20));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(10, 20));
-  View v3;
-  v3.SetPreferredSize(gfx::Size(10, 20));
   ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1, 2, 1);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v2);
-  layout()->AddView(&v3);
+  auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
+  auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(100, 40), pref);
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
-  ExpectViewBoundsEquals(0, 20, 10, 20, &v2);
-  ExpectViewBoundsEquals(50, 20, 10, 20, &v3);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 100, 20, v1);
+  ExpectViewBoundsEquals(0, 20, 10, 20, v2);
+  ExpectViewBoundsEquals(50, 20, 10, 20, v3);
 }
 
 
@@ -362,28 +331,20 @@
   set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
                  GridLayout::USE_PREF, 0, 0);
 
-  View v1;
-  v1.SetPreferredSize(gfx::Size(10, 10));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(10, 10));
-  View v3;
-  v3.SetPreferredSize(gfx::Size(25, 20));
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1);
-  layout()->AddView(&v2);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
+  auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
   layout()->StartRow(0, 0);
-  layout()->AddView(&v3, 2, 1);
+  auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(25, 20)), 2, 1);
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(25, 30), pref);
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 10, 10, &v1);
-  ExpectViewBoundsEquals(12, 0, 10, 10, &v2);
-  ExpectViewBoundsEquals(0, 10, 25, 20, &v3);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 10, 10, v1);
+  ExpectViewBoundsEquals(12, 0, 10, 10, v2);
+  ExpectViewBoundsEquals(0, 10, 25, 20, v3);
 }
 
 // Verifies the sizing of a view that doesn't start in the first column
@@ -398,31 +359,20 @@
   set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
                  GridLayout::FIXED, 10, 0);
 
-  View v1;
-  v1.SetPreferredSize(gfx::Size(10, 10));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(20, 10));
-
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1);
-  layout()->AddView(&v2, 2, 1);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
+  auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(20, 10)), 2, 1);
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(30, 10), pref);
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 10, 10, &v1);
-  ExpectViewBoundsEquals(10, 0, 20, 10, &v2);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 10, 10, v1);
+  ExpectViewBoundsEquals(10, 0, 20, 10, v2);
 }
 
 TEST_F(GridLayoutTest, SameSizeColumns) {
-  View v1;
-  v1.SetPreferredSize(gfx::Size(50, 20));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(10, 10));
   ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
@@ -430,73 +380,53 @@
                 0, GridLayout::USE_PREF, 0, 0);
   c1->LinkColumnSizes(0, 1, -1);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1);
-  layout()->AddView(&v2);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20)));
+  auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(100, 20), pref);
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 50, 20, &v1);
-  ExpectViewBoundsEquals(50, 0, 10, 10, &v2);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 50, 20, v1);
+  ExpectViewBoundsEquals(50, 0, 10, 10, v2);
 }
 
 TEST_F(GridLayoutTest, HorizontalResizeTest1) {
-  View v1;
-  v1.SetPreferredSize(gfx::Size(50, 20));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(10, 10));
   ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1);
-  layout()->AddView(&v2);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20)));
+  auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
 
-  host().SetBounds(0, 0, 110, 20);
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
-  ExpectViewBoundsEquals(100, 0, 10, 10, &v2);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, 110, 20);
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 100, 20, v1);
+  ExpectViewBoundsEquals(100, 0, 10, 10, v2);
 }
 
 TEST_F(GridLayoutTest, HorizontalResizeTest2) {
-  View v1;
-  v1.SetPreferredSize(gfx::Size(50, 20));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(10, 10));
   ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1);
-  layout()->AddView(&v2);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20)));
+  auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
 
-  host().SetBounds(0, 0, 120, 20);
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 80, 20, &v1);
-  ExpectViewBoundsEquals(110, 0, 10, 10, &v2);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, 120, 20);
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 80, 20, v1);
+  ExpectViewBoundsEquals(110, 0, 10, 10, v2);
 }
 
 // Tests that space leftover due to rounding is distributed to the last
 // resizable column.
 TEST_F(GridLayoutTest, HorizontalResizeTest3) {
-  View v1;
-  v1.SetPreferredSize(gfx::Size(10, 10));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(10, 10));
-  View v3;
-  v3.SetPreferredSize(gfx::Size(10, 10));
   ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
@@ -505,65 +435,53 @@
   c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1);
-  layout()->AddView(&v2);
-  layout()->AddView(&v3);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
+  auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
+  auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
 
-  host().SetBounds(0, 0, 31, 10);
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 10, 10, &v1);
-  ExpectViewBoundsEquals(10, 0, 11, 10, &v2);
-  ExpectViewBoundsEquals(21, 0, 10, 10, &v3);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, 31, 10);
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 10, 10, v1);
+  ExpectViewBoundsEquals(10, 0, 11, 10, v2);
+  ExpectViewBoundsEquals(21, 0, 10, 10, v3);
 }
 
 TEST_F(GridLayoutTest, TestVerticalResize1) {
-  View v1;
-  v1.SetPreferredSize(gfx::Size(50, 20));
-  View v2;
-  v2.SetPreferredSize(gfx::Size(10, 10));
   ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::FILL, GridLayout::FILL,
                 1, GridLayout::USE_PREF, 0, 0);
   layout()->StartRow(1, 0);
-  layout()->AddView(&v1);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20)));
   layout()->StartRow(0, 0);
-  layout()->AddView(&v2);
+  auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(50, 30), pref);
 
-  host().SetBounds(0, 0, 50, 100);
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(0, 0, 50, 90, &v1);
-  ExpectViewBoundsEquals(0, 90, 50, 10, &v2);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, 50, 100);
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(0, 0, 50, 90, v1);
+  ExpectViewBoundsEquals(0, 90, 50, 10, v2);
 }
 
 TEST_F(GridLayoutTest, Border) {
-  host().SetBorder(CreateEmptyBorder(1, 2, 3, 4));
-  View v1;
-  v1.SetPreferredSize(gfx::Size(10, 20));
+  host()->SetBorder(CreateEmptyBorder(1, 2, 3, 4));
   ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1);
+  auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(16, 24), pref);
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
-  ExpectViewBoundsEquals(2, 1, 10, 20, &v1);
-
-  RemoveAll();
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
+  ExpectViewBoundsEquals(2, 1, 10, 20, v1);
 }
 
 TEST_F(GridLayoutTest, FixedSize) {
-  host().SetBorder(CreateEmptyBorder(2, 2, 2, 2));
+  host()->SetBorder(CreateEmptyBorder(2, 2, 2, 2));
 
   ColumnSet* set = layout()->AddColumnSet(0);
 
@@ -584,9 +502,9 @@
       layout()->AddView(CreateSizedView(gfx::Size(kPrefWidth, kPrefHeight)));
   }
 
-  layout()->Layout(&host());
+  layout()->Layout(host());
 
-  auto i = host().children().cbegin();
+  auto i = host()->children().cbegin();
   for (size_t row = 0; row < kRowCount; ++row) {
     for (size_t column = 0; column < kColumnCount; ++column, ++i) {
       ExpectViewBoundsEquals(
@@ -635,14 +553,13 @@
   layout()->AddView(CreateSizedView(gfx::Size(20, 10)));
   layout()->AddView(CreateSizedView(gfx::Size(20, 40)), 1, 2);
   layout()->StartRow(1, 0);
-  View* s3 = CreateSizedView(gfx::Size(20, 10));
-  layout()->AddView(s3);
+  auto* s3 = layout()->AddView(CreateSizedView(gfx::Size(20, 10)));
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(40, 40), pref);
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
   ExpectViewBoundsEquals(0, 10, 20, 10, s3);
 }
 
@@ -656,8 +573,7 @@
 
   layout()->StartRow(0, 0);
   layout()->AddView(CreateSizedView(gfx::Size(20, 20)));
-  View* s3 = CreateSizedView(gfx::Size(64, 64));
-  layout()->AddView(s3, 1, 3);
+  auto* s3 = layout()->AddView(CreateSizedView(gfx::Size(64, 64)), 1, 3);
 
   layout()->AddPaddingRow(0, 10);
 
@@ -667,8 +583,8 @@
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(84, 64), pref);
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
   ExpectViewBoundsEquals(20, 0, 64, 64, s3);
 }
 
@@ -681,16 +597,16 @@
                  0,GridLayout::USE_PREF, 0, 0);
 
   layout()->StartRow(0, 0);
-  View* view = CreateSizedView(gfx::Size(30, 40));
-  layout()->AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 10,
-                    0);
+  auto* view =
+      layout()->AddView(CreateSizedView(gfx::Size(30, 40)), 1, 1,
+                        GridLayout::LEADING, GridLayout::LEADING, 10, 0);
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(10, pref.width());
   EXPECT_EQ(40, pref.height());
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
   ExpectViewBoundsEquals(0, 0, 10, 40, view);
 }
 
@@ -703,16 +619,16 @@
                  0,GridLayout::USE_PREF, 0, 0);
 
   layout()->StartRow(0, 0);
-  View* view = CreateSizedView(gfx::Size(30, 40));
-  layout()->AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 0,
-                    10);
+  auto* view =
+      layout()->AddView(CreateSizedView(gfx::Size(30, 40)), 1, 1,
+                        GridLayout::LEADING, GridLayout::LEADING, 0, 10);
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(30, pref.width());
   EXPECT_EQ(10, pref.height());
 
-  host().SetBounds(0, 0, pref.width(), pref.height());
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(host());
   ExpectViewBoundsEquals(0, 0, 30, 10, view);
 }
 
@@ -728,17 +644,15 @@
 
   layout()->StartRow(0, 0);
   // span_view spans two columns and is twice as big the views added below.
-  View* span_view = CreateSizedView(gfx::Size(12, 40));
-  layout()->AddView(span_view, 2, 1, GridLayout::LEADING, GridLayout::LEADING);
+  View* span_view = layout()->AddView(CreateSizedView(gfx::Size(12, 40)), 2, 1,
+                                      GridLayout::LEADING, GridLayout::LEADING);
 
   layout()->StartRow(0, 0);
-  View* view1 = CreateSizedView(gfx::Size(2, 40));
-  View* view2 = CreateSizedView(gfx::Size(4, 40));
-  layout()->AddView(view1);
-  layout()->AddView(view2);
+  auto* view1 = layout()->AddView(CreateSizedView(gfx::Size(2, 40)));
+  auto* view2 = layout()->AddView(CreateSizedView(gfx::Size(4, 40)));
 
-  host().SetBounds(0, 0, 12, 80);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 12, 80);
+  layout()->Layout(host());
 
   ExpectViewBoundsEquals(0, 0, 12, 40, span_view);
 
@@ -770,20 +684,20 @@
 
   // Make a row containing a flexible view that trades width for height.
   layout()->StartRow(0, 0);
-  View* view1 = new FlexibleView(100);
-  layout()->AddView(view1, 1, 1, GridLayout::FILL, GridLayout::LEADING);
+  layout()->AddView(std::make_unique<FlexibleView>(100), 1, 1, GridLayout::FILL,
+                    GridLayout::LEADING);
 
   // The second row contains a view of fixed size that will enforce a column
   // width of 20 pixels.
   layout()->StartRow(0, 1);
-  View* view2 = CreateSizedView(gfx::Size(20, 20));
-  layout()->AddView(view2, 1, 1, GridLayout::FILL, GridLayout::LEADING);
+  layout()->AddView(CreateSizedView(gfx::Size(20, 20)), 1, 1, GridLayout::FILL,
+                    GridLayout::LEADING);
 
   // Add another flexible view in row three in order to ensure column set
   // ordering doesn't influence sizing behaviour.
   layout()->StartRow(0, 2);
-  View* view3 = new FlexibleView(40);
-  layout()->AddView(view3, 1, 1, GridLayout::FILL, GridLayout::LEADING);
+  layout()->AddView(std::make_unique<FlexibleView>(40), 1, 1, GridLayout::FILL,
+                    GridLayout::LEADING);
 
   // We expect a height of 50: 30 from the variable width view in the first row
   // plus 20 from the statically sized view in the second row. The flexible
@@ -792,13 +706,11 @@
 }
 
 TEST_F(GridLayoutTest, MinimumPreferredSize) {
-  View v1;
-  v1.SetPreferredSize(gfx::Size(10, 20));
   ColumnSet* set = layout()->AddColumnSet(0);
   set->AddColumn(GridLayout::FILL, GridLayout::FILL,
                  0, GridLayout::USE_PREF, 0, 0);
   layout()->StartRow(0, 0);
-  layout()->AddView(&v1);
+  layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
 
   gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(10, 20), pref);
@@ -806,8 +718,6 @@
   layout()->set_minimum_size(gfx::Size(40, 40));
   pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(40, 40), pref);
-
-  RemoveAll();
 }
 
 // Test that attempting a Layout() while nested in AddView() causes a DCHECK.
@@ -818,17 +728,15 @@
   set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0,
                  0);
   layout()->StartRow(0, 0);
-  LayoutOnAddView view;
-  EXPECT_DCHECK_DEATH(layout()->AddView(&view));
+  auto view = std::make_unique<LayoutOnAddView>();
+  EXPECT_DCHECK_DEATH(layout()->AddView(std::move(view)));
   // Death tests use fork(), so nothing should be added here.
-  EXPECT_FALSE(view.parent());
+  EXPECT_FALSE(view->parent());
 
   // If the View has nothing to change, adding should succeed.
-  view.set_target_size(view.GetPreferredSize());
-  layout()->AddView(&view);
-  EXPECT_TRUE(view.parent());
-
-  RemoveAll();
+  view->set_target_size(view->GetPreferredSize());
+  auto* view_ptr = layout()->AddView(std::move(view));
+  EXPECT_TRUE(view_ptr->parent());
 }
 
 TEST_F(GridLayoutTest, ColumnMinForcesPreferredWidth) {
@@ -838,8 +746,7 @@
   set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0,
                  100);
   layout()->StartRow(0, 0);
-  View* view1 = CreateSizedView(gfx::Size(20, 10));
-  layout()->AddView(view1);
+  layout()->AddView(CreateSizedView(gfx::Size(20, 10)));
 
   EXPECT_EQ(gfx::Size(100, 10), GetPreferredSize());
 }
@@ -855,26 +762,25 @@
   set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0,
                  0);
   layout()->StartRow(0, 0);
-  View* view1 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(125, 10));
-  layout()->AddView(view1);
-
-  View* view2 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(50, 10));
-  layout()->AddView(view2);
+  View* view1 = layout()->AddView(
+      CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(125, 10)));
+  View* view2 = layout()->AddView(
+      CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(50, 10)));
 
   EXPECT_EQ(gfx::Size(175, 10), GetPreferredSize());
 
-  host().SetBounds(0, 0, 175, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 175, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 125, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(125, 0, 50, 10), view2->bounds());
 
-  host().SetBounds(0, 0, 125, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 125, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 100, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(100, 0, 25, 10), view2->bounds());
 
-  host().SetBounds(0, 0, 120, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 120, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 100, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(100, 0, 20, 10), view2->bounds());
 }
@@ -889,15 +795,13 @@
   set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0,
                  0);
   layout()->StartRow(0, 0);
-  View* view1 = CreateViewWithMinAndPref(gfx::Size(20, 10), gfx::Size(100, 10));
-  layout()->AddView(view1);
+  View* view1 = layout()->AddView(
+      CreateViewWithMinAndPref(gfx::Size(20, 10), gfx::Size(100, 10)));
+  View* view2 = layout()->AddView(
+      CreateViewWithMinAndPref(gfx::Size(100, 10), gfx::Size(100, 10)));
 
-  View* view2 =
-      CreateViewWithMinAndPref(gfx::Size(100, 10), gfx::Size(100, 10));
-  layout()->AddView(view2);
-
-  host().SetBounds(0, 0, 110, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 110, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 20, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(20, 0, 100, 10), view2->bounds());
 }
@@ -912,44 +816,43 @@
   set->AddColumn(GridLayout::FILL, GridLayout::FILL, 2, GridLayout::USE_PREF, 0,
                  0);
   layout()->StartRow(0, 0);
-  View* view1 = CreateViewWithMinAndPref(gfx::Size(91, 10), gfx::Size(100, 10));
-  layout()->AddView(view1);
-
-  View* view2 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10));
-  layout()->AddView(view2);
+  View* view1 = layout()->AddView(
+      CreateViewWithMinAndPref(gfx::Size(91, 10), gfx::Size(100, 10)));
+  View* view2 = layout()->AddView(
+      CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)));
 
   // 200 is the preferred, each should get their preferred width.
-  host().SetBounds(0, 0, 200, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 200, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 100, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(100, 0, 100, 10), view2->bounds());
 
   // 1 pixel smaller than pref.
-  host().SetBounds(0, 0, 199, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 199, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 99, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(99, 0, 100, 10), view2->bounds());
 
   // 10 pixels smaller than pref.
-  host().SetBounds(0, 0, 190, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 190, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 92, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(92, 0, 98, 10), view2->bounds());
 
   // 11 pixels smaller than pref.
-  host().SetBounds(0, 0, 189, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 189, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 91, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(91, 0, 98, 10), view2->bounds());
 
   // 12 pixels smaller than pref.
-  host().SetBounds(0, 0, 188, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 188, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 91, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(91, 0, 97, 10), view2->bounds());
 
-  host().SetBounds(0, 0, 5, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 5, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 91, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(91, 0, 10, 10), view2->bounds());
 }
@@ -962,19 +865,19 @@
   set->AddColumn(GridLayout::FILL, GridLayout::FILL, 2, GridLayout::FIXED, 100,
                  0);
   layout()->StartRow(0, 0);
-  View* view1 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10));
-  layout()->AddView(view1);
-  View* view2 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10));
-  layout()->AddView(view2);
+  View* view1 = layout()->AddView(
+      CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)));
+  View* view2 = layout()->AddView(
+      CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)));
 
-  host().SetBounds(0, 0, 120, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 120, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 20, 10), view1->bounds());
   // Even though column 2 has a resize percent, it's FIXED, so it won't shrink.
   EXPECT_EQ(gfx::Rect(20, 0, 100, 10), view2->bounds());
 
-  host().SetBounds(0, 0, 10, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 10, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 10, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(10, 0, 100, 10), view2->bounds());
 }
@@ -987,23 +890,24 @@
   set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0,
                  0);
   layout()->StartRow(0, 0);
-  View* view1 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10));
-  layout()->AddView(view1);
-  View* view2 = CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10));
-  layout()->AddView(view2, 1, 1, GridLayout::FILL, GridLayout::FILL, 50, 10);
+  View* view1 = layout()->AddView(
+      CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)));
+  View* view2 = layout()->AddView(
+      CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)), 1, 1,
+      GridLayout::FILL, GridLayout::FILL, 50, 10);
 
-  host().SetBounds(0, 0, 80, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 80, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 30, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(30, 0, 50, 10), view2->bounds());
 
-  host().SetBounds(0, 0, 70, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 70, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 20, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(20, 0, 50, 10), view2->bounds());
 
-  host().SetBounds(0, 0, 10, 0);
-  layout()->Layout(&host());
+  host()->SetBounds(0, 0, 10, 0);
+  layout()->Layout(host());
   EXPECT_EQ(gfx::Rect(0, 0, 10, 10), view1->bounds());
   EXPECT_EQ(gfx::Rect(10, 0, 50, 10), view2->bounds());
 }
@@ -1028,15 +932,13 @@
                  0, 0);
   layout()->StartRow(0, 0);
   const int pref_height = 100;
-  // |view| is owned by parent.
-  SettablePreferredHeightView* view =
-      new SettablePreferredHeightView(pref_height);
+  auto view = std::make_unique<SettablePreferredHeightView>(pref_height);
   const gfx::Size pref(10, 20);
   view->SetPreferredSize(pref);
-  layout()->AddView(view);
+  layout()->AddView(std::move(view));
 
   EXPECT_EQ(pref, GetPreferredSize());
-  EXPECT_EQ(pref_height, host().GetHeightForWidth(5));
+  EXPECT_EQ(pref_height, host()->GetHeightForWidth(5));
 }
 
 }  // namespace views
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc
index 2c4fc63..f78181dc 100644
--- a/ui/views/window/dialog_client_view.cc
+++ b/ui/views/window/dialog_client_view.cc
@@ -449,7 +449,7 @@
   if (extra_view_)
     return;
 
-  extra_view_ = GetDialogDelegate()->CreateExtraView();
+  extra_view_ = GetDialogDelegate()->CreateExtraView().release();
   if (extra_view_ && Button::AsButton(extra_view_))
     extra_view_->SetGroup(kButtonGroup);
 }
diff --git a/ui/views/window/dialog_client_view_unittest.cc b/ui/views/window/dialog_client_view_unittest.cc
index a7be998..071bb2a 100644
--- a/ui/views/window/dialog_client_view_unittest.cc
+++ b/ui/views/window/dialog_client_view_unittest.cc
@@ -68,7 +68,9 @@
     // DialogDelegateView would delete this, but |this| is owned by the test.
   }
 
-  View* CreateExtraView() override { return next_extra_view_.release(); }
+  std::unique_ptr<View> CreateExtraView() override {
+    return std::move(next_extra_view_);
+  }
 
   bool GetExtraViewPadding(int* padding) override {
     if (extra_view_padding_)
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index badb55d..54dacf74f 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -122,7 +122,7 @@
   return true;
 }
 
-View* DialogDelegate::CreateExtraView() {
+std::unique_ptr<View> DialogDelegate::CreateExtraView() {
   return nullptr;
 }
 
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index 253777d..67fbab6a 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -5,6 +5,8 @@
 #ifndef UI_VIEWS_WINDOW_DIALOG_DELEGATE_H_
 #define UI_VIEWS_WINDOW_DIALOG_DELEGATE_H_
 
+#include <memory>
+
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
@@ -68,7 +70,7 @@
 
   // Override this function to display an extra view adjacent to the buttons.
   // Overrides may construct the view; this will only be called once per dialog.
-  virtual View* CreateExtraView();
+  virtual std::unique_ptr<View> CreateExtraView();
 
   // Override this function to adjust the padding between the extra view and
   // the confirm/cancel buttons. Note that if there are no buttons, this will