diff --git a/DEPS b/DEPS
index 09c08daf..466da64 100644
--- a/DEPS
+++ b/DEPS
@@ -128,7 +128,7 @@
   'checkout_simplechrome': '(checkout_chromeos and host_os == "linux") and ("{cros_board}" != "")',
   # Surround the board var in quotes so gclient doesn't try parsing the string
   # as an expression.
-  'cros_download_vm': '("{cros_board}" == "amd64-generic") or ("{cros_board}" == "betty") or ("{cros_board}" == "betty-pi-arc")',
+  'cros_download_vm': '("{cros_board}" == "amd64-generic") or ("{cros_board}" == "betty")',
   # Should we build and test for public (ie: full) CrOS images, or private
   # (ie: release) images.
   'use_public_cros_config': 'not checkout_src_internal',
@@ -162,27 +162,27 @@
   # 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': 'e7bd1cb4f6505ce57a2a08cdde99bccdbabb694b',
+  'skia_revision': '4dd7c01ca87e97eec0820eb39313bbe7de4dfbd3',
   # 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': '246cf2b1bea18b4edd54bd8a6118679fbacf95c3',
+  'v8_revision': 'd0581808098980bc6cb0ab62a361432267e68bad',
   # 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.
-  'swarming_revision': '96f125709acfd0b48fc1e5dae7d6ea42291726ac',
+  'swarming_revision': '885b3febcc170a60f25795304e60927b77d1e92d',
   # 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': '341482072a1909e6d4aa25e40954cb5dcdddc208',
+  'angle_revision': 'a905cbcdc21b5a09bb0bb1d5ef557c260378b2eb',
   # 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': '1f89f58faeb8287f29e3e5ba7d03d62f667404a0',
+  'swiftshader_revision': 'e3a5983705da24448382bc696b0663dc5d0a3ed7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'bf5be75f420ce3882089ba05a01f93ad43284db2',
+  'pdfium_revision': 'c171a2106b2b70c38f7cca7c26da9b67c413d05d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -225,7 +225,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': '5073a9e8d71d0f12f10635e6787b36a9bd21e04c',
+  'catapult_revision': '28837bfd862a41e35946d1c76a05ad242638fa78',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -241,7 +241,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.
-  'feed_revision': '4e6265827406b37da706e1e404cac5f99ae2854e',
+  'feed_revision': 'c25c8f41145ed610d1a73004e8bcd62d8e8111c1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_build-tools_version
   # and whatever else without interference from each other.
@@ -281,7 +281,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '605c2e3c0ea7cab09c6cc3bc2a48030ba78d3a03',
+  'spv_tools_revision': '3a762d54f649c756e6b4c25957d11bd0a9d71c1c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -293,11 +293,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'shaderc_revision': 'cb8b3fc5caf99c54fba88ac3c0a45ed6e50c0f43',
+  'shaderc_revision': 'c5b0621faea0cf8df848365edd002d4b19b1a740',
   # 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': 'd46cabd73fb4b06bc5a18ce56b2803bb4bb64d21',
+  'dawn_revision': '27a35eb2a93800e786c2d490e66c5e6c8dc77048',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -853,7 +853,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '16f6f6e4dd634dee420412e55bff13352e96bdad',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '62ba8cb7666f8c3ad0834b9ada6f34685a6bfc31',
       'condition': 'checkout_linux',
   },
 
@@ -878,7 +878,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b5578f4f0b22e51f6c2bf59c93edd4d2defb63f0',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6f18e68719fc02f9e08185e8c27a583deca2d1e5',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1146,7 +1146,7 @@
   },
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'c094391e954aa274b9dcce3d6afcb5ba6bae7eff',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'b8d86733e9d9c58e17028720751f96dad2df7a09',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + '51ca718c3adf0ddedacd7df25fe45f67dc5a9ce1',
@@ -1257,7 +1257,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'eb06d51adc82ace982e414969ab18cd35f85344c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '55783c1d04037e95075acbed55abb2968a5c5819',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1425,7 +1425,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7c4e67ff117d6c640e6dd17989afe2fb7da7eecb',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '3c5f91b74871ba0c716ab9650e9779064aa864ff',
+    Var('webrtc_git') + '/src.git' + '@' + 'f7457e55fe3d883bc7daa784f0c38ea76f82b873',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1487,7 +1487,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b3c3754a9c0fe3c9c4f09625e1c28c08a343d52d',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ce9b13eb99e18ea1aad83017f113a1fd814d31c3',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/state_serializer.cc b/android_webview/browser/state_serializer.cc
index 08ab52e..ef9fd9d 100644
--- a/android_webview/browser/state_serializer.cc
+++ b/android_webview/browser/state_serializer.cc
@@ -187,11 +187,22 @@
                                       base::PickleIterator* iterator,
                                       content::NavigationEntry* entry) {
   DCHECK(IsSupportedVersion(state_version));
+
+  GURL deserialized_url;
   {
     string url;
     if (!iterator->ReadString(&url))
       return false;
-    entry->SetURL(GURL(url));
+    deserialized_url = GURL(url);
+
+    // Note: The url will be cloberred by the SetPageState call below (see how
+    // RecursivelyGenerateFrameEntries uses PageState data to create
+    // FrameNavigationEntries).
+    //
+    // Nevertheless, we call SetURL here to temporarily set the URL, because it
+    // modifies the state that might be depended on in some calls below (e.g.
+    // the SetVirtualURL call).
+    entry->SetURL(deserialized_url);
   }
 
   {
@@ -201,8 +212,11 @@
     entry->SetVirtualURL(GURL(virtual_url));
   }
 
+  content::Referrer deserialized_referrer;
   {
-    content::Referrer referrer;
+    // Note: The referrer will be cloberred by the SetPageState call below (see
+    // how RecursivelyGenerateFrameEntries uses PageState data to create
+    // FrameNavigationEntries).
     string referrer_url;
     int policy;
 
@@ -211,9 +225,9 @@
     if (!iterator->ReadInt(&policy))
       return false;
 
-    referrer.url = GURL(referrer_url);
-    referrer.policy = static_cast<network::mojom::ReferrerPolicy>(policy);
-    entry->SetReferrer(referrer);
+    deserialized_referrer.url = GURL(referrer_url);
+    deserialized_referrer.policy =
+        static_cast<network::mojom::ReferrerPolicy>(policy);
   }
 
   {
@@ -227,8 +241,37 @@
     string content_state;
     if (!iterator->ReadString(&content_state))
       return false;
-    entry->SetPageState(
-        content::PageState::CreateFromEncodedData(content_state));
+
+    // In legacy output of WebViewProvider.saveState, the |content_state|
+    // might be empty - we need to gracefully handle such data when
+    // it is deserialized via WebViewProvider.restoreState.
+    if (content_state.empty()) {
+      // Ensure that the deserialized/restored content::NavigationEntry (and
+      // the content::FrameNavigationEntry underneath) has a valid PageState.
+      entry->SetPageState(content::PageState::CreateFromURL(deserialized_url));
+
+      // The |deserialized_referrer| might be inconsistent with the referrer
+      // embedded inside the PageState set above.  Nevertheless, to minimize
+      // changes to behavior of old session restore entries, we restore the
+      // deserialized referrer here.
+      //
+      // TODO(lukasza): Consider including the |deserialized_referrer| in the
+      // PageState set above + drop the SetReferrer call below.  This will
+      // slightly change the legacy behavior, but will make PageState and
+      // Referrer consistent.
+      entry->SetReferrer(deserialized_referrer);
+    } else {
+      // Note that PageState covers and will clobber some of the values covered
+      // by data within |iterator| (e.g. URL and referrer).
+      entry->SetPageState(
+          content::PageState::CreateFromEncodedData(content_state));
+
+      // |deserialized_url| and |deserialized_referrer| are redundant wrt
+      // PageState, but they should be consistent / in-sync.
+      DCHECK_EQ(deserialized_url, entry->GetURL());
+      DCHECK_EQ(deserialized_referrer.url, entry->GetReferrer().url);
+      DCHECK_EQ(deserialized_referrer.policy, entry->GetReferrer().policy);
+    }
   }
 
   {
diff --git a/android_webview/browser/state_serializer_unittest.cc b/android_webview/browser/state_serializer_unittest.cc
index 5cdf3b6..062147a 100644
--- a/android_webview/browser/state_serializer_unittest.cc
+++ b/android_webview/browser/state_serializer_unittest.cc
@@ -14,6 +14,7 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/page_state.h"
+#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -33,8 +34,6 @@
   referrer.url = GURL("http://referrer_url");
   referrer.policy = network::mojom::ReferrerPolicy::kOrigin;
   const base::string16 title(base::UTF8ToUTF16("title"));
-  const content::PageState page_state =
-      content::PageState::CreateFromEncodedData("completely bogus state");
   const bool has_post_data = true;
   const GURL original_request_url("http://original_request_url");
   const GURL base_url_for_data_url("http://base_url");
@@ -47,7 +46,6 @@
   entry->SetVirtualURL(virtual_url);
   entry->SetReferrer(referrer);
   entry->SetTitle(title);
-  entry->SetPageState(page_state);
   entry->SetHasPostData(has_post_data);
   entry->SetOriginalRequestURL(original_request_url);
   entry->SetBaseURLForDataURL(base_url_for_data_url);
@@ -174,6 +172,76 @@
   EXPECT_EQ(entry->GetHttpStatusCode(), copy->GetHttpStatusCode());
 }
 
+// This is a regression test for https://crbug.com/999078 - it checks that code
+// is able to safely restore entries that were serialized with an empty
+// PageState.
+TEST_F(AndroidWebViewStateSerializerTest,
+       TestDeserialization_20151204_EmptyPageState) {
+  // Test data.
+  GURL url("data:text/html,main_url");
+  GURL virtual_url("https://example.com/virtual_url");
+  content::Referrer referrer(GURL("https://example.com/referrer"),
+                             network::mojom::ReferrerPolicy::kDefault);
+  base::string16 title = base::UTF8ToUTF16("title");
+  std::string empty_encoded_page_state = "";
+  bool has_post_data = false;
+  GURL original_request_url("https://example.com/original");
+  GURL base_url_for_data_url("https://example.com/base_url_for_data_url");
+  bool is_overriding_user_agent = false;
+  int64_t timestamp = 123456;
+  int http_status_code = 404;
+
+  // Write data to |pickle| in a way that would trigger https://crbug.com/999078
+  // in the past.  The serialization format used below is based on version
+  // 20151204 (aka AW_STATE_VERSION_DATA_URL).
+  base::Pickle pickle;
+  pickle.WriteString(url.spec());
+  pickle.WriteString(virtual_url.spec());
+  pickle.WriteString(referrer.url.spec());
+  pickle.WriteInt(static_cast<int>(referrer.policy));
+  pickle.WriteString16(title);
+  pickle.WriteString(empty_encoded_page_state);
+  pickle.WriteBool(has_post_data);
+  pickle.WriteString(original_request_url.spec());
+  pickle.WriteString(base_url_for_data_url.spec());
+  pickle.WriteData(nullptr, 0);  // data_url_as_string
+  pickle.WriteBool(is_overriding_user_agent);
+  pickle.WriteInt64(timestamp);
+  pickle.WriteInt(http_status_code);
+
+  // Deserialize the |pickle|.
+  base::PickleIterator iterator(pickle);
+  std::unique_ptr<content::NavigationEntry> copy =
+      content::NavigationEntry::Create();
+  bool result =
+      internal::RestoreNavigationEntryFromPickle(&iterator, copy.get());
+  EXPECT_TRUE(result);
+
+  // In https://crbug.com/999078, the empty PageState would clobber the URL
+  // leading to renderer-side CHECKs later on.  Code should replace the empty
+  // PageState from the |pickle| with a real PageState that preserves the URL.
+  // Additionally, the referrer needs to be restored in the NavigationEntry
+  // (but not necessarily in the PageState - this preserves old behavior).
+  EXPECT_EQ(url, copy->GetURL());
+  EXPECT_FALSE(copy->GetPageState().ToEncodedData().empty());
+  EXPECT_EQ(referrer.url, copy->GetReferrer().url);
+  EXPECT_EQ(referrer.policy, copy->GetReferrer().policy);
+
+  // Verify that other properties have deserialized as expected.
+  EXPECT_EQ(virtual_url, copy->GetVirtualURL());
+  EXPECT_EQ(referrer.policy, copy->GetReferrer().policy);
+  EXPECT_EQ(title, copy->GetTitle());
+  EXPECT_EQ(has_post_data, copy->GetHasPostData());
+  EXPECT_EQ(original_request_url, copy->GetOriginalRequestURL());
+  EXPECT_EQ(base_url_for_data_url, copy->GetBaseURLForDataURL());
+  EXPECT_FALSE(copy->GetDataURLAsString());
+  EXPECT_EQ(is_overriding_user_agent, copy->GetIsOverridingUserAgent());
+  EXPECT_EQ(base::Time::FromDeltaSinceWindowsEpoch(
+                base::TimeDelta::FromMicroseconds(timestamp)),
+            copy->GetTimestamp());
+  EXPECT_EQ(http_status_code, copy->GetHttpStatusCode());
+}
+
 TEST_F(AndroidWebViewStateSerializerTest, TestEmptyDataURLSerialization) {
   std::unique_ptr<content::NavigationEntry> entry(
       content::NavigationEntry::Create());
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 4fb95e6d..bb7059f 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -1254,8 +1254,7 @@
 
   // Skip adjacent same changes.
   if (last_visible_ == real_visibility &&
-      (display::kInvalidDisplayId == display_id ||
-       last_visible_display_id_ == display_id)) {
+      last_visible_display_id_ == display_id) {
     return;
   }
 
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 201c6a8..861f0d2 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -1562,7 +1562,6 @@
 
   base::RunLoop().RunUntilIdle();
   GetAppListTestHelper()->CheckState(AppListViewState::kClosed);
-  LOG(ERROR) << "AHA";
   EXPECT_FALSE(GetAppListView()->is_in_drag());
 
   // Show the app list and verify the app list returns to peeking position.
@@ -1958,6 +1957,33 @@
            config.grid_fadeout_zone_height();
   }
 
+  // Calculates expected apps grid position on the search results page based on
+  // the display height and the search box in-screen bounds.
+  int ExpectedAppsGridTopForSearchResults(const app_list::AppListConfig& config,
+                                          int display_height,
+                                          const gfx::Rect& search_box_bounds) {
+    const int top =
+        ExpectedAppsGridTop(config, display_height, search_box_bounds);
+    // In the search results page, the apps grid is shown 24 dip below where
+    // they'd be shown in the apps page. The |top| was calculated relative to
+    // search box bounds in the search results page, so it has to be further
+    // offset by the difference between search box bottom bounds in the apps and
+    // search results page.
+    const int search_box_diff =
+        contents_view()
+            ->GetSearchBoxBounds(ash::AppListState::kStateApps)
+            .bottom() -
+        contents_view()
+            ->GetSearchBoxBounds(ash::AppListState::kStateSearchResults)
+            .bottom();
+    return top + search_box_diff +
+           24 /*apps grid offset in fullscreen search state*/;
+  }
+
+  app_list::ContentsView* contents_view() {
+    return GetAppListView()->app_list_main_view()->contents_view();
+  }
+
   app_list::AppsGridView* apps_grid_view() {
     return GetAppListView()
         ->app_list_main_view()
@@ -2026,6 +2052,7 @@
             search_box_bounds.y());
   EXPECT_EQ(ExpectedAppsGridTop(config, 900, search_box_bounds),
             apps_grid_view()->GetBoundsInScreen().y());
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
   // In apps state, search results page should be hidden behind the search
   // box.
   EXPECT_EQ(search_box_bounds, search_result_page()->GetBoundsInScreen());
@@ -2044,6 +2071,7 @@
   EXPECT_EQ(fullscreen_search_box_padding, search_box_bounds.y());
   EXPECT_EQ(ExpectedAppsGridTop(config, 900, search_box_bounds),
             apps_grid_view()->GetBoundsInScreen().y());
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
   EXPECT_EQ(search_box_bounds, search_result_page()->GetBoundsInScreen());
 
   // Move half way between peeking and closed state - the search box padding
@@ -2067,6 +2095,7 @@
             search_box_bounds.y());
   EXPECT_EQ(ExpectedAppsGridTop(config, 900, search_box_bounds),
             apps_grid_view()->GetBoundsInScreen().y());
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
   EXPECT_EQ(search_box_bounds, search_result_page()->GetBoundsInScreen());
 
   // Move to the closed state height, and verify the search box padding matches
@@ -2083,6 +2112,7 @@
             search_box_bounds.y());
   EXPECT_EQ(ExpectedAppsGridTop(config, 900, search_box_bounds),
             apps_grid_view()->GetBoundsInScreen().y());
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
   EXPECT_EQ(search_box_bounds, search_result_page()->GetBoundsInScreen());
 }
 
@@ -2136,8 +2166,15 @@
             search_result_page()->GetBoundsInScreen().y());
   EXPECT_EQ(search_results_height,
             search_result_page()->GetBoundsInScreen().height());
-  // Apps grid should be off screen.
-  EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  if (ScalableAppListEnabled()) {
+    EXPECT_EQ(
+        ExpectedAppsGridTopForSearchResults(config, 900, search_box_bounds),
+        apps_grid_view()->GetBoundsInScreen().y());
+  } else {
+    // Apps grid should be off screen.
+    EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  }
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
 
   // Move to the fullscreen position, and verify the search box padding is
   // equal to the expected fullscreen value.
@@ -2154,8 +2191,15 @@
             search_result_page()->GetBoundsInScreen().y());
   EXPECT_EQ(search_results_height,
             search_result_page()->GetBoundsInScreen().height());
-  // Apps grid should be off screen.
-  EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  if (ScalableAppListEnabled()) {
+    EXPECT_EQ(
+        ExpectedAppsGridTopForSearchResults(config, 900, search_box_bounds),
+        apps_grid_view()->GetBoundsInScreen().y());
+  } else {
+    // Apps grid should be off screen.
+    EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  }
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
 
   // Move half way between peeking and closed state - the search box padding
   // should be half distance between closed and peeking padding.
@@ -2178,8 +2222,15 @@
             search_result_page()->GetBoundsInScreen().y());
   EXPECT_EQ(search_results_height,
             search_result_page()->GetBoundsInScreen().height());
-  // Apps grid should be off screen.
-  EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  if (ScalableAppListEnabled()) {
+    EXPECT_EQ(
+        ExpectedAppsGridTopForSearchResults(config, 900, search_box_bounds),
+        apps_grid_view()->GetBoundsInScreen().y());
+  } else {
+    // Apps grid should be off screen.
+    EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  }
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
 
   // Move to the closed state height, and verify the search box padding matches
   // the state.
@@ -2197,8 +2248,15 @@
             search_result_page()->GetBoundsInScreen().y());
   EXPECT_EQ(search_results_height,
             search_result_page()->GetBoundsInScreen().height());
-  // Apps grid should be off screen.
-  EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  if (ScalableAppListEnabled()) {
+    EXPECT_EQ(
+        ExpectedAppsGridTopForSearchResults(config, 900, search_box_bounds),
+        apps_grid_view()->GetBoundsInScreen().y());
+  } else {
+    // Apps grid should be off screen.
+    EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  }
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
 }
 
 // Tests changing the active app list page while drag is in progress.
@@ -2245,9 +2303,18 @@
             search_result_page()->GetBoundsInScreen().y());
   EXPECT_EQ(search_results_height,
             search_result_page()->GetBoundsInScreen().height());
-  // Apps grid should be off screen.
-  EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  if (ScalableAppListEnabled()) {
+    EXPECT_EQ(
+        ExpectedAppsGridTopForSearchResults(config, 900, search_box_bounds),
+        apps_grid_view()->GetBoundsInScreen().y());
+  } else {
+    // Apps grid should be off screen.
+    EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  }
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
 
+  const gfx::Rect apps_grid_bounds_in_results_page =
+      apps_grid_view()->GetBoundsInScreen();
   const gfx::Rect app_list_bounds = GetAppListView()->GetBoundsInScreen();
 
   // Press ESC key - this should move the UI back to the app list.
@@ -2271,6 +2338,13 @@
   EXPECT_EQ(expected_search_box_top, search_box_bounds.y());
   EXPECT_EQ(ExpectedAppsGridTop(config, 900, search_box_bounds),
             apps_grid_view()->GetBoundsInScreen().y());
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
+  if (ScalableAppListEnabled()) {
+    EXPECT_EQ(apps_grid_bounds_in_results_page.y() - 24,
+              apps_grid_view()->GetBoundsInScreen().y());
+  }
+  EXPECT_EQ(apps_grid_bounds_in_results_page.size(),
+            apps_grid_view()->GetBoundsInScreen().size());
   EXPECT_EQ(search_box_bounds, search_result_page()->GetBoundsInScreen());
 
   // Enter text in the search box to transition back to search results page.
@@ -2289,8 +2363,15 @@
             search_result_page()->GetBoundsInScreen().y());
   EXPECT_EQ(search_results_height,
             search_result_page()->GetBoundsInScreen().height());
-  // Apps grid should be off screen.
-  EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  if (ScalableAppListEnabled()) {
+    EXPECT_EQ(
+        ExpectedAppsGridTopForSearchResults(config, 900, search_box_bounds),
+        apps_grid_view()->GetBoundsInScreen().y());
+  } else {
+    // Apps grid should be off screen.
+    EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  }
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
 }
 
 // Tests changing the active app list page in fullscreen state.
@@ -2315,6 +2396,7 @@
             search_box_bounds.y());
   EXPECT_EQ(ExpectedAppsGridTop(config, 900, search_box_bounds),
             apps_grid_view()->GetBoundsInScreen().y());
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
   EXPECT_EQ(search_box_bounds, search_result_page()->GetBoundsInScreen());
 
   const gfx::Rect app_list_bounds = GetAppListView()->GetBoundsInScreen();
@@ -2334,8 +2416,17 @@
             search_result_page()->GetBoundsInScreen().y());
   EXPECT_EQ(search_results_height,
             search_result_page()->GetBoundsInScreen().height());
-  // Apps grid should be off screen.
-  EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  if (ScalableAppListEnabled()) {
+    EXPECT_EQ(
+        ExpectedAppsGridTopForSearchResults(config, 900, search_box_bounds),
+        apps_grid_view()->GetBoundsInScreen().y());
+  } else {
+    // Apps grid should be off screen.
+    EXPECT_GT(apps_grid_view()->GetBoundsInScreen().y(), 900);
+  }
+  EXPECT_EQ(ScalableAppListEnabled(), apps_grid_view()->GetVisible());
+  const gfx::Rect apps_grid_bounds_in_results_page =
+      apps_grid_view()->GetBoundsInScreen();
 
   // Press ESC key - this should move the UI back to the app list.
   generator->PressKey(ui::KeyboardCode::VKEY_ESCAPE, 0);
@@ -2349,6 +2440,13 @@
             search_box_bounds.y());
   EXPECT_EQ(ExpectedAppsGridTop(config, 900, search_box_bounds),
             apps_grid_view()->GetBoundsInScreen().y());
+  EXPECT_TRUE(apps_grid_view()->GetVisible());
+  if (ScalableAppListEnabled()) {
+    EXPECT_EQ(apps_grid_bounds_in_results_page.y() - 24,
+              apps_grid_view()->GetBoundsInScreen().y());
+  }
+  EXPECT_EQ(apps_grid_bounds_in_results_page.size(),
+            apps_grid_view()->GetBoundsInScreen().size());
   EXPECT_EQ(search_box_bounds, search_result_page()->GetBoundsInScreen());
 }
 
diff --git a/ash/app_list/views/app_list_page.cc b/ash/app_list/views/app_list_page.cc
index 0af41f77..34ca386 100644
--- a/ash/app_list/views/app_list_page.cc
+++ b/ash/app_list/views/app_list_page.cc
@@ -34,6 +34,8 @@
   return base::nullopt;
 }
 
+void AppListPage::UpdateOpacityForState(ash::AppListState state) {}
+
 void AppListPage::UpdatePageBoundsForState(ash::AppListState state,
                                            const gfx::Rect& contents_bounds,
                                            const gfx::Rect& search_box_bounds) {
diff --git a/ash/app_list/views/app_list_page.h b/ash/app_list/views/app_list_page.h
index a60dfb9..ec698540 100644
--- a/ash/app_list/views/app_list_page.h
+++ b/ash/app_list/views/app_list_page.h
@@ -75,6 +75,13 @@
       const gfx::Rect& contents_bounds,
       const gfx::Rect& search_box_bounds) const = 0;
 
+  // Should update the app list page opacity for the current state. Called when
+  // the selected page changes without animation - if the page implements this,
+  // it should make sure the page transition animation updates the opacity as
+  // well.
+  // Default implementation is no-op.
+  virtual void UpdateOpacityForState(ash::AppListState state);
+
   // Convenience method that sets the page bounds to the bounds returned by
   // GetPageBoundsForState().
   void UpdatePageBoundsForState(ash::AppListState state,
diff --git a/ash/app_list/views/apps_container_view.cc b/ash/app_list/views/apps_container_view.cc
index 8e91c80..dc0e43f 100644
--- a/ash/app_list/views/apps_container_view.cc
+++ b/ash/app_list/views/apps_container_view.cc
@@ -202,10 +202,13 @@
     bool is_in_drag) {
   if (app_list_state == ash::AppListViewState::kClosed)
     return;
+
   apps_grid_view_->UpdateControlVisibility(app_list_state, is_in_drag);
-  page_switcher_->SetVisible(app_list_state ==
-                                 ash::AppListViewState::kFullscreenAllApps ||
-                             is_in_drag);
+  page_switcher_->SetVisible(
+      is_in_drag ||
+      app_list_state == ash::AppListViewState::kFullscreenAllApps ||
+      (app_list_features::IsScalableAppListEnabled() &&
+       app_list_state == ash::AppListViewState::kFullscreenSearch));
 
   // Ignore button press during dragging to avoid app list item views' opacity
   // being set to wrong value.
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index f96205a..b237468 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -1022,9 +1022,12 @@
     }
   }
 
-  const bool fullscreen_apps_in_drag =
-      app_list_state == ash::AppListViewState::kFullscreenAllApps || is_in_drag;
-  SetVisible(fullscreen_apps_in_drag);
+  const bool fullscreen_or_in_drag =
+      is_in_drag ||
+      app_list_state == ash::AppListViewState::kFullscreenAllApps ||
+      (app_list_features::IsScalableAppListEnabled() &&
+       app_list_state == ash::AppListViewState::kFullscreenSearch);
+  SetVisible(fullscreen_or_in_drag);
 }
 
 bool AppsGridView::OnKeyPressed(const ui::KeyEvent& event) {
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index c176818e..9f673bb 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -620,8 +620,10 @@
   gfx::Rect search_box_bounds = GetSearchBoxBounds(current_state);
 
   // Update app list pages.
-  for (AppListPage* page : app_list_pages_)
+  for (AppListPage* page : app_list_pages_) {
     page->UpdatePageBoundsForState(current_state, rect, search_box_bounds);
+    page->UpdateOpacityForState(current_state);
+  }
 
   UpdateExpandArrowOpacity(current_state, false);
 
@@ -737,8 +739,17 @@
                                    search_box_bounds);
   }
 
-  if (current_state == ash::AppListState::kStateApps) {
-    GetAppsContainerView()->UpdateYPositionAndOpacity(progress,
+  if (app_list_features::IsScalableAppListEnabled() ||
+      current_state == ash::AppListState::kStateApps) {
+    // Layout the apps container at the position where it would be with apps
+    // page active with the current app list height - use apps state app list
+    // progress to aciheve that.
+    const float apps_container_progress =
+        app_list_view_->is_in_drag()
+            ? app_list_view_->GetAppListTransitionProgress(
+                  AppListView::kProgressFlagNone)
+            : progress;
+    GetAppsContainerView()->UpdateYPositionAndOpacity(apps_container_progress,
                                                       restore_opacity);
   }
 }
@@ -896,18 +907,20 @@
 bool ContentsView::ShouldLayoutPage(AppListPage* page,
                                     ash::AppListState current_state,
                                     ash::AppListState target_state) const {
-  if (page == horizontal_page_container_) {
-    return (current_state == ash::AppListState::kStateSearchResults &&
-            target_state == ash::AppListState::kStateApps);
-  }
-
-  if (page == search_results_page_view_) {
+  if ((page == horizontal_page_container_ &&
+       app_list_features::IsScalableAppListEnabled()) ||
+      page == search_results_page_view_) {
     return ((current_state == ash::AppListState::kStateSearchResults &&
              target_state == ash::AppListState::kStateApps) ||
             (current_state == ash::AppListState::kStateApps &&
              target_state == ash::AppListState::kStateSearchResults));
   }
 
+  if (page == horizontal_page_container_) {
+    return (current_state == ash::AppListState::kStateSearchResults &&
+            target_state == ash::AppListState::kStateApps);
+  }
+
   if (page == assistant_page_view_) {
     return current_state == ash::AppListState::kStateEmbeddedAssistant ||
            target_state == ash::AppListState::kStateEmbeddedAssistant;
diff --git a/ash/app_list/views/horizontal_page_container.cc b/ash/app_list/views/horizontal_page_container.cc
index 6e1ea43..9debdca 100644
--- a/ash/app_list/views/horizontal_page_container.cc
+++ b/ash/app_list/views/horizontal_page_container.cc
@@ -11,6 +11,7 @@
 #include "ash/app_list/views/search_box_view.h"
 #include "ash/app_list/views/search_result_page_view.h"
 #include "ash/public/cpp/app_list/app_list_config.h"
+#include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/pagination/pagination_controller.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/chromeos/search_box/search_box_constants.h"
@@ -19,6 +20,17 @@
 
 namespace app_list {
 
+namespace {
+
+// The amount by which the apps container UI should be offset downwards when
+// shown on non apps page UI.
+constexpr int kNonAppsStateVerticalOffset = 24;
+
+// The opacity the apps container UI should have when shown on non apps page UI.
+constexpr float kNonAppsStateOpacity = 0.1;
+
+}  // namespace
+
 HorizontalPageContainer::HorizontalPageContainer(ContentsView* contents_view,
                                                  AppListModel* model)
     : contents_view_(contents_view) {
@@ -88,9 +100,23 @@
   const gfx::Rect to_rect =
       GetPageBoundsForState(to_state, contents_bounds, gfx::Rect());
   if (from_rect != to_rect) {
-    SetBoundsRect(from_rect);
-    auto settings = contents_view()->CreateTransitionAnimationSettings(layer());
+    DCHECK_EQ(from_rect.size(), to_rect.size());
+    DCHECK_EQ(from_rect.x(), to_rect.x());
+
     SetBoundsRect(to_rect);
+
+    gfx::Transform initial_transform;
+    initial_transform.Translate(0, from_rect.y() - to_rect.y());
+    layer()->SetTransform(initial_transform);
+
+    auto settings = contents_view()->CreateTransitionAnimationSettings(layer());
+    layer()->SetTransform(gfx::Transform());
+  }
+
+  // Set the page opacity.
+  if (app_list_features::IsScalableAppListEnabled()) {
+    auto settings = contents_view()->CreateTransitionAnimationSettings(layer());
+    UpdateOpacityForState(to_state);
   }
 
   for (size_t i = 0; i < horizontal_pages_.size(); ++i) {
@@ -115,7 +141,19 @@
     const gfx::Rect& search_box_bounds) const {
   if (state == ash::AppListState::kStateApps)
     return contents_bounds;
-  return GetBelowContentsOffscreenBounds(contents_bounds.size());
+  if (!app_list_features::IsScalableAppListEnabled())
+    return GetBelowContentsOffscreenBounds(contents_bounds.size());
+
+  gfx::Rect bounds = contents_bounds;
+  bounds.Offset(0, kNonAppsStateVerticalOffset);
+  return bounds;
+}
+
+void HorizontalPageContainer::UpdateOpacityForState(ash::AppListState state) {
+  if (!app_list_features::IsScalableAppListEnabled())
+    return;
+  layer()->SetOpacity(
+      state == ash::AppListState::kStateApps ? 1.0f : kNonAppsStateOpacity);
 }
 
 views::View* HorizontalPageContainer::GetFirstFocusableView() {
diff --git a/ash/app_list/views/horizontal_page_container.h b/ash/app_list/views/horizontal_page_container.h
index 6165b305..9668aa4 100644
--- a/ash/app_list/views/horizontal_page_container.h
+++ b/ash/app_list/views/horizontal_page_container.h
@@ -46,6 +46,7 @@
       ash::AppListState state,
       const gfx::Rect& contents_bounds,
       const gfx::Rect& search_box_bounds) const override;
+  void UpdateOpacityForState(ash::AppListState state) override;
   views::View* GetFirstFocusableView() override;
   views::View* GetLastFocusableView() override;
   bool ShouldShowSearchBox() const override;
diff --git a/ash/app_list/views/result_selection_controller.cc b/ash/app_list/views/result_selection_controller.cc
index e6a758de..2554cb5 100644
--- a/ash/app_list/views/result_selection_controller.cc
+++ b/ash/app_list/views/result_selection_controller.cc
@@ -37,8 +37,10 @@
 }
 
 ResultSelectionController::ResultSelectionController(
-    const ResultSelectionModel* result_container_views)
-    : result_selection_model_(result_container_views) {}
+    const ResultSelectionModel* result_container_views,
+    const base::RepeatingClosure& selection_change_callback)
+    : result_selection_model_(result_container_views),
+      selection_change_callback_(selection_change_callback) {}
 
 ResultSelectionController::~ResultSelectionController() = default;
 
@@ -99,6 +101,8 @@
 
   if (selected_result_)
     selected_result_->SetSelected(true, is_shift_tab);
+
+  selection_change_callback_.Run();
 }
 
 void ResultSelectionController::ClearSelection() {
@@ -230,6 +234,7 @@
   selected_location_details_ =
       std::make_unique<ResultLocationDetails>(location);
   selected_result_->SetSelected(true, reverse_tab_order);
+  selection_change_callback_.Run();
 }
 
 SearchResultBaseView* ResultSelectionController::GetResultAtLocation(
diff --git a/ash/app_list/views/result_selection_controller.h b/ash/app_list/views/result_selection_controller.h
index 7db7224..e6d8b7e 100644
--- a/ash/app_list/views/result_selection_controller.h
+++ b/ash/app_list/views/result_selection_controller.h
@@ -11,6 +11,7 @@
 #include "ash/app_list/app_list_export.h"
 #include "ash/app_list/views/search_result_base_view.h"
 #include "ash/app_list/views/search_result_container_view.h"
+#include "base/callback.h"
 #include "base/macros.h"
 
 namespace app_list {
@@ -77,8 +78,9 @@
     kResultChanged,
   };
 
-  explicit ResultSelectionController(
-      const ResultSelectionModel* result_container_views);
+  ResultSelectionController(
+      const ResultSelectionModel* result_container_views,
+      const base::RepeatingClosure& selection_change_callback);
   ~ResultSelectionController();
 
   // Returns the currently selected result.
@@ -147,6 +149,10 @@
   // |SearchResultContainerView|->|IsHorizontallyTraversable|.
   bool IsContainerAtIndexHorizontallyTraversable(int index) const;
 
+  // The callback run when the selected result changes (including when the
+  // selected result is cleared).
+  base::RepeatingClosure selection_change_callback_;
+
   // The currently selected result view
   SearchResultBaseView* selected_result_ = nullptr;
 
diff --git a/ash/app_list/views/result_selection_controller_unittest.cc b/ash/app_list/views/result_selection_controller_unittest.cc
index 2e2cb1b..0be7333 100644
--- a/ash/app_list/views/result_selection_controller_unittest.cc
+++ b/ash/app_list/views/result_selection_controller_unittest.cc
@@ -166,8 +166,10 @@
       base::i18n::SetICUDefaultLocale("en");
     }
 
-    result_selection_controller_ =
-        std::make_unique<ResultSelectionController>(&containers_);
+    result_selection_controller_ = std::make_unique<ResultSelectionController>(
+        &containers_,
+        base::BindRepeating(&ResultSelectionTest::OnSelectionChanged,
+                            base::Unretained(this)));
 
     testing::Test::SetUp();
   }
@@ -302,6 +304,7 @@
     for (size_t i = 1; i < locations.size(); i++) {
       ASSERT_EQ(ResultSelectionController::MoveResult::kResultChanged,
                 result_selection_controller_->MoveSelection(*forward));
+      EXPECT_EQ(1, GetAndResetSelectionChangeCount());
       ASSERT_EQ(*result_selection_controller_->selected_location_details(),
                 locations[i]);
     }
@@ -317,6 +320,7 @@
     // Expect loop back to first result.
     EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
               result_selection_controller_->MoveSelection(*forward));
+    EXPECT_EQ(1, GetAndResetSelectionChangeCount());
     ASSERT_EQ(*result_selection_controller_->selected_location_details(),
               locations[0]);
   }
@@ -331,6 +335,7 @@
     // Expect loop back to last result.
     EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
               result_selection_controller_->MoveSelection(*backward));
+    EXPECT_EQ(1, GetAndResetSelectionChangeCount());
     ASSERT_EQ(*result_selection_controller_->selected_location_details(),
               locations[3]);
   }
@@ -345,6 +350,7 @@
     // Expect no change in location.
     ASSERT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
               result_selection_controller_->MoveSelection(*forward));
+    EXPECT_EQ(0, GetAndResetSelectionChangeCount());
     ASSERT_EQ(*result_selection_controller_->selected_location_details(),
               locations[3]);
   }
@@ -359,6 +365,7 @@
     // Expect no change in location.
     ASSERT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
               result_selection_controller_->MoveSelection(*backward));
+    EXPECT_EQ(0, GetAndResetSelectionChangeCount());
     ASSERT_EQ(*result_selection_controller_->selected_location_details(),
               locations[0]);
   }
@@ -375,6 +382,7 @@
     for (size_t i = last_index; i > 0; i--) {
       ASSERT_EQ(ResultSelectionController::MoveResult::kResultChanged,
                 result_selection_controller_->MoveSelection(*backward));
+      EXPECT_EQ(1, GetAndResetSelectionChangeCount());
       ASSERT_EQ(*result_selection_controller_->selected_location_details(),
                 locations[i - 1]);
     }
@@ -428,6 +436,8 @@
               ? ResultSelectionController::MoveResult::kSelectionCycleRejected
               : ResultSelectionController::MoveResult::kResultChanged,
           result_selection_controller_->MoveSelection(*vertical_forward));
+      EXPECT_EQ(i == num_containers - 1 ? 0 : 1,
+                GetAndResetSelectionChangeCount());
     }
   }
 
@@ -453,6 +463,7 @@
 
     // Initialize the RSC for test.
     result_selection_controller_->ResetSelection(nullptr);
+    EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -461,6 +472,7 @@
     // expected to change.
     EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
               result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(0));
@@ -469,6 +481,7 @@
     // expected to change.
     EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
               result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(1));
@@ -477,6 +490,7 @@
     TestResultView* previous_result = GetCurrentSelection();
     EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
               result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -486,6 +500,7 @@
     // to be selected.
     EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
               result_selection_controller_->MoveSelection(shift_tab_key_));
+    EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(1));
@@ -494,6 +509,7 @@
     previous_result = GetCurrentSelection();
     EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
               result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -502,6 +518,7 @@
     // TAB - stay at the same result, but select next action.
     EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
               result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(0));
@@ -509,6 +526,7 @@
     // Shift-TAB - same result, but deselects actions.
     EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
               result_selection_controller_->MoveSelection(shift_tab_key_));
+    EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -516,6 +534,7 @@
     // TAB - reselect the first action.
     EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
               result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(0));
@@ -523,6 +542,7 @@
     // TAB - select the next action.
     EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
               result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(1));
@@ -531,6 +551,7 @@
     previous_result = GetCurrentSelection();
     EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
               result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(1, 0), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -540,6 +561,7 @@
     previous_result = GetCurrentSelection();
     EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
               result_selection_controller_->MoveSelection(shift_tab_key_));
+    EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(1));
@@ -548,6 +570,7 @@
     // Shift-TAB - move to previous action.
     EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
               result_selection_controller_->MoveSelection(shift_tab_key_));
+    EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(0));
@@ -575,6 +598,7 @@
 
     EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
               result_selection_controller_->MoveSelection(key_event));
+    EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
     if (expect_reverse) {
       ASSERT_EQ(create_test_location(1, 1), GetCurrentLocation());
@@ -606,7 +630,16 @@
   ui::KeyEvent shift_tab_key_ =
       ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
 
+  int GetAndResetSelectionChangeCount() {
+    const int result = selection_change_count_;
+    selection_change_count_ = 0;
+    return result;
+  }
+
+  void OnSelectionChanged() { selection_change_count_++; }
+
   bool is_rtl_ = false;
+  int selection_change_count_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(ResultSelectionTest);
 };
@@ -626,6 +659,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestSingleAxisTraversal(&down_arrow_, &up_arrow_);
 }
@@ -642,6 +676,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestSingleAxisTraversal(&tab_key_, &shift_tab_key_);
 }
@@ -661,6 +696,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestSingleAxisTraversal(forward, backward);
 }
@@ -677,6 +713,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestMultiAxisTraversal(false);
 }
@@ -693,6 +730,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestMultiAxisTraversal(true);
 }
@@ -704,6 +742,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestMultiAxisTraversal(false);
 }
@@ -715,6 +754,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestMultiAxisTraversal(true);
 }
@@ -727,6 +767,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestMultiAxisTraversal(false);
 }
@@ -739,6 +780,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestMultiAxisTraversal(true);
 }
@@ -750,6 +792,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestMultiAxisTraversal(false);
 }
@@ -761,6 +804,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestMultiAxisTraversal(false);
 }
@@ -772,6 +816,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestMultiAxisTraversal(false);
 }
@@ -783,6 +828,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   TestMultiAxisTraversal(false);
 }
@@ -812,6 +858,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -819,6 +866,7 @@
   // Shift TAB - reject.
   EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
             result_selection_controller_->MoveSelection(shift_tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -827,6 +875,7 @@
   // expected to change.
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
@@ -835,6 +884,7 @@
   TestResultView* previous_result = GetCurrentSelection();
   EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(1, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -843,6 +893,7 @@
   // TAB - next action selected.
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(1, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
@@ -850,6 +901,7 @@
   // TAB - rejected, as selection would cycle to the beginning.
   EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(1, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 }
@@ -869,6 +921,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
 
@@ -876,12 +929,14 @@
   // the first result).
   EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
             result_selection_controller_->MoveSelection(shift_tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
 
   // TAB - reject goting to the first result (event though it's the same as the
   // last result).
   EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
 }
 
@@ -902,6 +957,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -909,6 +965,7 @@
   // Shift TAB - reject going to the last result.
   EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
             result_selection_controller_->MoveSelection(shift_tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -917,6 +974,7 @@
   // expected to change.
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
@@ -924,6 +982,7 @@
   // TAB - rejected, as selection would cycle to the beginning.
   EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 }
@@ -946,6 +1005,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -953,6 +1013,7 @@
   // UP - reject going to the last result.
   EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
             result_selection_controller_->MoveSelection(up_arrow_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -961,6 +1022,7 @@
   // first element is the same as the last).
   EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
             result_selection_controller_->MoveSelection(down_arrow_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
 }
@@ -984,6 +1046,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ui::KeyEvent* forward = is_rtl_ ? &left_arrow_ : &right_arrow_;
   ui::KeyEvent* backward = is_rtl_ ? &right_arrow_ : &left_arrow_;
@@ -994,6 +1057,7 @@
   // TAB to select an action.
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
@@ -1001,6 +1065,7 @@
   TestResultView* previous_result = GetCurrentSelection();
   EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
             result_selection_controller_->MoveSelection(*forward));
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
   EXPECT_FALSE(previous_result->selected());
@@ -1008,6 +1073,7 @@
   // TAB to select an action.
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
@@ -1015,6 +1081,7 @@
   previous_result = GetCurrentSelection();
   EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
             result_selection_controller_->MoveSelection(*backward));
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
   EXPECT_FALSE(previous_result->selected());
@@ -1039,6 +1106,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -1046,6 +1114,7 @@
   // TAB to select an action.
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
@@ -1053,6 +1122,7 @@
   TestResultView* previous_result = GetCurrentSelection();
   EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
             result_selection_controller_->MoveSelection(down_arrow_));
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
   EXPECT_FALSE(previous_result->selected());
@@ -1060,6 +1130,7 @@
   // TAB to select an action.
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
@@ -1067,6 +1138,7 @@
   previous_result = GetCurrentSelection();
   EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
             result_selection_controller_->MoveSelection(up_arrow_));
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
   EXPECT_FALSE(previous_result->selected());
@@ -1090,6 +1162,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -1097,6 +1170,7 @@
   // TAB to select an action.
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
@@ -1127,6 +1201,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -1134,18 +1209,21 @@
   // DOWN to select another result.
   EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
             result_selection_controller_->MoveSelection(down_arrow_));
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
 
   // TAB to select an action.
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
   // Reset selection.
   TestResultView* pre_reset_selection = GetCurrentSelection();
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
   EXPECT_FALSE(pre_reset_selection->selected());
@@ -1169,6 +1247,7 @@
 
   // Initialize the RSC for test.
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -1176,6 +1255,7 @@
   // DOWN to select another result.
   EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
             result_selection_controller_->MoveSelection(down_arrow_));
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
 
@@ -1186,6 +1266,7 @@
             result_selection_controller_->MoveSelection(tab_key_));
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(2));
 
@@ -1203,6 +1284,7 @@
   // Shift-TAB move selection to the previous result.
   EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
             result_selection_controller_->MoveSelection(shift_tab_key_));
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(2));
 }
@@ -1254,6 +1336,7 @@
   // Set up non default selection,
   result_selection_controller_->ResetSelection(nullptr);
   result_selection_controller_->MoveSelection(down_arrow_);
+  EXPECT_EQ(2, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
 
   // Test that calling reset selection while selection changes are blocked does
@@ -1261,6 +1344,7 @@
   result_selection_controller_->set_block_selection_changes(true);
 
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(result_selection_controller_->selected_result());
 
@@ -1268,6 +1352,7 @@
   result_selection_controller_->set_block_selection_changes(false);
 
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(result_selection_controller_->selected_result());
@@ -1285,6 +1370,7 @@
   // not set the selected result.
   result_selection_controller_->set_block_selection_changes(true);
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
 
   EXPECT_FALSE(result_selection_controller_->selected_result());
   EXPECT_FALSE(result_selection_controller_->selected_location_details());
@@ -1293,6 +1379,7 @@
   result_selection_controller_->set_block_selection_changes(false);
 
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   EXPECT_TRUE(result_selection_controller_->selected_result());
   EXPECT_TRUE(result_selection_controller_->selected_location_details());
@@ -1313,6 +1400,7 @@
   };
 
   result_selection_controller_->ResetSelection(nullptr);
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
 
   // Test that calling move selection while selection chages are blocked does
@@ -1321,6 +1409,7 @@
 
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(down_arrow_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(result_selection_controller_->selected_result());
 
@@ -1328,6 +1417,7 @@
   result_selection_controller_->set_block_selection_changes(false);
   EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
             result_selection_controller_->MoveSelection(down_arrow_));
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(result_selection_controller_->selected_result());
@@ -1353,6 +1443,7 @@
 
   EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
             result_selection_controller_->MoveSelection(down_arrow_));
+  EXPECT_EQ(0, GetAndResetSelectionChangeCount());
   EXPECT_FALSE(result_selection_controller_->selected_result());
   EXPECT_FALSE(result_selection_controller_->selected_location_details());
 
@@ -1361,6 +1452,7 @@
 
   EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
             result_selection_controller_->MoveSelection(down_arrow_));
+  EXPECT_EQ(1, GetAndResetSelectionChangeCount());
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(result_selection_controller_->selected_result());
diff --git a/ash/app_list/views/search_result_page_view.cc b/ash/app_list/views/search_result_page_view.cc
index 32d6374..eb14c81 100644
--- a/ash/app_list/views/search_result_page_view.cc
+++ b/ash/app_list/views/search_result_page_view.cc
@@ -21,6 +21,7 @@
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/view_shadow.h"
+#include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "ui/chromeos/search_box/search_box_constants.h"
 #include "ui/compositor/layer.h"
@@ -170,7 +171,7 @@
   // set transparent so that the rounded corner is not overwritten.
   SetBackground(std::make_unique<SearchResultPageBackground>(
       AppListConfig::instance().card_background_color()));
-  views::ScrollView* const scroller = new views::ScrollView;
+  auto scroller = std::make_unique<views::ScrollView>();
   // Leaves a placeholder area for the search box and the separator below it.
   scroller->SetBorder(views::CreateEmptyBorder(gfx::Insets(
       kSearchBoxHeight + kSearchBoxBottomSpacing + kSeparatorThickness, 0, 0,
@@ -181,14 +182,16 @@
   // contents' size. Using zeroes doesn't prevent it from scrolling and sizing
   // correctly.
   scroller->ClipHeightTo(0, 0);
-  scroller->SetVerticalScrollBar(new ZeroWidthVerticalScrollBar);
+  scroller->SetVerticalScrollBar(new ZeroWidthVerticalScrollBar());
   scroller->SetBackgroundColor(SK_ColorTRANSPARENT);
-  AddChildView(scroller);
+  AddChildView(std::move(scroller));
 
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
-  result_selection_controller_ =
-      std::make_unique<ResultSelectionController>(&result_container_views_);
+  result_selection_controller_ = std::make_unique<ResultSelectionController>(
+      &result_container_views_,
+      base::BindRepeating(&SearchResultPageView::SelectedResultChanged,
+                          base::Unretained(this)));
 }
 
 SearchResultPageView::~SearchResultPageView() = default;
@@ -307,6 +310,26 @@
   Layout();
 }
 
+void SearchResultPageView::SelectedResultChanged() {
+  if (!result_selection_controller_->selected_location_details() ||
+      !result_selection_controller_->selected_result()) {
+    return;
+  }
+
+  const ResultLocationDetails* selection_details =
+      result_selection_controller_->selected_location_details();
+  views::View* selected_row = nullptr;
+  // For horizontal containers ensure that the whole container fits in the
+  // scroll view, to account for vertical padding within the container.
+  if (selection_details->container_is_horizontal) {
+    selected_row = result_container_views_[selection_details->container_index];
+  } else {
+    selected_row = result_selection_controller_->selected_result();
+  }
+
+  selected_row->ScrollViewToVisible();
+}
+
 void SearchResultPageView::OnSearchResultContainerResultsChanging() {
   // Block any result selection changes while result updates are in flight.
   // The selection will be reset once the results are all updated.
diff --git a/ash/app_list/views/search_result_page_view.h b/ash/app_list/views/search_result_page_view.h
index 725206e..145f9d0 100644
--- a/ash/app_list/views/search_result_page_view.h
+++ b/ash/app_list/views/search_result_page_view.h
@@ -89,6 +89,11 @@
   // Sort the result container views.
   void ReorderSearchResultContainers();
 
+  // Passed to |result_selection_controller_| as a callback that gets called
+  // when the currently selected result changes.
+  // Ensures that |scroller_| visible rect contains the newly selected result.
+  void SelectedResultChanged();
+
   AppListViewDelegate* view_delegate_;
 
   // The SearchResultContainerViews that compose the search page. All owned by
diff --git a/ash/assistant/assistant_ui_controller.cc b/ash/assistant/assistant_ui_controller.cc
index be63e0b..f00d85e 100644
--- a/ash/assistant/assistant_ui_controller.cc
+++ b/ash/assistant/assistant_ui_controller.cc
@@ -300,6 +300,15 @@
 
 void AssistantUiController::OnProactiveSuggestionsViewHoverChanged(
     bool is_hovering) {
+  if (!proactive_suggestions_view_ ||
+      !proactive_suggestions_view_->GetWidget() ||
+      proactive_suggestions_view_->GetWidget()->IsClosed()) {
+    // Hover changed events may occur during the proactive suggestions widget's
+    // close sequence. When this occurs, we quit early as the proactive
+    // suggestions view is being destroyed.
+    return;
+  }
+
   if (!is_hovering) {
     // When the user is no longer hovering over the proactive suggestions view
     // we need to reset the timer so that it will auto-close appropriately.
diff --git a/ash/events/switch_access_event_handler.cc b/ash/events/switch_access_event_handler.cc
index 0d2ec9f..f7b619d2 100644
--- a/ash/events/switch_access_event_handler.cc
+++ b/ash/events/switch_access_event_handler.cc
@@ -8,6 +8,8 @@
 #include "ash/public/cpp/switch_access_event_handler_delegate.h"
 #include "ash/shell.h"
 #include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/event_utils.h"
 
 namespace ash {
 
@@ -101,8 +103,17 @@
 bool SwitchAccessEventHandler::ShouldCancelEvent(
     const ui::KeyEvent& event) const {
   // Ignore virtual key events so users can type with the onscreen keyboard.
-  if (ignore_virtual_key_events_ && !event.HasNativeEvent())
-    return false;
+  if (ignore_virtual_key_events_ &&
+      event.source_device_id() == ui::ED_UNKNOWN_DEVICE) {
+    // When running Chrome OS on Linux, the source_device_id property is never
+    // populated.
+    auto* properties = event.properties();
+    bool is_linux_xevent =
+        properties &&
+        properties->find(ui::kPropertyKeyboardIBusFlag) != properties->end();
+    if (!is_linux_xevent)
+      return false;
+  }
 
   if (forward_key_events_)
     return true;
diff --git a/ash/public/cpp/shelf_config.h b/ash/public/cpp/shelf_config.h
index a4e2b80..4fb01a2 100644
--- a/ash/public/cpp/shelf_config.h
+++ b/ash/public/cpp/shelf_config.h
@@ -81,6 +81,8 @@
   // The extra padding added to status area tray buttons on the shelf.
   int status_area_hit_region_padding() const;
 
+  bool is_in_app() const { return !is_app_list_visible_; }
+
   int app_icon_group_margin() const { return app_icon_group_margin_; }
   SkColor shelf_control_permanent_highlight_background() const {
     return shelf_control_permanent_highlight_background_;
diff --git a/ash/shelf/scrollable_shelf_view.cc b/ash/shelf/scrollable_shelf_view.cc
index c1bd949..db757ae 100644
--- a/ash/shelf/scrollable_shelf_view.cc
+++ b/ash/shelf/scrollable_shelf_view.cc
@@ -18,6 +18,7 @@
 #include "ui/gfx/geometry/vector2d_conversions.h"
 #include "ui/gfx/skia_paint_util.h"
 #include "ui/views/focus/focus_search.h"
+#include "ui/views/view_targeter_delegate.h"
 
 namespace ash {
 
@@ -42,6 +43,9 @@
 // neglected.
 constexpr int kFlingVelocityThreshold = 1000;
 
+// Horizontal size of the tap areafor the overflow arrow button.
+constexpr int kArrowButtonTapAreaHorizontal = 32;
+
 // Sum of the shelf button size and the gap between shelf buttons.
 int GetUnit() {
   return ShelfConfig::Get()->button_size() +
@@ -85,17 +89,6 @@
 
 class ScrollableShelfView::GradientLayerDelegate : public ui::LayerDelegate {
  public:
-  struct FadeZone {
-    // Bounds of the fade in/out zone.
-    gfx::Rect zone_rect;
-
-    // Specifies the type of FadeZone: fade in or fade out.
-    bool fade_in = false;
-
-    // Indicates the drawing direction.
-    bool is_horizontal = false;
-  };
-
   GradientLayerDelegate() : layer_(ui::LAYER_TEXTURED) {
     layer_.set_delegate(this);
     layer_.SetFillsBoundsOpaquely(false);
@@ -155,6 +148,41 @@
 };
 
 ////////////////////////////////////////////////////////////////////////////////
+// ScrollableShelfArrowView
+
+class ScrollableShelfView::ScrollableShelfArrowView
+    : public ash::ScrollArrowView,
+      public views::ViewTargeterDelegate {
+ public:
+  explicit ScrollableShelfArrowView(ArrowType arrow_type,
+                                    bool is_horizontal_alignment,
+                                    Shelf* shelf,
+                                    ShelfButtonDelegate* shelf_button_delegate)
+      : ScrollArrowView(arrow_type,
+                        is_horizontal_alignment,
+                        shelf,
+                        shelf_button_delegate) {
+    SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
+  }
+  ~ScrollableShelfArrowView() override = default;
+
+  // views::ViewTargeterDelegate:
+  bool DoesIntersectRect(const views::View* target,
+                         const gfx::Rect& rect) const override {
+    DCHECK_EQ(target, this);
+    gfx::Rect bounds = gfx::Rect(size());
+
+    // Calculate padding for the tap area. (Should be 32 x shelfButtonSize)
+    constexpr int horizontalPadding =
+        (kArrowButtonTapAreaHorizontal - kArrowButtonSize) / 2;
+    const int verticalPadding =
+        (ShelfConfig::Get()->button_size() - kArrowButtonSize) / 2;
+    bounds.Inset(gfx::Insets(-verticalPadding, -horizontalPadding));
+    return bounds.Intersects(rect);
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
 // ScrollableShelfFocusSearch
 
 class ScrollableShelfFocusSearch : public views::FocusSearch {
@@ -242,12 +270,12 @@
   layer()->SetFillsBoundsOpaquely(false);
 
   // Initialize the left arrow button.
-  left_arrow_ = AddChildView(std::make_unique<ScrollArrowView>(
+  left_arrow_ = AddChildView(std::make_unique<ScrollableShelfArrowView>(
       ScrollArrowView::kLeft, GetShelf()->IsHorizontalAlignment(), GetShelf(),
       this));
 
   // Initialize the right arrow button.
-  right_arrow_ = AddChildView(std::make_unique<ScrollArrowView>(
+  right_arrow_ = AddChildView(std::make_unique<ScrollableShelfArrowView>(
       ScrollArrowView::kRight, GetShelf()->IsHorizontalAlignment(), GetShelf(),
       this));
 
@@ -272,7 +300,8 @@
     SetPaneFocusAndFocusDefault();
 
     // Clears the gradient shader when the focus ring shows.
-    GradientLayerDelegate::FadeZone fade_zone = {gfx::Rect(), false, false};
+    FadeZone fade_zone = {/*zone_rect=*/gfx::Rect(), /*fade_in=*/false,
+                          /*is_horizontal=*/false};
     gradient_layer_delegate_->set_fade_zone(fade_zone);
     gradient_layer_delegate_->layer()->SetBounds(layer()->bounds());
     SchedulePaint();
@@ -896,37 +925,77 @@
 }
 
 void ScrollableShelfView::UpdateGradientZone() {
+  FadeZone fade_zone = {/*zone_rect=*/gfx::Rect(), /*fade_in=*/false,
+                        /*is_horizontal=*/false};
+
+  // Calculates the bounds of the gradient zone based on the arrow buttons'
+  // location.
+  if (right_arrow_->GetVisible())
+    fade_zone = CalculateEndGradientZone();
+  else if (left_arrow_->GetVisible())
+    fade_zone = CalculateStartGradientZone();
+
+  gradient_layer_delegate_->set_fade_zone(fade_zone);
+  SchedulePaint();
+}
+
+ScrollableShelfView::FadeZone ScrollableShelfView::CalculateStartGradientZone()
+    const {
   gfx::Rect zone_rect;
   bool fade_in;
   const int zone_length = GetFadeZoneLength();
   const bool is_horizontal_alignment = GetShelf()->IsHorizontalAlignment();
+  const gfx::Rect left_arrow_bounds = left_arrow_->bounds();
 
-  // Calculates the bounds of the gradient zone based on the arrow buttons'
-  // location.
-  if (right_arrow_->GetVisible()) {
-    const gfx::Rect right_arrow_bounds = right_arrow_->bounds();
-    zone_rect = is_horizontal_alignment
-                    ? gfx::Rect(right_arrow_bounds.x() - zone_length, 0,
-                                zone_length, height())
-                    : gfx::Rect(0, right_arrow_bounds.y() - zone_length,
-                                width(), zone_length);
-    fade_in = false;
-  } else if (left_arrow_->GetVisible()) {
-    const gfx::Rect left_arrow_bounds = left_arrow_->bounds();
-    zone_rect =
-        is_horizontal_alignment
-            ? gfx::Rect(left_arrow_bounds.right(), 0, zone_length, height())
-            : gfx::Rect(0, left_arrow_bounds.bottom(), width(), zone_length);
-    fade_in = true;
+  if (is_horizontal_alignment) {
+    int x;
+
+    // Calculates the start location on x-axis of the gradient zone.
+    if (ShouldAdaptToRTL()) {
+      const gfx::Rect mirrored_left_arrow_bounds =
+          GetMirroredRect(left_arrow_bounds);
+      x = mirrored_left_arrow_bounds.x() - zone_length;
+    } else {
+      x = left_arrow_bounds.right();
+    }
+    zone_rect = gfx::Rect(x, 0, zone_length, height());
   } else {
-    zone_rect = gfx::Rect();
-    fade_in = false;
+    zone_rect = gfx::Rect(0, left_arrow_bounds.bottom(), width(), zone_length);
   }
 
-  GradientLayerDelegate::FadeZone fade_zone = {zone_rect, fade_in,
-                                               is_horizontal_alignment};
-  gradient_layer_delegate_->set_fade_zone(fade_zone);
-  SchedulePaint();
+  fade_in = !ShouldAdaptToRTL();
+
+  return {zone_rect, fade_in, is_horizontal_alignment};
+}
+
+ScrollableShelfView::FadeZone ScrollableShelfView::CalculateEndGradientZone()
+    const {
+  gfx::Rect zone_rect;
+  bool fade_in;
+  const int zone_length = GetFadeZoneLength();
+  const bool is_horizontal_alignment = GetShelf()->IsHorizontalAlignment();
+  const gfx::Rect right_arrow_bounds = right_arrow_->bounds();
+
+  if (is_horizontal_alignment) {
+    int x;
+
+    // Calculates the start location on x-axis of the gradient zone.
+    if (ShouldAdaptToRTL()) {
+      const gfx::Rect mirrored_right_arrow_bounds =
+          GetMirroredRect(right_arrow_bounds);
+      x = mirrored_right_arrow_bounds.right();
+    } else {
+      x = right_arrow_bounds.x() - zone_length;
+    }
+    zone_rect = gfx::Rect(x, 0, zone_length, height());
+  } else {
+    zone_rect = gfx::Rect(0, right_arrow_bounds.y() - zone_length, width(),
+                          zone_length);
+  }
+
+  fade_in = ShouldAdaptToRTL();
+
+  return {zone_rect, fade_in, is_horizontal_alignment};
 }
 
 int ScrollableShelfView::GetActualScrollOffset() const {
diff --git a/ash/shelf/scrollable_shelf_view.h b/ash/shelf/scrollable_shelf_view.h
index 73d47375..f34879e 100644
--- a/ash/shelf/scrollable_shelf_view.h
+++ b/ash/shelf/scrollable_shelf_view.h
@@ -88,6 +88,18 @@
 
  private:
   class GradientLayerDelegate;
+  class ScrollableShelfArrowView;
+
+  struct FadeZone {
+    // Bounds of the fade in/out zone.
+    gfx::Rect zone_rect;
+
+    // Specifies the type of FadeZone: fade in or fade out.
+    bool fade_in = false;
+
+    // Indicates the drawing direction.
+    bool is_horizontal = false;
+  };
 
   enum ScrollStatus {
     // Indicates whether the gesture scrolling is across the main axis.
@@ -195,6 +207,11 @@
   // Updates the gradient zone.
   void UpdateGradientZone();
 
+  // Calculates the bounds of the gradient zone before/after the shelf
+  // container.
+  FadeZone CalculateStartGradientZone() const;
+  FadeZone CalculateEndGradientZone() const;
+
   // Returns the actual scroll offset on the view's main axis. When the left
   // arrow button shows, |shelf_view_| is translated due to the change in
   // |shelf_container_view_|'s bounds. That translation offset is not included
diff --git a/ash/shelf/scrollable_shelf_view_unittest.cc b/ash/shelf/scrollable_shelf_view_unittest.cc
index 5af9f645..951ca412 100644
--- a/ash/shelf/scrollable_shelf_view_unittest.cc
+++ b/ash/shelf/scrollable_shelf_view_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/shelf/scrollable_shelf_view.h"
 
+#include "ash/public/cpp/shelf_config.h"
 #include "ash/shelf/shelf_test_util.h"
 #include "ash/shelf/shelf_tooltip_manager.h"
 #include "ash/shelf/shelf_view_test_api.h"
@@ -39,8 +40,6 @@
   ~ScrollableShelfViewTest() override = default;
 
   void SetUp() override {
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        chromeos::switches::kShelfScrollable);
     AshTestBase::SetUp();
     scrollable_shelf_view_ = GetPrimaryShelf()
                                  ->shelf_widget()
@@ -166,4 +165,46 @@
   EXPECT_FALSE(tooltip_manager->IsVisible());
 }
 
+// Test that tapping near the scroll arrow button triggers scrolling. (see
+// https://crbug.com/1004998)
+TEST_F(ScrollableShelfViewTest, ScrollAfterTappingNearScrollArrow) {
+  AddAppShortcutUntilOverflow();
+
+  ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
+            scrollable_shelf_view_->layout_strategy_for_test());
+
+  // Tap right arrow and check that the scrollable shelf now shows the left
+  // arrow only. Then do the same for the left arrow.
+  const gfx::Rect right_arrow =
+      scrollable_shelf_view_->right_arrow()->GetBoundsInScreen();
+  GetEventGenerator()->GestureTapAt(right_arrow.CenterPoint());
+  ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
+            scrollable_shelf_view_->layout_strategy_for_test());
+
+  const gfx::Rect left_arrow =
+      scrollable_shelf_view_->left_arrow()->GetBoundsInScreen();
+  GetEventGenerator()->GestureTapAt(left_arrow.CenterPoint());
+  ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
+            scrollable_shelf_view_->layout_strategy_for_test());
+
+  // Recalculate the right arrow  bounds considering the padding for the tap
+  // area.
+  const int horizontalPadding = (32 - right_arrow.width()) / 2;
+  const int verticalPadding =
+      (ShelfConfig::Get()->button_size() - right_arrow.height()) / 2;
+
+  // Tap near the right arrow and check that the scrollable shelf now shows the
+  // left arrow only. Then do the same for the left arrow.
+  GetEventGenerator()->GestureTapAt(
+      gfx::Point(right_arrow.top_right().x() - horizontalPadding,
+                 right_arrow.top_right().y() + verticalPadding));
+  ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
+            scrollable_shelf_view_->layout_strategy_for_test());
+
+  GetEventGenerator()->GestureTapAt(
+      gfx::Point(left_arrow.top_right().x(), left_arrow.top_right().y()));
+  EXPECT_EQ(ScrollableShelfView::kShowRightArrowButton,
+            scrollable_shelf_view_->layout_strategy_for_test());
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 3608326..5b80259c 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -376,10 +376,14 @@
 
   // We'll layout when our bounds change.
 
-  // Add the main shelf view as ShelfTooltipDelegate when scrollable shelf
-  // is not enabled.
-  if (!is_overflow_mode() && !chromeos::switches::ShouldShowScrollableShelf())
+  if (chromeos::switches::ShouldShowScrollableShelf()) {
+    UpdateVisibleIndice();
+    overflow_button_->SetVisible(false);
+  } else if (!is_overflow_mode()) {
+    // Add the main shelf view as ShelfTooltipDelegate when scrollable shelf
+    // is not enabled.
     shelf_->tooltip()->set_shelf_tooltip_delegate(this);
+  }
 }
 
 gfx::Rect ShelfView::GetIdealBoundsOfItemIcon(const ShelfID& id) {
@@ -876,6 +880,15 @@
     if (!button->IsIconSizeCurrent())
       ShelfItemChanged(i, model_->items()[i]);
   }
+
+  if (chromeos::switches::ShouldShowShelfHotseat() && IsTabletModeEnabled() &&
+      !ShelfConfig::Get()->is_in_app()) {
+    SetBackground(views::CreateRoundedRectBackground(
+        ShelfConfig::Get()->shelf_control_permanent_highlight_background(),
+        ShelfConfig::Get()->button_size() / 2));
+  } else {
+    SetBackground(views::CreateSolidBackground(SK_ColorTRANSPARENT));
+  }
 }
 
 bool ShelfView::ShouldEventActivateButton(View* view, const ui::Event& event) {
@@ -1185,10 +1198,8 @@
   // When the scrollable shelf is enabled, overflow mode is disabled. Meanwhile,
   // centering padding is calculated in ScrollableShelfView, which means that
   // |center_on_screen| is always false.
-  if (chromeos::switches::ShouldShowScrollableShelf()) {
-    last_visible_index_ = view_model()->view_size() - 1;
+  if (chromeos::switches::ShouldShowScrollableShelf())
     return strategy;
-  }
 
   // There are two possibilities. Either all the apps fit when centered
   // on the whole screen width, in which case we do that. Or, when space
@@ -2124,6 +2135,11 @@
   view->layer()->SetOpacity(0);
   view_model_->Add(view, model_index);
 
+  // When the scrollable shelf is enabled, |last_visible_index_| is always the
+  // index to the last shelf item.
+  if (chromeos::switches::ShouldShowScrollableShelf())
+    UpdateVisibleIndice();
+
   // Give the button its ideal bounds. That way if we end up animating the
   // button before this animation completes it doesn't appear at some random
   // spot (because it was in the middle of animating from 0,0 0x0 to its
@@ -2152,6 +2168,11 @@
   views::View* view = view_model_->view_at(model_index);
   view_model_->Remove(model_index);
 
+  // When the scrollable shelf is enabled, |last_visible_index_| is always the
+  // index to the last shelf item.
+  if (chromeos::switches::ShouldShowScrollableShelf())
+    UpdateVisibleIndice();
+
   {
     base::AutoReset<bool> cancelling_drag(&cancelling_drag_model_changed_,
                                           true);
@@ -2497,4 +2518,10 @@
   return item ? item->title : base::string16();
 }
 
+void ShelfView::UpdateVisibleIndice() {
+  DCHECK_EQ(true, chromeos::switches::ShouldShowScrollableShelf());
+  first_visible_index_ = view_model()->view_size() == 0 ? -1 : 0;
+  last_visible_index_ = model_->item_count() - 1;
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 06e48c2..521eea4 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -557,6 +557,10 @@
   // Different from GetTitleForView, |view| here must be a child view.
   base::string16 GetTitleForChildView(const views::View* view) const;
 
+  // Update |first_visible_index_| and |last_visible_index_| when the scrollable
+  // shelf is enabled.
+  void UpdateVisibleIndice();
+
   // The model; owned by Launcher.
   ShelfModel* model_;
 
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index ea9a2863..7fce164c 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -29,6 +29,7 @@
 #include "ash/shell.h"
 #include "ash/system/status_area_layout_manager.h"
 #include "ash/system/status_area_widget.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/command_line.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "ui/compositor/layer.h"
@@ -202,8 +203,12 @@
   const ShelfBackgroundType background_type =
       shelf_widget_->GetBackgroundType();
 
-  if (!opaque_background_.visible())
-    opaque_background_.SetVisible(true);
+  bool show_opaque_background =
+      !Shell::Get()->tablet_mode_controller()->InTabletMode() ||
+      ShelfConfig::Get()->is_in_app() ||
+      !chromeos::switches::ShouldShowShelfHotseat();
+  if (show_opaque_background != opaque_background_.visible())
+    opaque_background_.SetVisible(show_opaque_background);
 
   // Extend the opaque layer a little bit to handle "overshoot" gestures
   // gracefully (the user drags the shelf further than it can actually go).
diff --git a/ash/system/message_center/unified_message_center_bubble.cc b/ash/system/message_center/unified_message_center_bubble.cc
index a1f43c86..4b1d8c6e 100644
--- a/ash/system/message_center/unified_message_center_bubble.cc
+++ b/ash/system/message_center/unified_message_center_bubble.cc
@@ -14,6 +14,7 @@
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/system/unified/unified_system_tray_view.h"
+#include "ui/views/focus/focus_search.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
@@ -29,12 +30,13 @@
   init_params.max_width = kTrayMenuWidth;
   init_params.corner_radius = kUnifiedTrayCornerRadius;
   init_params.has_shadow = false;
+  init_params.close_on_deactivate = false;
 
   bubble_view_ = new TrayBubbleView(init_params);
 
   message_center_view_ =
       bubble_view_->AddChildView(std::make_unique<UnifiedMessageCenterView>(
-          nullptr /* parent */, tray->model()));
+          nullptr /* parent */, tray->model(), this));
 
   message_center_view_->AddObserver(this);
 
@@ -83,6 +85,14 @@
   bubble_view_->ChangeAnchorRect(resting_bounds);
 }
 
+void UnifiedMessageCenterBubble::FocusEntered(bool reverse) {
+  message_center_view_->FocusEntered(reverse);
+}
+
+bool UnifiedMessageCenterBubble::FocusOut(bool reverse) {
+  return tray_->FocusQuickSettings(reverse);
+}
+
 TrayBackgroundView* UnifiedMessageCenterBubble::GetTray() const {
   return tray_;
 }
diff --git a/ash/system/message_center/unified_message_center_bubble.h b/ash/system/message_center/unified_message_center_bubble.h
index 88bd1966..6d0bf39 100644
--- a/ash/system/message_center/unified_message_center_bubble.h
+++ b/ash/system/message_center/unified_message_center_bubble.h
@@ -28,8 +28,16 @@
   explicit UnifiedMessageCenterBubble(UnifiedSystemTray* tray);
   ~UnifiedMessageCenterBubble() override;
 
+  // Move the message center bubble to keep it on top of the quick settings
+  // widget whenever the quick settings widget is resized.
   void UpdatePosition();
 
+  // Inform message_center_view_ of focus being acquired.
+  void FocusEntered(bool reverse);
+
+  // Relinquish focus and transfer it to the quick settings widget.
+  bool FocusOut(bool reverse);
+
   // TrayBubbleBase:
   TrayBackgroundView* GetTray() const override;
   TrayBubbleView* GetBubbleView() const override;
@@ -43,6 +51,10 @@
   // views::WidgetObserver:
   void OnWidgetDestroying(views::Widget* widget) override;
 
+  UnifiedMessageCenterView* message_center_view() {
+    return message_center_view_;
+  }
+
  private:
   UnifiedSystemTray* const tray_;
 
diff --git a/ash/system/message_center/unified_message_center_bubble_unittest.cc b/ash/system/message_center/unified_message_center_bubble_unittest.cc
index 360b3f8..9539c22b 100644
--- a/ash/system/message_center/unified_message_center_bubble_unittest.cc
+++ b/ash/system/message_center/unified_message_center_bubble_unittest.cc
@@ -7,6 +7,8 @@
 #include <memory>
 
 #include "ash/public/cpp/ash_features.h"
+#include "ash/shell.h"
+#include "ash/system/message_center/unified_message_center_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/unified/unified_system_tray_bubble.h"
@@ -66,6 +68,38 @@
            tray_bounds.y();
   }
 
+  // Helper functions for focus cycle testing.
+  void DoTab() {
+    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
+    generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EventFlags::EF_NONE);
+  }
+
+  void DoShiftTab() {
+    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
+    generator.PressKey(ui::KeyboardCode::VKEY_TAB,
+                       ui::EventFlags::EF_SHIFT_DOWN);
+  }
+
+  views::View* GetFirstMessageCenterFocusable() {
+    return GetMessageCenterBubble()
+        ->message_center_view()
+        ->GetFirstFocusableChild();
+  }
+
+  views::View* GetLastMessageCenterFocusable() {
+    return GetMessageCenterBubble()
+        ->message_center_view()
+        ->GetLastFocusableChild();
+  }
+
+  views::View* GetFirstQuickSettingsFocusable() {
+    return GetSystemTrayBubble()->unified_view()->GetFirstFocusableChild();
+  }
+
+  views::View* GetLastQuickSettingsFocusable() {
+    return GetSystemTrayBubble()->unified_view()->GetLastFocusableChild();
+  }
+
  private:
   int id_ = 0;
   std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
@@ -117,4 +151,102 @@
   }
 }
 
+TEST_F(UnifiedMessageCenterBubbleTest, FocusCycle) {
+  EnableMessageCenterRefactor();
+  GetPrimaryUnifiedSystemTray()->ShowBubble(true);
+  AddNotification();
+  AddNotification();
+
+  views::Widget* quick_settings_widget =
+      GetSystemTrayBubble()->GetBubbleWidget();
+  views::Widget* message_center_widget =
+      GetMessageCenterBubble()->GetBubbleWidget();
+
+  // First tab should focus the first element in the quick settings bubble.
+  DoTab();
+  EXPECT_TRUE(quick_settings_widget->IsActive());
+  EXPECT_FALSE(message_center_widget->IsActive());
+  EXPECT_EQ(quick_settings_widget->GetFocusManager()->GetFocusedView(),
+            GetFirstQuickSettingsFocusable());
+
+  // Keep tabbing until we reach the last focusable element in the quick
+  // settings bubble.
+  while (quick_settings_widget->GetFocusManager()->GetFocusedView() !=
+         GetLastQuickSettingsFocusable()) {
+    DoTab();
+  }
+
+  // Tab at the last element in the quick settings bubble should move focus to
+  // the first element in the message center.
+  DoTab();
+  EXPECT_TRUE(message_center_widget->IsActive());
+  EXPECT_FALSE(quick_settings_widget->IsActive());
+  EXPECT_EQ(message_center_widget->GetFocusManager()->GetFocusedView(),
+            GetFirstMessageCenterFocusable());
+
+  // Keep tabbing until we reach the last focusable element in the message
+  // center bubble.
+  while (message_center_widget->GetFocusManager()->GetFocusedView() !=
+         GetLastMessageCenterFocusable()) {
+    DoTab();
+  }
+
+  // Tab at the last element in the message center bubble should move focus to
+  // the first element in the quick settings bubble.
+  DoTab();
+  EXPECT_TRUE(quick_settings_widget->IsActive());
+  EXPECT_FALSE(message_center_widget->IsActive());
+  EXPECT_EQ(quick_settings_widget->GetFocusManager()->GetFocusedView(),
+            GetFirstQuickSettingsFocusable());
+}
+
+TEST_F(UnifiedMessageCenterBubbleTest, ReverseFocusCycle) {
+  EnableMessageCenterRefactor();
+  GetPrimaryUnifiedSystemTray()->ShowBubble(true);
+  AddNotification();
+  AddNotification();
+
+  views::Widget* quick_settings_widget =
+      GetSystemTrayBubble()->GetBubbleWidget();
+  views::Widget* message_center_widget =
+      GetMessageCenterBubble()->GetBubbleWidget();
+
+  // First shift tab should focus the last element in the quick settings bubble.
+  DoShiftTab();
+  EXPECT_TRUE(quick_settings_widget->IsActive());
+  EXPECT_FALSE(message_center_widget->IsActive());
+  EXPECT_EQ(quick_settings_widget->GetFocusManager()->GetFocusedView(),
+            GetLastQuickSettingsFocusable());
+
+  // Keep shift tabbing until we reach the first focusable element in the quick
+  // settings bubble.
+  while (quick_settings_widget->GetFocusManager()->GetFocusedView() !=
+         GetFirstQuickSettingsFocusable()) {
+    DoShiftTab();
+  }
+
+  // Shift tab at the first element in the quick settings bubble should move
+  // focus to the last element in the message center.
+  DoShiftTab();
+  EXPECT_TRUE(message_center_widget->IsActive());
+  EXPECT_FALSE(quick_settings_widget->IsActive());
+  EXPECT_EQ(message_center_widget->GetFocusManager()->GetFocusedView(),
+            GetLastMessageCenterFocusable());
+
+  // Keep shift tabbing until we reach the first focusable element in the
+  // message center bubble.
+  while (message_center_widget->GetFocusManager()->GetFocusedView() !=
+         GetFirstMessageCenterFocusable()) {
+    DoShiftTab();
+  }
+
+  // Shift tab at the first element in the message center bubble should move
+  // focus to the last element in the quick settings bubble.
+  DoShiftTab();
+  EXPECT_TRUE(quick_settings_widget->IsActive());
+  EXPECT_FALSE(message_center_widget->IsActive());
+  EXPECT_EQ(quick_settings_widget->GetFocusManager()->GetFocusedView(),
+            GetLastQuickSettingsFocusable());
+}
+
 }  // namespace ash
diff --git a/ash/system/message_center/unified_message_center_view.cc b/ash/system/message_center/unified_message_center_view.cc
index d560bd6..41ed6624 100644
--- a/ash/system/message_center/unified_message_center_view.cc
+++ b/ash/system/message_center/unified_message_center_view.cc
@@ -15,6 +15,7 @@
 #include "ash/style/default_color_constants.h"
 #include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
 #include "ash/system/message_center/message_center_scroll_bar.h"
+#include "ash/system/message_center/unified_message_center_bubble.h"
 #include "ash/system/message_center/unified_message_list_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_popup_utils.h"
@@ -35,6 +36,7 @@
 #include "ui/views/background.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/scroll_view.h"
+#include "ui/views/focus/focus_search.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/widget/widget.h"
@@ -279,15 +281,18 @@
 
 UnifiedMessageCenterView::UnifiedMessageCenterView(
     UnifiedSystemTrayView* parent,
-    UnifiedSystemTrayModel* model)
+    UnifiedSystemTrayModel* model,
+    UnifiedMessageCenterBubble* bubble)
     : parent_(parent),
       model_(model),
+      message_center_bubble_(bubble),
       stacking_counter_(new StackingNotificationCounterView(this)),
       scroll_bar_(new MessageCenterScrollBar(this)),
       scroller_(new views::ScrollView()),
       message_list_view_(new UnifiedMessageListView(this, model)),
       last_scroll_position_from_bottom_(0),
-      animation_(std::make_unique<gfx::LinearAnimation>(this)) {
+      animation_(std::make_unique<gfx::LinearAnimation>(this)),
+      focus_search_(std::make_unique<views::FocusSearch>(this, false, false)) {
   message_list_view_->Init();
 
   AddChildView(stacking_counter_);
@@ -451,6 +456,29 @@
     return;
 
   OnMessageCenterScrolled();
+
+  if (features::IsUnifiedMessageCenterRefactorEnabled()) {
+    views::View* first_view = GetFirstFocusableChild();
+    views::View* last_view = GetLastFocusableChild();
+
+    // If we are cycling back to the first view from the last view or vice
+    // verse. Focus out of the message center to the quick settings bubble. The
+    // direction of the cycle determines where the focus will move to in quick
+    // settings.
+    bool focused_out = false;
+    if (before == last_view && now == first_view)
+      focused_out = message_center_bubble_->FocusOut(false /* reverse */);
+    else if (before == first_view && now == last_view)
+      focused_out = message_center_bubble_->FocusOut(true /* reverse */);
+
+    // Clear the focus state completely for the message center.
+    // We acquire the focus back from the quick settings widget based on the
+    // cycling direction.
+    if (focused_out) {
+      GetFocusManager()->ClearFocus();
+      GetFocusManager()->SetStoredFocusView(nullptr);
+    }
+  }
 }
 
 void UnifiedMessageCenterView::AnimationEnded(const gfx::Animation* animation) {
@@ -621,4 +649,32 @@
   SetNotificationRectBelowScroll(rect_below_scroll);
 }
 
+void UnifiedMessageCenterView::FocusEntered(bool reverse) {
+  views::View* focus_view =
+      reverse ? GetLastFocusableChild() : GetFirstFocusableChild();
+  GetFocusManager()->SetFocusedView(focus_view);
+}
+
+views::View* UnifiedMessageCenterView::GetFirstFocusableChild() {
+  views::FocusTraversable* dummy_focus_traversable;
+  views::View* dummy_focus_traversable_view;
+  return focus_search_->FindNextFocusableView(
+      nullptr, views::FocusSearch::SearchDirection::kForwards,
+      views::FocusSearch::TraversalDirection::kDown,
+      views::FocusSearch::StartingViewPolicy::kSkipStartingView,
+      views::FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
+      &dummy_focus_traversable, &dummy_focus_traversable_view);
+}
+
+views::View* UnifiedMessageCenterView::GetLastFocusableChild() {
+  views::FocusTraversable* dummy_focus_traversable;
+  views::View* dummy_focus_traversable_view;
+  return focus_search_->FindNextFocusableView(
+      nullptr, views::FocusSearch::SearchDirection::kBackwards,
+      views::FocusSearch::TraversalDirection::kDown,
+      views::FocusSearch::StartingViewPolicy::kSkipStartingView,
+      views::FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
+      &dummy_focus_traversable, &dummy_focus_traversable_view);
+}
+
 }  // namespace ash
diff --git a/ash/system/message_center/unified_message_center_view.h b/ash/system/message_center/unified_message_center_view.h
index aa9df6d..2f37b9b 100644
--- a/ash/system/message_center/unified_message_center_view.h
+++ b/ash/system/message_center/unified_message_center_view.h
@@ -26,6 +26,7 @@
 namespace ash {
 
 class MessageCenterScrollBar;
+class UnifiedMessageCenterBubble;
 class UnifiedSystemTrayModel;
 class UnifiedSystemTrayView;
 
@@ -92,7 +93,8 @@
       public gfx::AnimationDelegate {
  public:
   UnifiedMessageCenterView(UnifiedSystemTrayView* parent,
-                           UnifiedSystemTrayModel* model);
+                           UnifiedSystemTrayModel* model,
+                           UnifiedMessageCenterBubble* bubble);
   ~UnifiedMessageCenterView() override;
 
   // Sets the maximum height that the view can take.
@@ -119,6 +121,11 @@
   // Count number of notifications that are above visible area.
   int GetStackedNotificationCount() const;
 
+  // Set the first child view to be focused when focus is acquired.
+  // This is the first visible child unless reverse is true, in which case
+  // it is the last visible child.
+  void FocusEntered(bool reverse);
+
   // views::View:
   void AddedToWidget() override;
   void RemovedFromWidget() override;
@@ -148,6 +155,7 @@
 
  private:
   friend class UnifiedMessageCenterViewTest;
+  friend class UnifiedMessageCenterBubbleTest;
 
   // Starts the animation to hide the notification stacking bar.
   void StartHideStackingBarAnimation();
@@ -169,8 +177,15 @@
   // TopCornerBorder.
   void NotifyRectBelowScroll();
 
+  // Get first and last focusable child views. These functions are used to
+  // figure out if we need to focus out or to set the correct focused view
+  // when focus is acquired from another widget.
+  View* GetFirstFocusableChild();
+  View* GetLastFocusableChild();
+
   UnifiedSystemTrayView* const parent_;
   UnifiedSystemTrayModel* const model_;
+  UnifiedMessageCenterBubble* const message_center_bubble_;
   StackingNotificationCounterView* const stacking_counter_;
   MessageCenterScrollBar* const scroll_bar_;
   views::ScrollView* const scroller_;
@@ -189,6 +204,8 @@
       UnifiedMessageCenterAnimationState::IDLE;
   const std::unique_ptr<gfx::LinearAnimation> animation_;
 
+  const std::unique_ptr<views::FocusSearch> focus_search_;
+
   views::FocusManager* focus_manager_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(UnifiedMessageCenterView);
diff --git a/ash/system/message_center/unified_message_center_view_unittest.cc b/ash/system/message_center/unified_message_center_view_unittest.cc
index 4af630b..935a808e 100644
--- a/ash/system/message_center/unified_message_center_view_unittest.cc
+++ b/ash/system/message_center/unified_message_center_view_unittest.cc
@@ -43,7 +43,9 @@
 class TestUnifiedMessageCenterView : public UnifiedMessageCenterView {
  public:
   explicit TestUnifiedMessageCenterView(UnifiedSystemTrayModel* model)
-      : UnifiedMessageCenterView(nullptr, model) {}
+      : UnifiedMessageCenterView(nullptr /*parent*/,
+                                 model,
+                                 nullptr /*bubble*/) {}
 
   ~TestUnifiedMessageCenterView() override = default;
 
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc
index 1fbf3447..7061232 100644
--- a/ash/system/unified/unified_system_tray.cc
+++ b/ash/system/unified/unified_system_tray.cc
@@ -5,6 +5,7 @@
 #include "ash/system/unified/unified_system_tray.h"
 
 #include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/focus_cycler.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/shelf.h"
@@ -211,6 +212,31 @@
   ui_delegate_->SetTrayBubbleHeight(height);
 }
 
+bool UnifiedSystemTray::FocusMessageCenter(bool reverse) {
+  if (!IsMessageCenterBubbleShown())
+    return false;
+
+  views::Widget* message_center_widget =
+      message_center_bubble_->GetBubbleWidget();
+  message_center_widget->widget_delegate()->SetCanActivate(true);
+
+  Shell::Get()->focus_cycler()->FocusWidget(message_center_widget);
+  message_center_bubble_->FocusEntered(reverse);
+
+  return true;
+}
+
+bool UnifiedSystemTray::FocusQuickSettings(bool reverse) {
+  if (!IsBubbleShown())
+    return false;
+
+  views::Widget* quick_settings_widget = bubble_->GetBubbleWidget();
+  Shell::Get()->focus_cycler()->FocusWidget(quick_settings_widget);
+  bubble_->FocusEntered(reverse);
+
+  return true;
+}
+
 gfx::Rect UnifiedSystemTray::GetBubbleBoundsInScreen() const {
   return bubble_ ? bubble_->GetBoundsInScreen() : gfx::Rect();
 }
diff --git a/ash/system/unified/unified_system_tray.h b/ash/system/unified/unified_system_tray.h
index 8dfb7d5..eb1ba069 100644
--- a/ash/system/unified/unified_system_tray.h
+++ b/ash/system/unified/unified_system_tray.h
@@ -97,6 +97,10 @@
   // bubble is shown.
   void SetTrayBubbleHeight(int height);
 
+  bool FocusMessageCenter(bool reverse);
+
+  bool FocusQuickSettings(bool reverse);
+
   // TrayBackgroundView:
   bool PerformAction(const ui::Event& event) override;
   void ShowBubble(bool show_by_click) override;
diff --git a/ash/system/unified/unified_system_tray_bubble.cc b/ash/system/unified/unified_system_tray_bubble.cc
index 69231dbb..6c236df 100644
--- a/ash/system/unified/unified_system_tray_bubble.cc
+++ b/ash/system/unified/unified_system_tray_bubble.cc
@@ -253,6 +253,14 @@
   return free_space_height_above_anchor - kUnifiedMenuPadding * 2;
 }
 
+bool UnifiedSystemTrayBubble::FocusOut(bool reverse) {
+  return tray_->FocusMessageCenter(reverse);
+}
+
+void UnifiedSystemTrayBubble::FocusEntered(bool reverse) {
+  unified_view_->FocusEntered(reverse);
+}
+
 void UnifiedSystemTrayBubble::OnDisplayConfigurationChanged() {
   UpdateBubbleBounds();
 }
diff --git a/ash/system/unified/unified_system_tray_bubble.h b/ash/system/unified/unified_system_tray_bubble.h
index 18a1103..14806d2 100644
--- a/ash/system/unified/unified_system_tray_bubble.h
+++ b/ash/system/unified/unified_system_tray_bubble.h
@@ -84,6 +84,12 @@
   // collapsed / expanded.
   int GetCurrentTrayHeight() const;
 
+  // Relinquish focus and transfer it to the message center widget.
+  bool FocusOut(bool reverse);
+
+  // Inform UnifiedSystemTrayView of focus being acquired.
+  void FocusEntered(bool reverse);
+
   // TrayBubbleBase:
   TrayBackgroundView* GetTray() const override;
   TrayBubbleView* GetBubbleView() const override;
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc
index b5a69b5..d91c2ba 100644
--- a/ash/system/unified/unified_system_tray_controller.cc
+++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -300,6 +300,10 @@
     unified_view_->GetWidget()->CloseNow();
 }
 
+bool UnifiedSystemTrayController::FocusOut(bool reverse) {
+  return bubble_->FocusOut(reverse);
+}
+
 void UnifiedSystemTrayController::EnsureExpanded() {
   if (detailed_view_controller_) {
     detailed_view_controller_.reset();
diff --git a/ash/system/unified/unified_system_tray_controller.h b/ash/system/unified/unified_system_tray_controller.h
index 211472c..0412e791 100644
--- a/ash/system/unified/unified_system_tray_controller.h
+++ b/ash/system/unified/unified_system_tray_controller.h
@@ -101,6 +101,10 @@
   // Close the bubble. Called from a detailed view controller.
   void CloseBubble();
 
+  // Inform UnifiedSystemTrayBubble that UnifiedSystemTrayView is requesting to
+  // relinquish focus.
+  bool FocusOut(bool reverse);
+
   // Ensure the main view is expanded. Called from the slider bubble controller.
   void EnsureExpanded();
 
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc
index 89eccdb..4d5bb978 100644
--- a/ash/system/unified/unified_system_tray_view.cc
+++ b/ash/system/unified/unified_system_tray_view.cc
@@ -214,7 +214,8 @@
     views::View* default_start_view =
         search_direction == FocusSearch::SearchDirection::kForwards
             ? view_->system_tray_container_
-            : view_->notification_hidden_view_;
+            : view_->detailed_view_container_;
+
     return views::FocusSearch::FindNextFocusableView(
         starting_view ? starting_view : default_start_view, search_direction,
         traversal_direction,
@@ -279,8 +280,8 @@
       Shell::Get()->session_controller();
 
   if (!features::IsUnifiedMessageCenterRefactorEnabled()) {
-    message_center_view_ =
-        new UnifiedMessageCenterView(this, controller->model());
+    message_center_view_ = new UnifiedMessageCenterView(
+        this, controller->model(), nullptr /* message_center_bubble */);
     AddChildView(message_center_view_);
     layout->SetFlexForView(message_center_view_, 1);
   }
@@ -467,6 +468,34 @@
   return feature_pods_container_->GetVisibleCount();
 }
 
+views::View* UnifiedSystemTrayView::GetFirstFocusableChild() {
+  FocusTraversable* focus_traversable = GetFocusTraversable();
+  views::View* focus_traversable_view = this;
+  return focus_search_->FindNextFocusableView(
+      nullptr, FocusSearch::SearchDirection::kForwards,
+      FocusSearch::TraversalDirection::kDown,
+      FocusSearch::StartingViewPolicy::kSkipStartingView,
+      FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
+      &focus_traversable, &focus_traversable_view);
+}
+
+views::View* UnifiedSystemTrayView::GetLastFocusableChild() {
+  FocusTraversable* focus_traversable = GetFocusTraversable();
+  views::View* focus_traversable_view = this;
+  return focus_search_->FindNextFocusableView(
+      nullptr, FocusSearch::SearchDirection::kBackwards,
+      FocusSearch::TraversalDirection::kDown,
+      FocusSearch::StartingViewPolicy::kSkipStartingView,
+      FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
+      &focus_traversable, &focus_traversable_view);
+}
+
+void UnifiedSystemTrayView::FocusEntered(bool reverse) {
+  views::View* focus_view =
+      reverse ? GetLastFocusableChild() : GetFirstFocusableChild();
+  GetFocusManager()->SetFocusedView(focus_view);
+}
+
 void UnifiedSystemTrayView::OnGestureEvent(ui::GestureEvent* event) {
   gfx::Point screen_location = event->location();
   ConvertPointToScreen(this, &screen_location);
@@ -502,6 +531,19 @@
   return "UnifiedSystemTrayView";
 }
 
+void UnifiedSystemTrayView::AddedToWidget() {
+  focus_manager_ = GetFocusManager();
+  if (focus_manager_)
+    focus_manager_->AddFocusChangeListener(this);
+}
+
+void UnifiedSystemTrayView::RemovedFromWidget() {
+  if (!focus_manager_)
+    return;
+  focus_manager_->RemoveFocusChangeListener(this);
+  focus_manager_ = nullptr;
+}
+
 views::FocusTraversable* UnifiedSystemTrayView::GetFocusTraversable() {
   return this;
 }
@@ -518,4 +560,26 @@
   return this;
 }
 
+void UnifiedSystemTrayView::OnWillChangeFocus(views::View* before,
+                                              views::View* now) {}
+
+void UnifiedSystemTrayView::OnDidChangeFocus(views::View* before,
+                                             views::View* now) {
+  if (features::IsUnifiedMessageCenterRefactorEnabled()) {
+    views::View* first_view = GetFirstFocusableChild();
+    views::View* last_view = GetLastFocusableChild();
+
+    bool focused_out = false;
+    if (before == last_view && now == first_view)
+      focused_out = controller_->FocusOut(false);
+    else if (before == first_view && now == last_view)
+      focused_out = controller_->FocusOut(true);
+
+    if (focused_out) {
+      GetFocusManager()->ClearFocus();
+      GetFocusManager()->SetStoredFocusView(nullptr);
+    }
+  }
+}
+
 }  // namespace ash
diff --git a/ash/system/unified/unified_system_tray_view.h b/ash/system/unified/unified_system_tray_view.h
index a07a42e..d86674af 100644
--- a/ash/system/unified/unified_system_tray_view.h
+++ b/ash/system/unified/unified_system_tray_view.h
@@ -58,7 +58,8 @@
 // Note that the term "UnifiedSystemTray" refers to entire bubble containing
 // both (1) and (2).
 class ASH_EXPORT UnifiedSystemTrayView : public views::View,
-                                         public views::FocusTraversable {
+                                         public views::FocusTraversable,
+                                         public views::FocusChangeListener {
  public:
   // Get the background color of unified system tray.
   static SkColor GetBackgroundColor();
@@ -91,6 +92,11 @@
   void SaveFocus();
   void RestoreFocus();
 
+  // Set the first child view to be focused when focus is acquired.
+  // This is the first visible child unless reverse is true, in which case
+  // it is the last visible child.
+  void FocusEntered(bool reverse);
+
   // Change the expanded state. 0.0 if collapsed, and 1.0 if expanded.
   // Otherwise, it shows intermediate state.
   void SetExpandedAmount(double expanded_amount);
@@ -130,12 +136,18 @@
   void ChildPreferredSizeChanged(views::View* child) override;
   const char* GetClassName() const override;
   views::FocusTraversable* GetFocusTraversable() override;
+  void AddedToWidget() override;
+  void RemovedFromWidget() override;
 
   // views::FocusTraversable:
   views::FocusSearch* GetFocusSearch() override;
   views::FocusTraversable* GetFocusTraversableParent() override;
   views::View* GetFocusTraversableParentView() override;
 
+  // views::FocusChangeListener:
+  void OnWillChangeFocus(views::View* before, views::View* now) override;
+  void OnDidChangeFocus(views::View* before, views::View* now) override;
+
   NotificationHiddenView* notification_hidden_view_for_testing() {
     return notification_hidden_view_;
   }
@@ -143,6 +155,14 @@
   View* detailed_view_for_testing() { return detailed_view_container_; }
 
  private:
+  friend class UnifiedMessageCenterBubbleTest;
+
+  // Get first and last focusable child views. These functions are used to
+  // figure out if we need to focus out or to set the correct focused view
+  // when focus is acquired from another widget.
+  View* GetFirstFocusableChild();
+  View* GetLastFocusableChild();
+
   class FocusSearch;
 
   double expanded_amount_;
@@ -171,6 +191,9 @@
   views::View* saved_focused_view_ = nullptr;
 
   const std::unique_ptr<FocusSearch> focus_search_;
+
+  views::FocusManager* focus_manager_ = nullptr;
+
   const std::unique_ptr<ui::EventHandler> interacted_by_tap_recorder_;
 
   DISALLOW_COPY_AND_ASSIGN(UnifiedSystemTrayView);
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index 9c137128..1a10632 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -1019,23 +1019,21 @@
   }
   aura::Window* root_window = GetDefaultSnappedWindow()->GetRootWindow();
 
-  if (state_ == SplitViewState::kBothSnapped) {
-    // If overview is ended because of the window gets snapped, do not do
-    // exiting overview animation.
+  // If overview is ended because of a window getting snapped, suppress the
+  // overview exiting animation.
+  if (state_ == SplitViewState::kBothSnapped)
     overview_session->SetWindowListNotAnimatedWhenExiting(root_window);
-    return;
-  }
 
-  // If we're in clamshell splitview mode, do not auto snap overview window
-  // when overview ends.
+  // If clamshell split view mode is active, end it and bail out.
   if (split_view_type_ == SplitViewType::kClamshellType) {
     EndSplitView();
     return;
   }
 
-  // If split view mode is active but only has one snapped window when overview
-  // mode is ending, retrieve the first snappable window in the overview window
-  // grid and snap it.
+  // Tablet split view mode is active. If it still only has one snapped window,
+  // snap the first snappable window in the overview grid on the other side.
+  if (state_ == SplitViewState::kBothSnapped)
+    return;
   OverviewGrid* current_grid =
       overview_session->GetGridWithRootWindow(root_window);
   if (!current_grid || current_grid->empty())
@@ -1526,12 +1524,6 @@
   RestoreTransformIfApplicable(window);
   UpdateSplitViewStateAndNotifyObservers();
   UpdateWindowStackingAfterSnap(window);
-
-  // If there are two window snapped in clamshell mode, splitview mode is ended.
-  if (state_ == SplitViewState::kBothSnapped &&
-      split_view_type_ == SplitViewType::kClamshellType) {
-    EndSplitView();
-  }
 }
 
 void SplitViewController::OnSnappedWindowDetached(aura::Window* window,
diff --git a/base/android/java/src/org/chromium/base/ContextUtils.java b/base/android/java/src/org/chromium/base/ContextUtils.java
index 167282a..e7007cc 100644
--- a/base/android/java/src/org/chromium/base/ContextUtils.java
+++ b/base/android/java/src/org/chromium/base/ContextUtils.java
@@ -10,6 +10,7 @@
 import android.content.ContextWrapper;
 import android.content.SharedPreferences;
 import android.content.res.AssetManager;
+import android.os.Build;
 import android.os.Process;
 import android.preference.PreferenceManager;
 
@@ -17,6 +18,7 @@
 
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.MainDex;
+import org.chromium.base.annotations.VerifiesOnP;
 
 /**
  * This class provides Android application context related utility methods.
@@ -25,8 +27,6 @@
 public class ContextUtils {
     private static final String TAG = "ContextUtils";
     private static Context sApplicationContext;
-    // TODO(agrieve): Remove sProcessName caching when we stop supporting JB.
-    private static String sProcessName;
 
     /**
      * Initialization-on-demand holder. This exists for thread-safe lazy initialization.
@@ -131,35 +131,22 @@
     /**
      * @return Whether the process is isolated.
      */
+    @SuppressWarnings("NewApi")
     public static boolean isIsolatedProcess() {
-        try {
-            return (Boolean) Process.class.getMethod("isIsolated").invoke(null);
-        } catch (Exception e) { // No multi-catch below API level 19 for reflection exceptions.
-            // If fallback logic is ever needed, refer to:
-            // https://chromium-review.googlesource.com/c/chromium/src/+/905563/1
-            throw new RuntimeException(e);
-        }
+        // Was not made visible until Android P, but the method has always been there.
+        return Process.isIsolated();
     }
 
     /** @return The name of the current process. E.g. "org.chromium.chrome:privileged_process0". */
+    @VerifiesOnP
     public static String getProcessName() {
-        // Once we drop support JB, this method can be simplified to not cache sProcessName and call
-        // ActivityThread.currentProcessName().
-        if (sProcessName != null) {
-            return sProcessName;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            return Application.getProcessName();
         }
         try {
-            // An even more convenient ActivityThread.currentProcessName() exists, but was not added
-            // until JB MR2.
             Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
-            Object activityThread =
-                    activityThreadClazz.getMethod("currentActivityThread").invoke(null);
-            // Before JB MR2, currentActivityThread() returns null when called on a non-UI thread.
-            // Cache the name to allow other threads to access it.
-            sProcessName =
-                    (String) activityThreadClazz.getMethod("getProcessName").invoke(activityThread);
-            return sProcessName;
-        } catch (Exception e) { // No multi-catch below API level 19 for reflection exceptions.
+            return (String) activityThreadClazz.getMethod("currentProcessName").invoke(null);
+        } catch (Exception e) {
             // If fallback logic is ever needed, refer to:
             // https://chromium-review.googlesource.com/c/chromium/src/+/905563/1
             throw new RuntimeException(e);
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index deb412f..5936b57 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -4100,6 +4100,7 @@
   #       any apk that depends on this library.
   #   ignore_aidl: Whether to ignore .aidl files found with the .aar.
   #   ignore_assets: Whether to ignore assets found in the .aar.
+  #   ignore_manifest: Whether to ignore creating manifest.
   #   ignore_native_libraries: Whether to ignore .so files found in the .aar.
   #       See also extract_native_libraries.
   #   ignore_proguard_configs: Whether to ignore proguard configs.
@@ -4129,6 +4130,8 @@
     _unpack_target_name = "${target_name}__unpack_aar"
     _ignore_aidl = defined(invoker.ignore_aidl) && invoker.ignore_aidl
     _ignore_assets = defined(invoker.ignore_assets) && invoker.ignore_assets
+    _ignore_manifest =
+        defined(invoker.ignore_manifest) && invoker.ignore_manifest
     _ignore_native_libraries = defined(invoker.ignore_native_libraries) &&
                                invoker.ignore_native_libraries
     _ignore_proguard_configs = defined(invoker.ignore_proguard_configs) &&
@@ -4193,10 +4196,10 @@
       inputs = [
         invoker.aar_path,
       ]
-      outputs = [
-        "${_output_path}/AndroidManifest.xml",
-      ]
-
+      outputs = []
+      if (!_ignore_manifest) {
+        outputs += [ "${_output_path}/AndroidManifest.xml" ]
+      }
       if (!_strip_resources && _scanned_files.has_r_text_file) {
         # Certain packages, in particular Play Services have no R.txt even
         # though its presence is mandated by AAR spec. Such packages cause
@@ -4231,7 +4234,8 @@
         (_scanned_files.resources != [] || _scanned_files.has_r_text_file)
 
     # Create the android_resources target for resources.
-    if (_has_unignored_resources || !_scanned_files.is_manifest_empty) {
+    if (_has_unignored_resources ||
+        (!_scanned_files.is_manifest_empty && !_ignore_manifest)) {
       _res_target_name = "${target_name}__res"
       android_resources(_res_target_name) {
         forward_variables_from(invoker,
@@ -4246,7 +4250,9 @@
         }
         deps += [ ":$_unpack_target_name" ]
         android_manifest_dep = ":$_unpack_target_name"
-        android_manifest = "${_output_path}/AndroidManifest.xml"
+        if (!_ignore_manifest) {
+          android_manifest = "${_output_path}/AndroidManifest.xml"
+        }
         resource_dirs = []
         generated_resource_dirs = []
         if (!_strip_resources && _scanned_files.resources != []) {
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index e4c3ddf..af82c66 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8901536583754825344
\ No newline at end of file
+8901506071232259296
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 99e4d73..908d9741 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8901534789813246320
\ No newline at end of file
+8901506068351790832
\ No newline at end of file
diff --git a/cc/metrics/frame_sequence_tracker.cc b/cc/metrics/frame_sequence_tracker.cc
index 8786c6f..587c9bb 100644
--- a/cc/metrics/frame_sequence_tracker.cc
+++ b/cc/metrics/frame_sequence_tracker.cc
@@ -78,9 +78,7 @@
     CompositorFrameReportingController* compositor_frame_reporting_controller)
     : is_single_threaded_(is_single_threaded),
       compositor_frame_reporting_controller_(
-          compositor_frame_reporting_controller) {
-  StartSequence(FrameSequenceTrackerType::kUniversal);
-}
+          compositor_frame_reporting_controller) {}
 
 FrameSequenceTrackerCollection::~FrameSequenceTrackerCollection() {
   frame_trackers_.clear();
@@ -119,7 +117,6 @@
 void FrameSequenceTrackerCollection::ClearAll() {
   frame_trackers_.clear();
   removal_trackers_.clear();
-  StartSequence(FrameSequenceTrackerType::kUniversal);
 }
 
 void FrameSequenceTrackerCollection::NotifyBeginImplFrame(
diff --git a/cc/metrics/frame_sequence_tracker_unittest.cc b/cc/metrics/frame_sequence_tracker_unittest.cc
index 1c492db..f1a6ed6 100644
--- a/cc/metrics/frame_sequence_tracker_unittest.cc
+++ b/cc/metrics/frame_sequence_tracker_unittest.cc
@@ -88,6 +88,8 @@
     // The kTouchScroll tracker is created in the test constructor, and the
     // kUniversal tracker is created in the FrameSequenceTrackerCollection
     // constructor.
+    EXPECT_EQ(collection_.frame_trackers_.size(), 3u);
+    collection_.StartSequence(FrameSequenceTrackerType::kUniversal);
     EXPECT_EQ(collection_.frame_trackers_.size(), 4u);
 
     collection_.StopSequence(kCompositorAnimation);
@@ -152,13 +154,19 @@
 }
 
 TEST_F(FrameSequenceTrackerTest, UniversalTrackerCreation) {
-  // The universal tracker should have been created when the |collection_| is
-  // created.
-  EXPECT_TRUE(TrackerExists(FrameSequenceTrackerType::kUniversal));
+  // The universal tracker should be explicitly created by the object that
+  // manages the |collection_|
+  EXPECT_FALSE(TrackerExists(FrameSequenceTrackerType::kUniversal));
 }
 
-TEST_F(FrameSequenceTrackerTest, UniversalTrackerExistsAfterClearAll) {
+TEST_F(FrameSequenceTrackerTest, UniversalTrackerRestartableAfterClearAll) {
+  collection_.StartSequence(FrameSequenceTrackerType::kUniversal);
+  EXPECT_TRUE(TrackerExists(FrameSequenceTrackerType::kUniversal));
+
   collection_.ClearAll();
+  EXPECT_FALSE(TrackerExists(FrameSequenceTrackerType::kUniversal));
+
+  collection_.StartSequence(FrameSequenceTrackerType::kUniversal);
   EXPECT_TRUE(TrackerExists(FrameSequenceTrackerType::kUniversal));
 }
 
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 8d8d18e2..cd9fe74 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -3466,6 +3466,8 @@
   has_valid_layer_tree_frame_sink_ = true;
 
   auto* context_provider = layer_tree_frame_sink_->context_provider();
+  frame_trackers_.StartSequence(FrameSequenceTrackerType::kUniversal);
+
   if (context_provider) {
     max_texture_size_ =
         context_provider->ContextCapabilities().max_texture_size;
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 03dcf65..59c0b81 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1815,6 +1815,7 @@
         ":angle_egl_symbols",
         ":angle_gles_symbols",
         ":chrome_symbols",
+        ":crashpad_symbols",
         ":swiftshader_egl_symbols",
         ":swiftshader_gles_symbols",
       ]
@@ -1833,6 +1834,20 @@
         ":chrome",
       ]
     }
+    extract_symbols("crashpad_symbols") {
+      binary = "$root_out_dir/crashpad_handler"
+
+      if (current_cpu == "x86") {
+        # GYP used "ia32" so keep that naming for back-compat.
+        symbol_file = "$root_out_dir/crashpad.breakpad.ia32"
+      } else {
+        symbol_file = "$root_out_dir/crashpad.breakpad.$current_cpu"
+      }
+
+      deps = [
+        "//third_party/crashpad/crashpad/handler:crashpad_handler",
+      ]
+    }
     extract_symbols("swiftshader_egl_symbols") {
       binary = "$root_out_dir/swiftshader/libEGL.so"
 
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 90f21e2..aeac9d9 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -509,6 +509,7 @@
     "//chrome/android/features/keyboard_accessory:internal_java",
     "//chrome/android/features/media_router:java",
     "//chrome/android/features/test_dummy/internal:base_module_java",
+    "//chrome/android/modules/extra_icu/provider:java",
     "//chrome/browser/touch_to_fill/android/internal:java",
   ]
 
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 3d1fbe0..8b207ec 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -156,6 +156,7 @@
   "junit/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManagerTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTrackerTest.java",
+  "junit/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleHorizontalLayoutViewTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsViewBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsViewBinder.java
index 46580ed..706fa26 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsViewBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsViewBinder.java
@@ -28,10 +28,10 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.compositor.animation.CompositorAnimator;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.image_fetcher.ImageFetcher;
 import org.chromium.chrome.browser.modaldialog.AppModalPresenter;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
@@ -305,7 +305,7 @@
         mPulseAnimation.setEvaluator(new ArgbEvaluator());
         mPulseAnimation.setRepeatCount(ValueAnimator.INFINITE);
         mPulseAnimation.setRepeatMode(ValueAnimator.REVERSE);
-        mPulseAnimation.setInterpolator(CompositorAnimator.ACCELERATE_INTERPOLATOR);
+        mPulseAnimation.setInterpolator(Interpolators.ACCELERATE_INTERPOLATOR);
         mPulseAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationCancel(Animator animation) {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java
index d36c220..afb812c 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java
@@ -10,7 +10,7 @@
 import android.view.View;
 
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.compositor.animation.CompositorAnimator;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.widget.MaterialProgressBar;
 
 import java.util.ArrayDeque;
@@ -54,7 +54,7 @@
         ValueAnimator progressAnimation = ValueAnimator.ofInt(mLastProgress, progress);
         progressAnimation.setDuration(
                 mProgressBarSpeedMs * Math.abs(progress - mLastProgress) / 100);
-        progressAnimation.setInterpolator(CompositorAnimator.ACCELERATE_INTERPOLATOR);
+        progressAnimation.setInterpolator(Interpolators.ACCELERATE_INTERPOLATOR);
         progressAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
diff --git a/chrome/android/features/start_surface/DEPS b/chrome/android/features/start_surface/DEPS
index 3b1fce5..175bf31 100644
--- a/chrome/android/features/start_surface/DEPS
+++ b/chrome/android/features/start_surface/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
+  "+chrome/browser/ui/android/widget",
   "+content/public/android/java/src/org/chromium/content_public",
 ]
\ No newline at end of file
diff --git a/chrome/android/features/start_surface/internal/BUILD.gn b/chrome/android/features/start_surface/internal/BUILD.gn
index d7192a4..6f6748a5 100644
--- a/chrome/android/features/start_surface/internal/BUILD.gn
+++ b/chrome/android/features/start_surface/internal/BUILD.gn
@@ -95,6 +95,7 @@
     ":java_resources",
     "//base:base_java",
     "//chrome/android:chrome_java",
+    "//chrome/browser/ui/android/widget:java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:com_android_support_design_java",
     "//ui/android:ui_full_java",
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
index ab8cfd53..be45aa8 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceLayout.java
@@ -4,8 +4,6 @@
 
 package org.chromium.chrome.features.start_surface;
 
-import static org.chromium.chrome.browser.compositor.animation.CompositorAnimator.FAST_OUT_SLOW_IN_INTERPOLATOR;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -37,6 +35,7 @@
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.ui.resources.ResourceManager;
 
@@ -280,25 +279,25 @@
         animationList.add(CompositorAnimator.ofFloatProperty(
                 handler, sourceLayoutTab, LayoutTab.SCALE, () -> 1f, () -> {
                     return target.get().width() / (getWidth() * mDpToPx);
-                }, ZOOMING_DURATION, FAST_OUT_SLOW_IN_INTERPOLATOR));
+                }, ZOOMING_DURATION, Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR));
         animationList.add(CompositorAnimator.ofFloatProperty(
                 handler, sourceLayoutTab, LayoutTab.X, () -> 0f, () -> {
                     return target.get().left / mDpToPx;
-                }, ZOOMING_DURATION, FAST_OUT_SLOW_IN_INTERPOLATOR));
+                }, ZOOMING_DURATION, Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR));
         animationList.add(CompositorAnimator.ofFloatProperty(
                 handler, sourceLayoutTab, LayoutTab.Y, () -> 0f, () -> {
                     return target.get().top / mDpToPx;
-                }, ZOOMING_DURATION, FAST_OUT_SLOW_IN_INTERPOLATOR));
+                }, ZOOMING_DURATION, Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR));
         // TODO(crbug.com/964406): when shrinking to the bottom row, bottom of the tab goes up and
         // down, making the "create group" visible for a while.
         animationList.add(CompositorAnimator.ofFloatProperty(handler, sourceLayoutTab,
                 LayoutTab.MAX_CONTENT_HEIGHT, sourceLayoutTab.getUnclampedOriginalContentHeight(),
-                getWidth(), ZOOMING_DURATION, FAST_OUT_SLOW_IN_INTERPOLATOR));
+                getWidth(), ZOOMING_DURATION, Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR));
 
         CompositorAnimator backgroundAlpha =
                 CompositorAnimator.ofFloat(handler, 0f, 1f, BACKGROUND_FADING_DURATION_MS,
                         animator -> mBackgroundAlpha = animator.getAnimatedValue());
-        backgroundAlpha.setInterpolator(CompositorAnimator.FAST_OUT_LINEAR_IN_INTERPOLATOR);
+        backgroundAlpha.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN_INTERPOLATOR);
         animationList.add(backgroundAlpha);
 
         mTabToSwitcherAnimation = new AnimatorSet();
@@ -334,22 +333,24 @@
         // Zoom in the source tab
         animationList.add(CompositorAnimator.ofFloatProperty(handler, sourceLayoutTab,
                 LayoutTab.SCALE, source.width() / (getWidth() * mDpToPx), 1, ZOOMING_DURATION,
-                FAST_OUT_SLOW_IN_INTERPOLATOR));
+                Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR));
         animationList.add(CompositorAnimator.ofFloatProperty(handler, sourceLayoutTab, LayoutTab.X,
-                source.left / mDpToPx, 0f, ZOOMING_DURATION, FAST_OUT_SLOW_IN_INTERPOLATOR));
+                source.left / mDpToPx, 0f, ZOOMING_DURATION,
+                Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR));
         animationList.add(CompositorAnimator.ofFloatProperty(handler, sourceLayoutTab, LayoutTab.Y,
-                source.top / mDpToPx, 0f, ZOOMING_DURATION, FAST_OUT_SLOW_IN_INTERPOLATOR));
+                source.top / mDpToPx, 0f, ZOOMING_DURATION,
+                Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR));
         // TODO(crbug.com/964406): when shrinking to the bottom row, bottom of the tab goes up and
         // down, making the "create group" visible for a while.
         animationList.add(CompositorAnimator.ofFloatProperty(handler, sourceLayoutTab,
                 LayoutTab.MAX_CONTENT_HEIGHT, getWidth(),
                 sourceLayoutTab.getUnclampedOriginalContentHeight(), ZOOMING_DURATION,
-                FAST_OUT_SLOW_IN_INTERPOLATOR));
+                Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR));
 
         CompositorAnimator backgroundAlpha =
                 CompositorAnimator.ofFloat(handler, 1f, 0f, BACKGROUND_FADING_DURATION_MS,
                         animator -> mBackgroundAlpha = animator.getAnimatedValue());
-        backgroundAlpha.setInterpolator(CompositorAnimator.FAST_OUT_LINEAR_IN_INTERPOLATOR);
+        backgroundAlpha.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN_INTERPOLATOR);
         animationList.add(backgroundAlpha);
 
         mTabToSwitcherAnimation = new AnimatorSet();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
index 0e60b346..68a5698b6 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
@@ -4,11 +4,6 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
-import static org.chromium.chrome.browser.compositor.animation.CompositorAnimator.FAST_OUT_LINEAR_IN_INTERPOLATOR;
-import static org.chromium.chrome.browser.compositor.animation.CompositorAnimator.FAST_OUT_SLOW_IN_INTERPOLATOR;
-import static org.chromium.chrome.browser.compositor.animation.CompositorAnimator.LINEAR_INTERPOLATOR;
-import static org.chromium.chrome.browser.compositor.animation.CompositorAnimator.LINEAR_OUT_SLOW_IN_INTERPOLATOR;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -40,6 +35,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.widget.ScrimView;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
@@ -238,7 +234,7 @@
         mUngroupBarShow =
                 ObjectAnimator.ofFloat(mUngroupBar, View.TRANSLATION_Y, mUngroupBarHeight, 0);
         mUngroupBarShow.setDuration(DIALOG_ANIMATION_DURATION);
-        mUngroupBarShow.setInterpolator(LINEAR_OUT_SLOW_IN_INTERPOLATOR);
+        mUngroupBarShow.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
         mUngroupBarShow.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
@@ -258,7 +254,7 @@
         mUngroupBarHide =
                 ObjectAnimator.ofFloat(mUngroupBar, View.TRANSLATION_Y, 0, mUngroupBarHeight);
         mUngroupBarHide.setDuration(DIALOG_ANIMATION_DURATION);
-        mUngroupBarHide.setInterpolator(FAST_OUT_LINEAR_IN_INTERPOLATOR);
+        mUngroupBarHide.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN_INTERPOLATOR);
         mUngroupBarHide.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
@@ -347,7 +343,7 @@
 
         AnimatorSet cardZoomOutAnimatorSet = new AnimatorSet();
         cardZoomOutAnimatorSet.setDuration(DIALOG_ANIMATION_DURATION);
-        cardZoomOutAnimatorSet.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+        cardZoomOutAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
         cardZoomOutAnimatorSet.play(cardZoomOutMoveYAnimator)
                 .with(cardZoomOutMoveXAnimator)
                 .with(cardZoomOutScaleYAnimator)
@@ -358,7 +354,7 @@
         final ObjectAnimator cardZoomOutAlphaAnimator =
                 ObjectAnimator.ofFloat(mAnimationCardView, View.ALPHA, 1f, 0f);
         cardZoomOutAlphaAnimator.setDuration(DIALOG_ALPHA_ANIMATION_DURATION);
-        cardZoomOutAlphaAnimator.setInterpolator(LINEAR_INTERPOLATOR);
+        cardZoomOutAlphaAnimator.setInterpolator(Interpolators.LINEAR_INTERPOLATOR);
 
         // In the second half of the dialog showing animation, the dialog zooms out from where the
         // card stops at the end of the first half and moves towards where the dialog should be.
@@ -373,7 +369,7 @@
 
         AnimatorSet dialogZoomOutAnimatorSet = new AnimatorSet();
         dialogZoomOutAnimatorSet.setDuration(DIALOG_ANIMATION_DURATION);
-        dialogZoomOutAnimatorSet.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+        dialogZoomOutAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
         dialogZoomOutAnimatorSet.play(dialogZoomOutMoveYAnimator)
                 .with(dialogZoomOutMoveXAnimator)
                 .with(dialogZoomOutScaleYAnimator)
@@ -385,7 +381,7 @@
                 ObjectAnimator.ofFloat(mDialogContainerView, View.ALPHA, 0f, 1f);
         dialogZoomOutAlphaAnimator.setDuration(DIALOG_ALPHA_ANIMATION_DURATION);
         dialogZoomOutAlphaAnimator.setStartDelay(DIALOG_ALPHA_ANIMATION_DURATION);
-        dialogZoomOutAlphaAnimator.setInterpolator(LINEAR_INTERPOLATOR);
+        dialogZoomOutAlphaAnimator.setInterpolator(Interpolators.LINEAR_INTERPOLATOR);
 
         // During the whole dialog showing animation, the frame background scales up and moves so
         // that it looks like the card zooms out and becomes the dialog.
@@ -400,7 +396,7 @@
 
         AnimatorSet frameZoomOutAnimatorSet = new AnimatorSet();
         frameZoomOutAnimatorSet.setDuration(DIALOG_ANIMATION_DURATION);
-        frameZoomOutAnimatorSet.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+        frameZoomOutAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
         frameZoomOutAnimatorSet.play(frameZoomOutMoveYAnimator)
                 .with(frameZoomOutMoveXAnimator)
                 .with(frameZoomOutScaleYAnimator)
@@ -459,14 +455,14 @@
                 .with(dialogZoomInScaleYAnimator)
                 .with(dialogZoomInScaleXAnimator);
         dialogZoomInAnimatorSet.setDuration(DIALOG_ANIMATION_DURATION);
-        dialogZoomInAnimatorSet.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+        dialogZoomInAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
 
         // In the first half of the dialog hiding animation, the dialog fades out while it moves and
         // scales down.
         final ObjectAnimator dialogZoomInAlphaAnimator =
                 ObjectAnimator.ofFloat(mDialogContainerView, View.ALPHA, 1f, 0f);
         dialogZoomInAlphaAnimator.setDuration(DIALOG_ALPHA_ANIMATION_DURATION);
-        dialogZoomInAlphaAnimator.setInterpolator(LINEAR_INTERPOLATOR);
+        dialogZoomInAlphaAnimator.setInterpolator(Interpolators.LINEAR_INTERPOLATOR);
 
         // In the second half of the dialog hiding animation, the animation card zooms in from where
         // the dialog stops at the end of the first half and moves towards where the card should be.
@@ -485,7 +481,7 @@
                 .with(cardZoomInScaleXAnimator)
                 .with(cardZoomInScaleYAnimator);
         cardZoomInAnimatorSet.setDuration(DIALOG_ANIMATION_DURATION);
-        cardZoomInAnimatorSet.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+        cardZoomInAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
 
         // In the second half of the dialog hiding animation, the tab grid card fades in while it
         // scales down and moves.
@@ -493,7 +489,7 @@
                 ObjectAnimator.ofFloat(mAnimationCardView, View.ALPHA, 0f, 1f);
         cardZoomInAlphaAnimator.setDuration(DIALOG_ALPHA_ANIMATION_DURATION);
         cardZoomInAlphaAnimator.setStartDelay(DIALOG_ALPHA_ANIMATION_DURATION);
-        cardZoomInAlphaAnimator.setInterpolator(LINEAR_INTERPOLATOR);
+        cardZoomInAlphaAnimator.setInterpolator(Interpolators.LINEAR_INTERPOLATOR);
 
         // During the whole dialog hiding animation, the frame background scales down and moves so
         // that it looks like the dialog zooms in and becomes the card.
@@ -512,7 +508,7 @@
                 .with(frameZoomInScaleYAnimator)
                 .with(frameZoomInScaleXAnimator);
         frameZoomInAnimatorSet.setDuration(DIALOG_ANIMATION_DURATION);
-        frameZoomInAnimatorSet.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+        frameZoomInAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
 
         // At the end of the dialog hiding animation, the original tab grid card fades in.
         final ObjectAnimator tabFadeInAnimator =
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml
index afdd6f9..43161b36 100644
--- a/chrome/android/java/res/values/colors.xml
+++ b/chrome/android/java/res/values/colors.xml
@@ -104,7 +104,7 @@
     <color name="incognito_emphasis">@android:color/white</color>
 
     <!-- Overlay Panel colors -->
-    <color name="overlay_panel_bar_background_color">@color/default_bg_color_elev_2</color>
+    <color name="overlay_panel_bar_background_color">@color/default_bg_color_elev_4</color>
     <color name="overlay_panel_separator_line_color">@color/modern_secondary_color</color>
 
     <!-- Contextual Search colors -->
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBar.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBar.java
index 13afb833..fe5930f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBar.java
@@ -15,7 +15,6 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
-import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
@@ -38,13 +37,6 @@
     private BookmarkItem mCurrentFolder;
     private BookmarkDelegate mDelegate;
 
-    private BookmarkModelObserver mBookmarkModelObserver = new BookmarkModelObserver() {
-        @Override
-        public void bookmarkModelChanged() {
-            onSelectionStateChange(mDelegate.getSelectionDelegate().getSelectedItemsAsList());
-        }
-    };
-
     public BookmarkActionBar(Context context, AttributeSet attrs) {
         super(context, attrs);
         setNavigationOnClickListener(this);
@@ -163,7 +155,6 @@
         mDelegate = delegate;
         mDelegate.addUIObserver(this);
         if (!delegate.isDialogUi()) getMenu().removeItem(R.id.close_menu_id);
-        delegate.getModel().addObserver(mBookmarkModelObserver);
 
         getMenu().setGroupEnabled(R.id.selection_mode_menu_group, true);
     }
@@ -175,7 +166,6 @@
         if (mDelegate == null) return;
 
         mDelegate.removeUIObserver(this);
-        mDelegate.getModel().removeObserver(mBookmarkModelObserver);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
index a3551a4..c605c66 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.bookmarks;
 
 import android.os.SystemClock;
+import android.support.annotation.Nullable;
 import android.text.TextUtils;
 import android.util.Pair;
 
@@ -361,6 +362,7 @@
      * @return A BookmarkItem instance for the given BookmarkId.
      *         <code>null</code> if it doesn't exist.
      */
+    @Nullable
     public BookmarkItem getBookmarkById(BookmarkId id) {
         assert mIsNativeBookmarkModelLoaded;
         return BookmarkBridgeJni.get().getBookmarkByID(
@@ -754,7 +756,8 @@
     /**
      * Notifies the observer that bookmark model has been loaded.
      */
-    protected void notifyBookmarkModelLoaded() {
+    @VisibleForTesting
+    public void notifyBookmarkModelLoaded() {
         // Call isBookmarkModelLoaded() to do the check since it could be overridden by the child
         // class to add the addition logic.
         if (isBookmarkModelLoaded()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java
index ce1470b..e5d72b84 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java
@@ -146,7 +146,8 @@
     /**
      * @return The position of the given bookmark in adapter. Will return -1 if not found.
      */
-    private int getPositionForBookmark(BookmarkId bookmark) {
+    @Override
+    public int getPositionForBookmark(BookmarkId bookmark) {
         assert bookmark != null;
         int position = -1;
         for (int i = 0; i < getItemCount(); i++) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
index 6f32e23b..fcc7e7f7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
@@ -8,6 +8,7 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.AdapterDataObserver;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -76,6 +77,7 @@
     // TODO(crbug.com/160194): Clean up after bookmark reordering launches.
     private ItemsAdapter mAdapter;
     private BookmarkDragStateDelegate mDragStateDelegate;
+    private AdapterDataObserver mAdapterDataObserver;
 
     /**
      * An adapter responsible for managing bookmark items.
@@ -85,11 +87,14 @@
         void notifyDataSetChanged();
         void onBookmarkDelegateInitialized(BookmarkDelegate bookmarkDelegate);
         void search(String query);
+        void registerAdapterDataObserver(AdapterDataObserver observer);
+        void unregisterAdapterDataObserver(AdapterDataObserver observer);
 
         void moveUpOne(BookmarkId bookmarkId);
         void moveDownOne(BookmarkId bookmarkId);
 
         void highlightBookmark(BookmarkId bookmarkId);
+        int getPositionForBookmark(BookmarkId bookmarkId);
     }
 
     private final BookmarkModelObserver mBookmarkModelObserver = new BookmarkModelObserver() {
@@ -112,7 +117,6 @@
                     openFolder(parent.getId());
                 }
             }
-            mSelectionDelegate.clearSelection();
 
             // This is necessary as long as we rely on RecyclerView.ItemDecorations to apply padding
             // at the bottom of the bookmarks list to avoid the bottom navigation menu. This ensures
@@ -122,19 +126,12 @@
         }
 
         @Override
-        public void bookmarkNodeMoved(BookmarkItem oldParent, int oldIndex, BookmarkItem newParent,
-                int newIndex) {
-            mSelectionDelegate.clearSelection();
-        }
-
-        @Override
         public void bookmarkModelChanged() {
             // If the folder no longer exists in folder mode, we need to fall back. Relying on the
             // default behavior by setting the folder mode again.
             if (getCurrentState() == BookmarkUIState.STATE_FOLDER) {
                 setState(mStateStack.peek());
             }
-            mSelectionDelegate.clearSelection();
         }
     };
 
@@ -203,7 +200,10 @@
         mSelectionDelegate = new SelectionDelegate<BookmarkId>() {
             @Override
             public boolean toggleSelectionForItem(BookmarkId bookmark) {
-                if (!mBookmarkModel.getBookmarkById(bookmark).isEditable()) return false;
+                if (mBookmarkModel.getBookmarkById(bookmark) != null
+                        && !mBookmarkModel.getBookmarkById(bookmark).isEditable()) {
+                    return false;
+                }
                 return super.toggleSelectionForItem(bookmark);
             }
         };
@@ -227,6 +227,18 @@
         } else {
             mAdapter = new BookmarkItemsAdapter(activity);
         }
+        mAdapterDataObserver = new AdapterDataObserver() {
+            @Override
+            public void onItemRangeRemoved(int positionStart, int itemCount) {
+                syncAdapterAndSelectionDelegate();
+            }
+
+            @Override
+            public void onChanged() {
+                syncAdapterAndSelectionDelegate();
+            }
+        };
+        mAdapter.registerAdapterDataObserver(mAdapterDataObserver);
         mRecyclerView = mSelectableListLayout.initializeRecyclerView(
                 (RecyclerView.Adapter<RecyclerView.ViewHolder>) mAdapter);
 
@@ -296,6 +308,7 @@
      * Destroys and cleans up itself. This must be called after done using this class.
      */
     public void onDestroyed() {
+        mAdapter.unregisterAdapterDataObserver(mAdapterDataObserver);
         mIsDestroyed = true;
         RecordUserAction.record("MobileBookmarkManagerClose");
         mSelectableListLayout.onDestroyed();
@@ -446,13 +459,25 @@
             }
         }
 
-        mSelectionDelegate.clearSelection();
-
         for (BookmarkUIObserver observer : mUIObservers) {
             notifyStateChange(observer);
         }
     }
 
+    // TODO(lazzzis): This method can be moved to adapter after bookmark reordering launches.
+    /**
+     * Some bookmarks may be moved to another folder or removed in another devices. However, it may
+     * still be stored by {@link #mSelectionDelegate}, which causes incorrect selection counting.
+     */
+    private void syncAdapterAndSelectionDelegate() {
+        for (BookmarkId node : mSelectionDelegate.getSelectedItemsAsList()) {
+            if (mSelectionDelegate.isItemSelected(node)
+                    && mAdapter.getPositionForBookmark(node) == -1) {
+                mSelectionDelegate.toggleSelectionForItem(node);
+            }
+        }
+    }
+
     @Override
     public void moveDownOne(BookmarkId bookmarkId) {
         mAdapter.moveDownOne(bookmarkId);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
index d516476..2ff8000 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
@@ -265,7 +265,8 @@
     @Override
     public void onSearchStateSet() {}
 
-    boolean isItemSelected() {
+    @VisibleForTesting
+    public boolean isItemSelected() {
         return mDelegate.getSelectionDelegate().isItemSelected(mBookmarkId);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java
index 4c357ce5..4520927 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java
@@ -127,7 +127,8 @@
     /**
      * @return The position of the given bookmark in adapter. Will return -1 if not found.
      */
-    private int getPositionForBookmark(BookmarkId bookmark) {
+    @Override
+    public int getPositionForBookmark(BookmarkId bookmark) {
         assert bookmark != null;
         int position = -1;
         for (int i = 0; i < getItemCount(); i++) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/animation/CompositorAnimator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/animation/CompositorAnimator.java
index 94770aef..dc1cad0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/animation/CompositorAnimator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/animation/CompositorAnimator.java
@@ -10,12 +10,6 @@
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.provider.Settings;
-import android.support.v4.view.animation.FastOutLinearInInterpolator;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.LinearInterpolator;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -25,6 +19,7 @@
 import org.chromium.base.ObserverList;
 import org.chromium.base.Supplier;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -110,19 +105,6 @@
      */
     private boolean mDidUpdateToCompletion;
 
-    /** Reference to one of each standard interpolator to avoid allocations. */
-    public static final AccelerateInterpolator ACCELERATE_INTERPOLATOR =
-            new AccelerateInterpolator();
-    public static final DecelerateInterpolator DECELERATE_INTERPOLATOR =
-            new DecelerateInterpolator();
-    public static final FastOutSlowInInterpolator FAST_OUT_SLOW_IN_INTERPOLATOR =
-            new FastOutSlowInInterpolator();
-    public static final LinearOutSlowInInterpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR =
-            new LinearOutSlowInInterpolator();
-    public static final FastOutLinearInInterpolator FAST_OUT_LINEAR_IN_INTERPOLATOR =
-            new FastOutLinearInInterpolator();
-    public static final LinearInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
-
     /**
      * A utility for creating a basic animator.
      * @param handler The {@link CompositorAnimationHandler} responsible for running the animation.
@@ -201,7 +183,7 @@
             final T target, final FloatProperty<T> property, float startValue, float endValue,
             long durationMs) {
         return ofFloatProperty(handler, target, property, startValue, endValue, durationMs,
-                DECELERATE_INTERPOLATOR);
+                Interpolators.DECELERATE_INTERPOLATOR);
     }
 
     /**
@@ -218,7 +200,7 @@
             final T target, final FloatProperty<T> property, Supplier<Float> startValue,
             Supplier<Float> endValue, long durationMs) {
         return ofFloatProperty(handler, target, property, startValue, endValue, durationMs,
-                DECELERATE_INTERPOLATOR);
+                Interpolators.DECELERATE_INTERPOLATOR);
     }
 
     /** An interface for listening for frames of an animation. */
@@ -239,7 +221,7 @@
 
         // The default interpolator is decelerate; this mimics the existing ChromeAnimation
         // behavior.
-        mTimeInterpolator = DECELERATE_INTERPOLATOR;
+        mTimeInterpolator = Interpolators.DECELERATE_INTERPOLATOR;
 
         // By default, animate for 0 to 1.
         setValues(0, 1);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchCaptionControl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchCaptionControl.java
index ecc75e7..96f743d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchCaptionControl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchCaptionControl.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelAnimation;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelTextViewInflater;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
 
 /**
@@ -276,7 +277,7 @@
                 OverlayPanelAnimation.BASE_ANIMATION_DURATION_MS, null);
         mTransitionAnimator.addUpdateListener(
                 animator -> mAnimationPercentage = animator.getAnimatedValue());
-        mTransitionAnimator.setInterpolator(CompositorAnimator.FAST_OUT_SLOW_IN_INTERPOLATOR);
+        mTransitionAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
 
         mTransitionAnimator.start();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
index 2f2ba79..e04b5bd43 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.resources.ResourceManager;
@@ -281,7 +282,7 @@
             float progress = mOffset / getWidth();
             float direction = Math.signum(progress);
             float smoothedProgress =
-                    CompositorAnimator.DECELERATE_INTERPOLATOR.getInterpolation(Math.abs(progress));
+                    Interpolators.DECELERATE_INTERPOLATOR.getInterpolation(Math.abs(progress));
 
             float maxSlide = getWidth() / 5.f;
             rightX = direction * smoothedProgress * maxSlide;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackViewAnimation.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackViewAnimation.java
index a564572..4617502b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackViewAnimation.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackViewAnimation.java
@@ -15,11 +15,11 @@
 import android.widget.FrameLayout;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.compositor.animation.CompositorAnimator;
 import org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation.OverviewAnimationType;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabThemeColorHelper;
 import org.chromium.chrome.browser.tabmodel.TabList;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 
 /**
  * A factory that builds Android view animations for the tab stack.
@@ -87,19 +87,19 @@
         PropertyValuesHolder viewAlpha = PropertyValuesHolder.ofFloat(View.ALPHA, 0.f, 1.f);
         ObjectAnimator viewAlphaAnimator = ObjectAnimator.ofPropertyValuesHolder(view, viewAlpha);
         viewAlphaAnimator.setDuration(TAB_OPENED_VIEW_ANIMATION_DURATION);
-        viewAlphaAnimator.setInterpolator(CompositorAnimator.FAST_OUT_SLOW_IN_INTERPOLATOR);
+        viewAlphaAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
 
         PropertyValuesHolder yTranslation =
                 PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, mTranslationYStart, 0.f);
         ObjectAnimator viewYTranslationAnimator =
                 ObjectAnimator.ofPropertyValuesHolder(view, yTranslation);
         viewYTranslationAnimator.setDuration(TAB_OPENED_VIEW_ANIMATION_DURATION);
-        viewYTranslationAnimator.setInterpolator(CompositorAnimator.FAST_OUT_SLOW_IN_INTERPOLATOR);
+        viewYTranslationAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
 
         PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat(View.ALPHA, 0.f, 1.f);
         ObjectAnimator bgAlphaAnimator = ObjectAnimator.ofPropertyValuesHolder(bgView, bgAlpha);
         bgAlphaAnimator.setDuration(TAB_OPENED_BG_ANIMATION_DURATION);
-        bgAlphaAnimator.setInterpolator(CompositorAnimator.FAST_OUT_SLOW_IN_INTERPOLATOR);
+        bgAlphaAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
 
         AnimatorSet set = new AnimatorSet();
         set.playTogether(viewAlphaAnimator, viewYTranslationAnimator, bgAlphaAnimator);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index 8d0563ff..364e26f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -372,6 +372,7 @@
         downloadItem.setSystemDownloadId(
                 DownloadManagerBridge.getDownloadIdForDownloadGuid(downloadInfo.getDownloadGuid()));
         updateDownloadProgress(downloadItem, status);
+        updateDownloadInfoBar(downloadItem);
     }
 
     @Override
@@ -382,6 +383,7 @@
             removeAutoResumableDownload(item.getId());
         }
         updateDownloadProgress(item, DownloadStatus.IN_PROGRESS);
+        updateDownloadInfoBar(item);
         scheduleUpdateIfNeeded();
     }
 
@@ -390,6 +392,7 @@
         DownloadItem item = new DownloadItem(false, downloadInfo);
         removeAutoResumableDownload(item.getId());
         updateDownloadProgress(new DownloadItem(false, downloadInfo), DownloadStatus.CANCELLED);
+        updateDownloadInfoBar(item);
     }
 
     @Override
@@ -410,6 +413,7 @@
                     UmaBackgroundDownload.INTERRUPTED, downloadInfo.getDownloadGuid());
         }
         updateDownloadProgress(item, status);
+        updateDownloadInfoBar(item);
 
         if (FeatureUtilities.isDownloadAutoResumptionEnabledInNative()) return;
         DownloadProgress progress = mDownloadProgressMap.get(item.getId());
@@ -460,6 +464,12 @@
         }
     }
 
+    private void updateDownloadInfoBar(DownloadItem item) {
+        DownloadInfoBarController infobarController =
+                getInfoBarController(item.getDownloadInfo().isOffTheRecord());
+        if (infobarController != null) infobarController.onDownloadItemUpdated(item);
+    }
+
     /**
      * Broadcast that a download was successful.
      * @param downloadInfo info about the download.
@@ -1769,9 +1779,6 @@
         for (DownloadObserver adapter : mDownloadObservers) {
             adapter.onDownloadItemCreated(item);
         }
-        DownloadInfoBarController infobarController =
-                getInfoBarController(item.getDownloadInfo().isOffTheRecord());
-        if (infobarController != null) infobarController.onDownloadItemUpdated(item);
     }
 
     @CalledByNative
@@ -1779,10 +1786,6 @@
         for (DownloadObserver adapter : mDownloadObservers) {
             adapter.onDownloadItemUpdated(item);
         }
-
-        DownloadInfoBarController infobarController =
-                getInfoBarController(item.getDownloadInfo().isOffTheRecord());
-        if (infobarController != null) infobarController.onDownloadItemUpdated(item);
     }
 
     @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
index 5054a5b..931f4a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
@@ -129,8 +129,9 @@
     }
 
     private boolean isShowingDownloadInfoBar() {
-        return DownloadManagerService.getDownloadManagerService()
-                .getInfoBarController(Profile.getLastUsedProfile().isOffTheRecord())
-                .isShowing();
+        DownloadInfoBarController infoBarController =
+                DownloadManagerService.getDownloadManagerService().getInfoBarController(
+                        Profile.getLastUsedProfile().isOffTheRecord());
+        return infoBarController == null ? false : infoBarController.isShowing();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java
index 98286d6..6a653c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java
@@ -12,11 +12,11 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.widget.animation.CancelAwareAnimatorListener;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 
 /**
  * A tablet specific version of the {@link FindToolbar}.
@@ -58,7 +58,7 @@
 
         mAnimationEnter = ObjectAnimator.ofFloat(this, View.TRANSLATION_X, translateWidth, 0);
         mAnimationEnter.setDuration(ENTER_EXIT_ANIMATION_DURATION_MS);
-        mAnimationEnter.setInterpolator(new DecelerateInterpolator());
+        mAnimationEnter.setInterpolator(Interpolators.DECELERATE_INTERPOLATOR);
         mAnimationEnter.addListener(new CancelAwareAnimatorListener() {
             @Override
             public void onStart(Animator animation) {
@@ -75,7 +75,7 @@
 
         mAnimationLeave = ObjectAnimator.ofFloat(this, View.TRANSLATION_X, 0, translateWidth);
         mAnimationLeave.setDuration(ENTER_EXIT_ANIMATION_DURATION_MS);
-        mAnimationLeave.setInterpolator(new DecelerateInterpolator());
+        mAnimationLeave.setInterpolator(Interpolators.DECELERATE_INTERPOLATOR);
         mAnimationLeave.addListener(new CancelAwareAnimatorListener() {
             @Override
             public void onStart(Animator animator) {
@@ -146,7 +146,7 @@
 
         mCurrentAnimation = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, translationY);
         mCurrentAnimation.setDuration(MAKE_ROOM_ANIMATION_DURATION_MS);
-        mCurrentAnimation.setInterpolator(new DecelerateInterpolator());
+        mCurrentAnimation.setInterpolator(Interpolators.DECELERATE_INTERPOLATOR);
         mCurrentAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
index e529a17..49a8d2e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
@@ -14,11 +14,11 @@
 import android.view.animation.Animation.AnimationListener;
 import android.view.animation.AnimationSet;
 import android.view.animation.DecelerateInterpolator;
-import android.view.animation.LinearInterpolator;
 import android.view.animation.ScaleAnimation;
 import android.view.animation.Transformation;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 
 /**
  * The SideSlideLayout can be used whenever the user navigates the contents
@@ -64,7 +64,6 @@
     private static final int NAVIGATION_REVERSAL_MS = 3 * 1000;
 
     private final DecelerateInterpolator mDecelerateInterpolator;
-    private final LinearInterpolator mLinearInterpolator;
     private final float mTotalDragDistance;
     private final int mMediumAnimationDuration;
     private final int mCircleWidth;
@@ -141,7 +140,6 @@
 
         setWillNotDraw(false);
         mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
-        mLinearInterpolator = new LinearInterpolator();
 
         mCircleWidth = (int) getResources().getDimensionPixelSize(R.dimen.navigation_bubble_size);
 
@@ -199,7 +197,7 @@
             mAnimationViewWidth = mArrowViewWidth;
             ScaleAnimation scalingDown =
                     new ScaleAnimation(1, 0, 1, 0, mArrowViewWidth / 2, mArrowView.getHeight() / 2);
-            scalingDown.setInterpolator(mLinearInterpolator);
+            scalingDown.setInterpolator(Interpolators.LINEAR_INTERPOLATOR);
             scalingDown.setDuration(SCALE_DOWN_DURATION_MS);
             Animation fadingOut = new AlphaAnimation(1, 0);
             fadingOut.setInterpolator(mDecelerateInterpolator);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/translate/TranslateTabLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/translate/TranslateTabLayout.java
index d9089f0..f81fe9a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/translate/TranslateTabLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/translate/TranslateTabLayout.java
@@ -14,11 +14,11 @@
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
-import android.view.animation.DecelerateInterpolator;
 
 import androidx.annotation.NonNull;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 
 /**
  * TabLayout shown in the TranslateCompactInfoBar.
@@ -216,7 +216,7 @@
                 getLayoutDirection() == LAYOUT_DIRECTION_RTL ? 0 : maxScrollDistance);
         mScrollToEndAnimator.setStartDelay(START_POSITION_WAIT_DURATION_MS);
         mScrollToEndAnimator.setDuration(SCROLL_DURATION_MS);
-        mScrollToEndAnimator.setInterpolator(new DecelerateInterpolator());
+        mScrollToEndAnimator.setInterpolator(Interpolators.DECELERATE_INTERPOLATOR);
         mScrollToEndAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java
index ebb767c0..d0d262c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java
@@ -370,20 +370,10 @@
      * @return The available space that can be used to install WebAPK. Negative value means there is
      * less free space available than the system's minimum by the given amount.
      */
-    @SuppressWarnings("deprecation")
     public static long getAvailableSpaceAboveLowSpaceLimit() {
-        long partitionAvailableBytes;
-        long partitionTotalBytes;
         StatFs partitionStats = new StatFs(Environment.getDataDirectory().getAbsolutePath());
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
-            partitionAvailableBytes = partitionStats.getAvailableBytes();
-            partitionTotalBytes = partitionStats.getTotalBytes();
-        } else {
-            // these APIs were deprecated in API level 18.
-            long blockSize = partitionStats.getBlockSize();
-            partitionAvailableBytes = blockSize * (long) partitionStats.getAvailableBlocks();
-            partitionTotalBytes = blockSize * (long) partitionStats.getBlockCount();
-        }
+        long partitionAvailableBytes = partitionStats.getAvailableBytes();
+        long partitionTotalBytes = partitionStats.getTotalBytes();
         long minimumFreeBytes = getLowSpaceLimitBytes(partitionTotalBytes);
 
         long webApkExtraSpaceBytes = 0;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateController.java b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateController.java
index 9c49e107..fa50584c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateController.java
@@ -128,7 +128,7 @@
 
     private void updateNightMode() {
         boolean powerSaveModeOn = mPowerSaveModeMonitor.powerSavingIsOn();
-        final int themeSetting = mChromePreferenceManager.readInt(UI_THEME_SETTING_KEY);
+        final int themeSetting = NightModeUtils.getThemeSetting();
         final boolean newNightModeOn = themeSetting == ThemePreferences.ThemeSetting.SYSTEM_DEFAULT
                         && (powerSaveModeOn || mSystemNightModeMonitor.isSystemNightModeOn())
                 || themeSetting == ThemePreferences.ThemeSetting.DARK;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java
index f74f8588..3152fad 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.night_mode;
 
+import static org.chromium.chrome.browser.preferences.ChromePreferenceManager.UI_THEME_SETTING_KEY;
+
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -15,6 +17,9 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.ChromeBaseAppCompatActivity;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+import org.chromium.chrome.browser.preferences.themes.ThemePreferences;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 
 /**
  * Helper methods for supporting night mode.
@@ -125,6 +130,22 @@
         return wrapper;
     }
 
+    /**
+     * The current theme setting, reflecting either the user setting or the default if the user has
+     * not explicitly set a preference.
+     * @return The current theme setting. See {@link ThemePreferences.ThemeSetting}.
+     */
+    public static @ThemePreferences.ThemeSetting int getThemeSetting() {
+        int userSetting = ChromePreferenceManager.getInstance().readInt(UI_THEME_SETTING_KEY, -1);
+        if (userSetting == -1) {
+            return FeatureUtilities.isNightModeDefaultToLight()
+                    ? ThemePreferences.ThemeSetting.LIGHT
+                    : ThemePreferences.ThemeSetting.SYSTEM_DEFAULT;
+        } else {
+            return userSetting;
+        }
+    }
+
     @VisibleForTesting
     public static void setNightModeSupportedForTesting(@Nullable Boolean nightModeSupported) {
         sNightModeSupportedForTest = nightModeSupported;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 5216811..2c3b28f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -9,7 +9,6 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.support.v4.view.ViewCompat;
 import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -698,9 +697,11 @@
 
     @Override
     public void destroy() {
-        assert !mIsDestroyed;
-        assert !ViewCompat
-                .isAttachedToWindow(getView()) : "Destroy called before removed from window";
+        // Temporarily removing asserts during window when they're not compiled out of release
+        // builds, see https://crbug.com/992585 for more information.
+        // assert !mIsDestroyed;
+        // assert !ViewCompat
+        //        .isAttachedToWindow(getView()) : "Destroy called before removed from window";
         if (mIsLoaded && !mTab.isHidden()) recordNTPHidden();
 
         mNewTabPageManager.onDestroy();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 85d6f0c..8b49fee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -79,7 +79,7 @@
                    LocationBarVoiceRecognitionHandler.Delegate {
     protected ImageButton mDeleteButton;
     protected ImageButton mMicButton;
-    protected View mUrlBar;
+    protected UrlBar mUrlBar;
     private final boolean mIsTablet;
 
     protected UrlBarCoordinator mUrlCoordinator;
@@ -193,7 +193,7 @@
         mAutocompleteCoordinator = AutocompleteCoordinatorFactory.createAutocompleteCoordinator(
                 this, this, embedder, mUrlCoordinator);
         addUrlFocusChangeListener(mAutocompleteCoordinator);
-        mUrlCoordinator.setUrlTextChangeListener(mAutocompleteCoordinator);
+        mUrlCoordinator.addUrlTextChangeListener(mAutocompleteCoordinator);
 
         mMicButton = findViewById(R.id.mic_button);
 
@@ -221,6 +221,7 @@
 
         updateShouldAnimateIconChanges();
         mUrlBar.setOnKeyListener(new UrlBarKeyListener());
+        mUrlCoordinator.addUrlTextChangeListener(mStatusViewCoordinator);
 
         // mLocationBar's direction is tied to this UrlBar's text direction. Icons inside the
         // location bar, e.g. lock, refresh, X, should be reversed if UrlBar's text is RTL.
@@ -435,7 +436,7 @@
                     mUrlCoordinator.setUrlBarData(UrlBarData.forNonUrlText(existingText),
                             UrlBar.ScrollType.NO_SCROLL,
                             UrlBarCoordinator.SelectionState.SELECT_END);
-                    mAutocompleteCoordinator.onTextChangedForAutocomplete();
+                    forceOnTextChanged();
                 }
             }
 
@@ -547,7 +548,7 @@
             // This must be happen after requestUrlFocus(), which changes the selection.
             mUrlCoordinator.setUrlBarData(UrlBarData.forNonUrlText(pastedText),
                     UrlBar.ScrollType.NO_SCROLL, UrlBarCoordinator.SelectionState.SELECT_END);
-            mAutocompleteCoordinator.onTextChangedForAutocomplete();
+            forceOnTextChanged();
         } else {
             ToolbarManager.recordOmniboxFocusReason(ToolbarManager.OmniboxFocusReason.FAKE_BOX_TAP);
         }
@@ -910,7 +911,7 @@
     private boolean setUrlBarTextEmpty() {
         boolean textChanged = mUrlCoordinator.setUrlBarData(
                 UrlBarData.EMPTY, UrlBar.ScrollType.SCROLL_TO_BEGINNING, SelectionState.SELECT_ALL);
-        mAutocompleteCoordinator.onTextChangedForAutocomplete();
+        forceOnTextChanged();
         return textChanged;
     }
 
@@ -1112,4 +1113,11 @@
     public StatusViewCoordinator getStatusViewCoordinatorForTesting() {
         return mStatusViewCoordinator;
     }
+
+    private void forceOnTextChanged() {
+        String textWithoutAutocomplete = mUrlCoordinator.getTextWithoutAutocomplete();
+        String textWithAutocomplete = mUrlCoordinator.getTextWithoutAutocomplete();
+        mAutocompleteCoordinator.onTextChanged(textWithoutAutocomplete, textWithAutocomplete);
+        mStatusViewCoordinator.onTextChanged(textWithoutAutocomplete, textWithAutocomplete);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index 2f99f8c..c7956f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -212,10 +212,12 @@
     /** Provides updates about the URL text changes. */
     public interface UrlTextChangeListener {
         /**
-         * Called when the text state has changed and the autocomplete suggestions should be
-         * refreshed.
+         * Called when the text state has changed.
+         * @param textWithoutAutocomplete The url bar text without autocompletion.
+         * @param textWithAutocomplete The url bar text with autocompletion.
          */
-        void onTextChangedForAutocomplete();
+        // TODO(crbug.com/1003080): Consider splitting these into two different callbacks.
+        void onTextChanged(String textWithoutAutocomplete, String textWithAutocomplete);
     }
 
     /** Delegate that provides the additional functionality to the textual context menus. */
@@ -953,7 +955,7 @@
         // crbug.com/764749
         Log.w(TAG, "Text change observed, triggering autocomplete.");
 
-        mTextChangeListener.onTextChangedForAutocomplete();
+        mTextChangeListener.onTextChanged(getTextWithoutAutocomplete(), getTextWithAutocomplete());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
index 12b03ec..8aa56a0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
@@ -56,8 +56,8 @@
     }
 
     /** @see UrlBarMediator#setDelegate(UrlBarDelegate) */
-    public void setUrlTextChangeListener(UrlTextChangeListener listener) {
-        mMediator.setUrlTextChangeListener(listener);
+    public void addUrlTextChangeListener(UrlTextChangeListener listener) {
+        mMediator.addUrlTextChangeListener(listener);
     }
 
     /** @see UrlBarMediator#setUrlBarData(UrlBarData, int, int) */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
index ec9cdeb..5cb8764 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
@@ -29,11 +29,13 @@
 
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Handles collecting and pushing state information to the UrlBar model.
  */
-class UrlBarMediator implements UrlBar.UrlBarTextContextMenuDelegate {
+class UrlBarMediator implements UrlBar.UrlBarTextContextMenuDelegate, UrlBar.UrlTextChangeListener {
     private final PropertyModel mModel;
 
     private Callback<Boolean> mOnFocusChangeCallback;
@@ -43,12 +45,15 @@
     private @ScrollType int mScrollType = UrlBar.ScrollType.NO_SCROLL;
     private @SelectionState int mSelectionState = UrlBarCoordinator.SelectionState.SELECT_ALL;
 
+    private final List<UrlTextChangeListener> mUrlTextChangeListeners = new ArrayList<>();
+
     public UrlBarMediator(PropertyModel model) {
         mModel = model;
 
         mModel.set(UrlBarProperties.FOCUS_CHANGE_CALLBACK, this::onUrlFocusChange);
         mModel.set(UrlBarProperties.SHOW_CURSOR, false);
         mModel.set(UrlBarProperties.TEXT_CONTEXT_MENU_DELEGATE, this);
+        mModel.set(UrlBarProperties.URL_TEXT_CHANGE_LISTENER, this);
         setUseDarkTextColors(true);
     }
 
@@ -59,9 +64,9 @@
         mModel.set(UrlBarProperties.DELEGATE, delegate);
     }
 
-    /** Set the listener to be notified when the url text chagnes. */
-    public void setUrlTextChangeListener(UrlTextChangeListener listener) {
-        mModel.set(UrlBarProperties.URL_TEXT_CHANGE_LISTENER, listener);
+    /** @see UrlBarMediator#setDelegate(UrlBarDelegate) */
+    public void addUrlTextChangeListener(UrlTextChangeListener listener) {
+        mUrlTextChangeListeners.add(listener);
     }
 
     /**
@@ -334,4 +339,12 @@
 
         return url.substring(0, pathIndex);
     }
+
+    @Override
+    public void onTextChanged(String textWithoutAutocomplete, String textWithAutocomplete) {
+        for (int i = 0; i < mUrlTextChangeListeners.size(); i++) {
+            mUrlTextChangeListeners.get(i).onTextChanged(
+                    textWithoutAutocomplete, textWithAutocomplete);
+        }
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index dfd5de1..35e4ac0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -5,25 +5,63 @@
 package org.chromium.chrome.browser.omnibox.status;
 
 import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.text.TextUtils;
 import android.view.View;
 
 import androidx.annotation.ColorRes;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.StringRes;
 
+import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils;
+import org.chromium.chrome.browser.omnibox.UrlBar;
+import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinatorFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.toolbar.ToolbarCommonPropertiesModel;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.components.security_state.ConnectionSecurityLevel;
+import org.chromium.content_public.browser.BrowserStartupController;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Contains the controller logic of the Status component.
  */
 class StatusMediator {
+    @VisibleForTesting
+    class StatusMediatorDelegate {
+        /** @see {@link AutocompleteCoordinatorFactory#qualifyPartialURLQuery} */
+        boolean isUrlValid(String partialUrl) {
+            return BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
+                           .isFullBrowserStarted()
+                    && AutocompleteCoordinatorFactory.qualifyPartialURLQuery(partialUrl) != null;
+        }
+
+        /** @see {@link SearchEngineLogoUtils#getSearchEngineLogoFavicon} */
+        void getSearchEngineLogoFavicon(Resources res, Callback<Bitmap> callback) {
+            SearchEngineLogoUtils.getSearchEngineLogoFavicon(
+                    Profile.getLastUsedProfile().getOriginalProfile(), res, callback);
+        }
+
+        /** @see {@link SearchEngineLogoUtils#shouldShowSearchEngineLogo} */
+        boolean shouldShowSearchEngineLogo() {
+            return SearchEngineLogoUtils.shouldShowSearchEngineLogo();
+        }
+
+        /** @see {@link SearchEngineLogoUtils#shouldShowSearchLoupeEverywhere} */
+        boolean shouldShowSearchLoupeEverywhere() {
+            return SearchEngineLogoUtils.shouldShowSearchLoupeEverywhere();
+        }
+
+        /** @see {@link SearchEngineLogoUtils#doesUrlMatchDefaultSearchEngine} */
+        boolean doesUrlMatchDefaultSearchEngine(String url) {
+            return SearchEngineLogoUtils.doesUrlMatchDefaultSearchEngine(url);
+        }
+    }
+
     // The size that the icon should be displayed as.
     private static final int SEARCH_ENGINE_LOGO_ICON_TARGET_SIZE_DP = 24;
     // The size given to LargeIconBridge to increase the probability that we'll get an icon back.
@@ -53,11 +91,15 @@
     private @StringRes int mSecurityIconDescriptionRes;
     private @DrawableRes int mNavigationIconTintRes;
 
+    private StatusMediatorDelegate mDelegate;
     private Resources mResources;
     private ToolbarCommonPropertiesModel mToolbarCommonPropertiesModel;
+    private String mUrlBarTextWithAutocomplete = "";
+    private boolean mUrlBarTextIsValidUrl;
 
     StatusMediator(PropertyModel model, Resources resources) {
         mModel = model;
+        mDelegate = new StatusMediatorDelegate();
         updateColorTheme();
 
         mResources = resources;
@@ -66,7 +108,8 @@
     /**
      * Set the ToolbarDataProvider for this class.
      */
-    void setToolbarDataProvider(ToolbarCommonPropertiesModel toolbarCommonPropertiesModel) {
+    void setToolbarCommonPropertiesModel(
+            ToolbarCommonPropertiesModel toolbarCommonPropertiesModel) {
         mToolbarCommonPropertiesModel = toolbarCommonPropertiesModel;
     }
 
@@ -342,28 +385,30 @@
         boolean showUnfocusedSearchResultsPage = !mUrlHasFocus
                 && mToolbarCommonPropertiesModel != null
                 && mToolbarCommonPropertiesModel.getDisplaySearchTerms() != null
-                && SearchEngineLogoUtils.doesUrlMatchDefaultSearchEngine(
+                && mDelegate.doesUrlMatchDefaultSearchEngine(
                         mToolbarCommonPropertiesModel.getCurrentUrl());
-        if (SearchEngineLogoUtils.shouldShowSearchEngineLogo() && mIsSearchEngineStateSetup
+        if (mDelegate.shouldShowSearchEngineLogo() && mIsSearchEngineStateSetup
                 && (showFocused || showUnfocusedNewTabPage || showUnfocusedSearchResultsPage)) {
             mShouldCancelCustomFavicon = false;
-            mModel.set(StatusProperties.STATUS_ICON_TINT_RES, 0);
-            if (mIsSearchEngineGoogle) {
+            // If the current url text is a valid url, then swap the dse icon for a globe.
+            if (mUrlBarTextIsValidUrl) {
+                mModel.set(StatusProperties.STATUS_ICON_RES, R.drawable.ic_globe_24dp);
+            } else if (mIsSearchEngineGoogle) {
                 mModel.set(StatusProperties.STATUS_ICON_RES,
-                        SearchEngineLogoUtils.shouldShowSearchLoupeEverywhere()
-                                ? R.drawable.omnibox_search
+                        mDelegate.shouldShowSearchLoupeEverywhere()
+                                ? R.drawable.ic_search
                                 : R.drawable.ic_logo_googleg_24dp);
             } else {
                 mModel.set(StatusProperties.STATUS_ICON_RES, R.drawable.ic_search);
-                if (!SearchEngineLogoUtils.shouldShowSearchLoupeEverywhere()) {
-                    SearchEngineLogoUtils.getSearchEngineLogoFavicon(
-                            Profile.getLastUsedProfile().getOriginalProfile(), mResources,
-                            (favicon) -> {
-                                if (favicon == null || mShouldCancelCustomFavicon) return;
-                                mModel.set(StatusProperties.STATUS_ICON, favicon);
-                            });
+                if (!mDelegate.shouldShowSearchLoupeEverywhere()) {
+                    mDelegate.getSearchEngineLogoFavicon(mResources, (favicon) -> {
+                        if (favicon == null || mShouldCancelCustomFavicon) return;
+                        mModel.set(StatusProperties.STATUS_ICON, favicon);
+                    });
                 }
             }
+            // None of the icons associated with dse should be tinted.
+            mModel.set(StatusProperties.STATUS_ICON_TINT_RES, 0);
             return;
         } else {
             mShouldCancelCustomFavicon = true;
@@ -411,4 +456,22 @@
 
         return 0;
     }
+
+    /** @see {@link UrlBar.UrlTextChangeListener} */
+    void onTextChanged(String urlTextWithoutAutocomplete, String urlTextWithAutocomplete) {
+        if (TextUtils.equals(mUrlBarTextWithAutocomplete, urlTextWithAutocomplete)) {
+            return;
+        }
+
+        mUrlBarTextWithAutocomplete = urlTextWithAutocomplete;
+        boolean isValid = mDelegate.isUrlValid(mUrlBarTextWithAutocomplete);
+        if (isValid != mUrlBarTextIsValidUrl) {
+            mUrlBarTextIsValidUrl = isValid;
+            updateLocationBarIcon();
+        }
+    }
+
+    void setDelegateForTesting(StatusMediatorDelegate delegate) {
+        mDelegate = delegate;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
index 92f5e356..0d7a07f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
@@ -11,6 +11,7 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.browser.page_info.PageInfoController;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -20,7 +21,7 @@
  * A component for displaying a status icon (e.g. security icon or navigation icon) and optional
  * verbose status text.
  */
-public class StatusViewCoordinator implements View.OnClickListener {
+public class StatusViewCoordinator implements View.OnClickListener, UrlBar.UrlTextChangeListener {
     private final StatusView mStatusView;
     private final StatusMediator mMediator;
     private final PropertyModel mModel;
@@ -64,7 +65,7 @@
      */
     public void setToolbarDataProvider(ToolbarDataProvider toolbarDataProvider) {
         mToolbarDataProvider = toolbarDataProvider;
-        mMediator.setToolbarDataProvider(mToolbarDataProvider);
+        mMediator.setToolbarCommonPropertiesModel(mToolbarDataProvider);
         // Update status immediately after receiving the data provider to avoid initial presence
         // glitch on tablet devices. This glitch would be typically seen upon launch of app, right
         // before the landing page is presented to the user.
@@ -212,4 +213,9 @@
     public int getStatusIconWidth() {
         return mStatusView.getStatusIconWidth();
     }
+
+    @Override
+    public void onTextChanged(String textWithoutAutocomplete, String textWithAutocomplete) {
+        mMediator.onTextChanged(textWithoutAutocomplete, textWithAutocomplete);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java
index b94819e..cdab2c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java
@@ -254,8 +254,8 @@
     }
 
     @Override
-    public void onTextChangedForAutocomplete() {
-        mMediator.onTextChangedForAutocomplete();
+    public void onTextChanged(String textWithoutAutocomplete, String textWithAutocomplete) {
+        mMediator.onTextChanged(textWithoutAutocomplete, textWithAutocomplete);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index 7310ec68..3581f2f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -553,7 +553,8 @@
         if (!isUrlSuggestion) refineText = TextUtils.concat(refineText, " ").toString();
 
         mDelegate.setOmniboxEditingText(refineText);
-        onTextChangedForAutocomplete();
+        onTextChanged(mUrlBarEditingTextProvider.getTextWithoutAutocomplete(),
+                mUrlBarEditingTextProvider.getTextWithAutocomplete());
         if (isUrlSuggestion) {
             RecordUserAction.record("MobileOmniboxRefineSuggestion.Url");
         } else {
@@ -713,7 +714,7 @@
      * Notifies the autocomplete system that the text has changed that drives autocomplete and the
      * autocomplete suggestions should be updated.
      */
-    public void onTextChangedForAutocomplete() {
+    public void onTextChanged(String textWithoutAutocomplete, String textWithAutocomplete) {
         // crbug.com/764749
         Log.w(TAG, "onTextChangedForAutocomplete");
 
@@ -729,7 +730,7 @@
         }
 
         stopAutocomplete(false);
-        if (TextUtils.isEmpty(mUrlBarEditingTextProvider.getTextWithoutAutocomplete())) {
+        if (TextUtils.isEmpty(textWithoutAutocomplete)) {
             // crbug.com/764749
             Log.w(TAG, "onTextChangedForAutocomplete: url is empty");
             hideSuggestions();
@@ -737,8 +738,6 @@
         } else {
             assert mRequestSuggestions == null : "Multiple omnibox requests in flight.";
             mRequestSuggestions = () -> {
-                String textWithoutAutocomplete =
-                        mUrlBarEditingTextProvider.getTextWithoutAutocomplete();
                 boolean preventAutocomplete = !mUrlBarEditingTextProvider.shouldAutocomplete();
                 mRequestSuggestions = null;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
index bbe68e9..10a6756 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
@@ -15,8 +15,6 @@
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Build;
-import android.support.v4.view.animation.FastOutLinearInInterpolator;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
 import android.view.Gravity;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
@@ -30,6 +28,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.widget.AlwaysDismissedDialog;
 import org.chromium.chrome.browser.ui.widget.animation.AnimatorProperties;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.util.ColorUtils;
 
 /**
@@ -185,7 +184,7 @@
             AnimatorSet alphaSet = new AnimatorSet();
             alphaSet.playTogether(scrimFader, alphaAnimator);
             alphaSet.setDuration(DIALOG_ENTER_ANIMATION_MS);
-            alphaSet.setInterpolator(new LinearOutSlowInInterpolator());
+            alphaSet.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
             alphaSet.start();
         }
     }
@@ -206,7 +205,7 @@
 
             AnimatorSet current = new AnimatorSet();
             current.setDuration(DIALOG_EXIT_ANIMATION_MS);
-            current.setInterpolator(new FastOutLinearInInterpolator());
+            current.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN_INTERPOLATOR);
             if (mIsDialogClosing) {
                 Animator scrimFader = ObjectAnimator.ofInt(mFullContainer.getBackground(),
                         AnimatorProperties.DRAWABLE_ALPHA_PROPERTY, 127, 0);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
index aa5363f..f3b0bab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
@@ -9,7 +9,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.support.v4.view.MarginLayoutParamsCompat;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
 import android.support.v7.widget.GridLayout;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
@@ -39,6 +38,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.widget.DualControlLayout;
 import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.widget.prefeditor.EditableOption;
 import org.chromium.ui.HorizontalListDividerDrawable;
 import org.chromium.ui.UiUtils;
@@ -546,7 +546,7 @@
             public void run() {
                 Animation out = new AlphaAnimation(mUpdatedView.getAlpha(), 0.0f);
                 out.setDuration(UPDATE_TEXT_ANIMATION_DURATION_MS);
-                out.setInterpolator(new LinearOutSlowInInterpolator());
+                out.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
                 out.setFillAfter(true);
                 mUpdatedView.startAnimation(out);
             }
@@ -717,7 +717,7 @@
             // to avoid restarting a previous or current fade in animation.
             Animation in = new AlphaAnimation(mUpdatedView.getAlpha(), 1.0f);
             in.setDuration(UPDATE_TEXT_ANIMATION_DURATION_MS);
-            in.setInterpolator(new LinearOutSlowInInterpolator());
+            in.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
             in.setFillAfter(true);
             mUpdatedView.startAnimation(in);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
index 78873e1..c178d38 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
@@ -18,7 +18,6 @@
 import android.graphics.Bitmap;
 import android.os.Handler;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
 import android.text.SpannableString;
 import android.text.TextUtils;
 import android.text.method.LinkMovementMethod;
@@ -46,6 +45,7 @@
 import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.SectionSeparator;
 import org.chromium.chrome.browser.ui.widget.FadingEdgeScrollView;
 import org.chromium.chrome.browser.ui.widget.animation.FocusAnimator;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.widget.prefeditor.EditableOption;
 import org.chromium.chrome.browser.widget.prefeditor.EditorDialog;
 import org.chromium.chrome.browser.widget.prefeditor.EditorObserverForTest;
@@ -1203,7 +1203,7 @@
             mSheetAnimator = ObjectAnimator.ofFloat(
                     mRequestView, View.TRANSLATION_Y, mAnimatorTranslation, 0);
             mSheetAnimator.setDuration(DIALOG_ENTER_ANIMATION_MS);
-            mSheetAnimator.setInterpolator(new LinearOutSlowInInterpolator());
+            mSheetAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
             mSheetAnimator.addListener(this);
             mSheetAnimator.start();
         }
@@ -1269,7 +1269,7 @@
 
             mSheetAnimator = containerAnimator;
             mSheetAnimator.setDuration(DIALOG_ENTER_ANIMATION_MS);
-            mSheetAnimator.setInterpolator(new LinearOutSlowInInterpolator());
+            mSheetAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
             mSheetAnimator.addListener(this);
             mSheetAnimator.start();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
index 73a8819..902e439 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -217,6 +217,12 @@
     public static final String NIGHT_MODE_AVAILABLE_KEY = "night_mode_available";
 
     /**
+     * Whether or not night mode should set "light" as the default option.
+     * Default value is false.
+     */
+    public static final String NIGHT_MODE_DEFAULT_TO_LIGHT = "night_mode_default_to_light";
+
+    /**
      * Whether or not night mode is available for custom tabs.
      * Default value is false.
      */
@@ -224,7 +230,8 @@
 
     /**
      * The current theme setting in the user settings.
-     * Default value is System default (see {@link ThemePreference.ThemeSetting}).
+     * Default value is -1. Use NightModeUtils#getThemeSetting() to retrieve current setting or
+     * default theme.
      */
     public static final String UI_THEME_SETTING_KEY = "ui_theme_setting";
 
@@ -721,8 +728,18 @@
      * @return The value of the preference.
      */
     public int readInt(String key) {
+        return readInt(key, 0);
+    }
+
+    /**
+     * Reads the given int value from the named shared preference.
+     * @param key The name of the preference to return.
+     * @param defaultValue The default value to return if the preference is not set.
+     * @return The value of the preference.
+     */
+    public int readInt(String key, int defaultValue) {
         try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
-            return mSharedPreferences.getInt(key, 0);
+            return mSharedPreferences.getInt(key, defaultValue);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/PasswordEntryEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/PasswordEntryEditor.java
index b98b8d6..6cd8497 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/PasswordEntryEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/PasswordEntryEditor.java
@@ -134,7 +134,10 @@
             mPasswordLabel.setError(getContext().getString(
                     R.string.pref_edit_dialog_field_required_validation_message));
         } else {
-            // TODO(crbug.com/377410): Save the changes if everything was ok.
+            PasswordEditingDelegateProvider.getInstance()
+                    .getPasswordEditingDelegate()
+                    .editSavedPasswordEntry(
+                            mUsernameText.getText().toString(), mPasswordText.getText().toString());
             getActivity().finish();
         }
     }
@@ -144,4 +147,10 @@
         super.onSaveInstanceState(savedInstanceState);
         savedInstanceState.putBoolean(VIEW_BUTTON_PRESSED, mViewButtonPressed);
     }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        PasswordEditingDelegateProvider.getInstance().getPasswordEditingDelegate().destroy();
+    }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/PasswordEntryViewer.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/PasswordEntryViewer.java
index e212d36..86cb1b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/PasswordEntryViewer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/PasswordEntryViewer.java
@@ -199,6 +199,9 @@
             if (mCopyButtonPressed) copyPassword();
         }
         PasswordManagerHandlerProvider.getInstance().addObserver(this);
+        PasswordManagerHandlerProvider.getInstance()
+                .getPasswordManagerHandler()
+                .updatePasswordLists();
     }
 
     @Override
@@ -434,13 +437,13 @@
     @Override
     public void passwordListAvailable(int count) {
         if (mException) return;
-        TextView passwordsLinkTextView = mView.findViewById(R.id.passwords_link);
+        TextView passwordTextView = mView.findViewById(R.id.password_entry_viewer_password);
         SavedPasswordEntry SavedPasswordEntry = PasswordManagerHandlerProvider.getInstance()
                                                         .getPasswordManagerHandler()
                                                         .getSavedPasswordEntry(mID);
         setRowText(R.id.url_row, SavedPasswordEntry.getUrl());
         setRowText(R.id.username_row, SavedPasswordEntry.getUserName());
-        passwordsLinkTextView.setText(SavedPasswordEntry.getPassword());
+        passwordTextView.setText(SavedPasswordEntry.getPassword());
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/ThemePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/ThemePreferences.java
index 34d0cb1..51dc3a7a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/ThemePreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/ThemePreferences.java
@@ -16,6 +16,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.night_mode.NightModeUtils;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.preferences.PreferenceUtils;
 import org.chromium.ui.UiUtils;
@@ -52,9 +53,9 @@
         ChromePreferenceManager chromePreferenceManager = ChromePreferenceManager.getInstance();
         RadioButtonGroupThemePreference radioButtonGroupThemePreference =
                 (RadioButtonGroupThemePreference) findPreference(PREF_UI_THEME_PREF);
-        radioButtonGroupThemePreference.initialize(
-                chromePreferenceManager.readInt(UI_THEME_SETTING_KEY),
+        radioButtonGroupThemePreference.initialize(NightModeUtils.getThemeSetting(),
                 chromePreferenceManager.readBoolean(DARKEN_WEBSITES_ENABLED_KEY, false));
+
         radioButtonGroupThemePreference.setOnPreferenceChangeListener((preference, newValue) -> {
             if (ChromeFeatureList.isEnabled(
                         ChromeFeatureList.DARKEN_WEBSITES_CHECKBOX_IN_THEMES_SETTING)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
index 60cc561..2c168922 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
@@ -92,8 +92,10 @@
         mPendingSearchPromoDecision = false;
         getAutocompleteCoordinator().setShouldPreventOmniboxAutocomplete(
                 mPendingSearchPromoDecision);
-        if (!TextUtils.isEmpty(mUrlCoordinator.getTextWithAutocomplete())) {
-            mAutocompleteCoordinator.onTextChangedForAutocomplete();
+        String textWithAutocomplete = mUrlCoordinator.getTextWithAutocomplete();
+        if (!TextUtils.isEmpty(textWithAutocomplete)) {
+            mAutocompleteCoordinator.onTextChanged(
+                    mUrlCoordinator.getTextWithoutAutocomplete(), textWithAutocomplete);
         }
 
         if (mPendingBeginQuery) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java
index ef23602..ca70786 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java
@@ -19,7 +19,6 @@
 import android.view.View.OnClickListener;
 import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
-import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -29,6 +28,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 
@@ -114,7 +114,7 @@
                 mContainerView.removeOnLayoutChangeListener(this);
                 mContainerView.setTranslationY(getYPositionForMoveAnimation());
                 Animator animator = ObjectAnimator.ofFloat(mContainerView, View.TRANSLATION_Y, 0);
-                animator.setInterpolator(new DecelerateInterpolator());
+                animator.setInterpolator(Interpolators.DECELERATE_INTERPOLATOR);
                 animator.setDuration(mAnimationDuration);
                 startAnimatorOnSurfaceView(animator);
             }
@@ -135,7 +135,7 @@
         });
         Animator moveAnimator = ObjectAnimator.ofFloat(
                 mContainerView, View.TRANSLATION_Y, getYPositionForMoveAnimation());
-        moveAnimator.setInterpolator(new DecelerateInterpolator());
+        moveAnimator.setInterpolator(Interpolators.DECELERATE_INTERPOLATOR);
         Animator fadeOut = ObjectAnimator.ofFloat(mContainerView, View.ALPHA, 0f);
         fadeOut.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRecyclerView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRecyclerView.java
index cba965f8..cc634e39 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRecyclerView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRecyclerView.java
@@ -15,7 +15,6 @@
 import android.graphics.Canvas;
 import android.os.Build;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.animation.FastOutLinearInInterpolator;
 import android.support.v7.view.ContextThemeWrapper;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -36,6 +35,7 @@
 import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
 import org.chromium.chrome.browser.ntp.cards.ScrollToLoadListener;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
 
 import java.util.ArrayList;
@@ -50,7 +50,8 @@
  * New Tab page receives focus when clicked.
  */
 public class SuggestionsRecyclerView extends RecyclerView {
-    private static final Interpolator DISMISS_INTERPOLATOR = new FastOutLinearInInterpolator();
+    private static final Interpolator DISMISS_INTERPOLATOR =
+            Interpolators.FAST_OUT_LINEAR_IN_INTERPOLATOR;
     private static final int DISMISS_ANIMATION_TIME_MS = 300;
 
     private final GestureDetector mGestureDetector;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
index a4b190a2..8aac964 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
@@ -239,14 +239,7 @@
     public void maybeUpdateNavigationHistory() {
         WebContents webContents = mTab.getWebContents();
         if (mClearAllForwardHistoryRequired && webContents != null) {
-            NavigationController navigationController =
-                    webContents.getNavigationController();
-            int lastCommittedEntryIndex = getLastCommittedEntryIndex();
-            while (navigationController.canGoForward()) {
-                boolean ret = navigationController.removeEntryAtIndex(
-                        lastCommittedEntryIndex + 1);
-                assert ret;
-            }
+            webContents.getNavigationController().pruneForwardEntries();
         } else if (mShouldClearRedirectHistoryForTabClobbering
                 && webContents != null) {
             // http://crbug/479056: Even if we clobber the current tab, we want to remove
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java
index f7ea38be..dc1b414 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java
@@ -14,7 +14,6 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.animation.LinearInterpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
 import android.widget.ImageView;
@@ -29,6 +28,7 @@
 import org.chromium.chrome.browser.appmenu.AppMenuButtonHelper;
 import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper;
 import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper.MenuButtonState;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.widget.PulseDrawable;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
@@ -324,7 +324,7 @@
 
         // Create menu button ObjectAnimator.
         ObjectAnimator menuButtonFadeAnimator = ObjectAnimator.ofFloat(menuButton, View.ALPHA, 0.f);
-        menuButtonFadeAnimator.setInterpolator(new LinearInterpolator());
+        menuButtonFadeAnimator.setInterpolator(Interpolators.LINEAR_INTERPOLATOR);
 
         // Create AnimatorSet and listeners.
         AnimatorSet set = new AnimatorSet();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
index 8fe04f4..e35d0a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
@@ -13,7 +13,6 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewStub;
-import android.view.animation.LinearInterpolator;
 
 import androidx.annotation.Nullable;
 
@@ -32,6 +31,7 @@
 import org.chromium.chrome.browser.toolbar.NewTabButton;
 import org.chromium.chrome.browser.toolbar.TabCountProvider;
 import org.chromium.chrome.browser.ui.widget.animation.CancelAwareAnimatorListener;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.ui.UiUtils;
@@ -169,7 +169,7 @@
         if (showZoomingAnimation && inTabSwitcherMode) {
             mVisiblityAnimator.setStartDelay(duration);
         }
-        mVisiblityAnimator.setInterpolator(new LinearInterpolator());
+        mVisiblityAnimator.setInterpolator(Interpolators.LINEAR_INTERPOLATOR);
 
         // TODO(https://crbug.com/914868): Use consistent logic here for setting clickable/enabled
         // on mIncognitoToggleTabLayout & mNewTabButton?
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 758ae39d..89c8e6e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -25,7 +25,6 @@
 import android.os.SystemClock;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.support.v7.graphics.drawable.DrawableWrapper;
 import android.util.AttributeSet;
 import android.util.Property;
@@ -38,8 +37,6 @@
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.ViewTreeObserver;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
 import android.widget.ImageView;
@@ -72,6 +69,7 @@
 import org.chromium.chrome.browser.toolbar.TabSwitcherDrawable;
 import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator.UrlExpansionObserver;
 import org.chromium.chrome.browser.ui.widget.animation.CancelAwareAnimatorListener;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.MathUtils;
@@ -126,9 +124,6 @@
 
     static final int LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA = 51;
 
-    private static final Interpolator NTP_SEARCH_BOX_EXPANSION_INTERPOLATOR =
-            new FastOutSlowInInterpolator();
-
     private TabCountProvider mTabCountProvider;
 
     protected LocationBarPhone mLocationBar;
@@ -1168,8 +1163,9 @@
         // Linearly interpolate between the bounds of the search box on the NTP and the omnibox
         // background bounds. |shrinkage| is the scaling factor for the offset -- if it's 1, we are
         // shrinking the omnibox down to the size of the search box.
-        float shrinkage =
-                1f - NTP_SEARCH_BOX_EXPANSION_INTERPOLATOR.getInterpolation(mUrlExpansionPercent);
+        float shrinkage = 1f
+                - Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR.getInterpolation(
+                        mUrlExpansionPercent);
 
         int leftBoundDifference = mNtpSearchBoxBounds.left - mLocationBarBackgroundBounds.left;
         int rightBoundDifference = mNtpSearchBoxBounds.right - mLocationBarBackgroundBounds.right;
@@ -1685,7 +1681,7 @@
                 ObjectAnimator.ofFloat(this, mTabSwitcherModePercentProperty, 1.f);
         enterAnimation.setDuration(
                 TopToolbarCoordinator.TAB_SWITCHER_MODE_NORMAL_ANIMATION_DURATION_MS);
-        enterAnimation.setInterpolator(new LinearInterpolator());
+        enterAnimation.setInterpolator(Interpolators.LINEAR_INTERPOLATOR);
 
         return enterAnimation;
     }
@@ -1696,7 +1692,7 @@
         exitAnimation.setDuration(animateNormalToolbar
                         ? TopToolbarCoordinator.TAB_SWITCHER_MODE_NORMAL_ANIMATION_DURATION_MS
                         : TAB_SWITCHER_MODE_EXIT_FADE_ANIMATION_DURATION_MS);
-        exitAnimation.setInterpolator(new LinearInterpolator());
+        exitAnimation.setInterpolator(Interpolators.LINEAR_INTERPOLATOR);
         exitAnimation.addListener(new CancelAwareAnimatorListener() {
             @Override
             public void onEnd(Animator animation) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
index 7918529..aa2b11c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -76,6 +76,7 @@
     private static Boolean sIsAdaptiveToolbarEnabled;
     private static Boolean sIsLabeledBottomToolbarEnabled;
     private static Boolean sIsNightModeAvailable;
+    private static Boolean sNightModeDefaultToLight;
     private static Boolean sIsNightModeForCustomTabsAvailable;
     private static Boolean sShouldPrioritizeBootstrapTasks;
     private static Boolean sIsGridTabSwitcherEnabled;
@@ -174,6 +175,7 @@
         cacheAdaptiveToolbarEnabled();
         cacheLabeledBottomToolbarEnabled();
         cacheNightModeAvailable();
+        cacheNightModeDefaultToLight();
         cacheNightModeForCustomTabsAvailable();
         cacheDownloadAutoResumptionEnabledInNative();
         cachePrioritizeBootstrapTasks();
@@ -419,6 +421,50 @@
     }
 
     /**
+     * Cache whether or not to default to the light theme when the night mode feature is enabled.
+     */
+    public static void cacheNightModeDefaultToLight() {
+        // Do not cache on Q (where defaulting to light theme does not apply) or if night mode is
+        // not enabled.
+        if (BuildInfo.isAtLeastQ()
+                || !ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_NIGHT_MODE)) {
+            return;
+        }
+
+        String lightModeDefaultParam = "default_light_theme";
+        boolean lightModeAsDefault = ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
+                ChromeFeatureList.ANDROID_NIGHT_MODE, lightModeDefaultParam, false);
+
+        ChromePreferenceManager.getInstance().writeBoolean(
+                ChromePreferenceManager.NIGHT_MODE_DEFAULT_TO_LIGHT, lightModeAsDefault);
+    }
+
+    /**
+     * @return Whether or not to default to the light theme when the night mode feature is enabled.
+     */
+    public static boolean isNightModeDefaultToLight() {
+        if (BuildInfo.isAtLeastQ()) sNightModeDefaultToLight = false;
+
+        if (sNightModeDefaultToLight == null) {
+            ChromePreferenceManager prefManager = ChromePreferenceManager.getInstance();
+
+            sNightModeDefaultToLight = prefManager.readBoolean(
+                    ChromePreferenceManager.NIGHT_MODE_DEFAULT_TO_LIGHT, false);
+        }
+
+        return sNightModeDefaultToLight;
+    }
+
+    /**
+     * Toggles whether the night mode experiment is enabled for testing. Should be reset back to
+     * null after the test has finished.
+     */
+    @VisibleForTesting
+    public static void setNightModeDefaultToLightForTesting(@Nullable Boolean available) {
+        sNightModeDefaultToLight = available;
+    }
+
+    /**
      * Cache whether or not night mode is available for custom tabs (i.e. night mode experiment is
      * enabled), so the value is immediately available on next start-up.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index 0582903..639fa7c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -408,9 +408,6 @@
      * If immersive mode is not supported, this method no-ops.
      */
     private void enterImmersiveMode() {
-        // Immersive mode is only supported in API 19+.
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
-
         if (mSetImmersiveRunnable == null) {
             final View decor = getWindow().getDecorView();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/addtohomescreen/AddToHomescreenView.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/addtohomescreen/AddToHomescreenView.java
index 400c755d..0966014 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/addtohomescreen/AddToHomescreenView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/addtohomescreen/AddToHomescreenView.java
@@ -112,7 +112,7 @@
         // title is the title of the page.
         mProgressBarView = view.findViewById(R.id.spinny);
         mIconView = (ImageView) view.findViewById(R.id.icon);
-        mShortcutTitleInput = (EditText) view.findViewById(R.id.text);
+        mShortcutTitleInput = view.findViewById(R.id.text);
         mAppLayout = (LinearLayout) view.findViewById(R.id.app_info);
 
         mAppNameView = (TextView) mAppLayout.findViewById(R.id.name);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/ChromeTextInputLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ChromeTextInputLayout.java
index 6225f78..c868616 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/ChromeTextInputLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ChromeTextInputLayout.java
@@ -15,7 +15,6 @@
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -24,7 +23,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.Interpolator;
 import android.widget.EditText;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
@@ -35,6 +33,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.ui.base.LocalizationUtils;
 
 import java.lang.annotation.Retention;
@@ -77,7 +76,6 @@
     private HashSet<OnEditTextFocusChangeListener> mListeners = new HashSet<>();
 
     private CharSequence mHint;
-    private Interpolator mInterpolator;
 
     private boolean mShouldDisplayError;
     private @LabelStatus int mLabelStatus;
@@ -149,7 +147,6 @@
                 new int[] {getColorAttribute(context, R.attr.colorControlActivated),
                         mLabel.getCurrentTextColor()}));
 
-        mInterpolator = new FastOutSlowInInterpolator();
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
         ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate());
     }
@@ -331,7 +328,7 @@
                             mLabelStatus = LabelStatus.COLLAPSED;
                         }
                     })
-                    .setInterpolator(mInterpolator)
+                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR)
                     .start();
         } else {
             mLabel.setTranslationY(mCollapsedLabelTranslationY);
@@ -358,7 +355,7 @@
                             mLabelStatus = LabelStatus.EXPANDED;
                         }
                     })
-                    .setInterpolator(mInterpolator)
+                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR)
                     .start();
         } else {
             mLabel.setScaleX(mExpandedTextScale);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/ContextMenuDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ContextMenuDialog.java
index 2e97766..38c41b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/ContextMenuDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ContextMenuDialog.java
@@ -8,7 +8,6 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
@@ -20,6 +19,7 @@
 import android.view.animation.ScaleAnimation;
 
 import org.chromium.chrome.browser.ui.widget.AlwaysDismissedDialog;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 
 /**
  * ContextMenuDialog is a subclass of AlwaysDismissedDialog that ensures that the proper scale
@@ -166,7 +166,7 @@
         long duration = isEnterAnimation ? ENTER_ANIMATION_DURATION_MS : EXIT_ANIMATION_DURATION_MS;
 
         animation.setDuration(duration);
-        animation.setInterpolator(new LinearOutSlowInInterpolator());
+        animation.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
         return animation;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
index 3b0919f93..8e02339 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
@@ -14,7 +14,6 @@
 import android.graphics.drawable.Animatable;
 import android.graphics.drawable.Drawable;
 import android.os.SystemClock;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.support.v4.view.animation.PathInterpolatorCompat;
 import android.view.animation.Interpolator;
 
@@ -23,6 +22,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.util.MathUtils;
 
 /**
@@ -150,7 +150,7 @@
         };
 
         return new PulseDrawable(
-                context, new FastOutSlowInInterpolator(), painter, pulseEndAuthority);
+                context, Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR, painter, pulseEndAuthority);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
index 09ce7b4..279c59e5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
@@ -9,7 +9,6 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Color;
-import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
 import android.os.Build;
 import android.util.AttributeSet;
@@ -24,7 +23,6 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ObserverList;
 import org.chromium.base.Supplier;
 import org.chromium.base.VisibleForTesting;
@@ -516,7 +514,7 @@
 
         mToolbarHolder =
                 (TouchRestrictingFrameLayout) findViewById(R.id.bottom_sheet_toolbar_container);
-        setBackground(mToolbarHolder);
+        mToolbarHolder.setBackgroundResource(R.drawable.top_round);
 
         mDefaultToolbarView = mToolbarHolder.findViewById(R.id.bottom_sheet_toolbar);
 
@@ -525,7 +523,7 @@
         mBottomSheetContentContainer =
                 (TouchRestrictingFrameLayout) findViewById(R.id.bottom_sheet_content);
         mBottomSheetContentContainer.setBottomSheet(this);
-        setBackground(mBottomSheetContentContainer);
+        mBottomSheetContentContainer.setBackgroundResource(R.drawable.top_round);
 
         // Listen to height changes on the root.
         root.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@@ -653,18 +651,6 @@
         mSheetContainer.removeView(this);
     }
 
-    /**
-     * Sets the background with round corner drawable, adjusts the color for dark mode.
-     * @param view View in BottomSheet to set the drawable to.
-     */
-    private static void setBackground(View view) {
-        view.setBackgroundResource(R.drawable.top_round);
-        view.getBackground().setColorFilter(
-                ApiCompatibilityUtils.getColor(
-                        view.getContext().getResources(), org.chromium.ui.R.color.sheet_bg_color),
-                Mode.MULTIPLY);
-    }
-
     @Override
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorDialog.java
index 963a970..704cca8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorDialog.java
@@ -16,8 +16,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.support.v4.view.MarginLayoutParamsCompat;
-import android.support.v4.view.animation.FastOutLinearInInterpolator;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
 import android.support.v7.widget.Toolbar.OnMenuItemClickListener;
 import android.text.InputFilter;
 import android.text.Spanned;
@@ -51,6 +49,7 @@
 import org.chromium.chrome.browser.ui.widget.AlwaysDismissedDialog;
 import org.chromium.chrome.browser.ui.widget.FadingEdgeScrollView;
 import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 
 import java.util.ArrayList;
@@ -310,7 +309,7 @@
 
         mDialogInOutAnimator = animatorSet;
         mDialogInOutAnimator.setDuration(DIALOG_EXIT_ANIMATION_MS);
-        mDialogInOutAnimator.setInterpolator(new FastOutLinearInInterpolator());
+        mDialogInOutAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN_INTERPOLATOR);
         mDialogInOutAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -562,7 +561,7 @@
 
         mDialogInOutAnimator = animatorSet;
         mDialogInOutAnimator.setDuration(DIALOG_ENTER_ANIMATION_MS);
-        mDialogInOutAnimator.setInterpolator(new LinearOutSlowInInterpolator());
+        mDialogInOutAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
         mDialogInOutAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
index 503e49f7..4a5d656 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
@@ -32,6 +32,7 @@
 import android.widget.TextView.OnEditorActionListener;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.IntDef;
 import androidx.annotation.StringRes;
 
 import org.chromium.base.ApiCompatibilityUtils;
@@ -51,6 +52,8 @@
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.UiUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
@@ -79,6 +82,14 @@
         void onEndSearch();
     }
 
+    @IntDef({ViewType.NORMAL_VIEW, ViewType.SELECTION_VIEW, ViewType.SEARCH_VIEW})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ViewType {
+        int NORMAL_VIEW = 0;
+        int SELECTION_VIEW = 1;
+        int SEARCH_VIEW = 2;
+    }
+
     /** No navigation button is displayed. **/
     public static final int NAVIGATION_BUTTON_NONE = 0;
     /** Button to navigate back. This calls {@link #onNavigationBack()}. **/
@@ -133,6 +144,9 @@
     private int mHideInfoStringId;
     private int mExtraMenuItemId;
 
+    // current view type that SelectableListToolbar is showing
+    private int mViewType;
+
     /**
      * Constructor for inflating from XML.
      */
@@ -527,6 +541,10 @@
     }
 
     protected void showNormalView() {
+        // hide overflow menu explicitly: crbug.com/999269
+        hideOverflowMenu();
+
+        mViewType = ViewType.NORMAL_VIEW;
         getMenu().setGroupVisible(mNormalGroupResId, true);
         getMenu().setGroupVisible(mSelectedGroupResId, false);
         if (mHasSearchView) {
@@ -546,6 +564,8 @@
     }
 
     protected void showSelectionView(List<E> selectedItems, boolean wasSelectionEnabled) {
+        mViewType = ViewType.SELECTION_VIEW;
+
         getMenu().setGroupVisible(mNormalGroupResId, false);
         getMenu().setGroupVisible(mSelectedGroupResId, true);
         getMenu().setGroupEnabled(mSelectedGroupResId, !selectedItems.isEmpty());
@@ -563,6 +583,8 @@
     }
 
     private void showSearchViewInternal() {
+        mViewType = ViewType.SEARCH_VIEW;
+
         getMenu().setGroupVisible(mNormalGroupResId, false);
         getMenu().setGroupVisible(mSelectedGroupResId, false);
         mNumberRollView.setVisibility(View.GONE);
@@ -757,4 +779,9 @@
             child.setFocusableInTouchMode(true);
         }
     }
+
+    @VisibleForTesting
+    public @ViewType int getCurrentViewType() {
+        return mViewType;
+    }
 }
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 66f11c55..aa9e21f 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -4020,6 +4020,13 @@
         Test Dummy
       </message>
 
+      <message name="IDS_EXTRA_ICU_MODULE_TITLE"
+        desc="Text shown when the extra ICU module is referenced in install start, success,
+              failure UI (e.g. in IDS_MODULE_INSTALL_START_TEXT, which will expand to
+              'Installing Extra ICU for Chrome…').">
+        Extra ICU
+      </message>
+
     </messages>
   </release>
 </grit>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java
index 6ce7938..01d74356 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java
@@ -13,6 +13,8 @@
 import static org.chromium.chrome.browser.ViewHighlighterTestUtils.checkHighlightPulse;
 
 import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.v7.widget.RecyclerView.AdapterDataObserver;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.view.View;
 
@@ -28,8 +30,11 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
+import org.chromium.chrome.browser.bookmarks.BookmarkPromoHeader.PromoState;
 import org.chromium.chrome.browser.night_mode.NightModeTestUtils;
 import org.chromium.chrome.browser.ui.widget.ListMenuButton;
+import org.chromium.chrome.browser.widget.selection.SelectableListToolbar.ViewType;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.util.BookmarkTestUtil;
 import org.chromium.chrome.test.util.browser.Features;
@@ -96,6 +101,8 @@
         // Callback occurs when Item "test" is selected.
         CriteriaHelper.pollUiThread(test::isChecked, "Expected item \"test\" to become selected");
 
+        Assert.assertEquals("Expected bookmark toolbar to be selection mode",
+                mManager.getToolbarForTests().getCurrentViewType(), ViewType.SELECTION_VIEW);
         Assert.assertEquals("Expected more button of selected item to be gone when drag is active.",
                 View.GONE, testMoreButton.getVisibility());
         Assert.assertEquals(
@@ -210,10 +217,10 @@
             mBookmarkModel.addObserver(bookmarkModelObserver);
         });
 
-        View foo = mItemsContainer.findViewHolderForAdapterPosition(3).itemView;
-        Assert.assertEquals("Wrong bookmark item selected.", TEST_PAGE_TITLE_FOO,
-                ((BookmarkItemRow) foo).getTitle());
-        toggleSelectionAndEndAnimation(fooId, (BookmarkRow) foo);
+        BookmarkRow foo =
+                (BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(3).itemView;
+        Assert.assertEquals("Wrong bookmark item selected.", TEST_PAGE_TITLE_FOO, foo.getTitle());
+        toggleSelectionAndEndAnimation(fooId, foo);
 
         // Starts as last bookmark (2nd index) and ends as 0th bookmark (promo header not included).
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -228,6 +235,7 @@
                     mBookmarkModel.getChildIDs(mBookmarkModel.getDefaultFolder(), true, true);
             // Exclude partner bookmarks folder
             Assert.assertEquals(expected, observed.subList(0, 3));
+            Assert.assertTrue("The selected item should stay selected", foo.isItemSelected());
         });
     }
 
@@ -275,11 +283,11 @@
             mBookmarkModel.addObserver(bookmarkModelObserver);
         });
 
-        View test = mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
-        Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
-                ((BookmarkFolderRow) test).getTitle());
+        BookmarkFolderRow test =
+                (BookmarkFolderRow) mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
+        Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE, test.getTitle());
 
-        toggleSelectionAndEndAnimation(testId, (BookmarkRow) test);
+        toggleSelectionAndEndAnimation(testId, test);
 
         // Starts as 0th bookmark (not counting promo header) and ends as last (index 3).
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -294,6 +302,7 @@
                     mBookmarkModel.getChildIDs(mBookmarkModel.getDefaultFolder(), true, true);
             // Exclude partner bookmarks folder
             Assert.assertEquals(expected, observed.subList(0, 4));
+            Assert.assertTrue("The selected item should stay selected", test.isItemSelected());
         });
     }
 
@@ -337,11 +346,11 @@
             mBookmarkModel.addObserver(bookmarkModelObserver);
         });
 
-        View test = mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
-        Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
-                ((BookmarkFolderRow) test).getTitle());
+        BookmarkFolderRow test =
+                (BookmarkFolderRow) mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
+        Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE, test.getTitle());
 
-        toggleSelectionAndEndAnimation(testId, (BookmarkRow) test);
+        toggleSelectionAndEndAnimation(testId, test);
 
         // Starts as 0th bookmark (not counting promo header) and ends at the 1st index.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -356,6 +365,7 @@
                     mBookmarkModel.getChildIDs(mBookmarkModel.getDefaultFolder(), true, true);
             // Exclude partner bookmarks folder
             Assert.assertEquals(expected, observed.subList(0, 3));
+            Assert.assertTrue("The selected item should stay selected", test.isItemSelected());
         });
     }
 
@@ -734,6 +744,135 @@
                 checkHighlightOff(itemASecondView));
     }
 
+    @Test
+    @SmallTest
+    public void testAddBookmarkInBackgroundWithSelection() throws Exception {
+        BookmarkId id = addBookmark(TEST_PAGE_TITLE_FOO, mTestPageFoo);
+        BookmarkPromoHeader.forcePromoStateForTests(PromoState.PROMO_NONE);
+        openBookmarkManager();
+        Assert.assertEquals(1, getAdapter().getItemCount());
+        BookmarkRow row =
+                (BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(0).itemView;
+        toggleSelectionAndEndAnimation(id, row);
+        CallbackHelper helper = new CallbackHelper();
+        getAdapter().registerAdapterDataObserver(new AdapterDataObserver() {
+            @Override
+            public void onChanged() {
+                helper.notifyCalled();
+            }
+        });
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mBookmarkModel.addBookmark(
+                    mBookmarkModel.getDefaultFolder(), 1, TEST_PAGE_TITLE_GOOGLE, mTestPage);
+        });
+
+        helper.waitForCallback(0, 1);
+        RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertTrue(isItemPresentInBookmarkList(TEST_PAGE_TITLE_FOO));
+            Assert.assertTrue(isItemPresentInBookmarkList(TEST_PAGE_TITLE_GOOGLE));
+            Assert.assertEquals(2, getAdapter().getItemCount());
+            Assert.assertTrue("The selected row should be kept selected", row.isItemSelected());
+        });
+    }
+
+    @Test
+    @SmallTest
+    public void testDeleteAllSelectedBookmarksInBackground() throws Exception {
+        // selected on bookmark and then remove that in background
+        // in the meantime, the toolbar changes from selection mode to normal mode
+        BookmarkId fooId = addBookmark(TEST_PAGE_TITLE_FOO, mTestPageFoo);
+        BookmarkId googleId = addBookmark(TEST_PAGE_TITLE_GOOGLE, mTestPage);
+        BookmarkId aId = addBookmark(TEST_TITLE_A, TEST_URL_A);
+        BookmarkPromoHeader.forcePromoStateForTests(PromoState.PROMO_NONE);
+        openBookmarkManager();
+        Assert.assertEquals(3, getAdapter().getItemCount());
+        BookmarkRow row =
+                (BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
+        toggleSelectionAndEndAnimation(googleId, row);
+        CallbackHelper helper = new CallbackHelper();
+        mManager.getSelectionDelegate().addObserver((x) -> { helper.notifyCalled(); });
+
+        removeBookmark(googleId);
+
+        RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
+        helper.waitForCallback(0, 1);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertFalse(
+                    "Item is not deleted", isItemPresentInBookmarkList(TEST_PAGE_TITLE_GOOGLE));
+            Assert.assertEquals(2, getReorderAdapter().getItemCount());
+            Assert.assertEquals("Bookmark View should be back to normal view",
+                    mManager.getToolbarForTests().getCurrentViewType(), ViewType.NORMAL_VIEW);
+        });
+    }
+
+    @Test
+    @SmallTest
+    public void testDeleteSomeSelectedBookmarksInBackground() throws Exception {
+        // selected on bookmarks and then remove one of them in background
+        // in the meantime, the toolbar stays in selection mode
+        BookmarkId fooId = addBookmark(TEST_PAGE_TITLE_FOO, mTestPageFoo);
+        BookmarkId googleId = addBookmark(TEST_PAGE_TITLE_GOOGLE, mTestPage);
+        BookmarkId aId = addBookmark(TEST_TITLE_A, TEST_URL_A);
+        BookmarkPromoHeader.forcePromoStateForTests(PromoState.PROMO_NONE);
+        openBookmarkManager();
+        Assert.assertEquals(3, getAdapter().getItemCount());
+        BookmarkRow row =
+                (BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
+        toggleSelectionAndEndAnimation(googleId, row);
+        BookmarkRow aRow =
+                (BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(0).itemView;
+        toggleSelectionAndEndAnimation(aId, aRow);
+        CallbackHelper helper = new CallbackHelper();
+        mManager.getSelectionDelegate().addObserver((x) -> { helper.notifyCalled(); });
+
+        removeBookmark(googleId);
+
+        RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
+        helper.waitForCallback(0, 1);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertFalse(
+                    "Item is not deleted", isItemPresentInBookmarkList(TEST_PAGE_TITLE_GOOGLE));
+            Assert.assertEquals(2, getReorderAdapter().getItemCount());
+            Assert.assertTrue("Item selected should not be cleared", aRow.isItemSelected());
+            Assert.assertEquals("Should stay in selection mode because there is one selected",
+                    mManager.getToolbarForTests().getCurrentViewType(), ViewType.SELECTION_VIEW);
+        });
+    }
+
+    @Test
+    @SmallTest
+    public void testUpdateSelectedBookmarkInBackground() throws Exception {
+        BookmarkId id = addBookmark(TEST_PAGE_TITLE_FOO, mTestPageFoo);
+        BookmarkPromoHeader.forcePromoStateForTests(PromoState.PROMO_NONE);
+        openBookmarkManager();
+        Assert.assertEquals(1, getAdapter().getItemCount());
+        BookmarkRow row =
+                (BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(0).itemView;
+        toggleSelectionAndEndAnimation(id, row);
+        CallbackHelper helper = new CallbackHelper();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> mBookmarkModel.addObserver(new BookmarkModelObserver() {
+                    @Override
+                    public void bookmarkModelChanged() {
+                        helper.notifyCalled();
+                    }
+                }));
+
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> mBookmarkModel.setBookmarkTitle(id, TEST_PAGE_TITLE_GOOGLE));
+
+        helper.waitForCallback(0, 1);
+        RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertFalse(isItemPresentInBookmarkList(TEST_PAGE_TITLE_FOO));
+            Assert.assertTrue(isItemPresentInBookmarkList(TEST_PAGE_TITLE_GOOGLE));
+            Assert.assertEquals(1, getAdapter().getItemCount());
+            Assert.assertTrue("The selected row should stay selected", row.isItemSelected());
+        });
+    }
+
     @Override
     protected void openBookmarkManager() throws InterruptedException {
         super.openBookmarkManager();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
index 5b910a8..d2faf5a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
@@ -161,7 +161,7 @@
         }
     }
 
-    private boolean isItemPresentInBookmarkList(final String expectedTitle) {
+    protected boolean isItemPresentInBookmarkList(final String expectedTitle) {
         return TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() {
             @Override
             public Boolean call() throws Exception {
@@ -531,7 +531,7 @@
                 () -> mBookmarkModel.addFolder(mBookmarkModel.getDefaultFolder(), 0, title));
     }
 
-    private void removeBookmark(final BookmarkId bookmarkId) {
+    protected void removeBookmark(final BookmarkId bookmarkId) {
         TestThreadUtils.runOnUiThreadBlocking(() -> mBookmarkModel.deleteBookmark(bookmarkId));
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java
index 82cfcf8f..972072e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java
@@ -271,9 +271,9 @@
         final CallbackHelper autocompleteHelper = new CallbackHelper();
         final AtomicReference<String> requestedAutocompleteText = new AtomicReference<String>();
         final AtomicBoolean didPreventInlineAutocomplete = new AtomicBoolean();
-        mUrlBar.setUrlTextChangeListener(() -> {
+        mUrlBar.setUrlTextChangeListener((textWithoutAutocomplete, textWithAutocomplete) -> {
             autocompleteHelper.notifyCalled();
-            requestedAutocompleteText.set(mUrlBar.getTextWithoutAutocomplete());
+            requestedAutocompleteText.set(textWithoutAutocomplete);
             didPreventInlineAutocomplete.set(!mUrlBar.shouldAutocomplete());
             mUrlBar.setUrlTextChangeListener(null);
         });
@@ -378,7 +378,7 @@
     public void testSendCursorPosition() throws InterruptedException, TimeoutException {
         final CallbackHelper autocompleteHelper = new CallbackHelper();
         final AtomicInteger cursorPositionUsed = new AtomicInteger();
-        mUrlBar.setUrlTextChangeListener(() -> {
+        mUrlBar.setUrlTextChangeListener((textWithoutAutocomplete, textWithAutocomplete) -> {
             int cursorPosition = mUrlBar.getSelectionEnd() == mUrlBar.getSelectionStart()
                     ? mUrlBar.getSelectionStart()
                     : -1;
@@ -456,7 +456,7 @@
 
         final CallbackHelper autocompleteHelper = new CallbackHelper();
         final AtomicBoolean didPreventInlineAutocomplete = new AtomicBoolean();
-        mUrlBar.setUrlTextChangeListener(() -> {
+        mUrlBar.setUrlTextChangeListener((textWithoutAutocomplete, textWithAutocomplete) -> {
             if (!TextUtils.equals(textToBeEntered, mUrlBar.getTextWithoutAutocomplete())) return;
             didPreventInlineAutocomplete.set(!mUrlBar.shouldAutocomplete());
             autocompleteHelper.notifyCalled();
@@ -489,7 +489,7 @@
 
         final CallbackHelper autocompleteHelper = new CallbackHelper();
         final AtomicBoolean didPreventInlineAutocomplete = new AtomicBoolean();
-        mUrlBar.setUrlTextChangeListener(() -> {
+        mUrlBar.setUrlTextChangeListener((textWithoutAutocomplete, textWithAutocomplete) -> {
             if (!TextUtils.equals("test", mUrlBar.getTextWithoutAutocomplete())) return;
             didPreventInlineAutocomplete.set(!mUrlBar.shouldAutocomplete());
             autocompleteHelper.notifyCalled();
@@ -555,7 +555,8 @@
     public void testBatchModeChangesTriggerCorrectSuggestions() throws InterruptedException {
         final AtomicReference<String> requestedAutocompleteText = new AtomicReference<String>();
         mUrlBar.setUrlTextChangeListener(
-                () -> { requestedAutocompleteText.set(mUrlBar.getTextWithoutAutocomplete()); });
+                (textWithoutAutocomplete, textWithAutocomplete)
+                        -> requestedAutocompleteText.set(mUrlBar.getTextWithoutAutocomplete()));
 
         toggleFocusAndIgnoreImeOperations(mUrlBar, true);
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferencesTest.java
index 9e548ed..d14af979 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferencesTest.java
@@ -37,6 +37,8 @@
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.nullValue;
 import static org.hamcrest.Matchers.sameInstance;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
 
 import static org.chromium.chrome.test.util.ViewUtils.VIEW_GONE;
 import static org.chromium.chrome.test.util.ViewUtils.VIEW_INVISIBLE;
@@ -74,17 +76,19 @@
 import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.Callback;
 import org.chromium.base.CollectionUtil;
 import org.chromium.base.IntStringCallback;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.ScalableTimeout;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.history.HistoryActivity;
@@ -101,6 +105,7 @@
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.signin.ChromeSigninController;
 import org.chromium.components.sync.ModelType;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 import java.io.File;
@@ -119,6 +124,9 @@
 @RunWith(BaseJUnit4ClassRunner.class)
 public class SavePasswordsPreferencesTest {
     private static final long UI_UPDATING_TIMEOUT_MS = 3000;
+    @Mock
+    private PasswordEditingDelegate mMockPasswordEditingDelegate;
+
     @Rule
     public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
 
@@ -225,6 +233,15 @@
         @Override
         public void showPasswordEntryEditingView(Context context, int index) {
             mLastEntryIndex = index;
+            Bundle fragmentArgs = new Bundle();
+            fragmentArgs.putString(
+                    PasswordEntryEditor.CREDENTIAL_URL, getSavedPasswordEntry(index).getUrl());
+            fragmentArgs.putString(PasswordEntryEditor.CREDENTIAL_NAME,
+                    getSavedPasswordEntry(index).getUserName());
+            fragmentArgs.putString(PasswordEntryEditor.CREDENTIAL_PASSWORD,
+                    getSavedPasswordEntry(index).getPassword());
+            PreferencesLauncher.launchSettingsPage(
+                    context, PasswordEntryEditor.class, fragmentArgs);
         }
     }
 
@@ -256,6 +273,10 @@
      */
     private final ManualCallbackDelayer mManualDelayer = new ManualCallbackDelayer();
 
+    public SavePasswordsPreferencesTest() {
+        MockitoAnnotations.initMocks(this);
+    }
+
     private void overrideProfileSyncService(
             final boolean usingPassphrase, final boolean syncingPasswords) {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -723,6 +744,8 @@
     @Features.EnableFeatures(ChromeFeatureList.PASSWORD_EDITING_ANDROID)
     public void testSelectedStoredPasswordIndexIsSameAsInShowPasswordEntryEditingView()
             throws Exception {
+        PasswordEditingDelegateProvider.getInstance().setPasswordEditingDelegate(
+                mMockPasswordEditingDelegate);
         setPasswordSourceWithMultipleEntries( // Initialize preferences
                 new SavedPasswordEntry[] {new SavedPasswordEntry("https://example.com",
                                                   "example user", "example password"),
@@ -746,6 +769,8 @@
     @Feature({"Preferences"})
     @Features.EnableFeatures(ChromeFeatureList.PASSWORD_EDITING_ANDROID)
     public void testPasswordDataDisplayedInEditingActivity() throws Exception {
+        PasswordEditingDelegateProvider.getInstance().setPasswordEditingDelegate(
+                mMockPasswordEditingDelegate);
         Bundle fragmentArgs = new Bundle();
         fragmentArgs.putString(PasswordEntryEditor.CREDENTIAL_URL, "https://example.com");
         fragmentArgs.putString(PasswordEntryEditor.CREDENTIAL_NAME, "test user");
@@ -759,17 +784,16 @@
     }
 
     /**
-     * Check that the changes of password data in the password editing activity are preserved and
-     * shown in the password viewing activity and in the list of passwords after the save button
-     * was clicked.
+     * Check that the password editing method from the PasswordEditingDelegate was called when the
+     * save button in the password editing activity was clicked.
      */
-    // TODO(izuzic): Remove @Ignore when saving of changes is enabled again.
-    @Ignore("The test is ignored because saving the changes of credentials is currently disabled.")
     @Test
     @SmallTest
     @Feature({"Preferences"})
     @Features.EnableFeatures(ChromeFeatureList.PASSWORD_EDITING_ANDROID)
-    public void testChangeOfStoredPasswordDataIsPreserved() throws Exception {
+    public void testPasswordEditingMethodWasCalled() throws Exception {
+        PasswordEditingDelegateProvider.getInstance().setPasswordEditingDelegate(
+                mMockPasswordEditingDelegate);
         setPasswordSource(new SavedPasswordEntry("https://example.com", "test user", "password"));
 
         PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(),
@@ -783,6 +807,38 @@
 
         Espresso.onView(withSaveMenuIdOrText()).perform(click());
 
+        verify(mMockPasswordEditingDelegate).editSavedPasswordEntry("test user new", "password");
+
+        // Verify that the delegate was destroyed when the password editing activity finished.
+        waitForEvent().destroy();
+    }
+
+    /**
+     * Check that the changes of password data are shown in the password viewing activity and in the
+     * list of passwords after the save button was clicked.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    @Features.EnableFeatures(ChromeFeatureList.PASSWORD_EDITING_ANDROID)
+    public void testChangeOfStoredPasswordDataIsPropagated() throws Exception {
+        PasswordEditingDelegateProvider.getInstance().setPasswordEditingDelegate(
+                mMockPasswordEditingDelegate);
+        setPasswordSource(new SavedPasswordEntry("https://example.com", "test user", "password"));
+
+        PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(),
+                SavePasswordsPreferences.class.getName());
+
+        Espresso.onView(withText(containsString("test user"))).perform(click());
+
+        Espresso.onView(withEditMenuIdOrText()).perform(click());
+
+        // Performing a change of saved credentials.
+        mHandler.mSavedPasswords.set(
+                0, new SavedPasswordEntry("https://example.com", "test user new", "password"));
+
+        Espresso.onView(withSaveMenuIdOrText()).perform(click());
+
         // Check if the password viewing activity has the updated data.
         Espresso.onView(withText("test user new")).check(matches(isDisplayed()));
 
@@ -800,6 +856,8 @@
     @Feature({"Preferences"})
     @Features.EnableFeatures(ChromeFeatureList.PASSWORD_EDITING_ANDROID)
     public void testStoredPasswordCanBeUnmaskedAndMaskedAgain() throws Exception {
+        PasswordEditingDelegateProvider.getInstance().setPasswordEditingDelegate(
+                mMockPasswordEditingDelegate);
         Bundle fragmentArgs = new Bundle();
         fragmentArgs.putString(SavePasswordsPreferences.PASSWORD_LIST_NAME, "test user");
         fragmentArgs.putString(SavePasswordsPreferences.PASSWORD_LIST_URL, "https://example.com");
@@ -2137,4 +2195,9 @@
                                 allOf(withId(R.id.search_src_text), withText("Zeu"))));
         Espresso.onView(withId(R.id.search_src_text)).check(matches(withText("Zeu")));
     }
+
+    PasswordEditingDelegate waitForEvent() {
+        return verify(mMockPasswordEditingDelegate,
+                timeout(ScalableTimeout.scaleTimeout(CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL)));
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/themes/ThemePreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/themes/ThemePreferencesTest.java
index cd987bb..a246619d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/themes/ThemePreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/themes/ThemePreferencesTest.java
@@ -14,27 +14,50 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.BuildInfo;
+import org.chromium.base.test.params.ParameterAnnotations;
+import org.chromium.base.test.params.ParameterSet;
+import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.night_mode.NightModeUtils;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.preferences.Preferences;
 import org.chromium.chrome.browser.preferences.PreferencesTest;
 import org.chromium.chrome.browser.preferences.themes.ThemePreferences.ThemeSetting;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.widget.RadioButtonWithDescription;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * Tests for ThemePreferences.
  */
-@RunWith(ChromeJUnit4ClassRunner.class)
+// clang-format off
 @Features.EnableFeatures(ANDROID_NIGHT_MODE)
+@RunWith(ParameterizedRunner.class)
+@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 public class ThemePreferencesTest extends DummyUiActivityTestCase {
+    // clang-format on
+    @ParameterAnnotations.ClassParameter
+    private static List<ParameterSet> sClassParams =
+            Arrays.asList(new ParameterSet().value(false).name("DefaultLightDisabled"),
+                    new ParameterSet().value(true).name("DefaultLightEnabled"));
+
+    private boolean mDefaultToLight;
     private ThemePreferences mFragment;
     private RadioButtonGroupThemePreference mPreference;
 
+    public ThemePreferencesTest(boolean defaultToLight) {
+        mDefaultToLight = defaultToLight;
+        FeatureUtilities.setNightModeDefaultToLightForTesting(defaultToLight);
+    }
+
     @Override
     public void setUpTest() throws Exception {
         super.setUpTest();
@@ -49,6 +72,7 @@
     @Override
     public void tearDownTest() throws Exception {
         ChromePreferenceManager.getInstance().removeKey(UI_THEME_SETTING_KEY);
+        FeatureUtilities.setNightModeDefaultToLightForTesting(null);
         super.tearDownTest();
     }
 
@@ -57,6 +81,20 @@
     @Feature({"Themes"})
     public void testSelectThemes() {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
+            // Default to light parameter is only applicable pre-Q.
+            if (mDefaultToLight && BuildInfo.isAtLeastQ()) {
+                Assert.assertFalse("Q should not default to light.",
+                        FeatureUtilities.isNightModeDefaultToLight());
+                return;
+            }
+
+            int expectedDefaultTheme =
+                    mDefaultToLight ? ThemeSetting.LIGHT : ThemeSetting.SYSTEM_DEFAULT;
+            Assert.assertEquals("Incorrect default theme setting.", expectedDefaultTheme,
+                    NightModeUtils.getThemeSetting());
+            assertButtonCheckedCorrectly(
+                    mDefaultToLight ? "Light" : "System default", expectedDefaultTheme);
+
             // Select System default
             Assert.assertEquals(R.id.system_default, getButton(0).getId());
             selectButton(0);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateControllerTest.java
index c9c3897..1ab2fe3a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateControllerTest.java
@@ -17,6 +17,8 @@
 import static org.chromium.base.ApplicationState.HAS_STOPPED_ACTIVITIES;
 import static org.chromium.chrome.browser.preferences.ChromePreferenceManager.UI_THEME_SETTING_KEY;
 
+import android.os.Build;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -27,8 +29,10 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.preferences.themes.ThemePreferences;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 
 /**
  * Unit tests for {@link GlobalNightModeStateController}.
@@ -101,6 +105,18 @@
     }
 
     @Test
+    @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.P)
+    public void testUpdateNightMode_PowerSaveMode_DefaultsToLight() {
+        FeatureUtilities.setNightModeDefaultToLightForTesting(true);
+
+        // Enable power save mode and verify night mode is not enabled.
+        setIsPowerSaveMode(true);
+        assertFalse(mGlobalNightModeStateController.isInNightMode());
+
+        FeatureUtilities.setNightModeDefaultToLightForTesting(null);
+    }
+
+    @Test
     public void testUpdateNightMode_SystemNightMode() {
         // Enable system night mode and verify night mode is enabled.
         setSystemNightMode(true);
@@ -112,6 +128,18 @@
     }
 
     @Test
+    @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.P)
+    public void testUpdateNightMode_SystemNightMode_DefaultsToLight() {
+        FeatureUtilities.setNightModeDefaultToLightForTesting(true);
+
+        // Enable system night mode and verify night mode is not enabled.
+        setSystemNightMode(true);
+        assertFalse(mGlobalNightModeStateController.isInNightMode());
+
+        FeatureUtilities.setNightModeDefaultToLightForTesting(null);
+    }
+
+    @Test
     public void testUpdateNightMode_Preference() {
         // Set preference to dark theme and verify night mode is enabled.
         ChromePreferenceManager.getInstance().writeInt(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
index 669b7f4..b3486dff 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
@@ -11,9 +11,12 @@
 import android.text.SpannableStringBuilder;
 
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
@@ -28,6 +31,16 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class UrlBarMediatorUnitTest {
+    @Mock
+    UrlBar.UrlTextChangeListener mMockListener;
+    @Mock
+    UrlBar.UrlTextChangeListener mAnotherMockListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
     @Test
     public void setUrlData_SendsUpdates() {
         PropertyModel model = new PropertyModel(UrlBarProperties.ALL_KEYS);
@@ -250,6 +263,24 @@
                 mediator.getReplacementCutCopyText("www.test.com/foo", 0, 16));
     }
 
+    @Test
+    public void urlTextChangeListenerCompositeObserver() {
+        PropertyModel model = new PropertyModel(UrlBarProperties.ALL_KEYS);
+        UrlBarMediator mediator = new UrlBarMediator(model);
+        mediator.addUrlTextChangeListener(mMockListener);
+
+        String text = "foo";
+        String textWithAutocomplete = "foo.bar";
+        mediator.onTextChanged(text, textWithAutocomplete);
+        Mockito.verify(mMockListener, Mockito.times(1)).onTextChanged(text, textWithAutocomplete);
+
+        mediator.addUrlTextChangeListener(mAnotherMockListener);
+        mediator.onTextChanged(text, textWithAutocomplete);
+        Mockito.verify(mMockListener, Mockito.times(2)).onTextChanged(text, textWithAutocomplete);
+        Mockito.verify(mAnotherMockListener, Mockito.times(1))
+                .onTextChanged(text, textWithAutocomplete);
+    }
+
     private static SpannableStringBuilder spannable(String text) {
         return new SpannableStringBuilder(text);
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
new file mode 100644
index 0000000..3e697c5
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
@@ -0,0 +1,134 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omnibox.status;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.Callback;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Unit tests for {@link StatusMediator}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public final class StatusMediatorUnitTest {
+    private static final String TEST_SEARCH_URL = "https://www.test.com";
+
+    @Mock
+    Resources mResources;
+    @Mock
+    StatusMediator.StatusMediatorDelegate mDelegate;
+    @Mock
+    Bitmap mBitmap;
+    @Captor
+    ArgumentCaptor<Callback<Bitmap>> mCallbackCaptor;
+
+    PropertyModel mModel;
+    StatusMediator mMediator;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mModel = new PropertyModel(StatusProperties.ALL_KEYS);
+        mMediator = new StatusMediator(mModel, mResources);
+        mMediator.setDelegateForTesting(mDelegate);
+    }
+
+    @Test
+    @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO)
+    public void searchEngineLogo_showGoogleLogo() {
+        setupSearchEngineLogoForTesting(false, true, false);
+
+        mMediator.setUrlHasFocus(true);
+        mMediator.setShowIconsWhenUrlFocused(true);
+        mMediator.updateSearchEngineStatusIcon(true, true, TEST_SEARCH_URL);
+        Assert.assertEquals(
+                R.drawable.ic_logo_googleg_24dp, mModel.get(StatusProperties.STATUS_ICON_RES));
+    }
+
+    @Test
+    @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO)
+    public void searchEngineLogo_showGoogleLogo_searchLoupeEverywhere() {
+        setupSearchEngineLogoForTesting(false, true, true);
+
+        mMediator.setUrlHasFocus(true);
+        mMediator.setShowIconsWhenUrlFocused(true);
+        mMediator.updateSearchEngineStatusIcon(true, true, TEST_SEARCH_URL);
+        Assert.assertEquals(R.drawable.ic_search, mModel.get(StatusProperties.STATUS_ICON_RES));
+    }
+
+    @Test
+    @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO)
+    public void searchEngineLogo_showNonGoogleLogo() {
+        setupSearchEngineLogoForTesting(false, false, false);
+
+        mMediator.setUrlHasFocus(true);
+        mMediator.setShowIconsWhenUrlFocused(true);
+
+        // Clear invocations since the setup methods call updateLocationBarIcon.
+        Mockito.clearInvocations(mDelegate);
+        mMediator.updateSearchEngineStatusIcon(true, false, TEST_SEARCH_URL);
+        Assert.assertEquals(R.drawable.ic_search, mModel.get(StatusProperties.STATUS_ICON_RES));
+        Mockito.verify(mDelegate, Mockito.times(1))
+                .getSearchEngineLogoFavicon(Mockito.any(), Mockito.any());
+        mCallbackCaptor.getValue().onResult(mBitmap);
+        Assert.assertEquals(mBitmap, mModel.get(StatusProperties.STATUS_ICON));
+    }
+
+    @Test
+    @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO)
+    public void searchEngineLogo_showNonGoogleLogo_searchLoupeEverywhere() {
+        setupSearchEngineLogoForTesting(false, false, true);
+
+        mMediator.setUrlHasFocus(true);
+        mMediator.setShowIconsWhenUrlFocused(true);
+
+        // Clear invocations since the setup methods call updateLocationBarIcon.
+        Mockito.clearInvocations(mDelegate);
+        mMediator.updateSearchEngineStatusIcon(true, false, TEST_SEARCH_URL);
+        Assert.assertEquals(R.drawable.ic_search, mModel.get(StatusProperties.STATUS_ICON_RES));
+        Mockito.verify(mDelegate, Mockito.times(0))
+                .getSearchEngineLogoFavicon(Mockito.any(), Mockito.any());
+    }
+
+    @Test
+    @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO)
+    public void searchEngineLogo_onTextChanged_globeReplacesIconWhenTextIsSite() {
+        setupSearchEngineLogoForTesting(true, false, false);
+
+        mMediator.setUrlHasFocus(true);
+        mMediator.setShowIconsWhenUrlFocused(true);
+        mMediator.onTextChanged("", "testing");
+        Assert.assertEquals(R.drawable.ic_globe_24dp, mModel.get(StatusProperties.STATUS_ICON_RES));
+    }
+
+    private void setupSearchEngineLogoForTesting(
+            boolean validUrl, boolean showGoogle, boolean loupeEverywhere) {
+        Mockito.doReturn(validUrl).when(mDelegate).isUrlValid(Mockito.any());
+        Mockito.doReturn(true).when(mDelegate).shouldShowSearchEngineLogo();
+        Mockito.doReturn(loupeEverywhere).when(mDelegate).shouldShowSearchLoupeEverywhere();
+        Mockito.doNothing().when(mDelegate).getSearchEngineLogoFavicon(
+                Mockito.any(), mCallbackCaptor.capture());
+        mMediator.updateSearchEngineStatusIcon(true, showGoogle, TEST_SEARCH_URL);
+    }
+}
diff --git a/chrome/android/modules/chrome_feature_modules.gni b/chrome/android/modules/chrome_feature_modules.gni
index b311304..b815f12 100644
--- a/chrome/android/modules/chrome_feature_modules.gni
+++ b/chrome/android/modules/chrome_feature_modules.gni
@@ -8,6 +8,7 @@
 import("//chrome/android/features/dev_ui/dev_ui_module.gni")
 import("//chrome/android/features/tab_ui/tab_ui_module.gni")
 import("//chrome/android/modules/buildflags.gni")
+import("//chrome/android/modules/extra_icu/extra_icu_module.gni")
 import("//chrome/android/modules/test_dummy/test_dummy_module.gni")
 import("//device/vr/buildflags/buildflags.gni")
 
@@ -35,7 +36,10 @@
 # Each new module needs to add a desc to one of the lists below.
 
 # Modules shipped in Chrome Modern (Android L+).
-chrome_modern_module_descs = [ test_dummy_module_desc ]
+chrome_modern_module_descs = [
+  test_dummy_module_desc,
+  extra_icu_module_desc,
+]
 if (enable_vr) {
   chrome_modern_module_descs += [ vr_module_desc ]
 }
diff --git a/chrome/android/modules/extra_icu/extra_icu_module.gni b/chrome/android/modules/extra_icu/extra_icu_module.gni
new file mode 100644
index 0000000..732dca1
--- /dev/null
+++ b/chrome/android/modules/extra_icu/extra_icu_module.gni
@@ -0,0 +1,13 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+extra_icu_module_desc = {
+  name = "extra_icu"
+  android_manifest =
+      "//chrome/android/modules/extra_icu/internal/java/AndroidManifest.xml"
+  java_deps = [
+    "//chrome/android/modules/extra_icu/internal:icudtl_extra_assets",
+    "//chrome/android/modules/extra_icu/internal:java",
+  ]
+}
diff --git a/chrome/android/modules/extra_icu/internal/BUILD.gn b/chrome/android/modules/extra_icu/internal/BUILD.gn
new file mode 100644
index 0000000..0b041aa4
--- /dev/null
+++ b/chrome/android/modules/extra_icu/internal/BUILD.gn
@@ -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.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  java_files =
+      [ "java/src/org/chromium/chrome/modules/extra_icu/ExtraIcuImpl.java" ]
+  deps = [
+    "//base:base_java",
+    "//chrome/android/modules/extra_icu/public:java",
+  ]
+}
+
+android_assets("icudtl_extra_assets") {
+  sources = [
+    "//third_party/icu/android_small/icudtl_extra.dat",
+  ]
+  disable_compression = true
+}
diff --git a/chrome/android/modules/extra_icu/internal/java/AndroidManifest.xml b/chrome/android/modules/extra_icu/internal/java/AndroidManifest.xml
new file mode 100644
index 0000000..c7aab1c
--- /dev/null
+++ b/chrome/android/modules/extra_icu/internal/java/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:dist="http://schemas.android.com/apk/distribution"
+    featureSplit="extra_icu">
+
+    <!-- Always install extra ICU module on first Chrome install or update. -->
+    <dist:module
+        dist:onDemand="false"
+        dist:title="@string/extra_icu_module_title">
+        <dist:fusing dist:include="false" />
+    </dist:module>
+
+    <application />
+</manifest>
diff --git a/chrome/android/modules/extra_icu/internal/java/src/org/chromium/chrome/modules/extra_icu/ExtraIcuImpl.java b/chrome/android/modules/extra_icu/internal/java/src/org/chromium/chrome/modules/extra_icu/ExtraIcuImpl.java
new file mode 100644
index 0000000..041da1ed
--- /dev/null
+++ b/chrome/android/modules/extra_icu/internal/java/src/org/chromium/chrome/modules/extra_icu/ExtraIcuImpl.java
@@ -0,0 +1,11 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.modules.extra_icu;
+
+import org.chromium.base.annotations.UsedByReflection;
+
+/** Interface implementation inside the module. */
+@UsedByReflection("ExtraIcuModule")
+public class ExtraIcuImpl implements ExtraIcu {}
diff --git a/chrome/android/modules/extra_icu/provider/BUILD.gn b/chrome/android/modules/extra_icu/provider/BUILD.gn
new file mode 100644
index 0000000..ce7c348
--- /dev/null
+++ b/chrome/android/modules/extra_icu/provider/BUILD.gn
@@ -0,0 +1,29 @@
+# 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("//build/config/android/rules.gni")
+
+android_library("java") {
+  deps = [
+    "//base:base_java",
+    "//chrome/android/modules/extra_icu/public:java",
+  ]
+  java_files = [ "java/src/org/chromium/chrome/modules/extra_icu/ExtraIcuModuleProvider.java" ]
+}
+
+generate_jni("jni_headers") {
+  sources = [
+    "java/src/org/chromium/chrome/modules/extra_icu/ExtraIcuModuleProvider.java",
+  ]
+}
+
+source_set("native") {
+  deps = [
+    ":jni_headers",
+  ]
+  sources = [
+    "module_provider.cc",
+    "module_provider.h",
+  ]
+}
diff --git a/chrome/android/modules/extra_icu/provider/java/src/org/chromium/chrome/modules/extra_icu/ExtraIcuModuleProvider.java b/chrome/android/modules/extra_icu/provider/java/src/org/chromium/chrome/modules/extra_icu/ExtraIcuModuleProvider.java
new file mode 100644
index 0000000..9a3adf02
--- /dev/null
+++ b/chrome/android/modules/extra_icu/provider/java/src/org/chromium/chrome/modules/extra_icu/ExtraIcuModuleProvider.java
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.modules.extra_icu;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/** Java side of the extra ICU module installer. */
+@JNINamespace("extra_icu")
+public class ExtraIcuModuleProvider {
+    /** Returns true if the extra ICU module is installed. */
+    @CalledByNative
+    private static boolean isModuleInstalled() {
+        return ExtraIcuModule.isInstalled();
+    }
+
+    private ExtraIcuModuleProvider() {}
+}
diff --git a/chrome/android/modules/extra_icu/provider/module_provider.cc b/chrome/android/modules/extra_icu/provider/module_provider.cc
new file mode 100644
index 0000000..f2ec1fc
--- /dev/null
+++ b/chrome/android/modules/extra_icu/provider/module_provider.cc
@@ -0,0 +1,16 @@
+// 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/android/modules/extra_icu/provider/module_provider.h"
+#include "chrome/android/modules/extra_icu/provider/jni_headers/ExtraIcuModuleProvider_jni.h"
+
+namespace extra_icu {
+
+// static
+bool ModuleProvider::IsModuleInstalled() {
+  return Java_ExtraIcuModuleProvider_isModuleInstalled(
+      base::android::AttachCurrentThread());
+}
+
+}  // namespace extra_icu
diff --git a/chrome/android/modules/extra_icu/provider/module_provider.h b/chrome/android/modules/extra_icu/provider/module_provider.h
new file mode 100644
index 0000000..b3c0bc8
--- /dev/null
+++ b/chrome/android/modules/extra_icu/provider/module_provider.h
@@ -0,0 +1,19 @@
+// 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_ANDROID_MODULES_EXTRA_ICU_PROVIDER_MODULE_PROVIDER_H_
+#define CHROME_ANDROID_MODULES_EXTRA_ICU_PROVIDER_MODULE_PROVIDER_H_
+
+namespace extra_icu {
+
+// Native side of the extra ICU module installer.
+class ModuleProvider {
+ public:
+  // Returns true if the extra ICU module is installed.
+  static bool IsModuleInstalled();
+};
+
+}  // namespace extra_icu
+
+#endif  // CHROME_ANDROID_MODULES_EXTRA_ICU_PROVIDER_MODULE_PROVIDER_H_
diff --git a/chrome/android/modules/extra_icu/public/BUILD.gn b/chrome/android/modules/extra_icu/public/BUILD.gn
new file mode 100644
index 0000000..7a9a943
--- /dev/null
+++ b/chrome/android/modules/extra_icu/public/BUILD.gn
@@ -0,0 +1,16 @@
+# 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("//build/config/android/rules.gni")
+
+android_library("java") {
+  java_files =
+      [ "java/src/org/chromium/chrome/modules/extra_icu/ExtraIcu.java" ]
+  deps = [
+    "//components/module_installer/android:module_installer_java",
+    "//components/module_installer/android:module_interface_java",
+  ]
+  annotation_processor_deps =
+      [ "//components/module_installer/android:module_interface_processor" ]
+}
diff --git a/chrome/android/modules/extra_icu/public/java/src/org/chromium/chrome/modules/extra_icu/ExtraIcu.java b/chrome/android/modules/extra_icu/public/java/src/org/chromium/chrome/modules/extra_icu/ExtraIcu.java
new file mode 100644
index 0000000..13beb9a9
--- /dev/null
+++ b/chrome/android/modules/extra_icu/public/java/src/org/chromium/chrome/modules/extra_icu/ExtraIcu.java
@@ -0,0 +1,11 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.modules.extra_icu;
+
+import org.chromium.components.module_installer.ModuleInterface;
+
+/** Interface into the extra ICU module. Only used to check whether module is installed. */
+@ModuleInterface(module = "extra_icu", impl = "org.chromium.chrome.modules.extra_icu.ExtraIcuImpl")
+public interface ExtraIcu {}
diff --git a/chrome/android/webapk/shell_apk/AndroidManifest.xml b/chrome/android/webapk/shell_apk/AndroidManifest.xml
index 1aa5ee6..ccfd7ad 100644
--- a/chrome/android/webapk/shell_apk/AndroidManifest.xml
+++ b/chrome/android/webapk/shell_apk/AndroidManifest.xml
@@ -20,12 +20,10 @@
     package="{{{manifest_package}}}"
     android:versionCode="{{{version_code}}}"
     android:versionName="{{{version_name}}}" >
-
-    <uses-permission android:name="android.permission.VIBRATE"></uses-permission>
     {{{raw_manifest_tags}}}
 
     <uses-sdk
-        android:minSdkVersion="16"
+        android:minSdkVersion="19"
         android:targetSdkVersion="28" />
 
     <application
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn
index e21dd83..49284bb7 100644
--- a/chrome/android/webapk/shell_apk/BUILD.gn
+++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -75,7 +75,7 @@
       "//chrome/android/webapk/libs/common:common_java",
       "//chrome/android/webapk/libs/common:splash_java",
     ]
-    min_sdk_version = 16
+    min_sdk_version = 19
   }
 }
 
@@ -92,7 +92,7 @@
       "src/org/chromium/webapk/shell_apk/WebApkServiceImplWrapper.java",
     ]
     deps += [ ":compiled_in_runtime_library_java" ]
-    min_sdk_version = 16
+    min_sdk_version = 19
   }
 }
 
@@ -178,7 +178,7 @@
 
     android_manifest = _manifest_output
     android_manifest_dep = ":$_manifest_target_name"
-    min_sdk_version = 16
+    min_sdk_version = 19
     target_sdk_version = 28
     never_incremental = true
     enable_multidex = false
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index 04977b6..b24b291 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 106
+current_shell_apk_version = 107
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/ChooseHostBrowserDialog.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/ChooseHostBrowserDialog.java
index 6736966..bdafce16 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/ChooseHostBrowserDialog.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/ChooseHostBrowserDialog.java
@@ -201,8 +201,8 @@
             Resources res = mContext.getResources();
             ImageView icon = (ImageView) convertView.findViewById(R.id.browser_icon);
             TextView name = (TextView) convertView.findViewById(R.id.browser_name);
-            WebApkUtils.setPaddingInPixel(
-                    name, res.getDimensionPixelSize(R.dimen.list_column_padding), 0, 0, 0);
+            name.setPaddingRelative(
+                    res.getDimensionPixelSize(R.dimen.list_column_padding), 0, 0, 0);
 
             BrowserItem item = mBrowsers.get(position);
             name.setEnabled(item.supportsWebApks());
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/InstallHostBrowserDialog.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/InstallHostBrowserDialog.java
index 6e02a854..5ce8f6a 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/InstallHostBrowserDialog.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/InstallHostBrowserDialog.java
@@ -49,7 +49,7 @@
 
         TextView name = (TextView) view.findViewById(R.id.browser_name);
         name.setText(hostBrowserApplicationName);
-        WebApkUtils.setPaddingInPixel(name,
+        name.setPaddingRelative(
                 context.getResources().getDimensionPixelSize(R.dimen.list_column_padding), 0, 0, 0);
 
         OnDismissListenerCanceler onDismissCanceler = new OnDismissListenerCanceler();
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java
index 0971d90a..b872bfa 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java
@@ -148,19 +148,6 @@
     }
 
     /**
-     * Android uses padding_left under API level 17 and uses padding_start after that.
-     * If we set the padding in resource file, android will create duplicated resource xml
-     * with the padding to be different.
-     */
-    public static void setPaddingInPixel(View view, int start, int top, int end, int bottom) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            view.setPaddingRelative(start, top, end, bottom);
-        } else {
-            view.setPadding(start, top, end, bottom);
-        }
-    }
-
-    /**
      * Imitates Chrome's @style/AlertDialogContent. We set the style via Java instead of via
      * specifying the style in the XML to avoid having layout files in both layout-v17/ and in
      * layout/.
@@ -173,11 +160,11 @@
                 TypedValue.COMPLEX_UNIT_PX, res.getDimension(R.dimen.headline_size_medium));
         int dialogContentPadding = res.getDimensionPixelSize(R.dimen.dialog_content_padding);
         int titleBottomPadding = res.getDimensionPixelSize(R.dimen.title_bottom_padding);
-        setPaddingInPixel(titleView, dialogContentPadding, dialogContentPadding,
+        titleView.setPaddingRelative(dialogContentPadding, dialogContentPadding,
                 dialogContentPadding, titleBottomPadding);
 
         int dialogContentTopPadding = res.getDimensionPixelSize(R.dimen.dialog_content_top_padding);
-        setPaddingInPixel(contentView, dialogContentPadding, dialogContentTopPadding,
+        contentView.setPaddingRelative(dialogContentPadding, dialogContentTopPadding,
                 dialogContentPadding, dialogContentPadding);
     }
 
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 86d865d..f400a329 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -141,7 +141,8 @@
 #include "base/environment.h"
 #endif
 
-#if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_ANDROID)
+#if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_ANDROID) || \
+    defined(OS_LINUX)
 #include "chrome/browser/policy/policy_path_parser.h"
 #include "components/crash/content/app/crashpad.h"
 #endif
@@ -645,8 +646,11 @@
 #if defined(OS_WIN) && !defined(CHROME_MULTIPLE_DLL_BROWSER)
   v8_crashpad_support::SetUp();
 #endif
+
 #if defined(OS_LINUX)
-  breakpad::SetFirstChanceExceptionHandler(v8::TryHandleWebAssemblyTrapPosix);
+  if (!crash_reporter::IsCrashpadEnabled()) {
+    breakpad::SetFirstChanceExceptionHandler(v8::TryHandleWebAssemblyTrapPosix);
+  }
 #endif
 
 #if defined(OS_POSIX)
@@ -1008,7 +1012,13 @@
       base::android::InitJavaExceptionReporterForChildProcess();
     }
 #else  // !defined(OS_ANDROID)
-    breakpad::InitCrashReporter(process_type);
+    if (crash_reporter::IsCrashpadEnabled()) {
+      crash_reporter::InitializeCrashpad(process_type.empty(), process_type);
+      crash_reporter::SetFirstChanceExceptionHandler(
+          v8::TryHandleWebAssemblyTrapPosix);
+    } else {
+      breakpad::InitCrashReporter(process_type);
+    }
 #endif  // defined(OS_ANDROID)
   }
 #endif  // defined(OS_POSIX) && !defined(OS_MACOSX)
@@ -1141,7 +1151,13 @@
       base::CommandLine::ForCurrentProcess();
   std::string process_type =
       command_line->GetSwitchValueASCII(switches::kProcessType);
-  breakpad::InitCrashReporter(process_type);
+  if (crash_reporter::IsCrashpadEnabled()) {
+    crash_reporter::InitializeCrashpad(false, process_type);
+    crash_reporter::SetFirstChanceExceptionHandler(
+        v8::TryHandleWebAssemblyTrapPosix);
+  } else {
+    breakpad::InitCrashReporter(process_type);
+  }
 
   // Reset the command line for the newly spawned process.
   crash_keys::SetCrashKeysFromCommandLine(*command_line);
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index f026f7d..6271da4 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -845,6 +845,10 @@
     "native_window_notification_source.h",
     "navigation_predictor/navigation_predictor.cc",
     "navigation_predictor/navigation_predictor.h",
+    "navigation_predictor/navigation_predictor_keyed_service.cc",
+    "navigation_predictor/navigation_predictor_keyed_service.h",
+    "navigation_predictor/navigation_predictor_keyed_service_factory.cc",
+    "navigation_predictor/navigation_predictor_keyed_service_factory.h",
     "net/chrome_cookie_notification_details.h",
     "net/chrome_mojo_proxy_resolver_factory.cc",
     "net/chrome_mojo_proxy_resolver_factory.h",
@@ -1376,8 +1380,6 @@
     "previews/previews_lite_page_decider.h",
     "previews/previews_lite_page_infobar_delegate.cc",
     "previews/previews_lite_page_infobar_delegate.h",
-    "previews/previews_lite_page_navigation_throttle.cc",
-    "previews/previews_lite_page_navigation_throttle.h",
     "previews/previews_lite_page_predictor.cc",
     "previews/previews_lite_page_predictor.h",
     "previews/previews_lite_page_redirect_url_loader.cc",
@@ -2891,6 +2893,7 @@
       ":explore_sites_proto",
       ":usage_stats_proto",
       "//chrome/android:jni_headers",
+      "//chrome/android/modules/extra_icu/provider:native",
       "//chrome/browser/android/thin_webview/internal",
       "//chrome/browser/android/webapk:proto",
       "//chrome/services/media_gallery_util/public/cpp",
@@ -3986,8 +3989,6 @@
   if (is_win || is_mac) {
     # Sources (generally "desktop OS importers") used only on Mac & Windows.
     sources += [
-      "crash_upload_list/crash_upload_list_crashpad.cc",
-      "crash_upload_list/crash_upload_list_crashpad.h",
       "recovery/recovery_install_global_error.cc",
       "recovery/recovery_install_global_error.h",
       "recovery/recovery_install_global_error_factory.cc",
@@ -4134,6 +4135,8 @@
 
   if (is_win || is_mac || is_linux) {
     sources += [
+      "crash_upload_list/crash_upload_list_crashpad.cc",
+      "crash_upload_list/crash_upload_list_crashpad.h",
       "payments/chrome_payment_request_delegate.cc",
       "payments/chrome_payment_request_delegate.h",
       "payments/payment_request_display_manager_factory.cc",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index de68e005..9d9e185c 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -4,6 +4,7 @@
   "+chrome/android/chrome_jni_headers",
   "+chrome/android/features/autofill_assistant/jni_headers",
   "+chrome/android/features/dev_ui",
+  "+chrome/android/modules",
   "+chrome/android/public/profiles/jni_headers",
   "+chrome/app",
   "+chrome/chrome_watcher",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 2a4ca23..90d4dc1bd 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -148,6 +148,7 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/forcedark/forcedark_switches.h"
 #include "third_party/leveldatabase/leveldb_features.h"
+#include "ui/accessibility/accessibility_features.h"
 #include "ui/accessibility/accessibility_switches.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
@@ -1286,6 +1287,12 @@
 };
 
 #if defined(OS_ANDROID)
+const FeatureEntry::FeatureParam kAndroidNightModeDefaultToLightConstant[] = {
+    {"default_light_theme", "true"}};
+const FeatureEntry::FeatureVariation kAndroidNightModeFeatureVariations[] = {
+    {"(default to light theme)", kAndroidNightModeDefaultToLightConstant,
+     base::size(kAndroidNightModeDefaultToLightConstant), nullptr}};
+
 const FeatureEntry::FeatureParam
     kOmniboxSearchEngineLogoRoundedEdgesVariationConstant[] = {
         {"rounded_edges", "true"}};
@@ -1711,7 +1718,7 @@
      SINGLE_VALUE_TYPE(chromeos::switches::kShelfHoverPreviews)},
     {"shelf-scrollable", flag_descriptions::kShelfScrollableName,
      flag_descriptions::kShelfScrollableDescription, kOsCrOS,
-     SINGLE_VALUE_TYPE(chromeos::switches::kShelfScrollable)},
+     FEATURE_VALUE_TYPE(chromeos::features::kShelfScrollable)},
     {"show-bluetooth-debug-log-toggle",
      flag_descriptions::kShowBluetoothDebugLogToggleName,
      flag_descriptions::kShowBluetoothDebugLogToggleDescription, kOsCrOS,
@@ -2072,11 +2079,6 @@
      flag_descriptions::kEnableLitePageServerPreviewsName,
      flag_descriptions::kEnableLitePageServerPreviewsDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(previews::features::kLitePageServerPreviews)},
-    {"enable-url-loader-lite-page-server-previews",
-     flag_descriptions::kEnableURLLoaderLitePageServerPreviewsName,
-     flag_descriptions::kEnableURLLoaderLitePageServerPreviewsName, kOsAndroid,
-     FEATURE_VALUE_TYPE(
-         previews::features::kHTTPSServerPreviewsUsingURLLoader)},
 #endif  // OS_ANDROID
 #if defined(OS_CHROMEOS) || defined(OS_LINUX)
     {"enable-save-data", flag_descriptions::kEnableSaveDataName,
@@ -2422,7 +2424,9 @@
 #if BUILDFLAG(ENABLE_ANDROID_NIGHT_MODE)
     {"enable-android-night-mode", flag_descriptions::kAndroidNightModeName,
      flag_descriptions::kAndroidNightModeDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kAndroidNightMode)},
+     FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kAndroidNightMode,
+                                    kAndroidNightModeFeatureVariations,
+                                    "AndroidNightMode")},
 #endif  // BUILDFLAG(ENABLE_ANDROID_NIGHT_MODE)
 #endif  // OS_ANDROID
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
@@ -2930,6 +2934,11 @@
      flag_descriptions::kOmniboxZeroSuggestionsOnNTPRealboxDescription,
      kOsDesktop, FEATURE_VALUE_TYPE(omnibox::kZeroSuggestionsOnNTPRealbox)},
 
+    {"omnibox-zero-suggestions-on-serp",
+     flag_descriptions::kOmniboxZeroSuggestionsOnSERPName,
+     flag_descriptions::kOmniboxZeroSuggestionsOnSERPDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(omnibox::kZeroSuggestionsOnSERP)},
+
     {"omnibox-material-design-weather-icons",
      flag_descriptions::kOmniboxMaterialDesignWeatherIconsName,
      flag_descriptions::kOmniboxMaterialDesignWeatherIconsDescription,
@@ -3460,6 +3469,11 @@
      kOsDesktop,
      FEATURE_VALUE_TYPE(features::kExperimentalAccessibilityLabels)},
 
+    {"enable-accessibility-expose-display-none",
+     flag_descriptions::kAccessibilityExposeDisplayNoneName,
+     flag_descriptions::kAccessibilityExposeDisplayNoneDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kEnableAccessibilityExposeDisplayNone)},
+
     {"enable-accessibility-object-model",
      flag_descriptions::kEnableAccessibilityObjectModelName,
      flag_descriptions::kEnableAccessibilityObjectModelDescription, kOsAll,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index f30aa40..ec3a4e6 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -223,7 +223,6 @@
     &password_manager::features::kPasswordManagerOnboardingAndroid,
     &password_manager::features::kLeakDetection,
     &safe_browsing::kCaptureSafetyNetId,
-    &safe_browsing::kSendOnFocusPing,
     &signin::kMiceFeature,
     &switches::kSyncManualStartAndroid,
     &unified_consent::kUnifiedConsent,
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc
index fd35f41..2a79a24 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.cc
+++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -7,6 +7,9 @@
 #include <stddef.h>
 
 #include <memory>
+#include <string>
+#include <utility>
+#include <vector>
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
@@ -332,11 +335,8 @@
   nav_params.source_contents = source;
   nav_params.window_action = NavigateParams::SHOW_WINDOW;
   nav_params.user_gesture = params.user_gesture;
-  if ((params.disposition == WindowOpenDisposition::NEW_POPUP ||
-       params.disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
-       params.disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
-       params.disposition == WindowOpenDisposition::NEW_WINDOW) &&
-      MaybeBlockPopup(source, base::Optional<GURL>(), &nav_params, &params,
+  if (ConsiderForPopupBlocking(params.disposition) &&
+      MaybeBlockPopup(source, nullptr, &nav_params, &params,
                       blink::mojom::WindowFeatures())) {
     return nullptr;
   }
diff --git a/chrome/browser/android/vr/arcore_device/arcore.h b/chrome/browser/android/vr/arcore_device/arcore.h
index 6de97776..5870424 100644
--- a/chrome/browser/android/vr/arcore_device/arcore.h
+++ b/chrome/browser/android/vr/arcore_device/arcore.h
@@ -55,12 +55,12 @@
       const mojom::XRRayPtr& ray,
       std::vector<mojom::XRHitResultPtr>* hit_results) = 0;
 
-  virtual base::Optional<int32_t> CreateAnchor(
+  virtual base::Optional<uint32_t> CreateAnchor(
       const mojom::VRPosePtr& pose) = 0;
-  virtual base::Optional<int32_t> CreateAnchor(const mojom::VRPosePtr& pose,
-                                               int32_t plane_id) = 0;
+  virtual base::Optional<uint32_t> CreateAnchor(const mojom::VRPosePtr& pose,
+                                                uint32_t plane_id) = 0;
 
-  virtual void DetachAnchor(int32_t anchor_id) = 0;
+  virtual void DetachAnchor(uint32_t anchor_id) = 0;
 
   virtual void Pause() = 0;
   virtual void Resume() = 0;
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
index 61ae8c56..2b6d3fb 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
@@ -580,7 +580,7 @@
                             CreateAnchorCallback callback) {
   DVLOG(2) << __func__;
 
-  base::Optional<int32_t> maybe_anchor_id = arcore_->CreateAnchor(anchor_pose);
+  base::Optional<uint32_t> maybe_anchor_id = arcore_->CreateAnchor(anchor_pose);
 
   if (maybe_anchor_id) {
     std::move(callback).Run(device::mojom::CreateAnchorResult::SUCCESS,
@@ -591,11 +591,11 @@
 }
 
 void ArCoreGl::CreatePlaneAnchor(mojom::VRPosePtr anchor_pose,
-                                 int32_t plane_id,
+                                 uint32_t plane_id,
                                  CreatePlaneAnchorCallback callback) {
   DVLOG(2) << __func__;
 
-  base::Optional<int32_t> maybe_anchor_id =
+  base::Optional<uint32_t> maybe_anchor_id =
       arcore_->CreateAnchor(anchor_pose, plane_id);
 
   if (maybe_anchor_id) {
@@ -606,7 +606,7 @@
   }
 }
 
-void ArCoreGl::DetachAnchor(int32_t anchor_id) {
+void ArCoreGl::DetachAnchor(uint32_t anchor_id) {
   DVLOG(2) << __func__;
 
   arcore_->DetachAnchor(anchor_id);
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.h b/chrome/browser/android/vr/arcore_device/arcore_gl.h
index e283a1f..da95ffe3 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.h
@@ -111,10 +111,10 @@
   void CreateAnchor(mojom::VRPosePtr anchor_pose,
                     CreateAnchorCallback callback) override;
   void CreatePlaneAnchor(mojom::VRPosePtr anchor_pose,
-                         int32_t plane_id,
+                         uint32_t plane_id,
                          CreatePlaneAnchorCallback callback) override;
 
-  void DetachAnchor(int32_t anchor_id) override;
+  void DetachAnchor(uint32_t anchor_id) override;
 
   // mojom::XRSessionController
   void SetFrameDataRestricted(bool restricted) override;
diff --git a/chrome/browser/android/vr/arcore_device/arcore_impl.cc b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
index fb80f464..fa0cdb33 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_impl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
@@ -319,7 +319,7 @@
       return;
     }
 
-    int32_t anchor_id;
+    AnchorId anchor_id;
     bool created;
     std::tie(anchor_id, created) = *maybe_anchor_id_and_created;
 
@@ -327,16 +327,17 @@
         << "Anchor creation is app-initiated - we should never encounter an "
            "anchor that was created outside of `ArCoreImpl::CreateAnchor()`.";
 
-    result.push_back(mojom::XRAnchorData::New(anchor_id, std::move(pose)));
+    result.push_back(
+        mojom::XRAnchorData::New(anchor_id.GetUnsafeValue(), std::move(pose)));
   });
 
   return result;
 }
 
-std::vector<int32_t> ArCoreImpl::GetAllAnchorIds() {
+std::vector<uint32_t> ArCoreImpl::GetAllAnchorIds() {
   EnsureArCoreAnchorsList();
 
-  std::vector<int32_t> result;
+  std::vector<uint32_t> result;
 
   ArSession_getAllAnchors(arcore_session_.get(), arcore_anchors_.get());
 
@@ -349,7 +350,7 @@
       return;
     }
 
-    int32_t anchor_id;
+    AnchorId anchor_id;
     bool created;
     std::tie(anchor_id, created) = *maybe_anchor_id_and_created;
 
@@ -440,12 +441,12 @@
       return;
     }
 
-    int32_t plane_id;
+    PlaneId plane_id;
     bool created;
     std::tie(plane_id, created) = *maybe_plane_id_and_created;
 
     result.push_back(mojom::XRPlaneData::New(
-        plane_id,
+        plane_id.GetUnsafeValue(),
         mojo::ConvertTo<device::mojom::XRPlaneOrientation>(plane_type),
         std::move(pose), std::move(vertices)));
   });
@@ -453,17 +454,16 @@
   return result;
 }
 
-std::vector<int32_t> ArCoreImpl::GetAllPlaneIds() {
+std::vector<uint32_t> ArCoreImpl::GetAllPlaneIds() {
   EnsureArCorePlanesList();
 
-  std::vector<int32_t> result;
+  std::vector<uint32_t> result;
 
   ArTrackableType plane_tracked_type = AR_TRACKABLE_PLANE;
   ArSession_getAllTrackables(arcore_session_.get(), plane_tracked_type,
                              arcore_planes_.get());
 
-  std::unordered_map<int32_t,
-                     device::internal::ScopedArCoreObject<ArTrackable*>>
+  std::map<PlaneId, device::internal::ScopedArCoreObject<ArTrackable*>>
       plane_id_to_plane_object;
 
   ForEachArCorePlane([this, &plane_id_to_plane_object, &result](
@@ -477,7 +477,7 @@
       return;
     }
 
-    int32_t plane_id;
+    PlaneId plane_id;
     bool created;
     std::tie(plane_id, created) = *maybe_plane_id_and_created;
 
@@ -496,8 +496,8 @@
 mojom::XRPlaneDetectionDataPtr ArCoreImpl::GetDetectedPlanesData() {
   TRACE_EVENT0("gpu", __FUNCTION__);
 
-  std::vector<mojom::XRPlaneDataPtr> updated_planes = GetUpdatedPlanesData();
-  std::vector<int32_t> all_plane_ids = GetAllPlaneIds();
+  auto updated_planes = GetUpdatedPlanesData();
+  auto all_plane_ids = GetAllPlaneIds();
 
   return mojom::XRPlaneDetectionData::New(all_plane_ids,
                                           std::move(updated_planes));
@@ -512,38 +512,38 @@
   return mojom::XRAnchorsData::New(all_anchor_ids, std::move(updated_anchors));
 }
 
-base::Optional<std::pair<int32_t, bool>> ArCoreImpl::CreateOrGetPlaneId(
+base::Optional<std::pair<PlaneId, bool>> ArCoreImpl::CreateOrGetPlaneId(
     void* plane_address) {
   auto it = ar_plane_address_to_id_.find(plane_address);
   if (it != ar_plane_address_to_id_.end()) {
     return std::make_pair(it->second, false);
   }
 
-  if (next_id_ == std::numeric_limits<int32_t>::max()) {
+  if (next_id_ == std::numeric_limits<uint32_t>::max()) {
     return base::nullopt;
   }
 
-  int32_t current_id = next_id_++;
+  uint32_t current_id = next_id_++;
   ar_plane_address_to_id_.emplace(plane_address, current_id);
 
-  return std::make_pair(current_id, true);
+  return std::make_pair(PlaneId(current_id), true);
 }
 
-base::Optional<std::pair<int32_t, bool>> ArCoreImpl::CreateOrGetAnchorId(
+base::Optional<std::pair<AnchorId, bool>> ArCoreImpl::CreateOrGetAnchorId(
     void* anchor_address) {
   auto it = ar_anchor_address_to_id_.find(anchor_address);
   if (it != ar_anchor_address_to_id_.end()) {
     return std::make_pair(it->second, false);
   }
 
-  if (next_id_ == std::numeric_limits<int32_t>::max()) {
+  if (next_id_ == std::numeric_limits<uint32_t>::max()) {
     return base::nullopt;
   }
 
-  int32_t current_id = next_id_++;
+  uint32_t current_id = next_id_++;
   ar_anchor_address_to_id_.emplace(anchor_address, current_id);
 
-  return std::make_pair(current_id, true);
+  return std::make_pair(AnchorId(current_id), true);
 }
 
 void ArCoreImpl::Pause() {
@@ -717,7 +717,7 @@
   return true;
 }
 
-base::Optional<int32_t> ArCoreImpl::CreateAnchor(
+base::Optional<uint32_t> ArCoreImpl::CreateAnchor(
     const device::mojom::VRPosePtr& pose) {
   DCHECK(pose);
 
@@ -734,7 +734,7 @@
     return base::nullopt;
   }
 
-  int32_t anchor_id;
+  AnchorId anchor_id;
   bool created;
   std::tie(anchor_id, created) = *maybe_anchor_id_and_created;
 
@@ -743,17 +743,17 @@
 
   anchor_id_to_anchor_object_[anchor_id] = std::move(ar_anchor);
 
-  return anchor_id;
+  return anchor_id.GetUnsafeValue();
 }
 
-base::Optional<int32_t> ArCoreImpl::CreateAnchor(
+base::Optional<uint32_t> ArCoreImpl::CreateAnchor(
     const device::mojom::VRPosePtr& pose,
-    int32_t plane_id) {
+    uint32_t plane_id) {
   DCHECK(pose);
 
   auto ar_pose = GetArPoseFromMojomPose(arcore_session_.get(), pose);
 
-  auto it = plane_id_to_plane_object_.find(plane_id);
+  auto it = plane_id_to_plane_object_.find(PlaneId(plane_id));
   if (it == plane_id_to_plane_object_.end()) {
     return base::nullopt;
   }
@@ -769,7 +769,7 @@
     return base::nullopt;
   }
 
-  int32_t anchor_id;
+  AnchorId anchor_id;
   bool created;
   std::tie(anchor_id, created) = *maybe_anchor_id_and_created;
 
@@ -778,11 +778,11 @@
 
   anchor_id_to_anchor_object_[anchor_id] = std::move(ar_anchor);
 
-  return anchor_id;
+  return anchor_id.GetUnsafeValue();
 }
 
-void ArCoreImpl::DetachAnchor(int32_t anchor_id) {
-  auto it = anchor_id_to_anchor_object_.find(anchor_id);
+void ArCoreImpl::DetachAnchor(uint32_t anchor_id) {
+  auto it = anchor_id_to_anchor_object_.find(AnchorId(anchor_id));
   if (it == anchor_id_to_anchor_object_.end()) {
     return;
   }
diff --git a/chrome/browser/android/vr/arcore_device/arcore_impl.h b/chrome/browser/android/vr/arcore_device/arcore_impl.h
index ed47b1f..21fd467b 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_impl.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_impl.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/scoped_generic.h"
+#include "base/util/type_safety/id_type.h"
 #include "chrome/browser/android/vr/arcore_device/arcore.h"
 #include "chrome/browser/android/vr/arcore_device/arcore_sdk.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
@@ -88,6 +89,9 @@
 
 }  // namespace internal
 
+using PlaneId = util::IdTypeU32<class PlaneTag>;
+using AnchorId = util::IdTypeU32<class AnchorTag>;
+
 // This class should be created and accessed entirely on a Gl thread.
 class ArCoreImpl : public ArCore {
  public:
@@ -114,12 +118,12 @@
   bool RequestHitTest(const mojom::XRRayPtr& ray,
                       std::vector<mojom::XRHitResultPtr>* hit_results) override;
 
-  base::Optional<int32_t> CreateAnchor(
+  base::Optional<uint32_t> CreateAnchor(
       const device::mojom::VRPosePtr& pose) override;
-  base::Optional<int32_t> CreateAnchor(const device::mojom::VRPosePtr& pose,
-                                       int32_t plane_id) override;
+  base::Optional<uint32_t> CreateAnchor(const device::mojom::VRPosePtr& pose,
+                                        uint32_t plane_id) override;
 
-  void DetachAnchor(int32_t anchor_id) override;
+  void DetachAnchor(uint32_t anchor_id) override;
 
  private:
   bool IsOnGlThread();
@@ -162,7 +166,7 @@
   // planes already have an ID assigned. The result includes freshly assigned
   // IDs for newly detected planes along with previously known IDs for updated
   // and unchanged planes. It excludes planes that are no longer being tracked.
-  std::vector<int32_t> GetAllPlaneIds();
+  std::vector<uint32_t> GetAllPlaneIds();
 
   // Returns vector containing information about all anchors updated in the
   // current frame.
@@ -170,23 +174,22 @@
 
   // The result will contain IDs of all anchors still tracked in the current
   // frame.
-  std::vector<int32_t> GetAllAnchorIds();
+  std::vector<uint32_t> GetAllAnchorIds();
 
-  int32_t next_id_ = 1;
-  std::unordered_map<void*, int32_t> ar_plane_address_to_id_;
-  std::unordered_map<int32_t,
-                     device::internal::ScopedArCoreObject<ArTrackable*>>
+  uint32_t next_id_ = 1;
+  std::map<void*, PlaneId> ar_plane_address_to_id_;
+  std::map<PlaneId, device::internal::ScopedArCoreObject<ArTrackable*>>
       plane_id_to_plane_object_;
-  std::unordered_map<void*, int32_t> ar_anchor_address_to_id_;
-  std::unordered_map<int32_t, device::internal::ScopedArCoreObject<ArAnchor*>>
+  std::map<void*, AnchorId> ar_anchor_address_to_id_;
+  std::map<AnchorId, device::internal::ScopedArCoreObject<ArAnchor*>>
       anchor_id_to_anchor_object_;
 
   // Returns tuple containing plane id and a boolean signifying that the plane
   // was created. The result will be a nullopt in case the ID should be assigned
   // but next ID would result in an integer overflow.
-  base::Optional<std::pair<int32_t, bool>> CreateOrGetPlaneId(
+  base::Optional<std::pair<PlaneId, bool>> CreateOrGetPlaneId(
       void* plane_address);
-  base::Optional<std::pair<int32_t, bool>> CreateOrGetAnchorId(
+  base::Optional<std::pair<AnchorId, bool>> CreateOrGetAnchorId(
       void* anchor_address);
 
   // Executes |fn| for each still tracked, non-subsumed plane present in
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.cc b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
index 8f973002..c3919a2 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.cc
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
@@ -225,13 +225,13 @@
       mojom::XRPlaneData::New(1, device::mojom::XRPlaneOrientation::HORIZONTAL,
                               std::move(pose), std::move(vertices)));
 
-  return mojom::XRPlaneDetectionData::New(std::vector<int32_t>{1},
+  return mojom::XRPlaneDetectionData::New(std::vector<uint32_t>{1},
                                           std::move(result));
 }
 
 mojom::XRAnchorsDataPtr FakeArCore::GetAnchorsData() {
   std::vector<mojom::XRAnchorDataPtr> result;
-  std::vector<int32_t> result_ids;
+  std::vector<uint32_t> result_ids;
 
   for (auto& anchor_id_and_data : anchors_) {
     mojom::VRPosePtr pose = mojom::VRPose::New();
@@ -246,13 +246,14 @@
   return mojom::XRAnchorsData::New(std::move(result_ids), std::move(result));
 }
 
-base::Optional<int32_t> FakeArCore::CreateAnchor(const mojom::VRPosePtr& pose,
-                                                 int32_t plane_id) {
+base::Optional<uint32_t> FakeArCore::CreateAnchor(const mojom::VRPosePtr& pose,
+                                                  uint32_t plane_id) {
   // TODO(992035): Fix this when implementing tests.
   return CreateAnchor(pose);
 }
 
-base::Optional<int32_t> FakeArCore::CreateAnchor(const mojom::VRPosePtr& pose) {
+base::Optional<uint32_t> FakeArCore::CreateAnchor(
+    const mojom::VRPosePtr& pose) {
   DCHECK(pose);
 
   gfx::Point3F position =
@@ -274,7 +275,7 @@
   return anchor_id;
 }
 
-void FakeArCore::DetachAnchor(int32_t anchor_id) {
+void FakeArCore::DetachAnchor(uint32_t anchor_id) {
   auto count = anchors_.erase(anchor_id);
   DCHECK_EQ(1u, count);
 }
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.h b/chrome/browser/android/vr/arcore_device/fake_arcore.h
index 6e92972..6735393f 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.h
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.h
@@ -40,11 +40,11 @@
   mojom::XRPlaneDetectionDataPtr GetDetectedPlanesData() override;
   mojom::XRAnchorsDataPtr GetAnchorsData() override;
 
-  base::Optional<int32_t> CreateAnchor(
+  base::Optional<uint32_t> CreateAnchor(
       const device::mojom::VRPosePtr& pose) override;
-  base::Optional<int32_t> CreateAnchor(const device::mojom::VRPosePtr& pose,
-                                       int32_t plane_id) override;
-  void DetachAnchor(int32_t anchor_id) override;
+  base::Optional<uint32_t> CreateAnchor(const device::mojom::VRPosePtr& pose,
+                                        uint32_t plane_id) override;
+  void DetachAnchor(uint32_t anchor_id) override;
 
   void SetCameraAspect(float aspect) { camera_aspect_ = aspect; }
 
@@ -63,8 +63,8 @@
     gfx::Quaternion orientation;
   };
 
-  int32_t next_id_ = 100;
-  std::unordered_map<int32_t, FakeAnchorData> anchors_;
+  uint32_t next_id_ = 100;
+  std::unordered_map<uint32_t, FakeAnchorData> anchors_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeArCore);
 };
diff --git a/chrome/browser/apps/platform_apps/shortcut_manager.cc b/chrome/browser/apps/platform_apps/shortcut_manager.cc
index bf82f7d..c2e7b96 100644
--- a/chrome/browser/apps/platform_apps/shortcut_manager.cc
+++ b/chrome/browser/apps/platform_apps/shortcut_manager.cc
@@ -26,7 +26,6 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_switches.h"
-#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/extension_set.h"
 
@@ -72,9 +71,7 @@
 }
 
 AppShortcutManager::AppShortcutManager(Profile* profile)
-    : profile_(profile),
-      is_profile_attributes_storage_observer_(false),
-      extension_registry_observer_(this) {
+    : profile_(profile), is_profile_attributes_storage_observer_(false) {
   // Use of g_browser_process requires that we are either on the UI thread, or
   // there are no threads initialized (such as in unit tests).
   DCHECK(!content::BrowserThread::IsThreadInitialized(
diff --git a/chrome/browser/apps/platform_apps/shortcut_manager.h b/chrome/browser/apps/platform_apps/shortcut_manager.h
index d2583a0..fad39152 100644
--- a/chrome/browser/apps/platform_apps/shortcut_manager.h
+++ b/chrome/browser/apps/platform_apps/shortcut_manager.h
@@ -10,15 +10,12 @@
 #include "base/scoped_observer.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
 #include "extensions/common/extension.h"
 
 class Profile;
 
-namespace extensions {
-class ExtensionRegistry;
-}
-
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
@@ -60,7 +57,7 @@
 
   ScopedObserver<extensions::ExtensionRegistry,
                  extensions::ExtensionRegistryObserver>
-      extension_registry_observer_;
+      extension_registry_observer_{this};
 
   base::WeakPtrFactory<AppShortcutManager> weak_ptr_factory_{this};
 
diff --git a/chrome/browser/autocomplete/shortcuts_extensions_manager.cc b/chrome/browser/autocomplete/shortcuts_extensions_manager.cc
index 51a01b7..5f6cf3a1 100644
--- a/chrome/browser/autocomplete/shortcuts_extensions_manager.cc
+++ b/chrome/browser/autocomplete/shortcuts_extensions_manager.cc
@@ -7,11 +7,10 @@
 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/omnibox/browser/shortcuts_backend.h"
-#include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
 
 ShortcutsExtensionsManager::ShortcutsExtensionsManager(Profile* profile)
-    : registry_observer_(this), profile_(profile) {
+    : profile_(profile) {
   DCHECK(profile_);
   registry_observer_.Add(extensions::ExtensionRegistry::Get(profile_));
 }
diff --git a/chrome/browser/autocomplete/shortcuts_extensions_manager.h b/chrome/browser/autocomplete/shortcuts_extensions_manager.h
index 4f60a5b..8f876d6 100644
--- a/chrome/browser/autocomplete/shortcuts_extensions_manager.h
+++ b/chrome/browser/autocomplete/shortcuts_extensions_manager.h
@@ -8,14 +8,11 @@
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 #include "base/supports_user_data.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
 
 class Profile;
 
-namespace extensions {
-class ExtensionRegistry;
-}
-
 // This class manages the removal of shortcuts associated with an extension when
 // that extension is unloaded.
 class ShortcutsExtensionsManager
@@ -34,7 +31,7 @@
  private:
   ScopedObserver<extensions::ExtensionRegistry,
                  extensions::ExtensionRegistryObserver>
-      registry_observer_;
+      registry_observer_{this};
   Profile* profile_;
 
   DISALLOW_COPY_AND_ASSIGN(ShortcutsExtensionsManager);
diff --git a/chrome/browser/banners/app_banner_manager_desktop.cc b/chrome/browser/banners/app_banner_manager_desktop.cc
index 1b55b61..3c5c5dd0 100644
--- a/chrome/browser/banners/app_banner_manager_desktop.cc
+++ b/chrome/browser/banners/app_banner_manager_desktop.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/banners/app_banner_settings_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
-#include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
@@ -55,7 +54,7 @@
 
 AppBannerManagerDesktop::AppBannerManagerDesktop(
     content::WebContents* web_contents)
-    : AppBannerManager(web_contents), registrar_observer_(this) {
+    : AppBannerManager(web_contents) {
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   extension_registry_ = extensions::ExtensionRegistry::Get(profile);
diff --git a/chrome/browser/banners/app_banner_manager_desktop.h b/chrome/browser/banners/app_banner_manager_desktop.h
index 0f5132b8..80ad977 100644
--- a/chrome/browser/banners/app_banner_manager_desktop.h
+++ b/chrome/browser/banners/app_banner_manager_desktop.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
 #include "chrome/browser/banners/app_banner_manager.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/app_registrar_observer.h"
@@ -82,7 +83,7 @@
   extensions::ExtensionRegistry* extension_registry_;
 
   ScopedObserver<web_app::AppRegistrar, web_app::AppRegistrarObserver>
-      registrar_observer_;
+      registrar_observer_{this};
 
   base::WeakPtrFactory<AppBannerManagerDesktop> weak_factory_{this};
 
diff --git a/chrome/browser/browsing_data/browsing_data_history_observer_service.cc b/chrome/browser/browsing_data/browsing_data_history_observer_service.cc
index 8c237ea9..2b3b5a7c 100644
--- a/chrome/browser/browsing_data/browsing_data_history_observer_service.cc
+++ b/chrome/browser/browsing_data/browsing_data_history_observer_service.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
 #include "chrome/common/buildflags.h"
-#include "components/history/core/browser/history_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 
 #if BUILDFLAG(ENABLE_SESSION_SERVICE)
@@ -18,7 +17,7 @@
 
 BrowsingDataHistoryObserverService::BrowsingDataHistoryObserverService(
     Profile* profile)
-    : profile_(profile), history_observer_(this) {
+    : profile_(profile) {
   auto* history_service = HistoryServiceFactory::GetForProfile(
       profile, ServiceAccessType::EXPLICIT_ACCESS);
   if (history_service)
diff --git a/chrome/browser/browsing_data/browsing_data_history_observer_service.h b/chrome/browser/browsing_data/browsing_data_history_observer_service.h
index 75b0167..f96f9590 100644
--- a/chrome/browser/browsing_data/browsing_data_history_observer_service.h
+++ b/chrome/browser/browsing_data/browsing_data_history_observer_service.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_HISTORY_OBSERVER_SERVICE_H_
 
 #include "base/scoped_observer.h"
+#include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -50,7 +51,7 @@
   Profile* profile_;
 
   ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
-      history_observer_;
+      history_observer_{this};
 
   DISALLOW_COPY_AND_ASSIGN(BrowsingDataHistoryObserverService);
 };
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 60585f96..613647d8 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -227,6 +227,7 @@
 
 #if defined(OS_LINUX)
 #include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/content/app/crashpad.h"
 #endif
 
 #if defined(OS_MACOSX)
@@ -1089,7 +1090,9 @@
 
 #if defined(OS_LINUX) || defined(OS_OPENBSD)
   // Set the product channel for crash reports.
-  breakpad::SetChannelCrashKey(chrome::GetChannelName());
+  if (!crash_reporter::IsCrashpadEnabled()) {
+    breakpad::SetChannelCrashKey(chrome::GetChannelName());
+  }
 #endif  // defined(OS_LINUX) || defined(OS_OPENBSD)
 
 #if defined(OS_MACOSX)
diff --git a/chrome/browser/chrome_browser_main_linux.cc b/chrome/browser/chrome_browser_main_linux.cc
index 452bca5e..39db450 100644
--- a/chrome/browser/chrome_browser_main_linux.cc
+++ b/chrome/browser/chrome_browser_main_linux.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/grit/chromium_strings.h"
 #include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/content/app/crashpad.h"
 #include "components/metrics/metrics_service.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
@@ -91,8 +92,10 @@
 void ChromeBrowserMainPartsLinux::PostProfileInit() {
   ChromeBrowserMainPartsPosix::PostProfileInit();
 
-  g_browser_process->metrics_service()->RecordBreakpadRegistration(
-      breakpad::IsCrashReporterEnabled());
+  bool enabled = (crash_reporter::IsCrashpadEnabled() &&
+                  crash_reporter::GetUploadsEnabled()) ||
+                 breakpad::IsCrashReporterEnabled();
+  g_browser_process->metrics_service()->RecordBreakpadRegistration(enabled);
 }
 
 void ChromeBrowserMainPartsLinux::PostMainMessageLoopStart() {
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 10bc86f..0900af1 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -100,7 +100,6 @@
 #include "chrome/browser/prerender/prerender_util.h"
 #include "chrome/browser/previews/previews_content_util.h"
 #include "chrome/browser/previews/previews_lite_page_decider.h"
-#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
 #include "chrome/browser/previews/previews_lite_page_url_loader_interceptor.h"
 #include "chrome/browser/previews/previews_service.h"
 #include "chrome/browser/previews/previews_service_factory.h"
@@ -463,6 +462,11 @@
 #include "chrome/browser/browser_switcher/browser_switcher_navigation_throttle.h"
 #endif
 
+#if defined(OS_LINUX)
+#include "components/crash/content/app/crash_switches.h"
+#include "components/crash/content/app/crashpad.h"
+#endif
+
 #if defined(OS_POSIX) && !defined(OS_MACOSX)
 #if !defined(OS_ANDROID)
 #include "base/debug/leak_annotations.h"
@@ -813,6 +817,12 @@
 }
 
 int GetCrashSignalFD(const base::CommandLine& command_line) {
+  if (crash_reporter::IsCrashpadEnabled()) {
+    int fd;
+    pid_t pid;
+    return crash_reporter::GetHandlerSocket(&fd, &pid) ? fd : -1;
+  }
+
   // Extensions have the same process type as renderers.
   if (command_line.HasSwitch(extensions::switches::kExtensionProcess)) {
     static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
@@ -2029,7 +2039,21 @@
 #if defined(OS_ANDROID)
   bool enable_crash_reporter = true;
 #else
-  bool enable_crash_reporter = breakpad::IsCrashReporterEnabled();
+  bool enable_crash_reporter = false;
+  if (crash_reporter::IsCrashpadEnabled()) {
+    command_line->AppendSwitch(crash_reporter::kEnableCrashpad);
+    enable_crash_reporter = true;
+
+    int fd;
+    pid_t pid;
+    if (crash_reporter::GetHandlerSocket(&fd, &pid)) {
+      command_line->AppendSwitchASCII(
+          crash_reporter::switches::kCrashpadHandlerPid,
+          base::NumberToString(pid));
+    }
+  } else {
+    enable_crash_reporter = breakpad::IsCrashReporterEnabled();
+  }
 #endif
   if (enable_crash_reporter) {
     std::string switch_value;
@@ -2990,12 +3014,9 @@
                                      frame_name, disposition, features,
                                      user_gesture, opener_suppressed);
   NavigateParams nav_params = blocked_params.CreateNavigateParams(web_contents);
-  if (MaybeBlockPopup(web_contents, opener_top_level_frame_url, &nav_params,
-                      nullptr /*=open_url_params*/,
-                      blocked_params.features())) {
-    return false;
-  }
-  return true;
+  return !MaybeBlockPopup(web_contents, &opener_top_level_frame_url,
+                          &nav_params, nullptr /*=open_url_params*/,
+                          blocked_params.features());
 }
 
 content::SpeechRecognitionManagerDelegate*
@@ -3446,8 +3467,8 @@
 
   // Handler to rewrite Preview's Server Lite Page, to show the original URL to
   // the user.
-  handler->AddHandlerPair(&HandlePreviewsLitePageURLRewrite,
-                          &HandlePreviewsLitePageURLRewriteReverse);
+  handler->AddHandlerPair(&previews::HandlePreviewsLitePageURLRewrite,
+                          &previews::HandlePreviewsLitePageURLRewriteReverse);
 }
 
 base::FilePath ChromeContentBrowserClient::GetDefaultDownloadDirectory() {
@@ -4881,7 +4902,7 @@
   // |network_loader_factory| and have interceptors create one themselves.
   // https://crbug.com/931786
   if (base::FeatureList::IsEnabled(
-          previews::features::kHTTPSServerPreviewsUsingURLLoader)) {
+          previews::features::kLitePageServerPreviews)) {
     interceptors.push_back(
         std::make_unique<previews::PreviewsLitePageURLLoaderInterceptor>(
             network_loader_factory,
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index acc6d52..eadcf18 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -1661,8 +1661,6 @@
   if (!err_msg.empty())
     return RespondNow(Error(err_msg));
 
-  // |NOT_READY| means service not running;
-  // |STOPPED| means service running;
   auto new_state = params->enabled ? ash::mojom::AssistantState::READY
                                    : ash::mojom::AssistantState::NOT_READY;
   if (ash::AssistantState::Get()->assistant_state() == new_state)
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index c0666042..d54f43a 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -148,6 +148,7 @@
 #include "components/arc/arc_util.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/content/app/crashpad.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/core/session_manager.h"
@@ -1117,6 +1118,11 @@
 void WizardController::OnChangedMetricsReportingState(bool enabled) {
   StatsReportingController::Get()->SetEnabled(
       ProfileManager::GetActiveUserProfile(), enabled);
+  if (crash_reporter::IsCrashpadEnabled()) {
+    crash_reporter::SetUploadConsent(enabled);
+    return;
+  }
+
   if (!enabled)
     return;
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
diff --git a/chrome/browser/content_settings/sound_content_setting_observer.cc b/chrome/browser/content_settings/sound_content_setting_observer.cc
index 101c0f48..67de4ba4 100644
--- a/chrome/browser/content_settings/sound_content_setting_observer.cc
+++ b/chrome/browser/content_settings/sound_content_setting_observer.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
@@ -29,9 +28,7 @@
 
 SoundContentSettingObserver::SoundContentSettingObserver(
     content::WebContents* contents)
-    : content::WebContentsObserver(contents),
-      logged_site_muted_ukm_(false),
-      observer_(this) {
+    : content::WebContentsObserver(contents), logged_site_muted_ukm_(false) {
   Profile* profile =
       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
   host_content_settings_map_ =
diff --git a/chrome/browser/content_settings/sound_content_setting_observer.h b/chrome/browser/content_settings/sound_content_setting_observer.h
index d9998d4..0844f68 100644
--- a/chrome/browser/content_settings/sound_content_setting_observer.h
+++ b/chrome/browser/content_settings/sound_content_setting_observer.h
@@ -8,13 +8,12 @@
 #include "base/scoped_observer.h"
 #include "build/build_config.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
-class HostContentSettingsMap;
-
 class SoundContentSettingObserver
     : public content::WebContentsObserver,
       public content::WebContentsUserData<SoundContentSettingObserver>,
@@ -72,7 +71,8 @@
 
   HostContentSettingsMap* host_content_settings_map_;
 
-  ScopedObserver<HostContentSettingsMap, content_settings::Observer> observer_;
+  ScopedObserver<HostContentSettingsMap, content_settings::Observer> observer_{
+      this};
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.cc b/chrome/browser/content_settings/tab_specific_content_settings.cc
index f775ad2..1071597 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.cc
+++ b/chrome/browser/content_settings/tab_specific_content_settings.cc
@@ -34,7 +34,6 @@
 #include "components/content_settings/core/browser/content_settings_info.h"
 #include "components/content_settings/core/browser/content_settings_registry.h"
 #include "components/content_settings/core/browser/content_settings_utils.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
@@ -112,8 +111,7 @@
       previous_protocol_handler_(ProtocolHandler::EmptyProtocolHandler()),
       pending_protocol_handler_setting_(CONTENT_SETTING_DEFAULT),
       load_plugins_link_enabled_(true),
-      microphone_camera_state_(MICROPHONE_CAMERA_NOT_ACCESSED),
-      observer_(this) {
+      microphone_camera_state_(MICROPHONE_CAMERA_NOT_ACCESSED) {
   ClearContentSettingsExceptForNavigationRelatedSettings();
   ClearNavigationRelatedContentSettings();
 
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.h b/chrome/browser/content_settings/tab_specific_content_settings.h
index 95277f2..aa45d1e 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.h
+++ b/chrome/browser/content_settings/tab_specific_content_settings.h
@@ -21,14 +21,13 @@
 #include "chrome/common/custom_handlers/protocol_handler.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/browser/content_settings_usages_state.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "net/cookies/canonical_cookie.h"
 
-class HostContentSettingsMap;
-
 namespace content {
 class NavigationHandle;
 }
@@ -453,7 +452,8 @@
   std::string media_stream_requested_video_device_;
 
   // Observer to watch for content settings changed.
-  ScopedObserver<HostContentSettingsMap, content_settings::Observer> observer_;
+  ScopedObserver<HostContentSettingsMap, content_settings::Observer> observer_{
+      this};
 
   // Stores content settings changed by the user via page info since the last
   // navigation. Used to determine whether to display the settings in page info.
diff --git a/chrome/browser/crash_upload_list/crash_upload_list.cc b/chrome/browser/crash_upload_list/crash_upload_list.cc
index a2073c0..6df26e5 100644
--- a/chrome/browser/crash_upload_list/crash_upload_list.cc
+++ b/chrome/browser/crash_upload_list/crash_upload_list.cc
@@ -11,7 +11,9 @@
 #else
 #include "base/files/file_path.h"
 #include "base/path_service.h"
+#include "chrome/browser/crash_upload_list/crash_upload_list_crashpad.h"
 #include "chrome/common/chrome_paths.h"
+#include "components/crash/content/app/crashpad.h"
 #include "components/upload_list/crash_upload_list.h"
 #include "components/upload_list/text_log_upload_list.h"
 #endif
@@ -32,6 +34,9 @@
           .AppendASCII(CrashUploadList::kReporterLogFilename);
   return new CrashUploadListAndroid(upload_log_path);
 #else
+  if (crash_reporter::IsCrashpadEnabled()) {
+    return new CrashUploadListCrashpad();
+  }
   base::FilePath crash_dir_path;
   base::PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dir_path);
   base::FilePath upload_log_path =
diff --git a/chrome/browser/download/download_request_limiter.cc b/chrome/browser/download/download_request_limiter.cc
index d0a0e209..d319114 100644
--- a/chrome/browser/download/download_request_limiter.cc
+++ b/chrome/browser/download/download_request_limiter.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "components/content_settings/core/browser/content_settings_details.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -104,8 +103,7 @@
       ui_status_(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT),
       origin_(url::Origin::Create(contents->GetVisibleURL())),
       download_count_(0),
-      download_seen_(false),
-      observer_(this) {
+      download_seen_(false) {
   observer_.Add(GetContentSettings(contents));
   NavigationEntry* last_entry =
       contents->GetController().GetLastCommittedEntry();
diff --git a/chrome/browser/download/download_request_limiter.h b/chrome/browser/download/download_request_limiter.h
index 90285fb..21e945f2 100644
--- a/chrome/browser/download/download_request_limiter.h
+++ b/chrome/browser/download/download_request_limiter.h
@@ -19,13 +19,12 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "url/origin.h"
 
-class HostContentSettingsMap;
-
 namespace content {
 class WebContents;
 }
@@ -215,7 +214,7 @@
     DownloadStatusMap download_status_map_;
 
     ScopedObserver<HostContentSettingsMap, content_settings::Observer>
-        observer_;
+        observer_{this};
 
     // Weak pointer factory for generating a weak pointer to pass to the
     // infobar.  User responses to the throttling prompt will be returned
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
index 60e9da0d..08f5ecc3 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
@@ -71,6 +71,19 @@
     list->Remove(index, nullptr);
 }
 
+autofill::AutofillManager* GetAutofillManager(
+    content::WebContents* web_contents) {
+  if (!web_contents) {
+    return nullptr;
+  }
+  autofill::ContentAutofillDriver* autofill_driver =
+      autofill::ContentAutofillDriverFactory::FromWebContents(web_contents)
+          ->DriverForFrame(web_contents->GetMainFrame());
+  if (!autofill_driver)
+    return nullptr;
+  return autofill_driver->autofill_manager();
+}
+
 }  // namespace
 
 namespace extensions {
@@ -471,17 +484,13 @@
   autofill::PersonalDataManager* personal_data =
       autofill::PersonalDataManagerFactory::GetForProfile(
           chrome_details_.GetProfile());
-  // Get the web contents to get autofill manager.
-  content::WebContents* web_contents = GetSenderWebContents();
-  if (!personal_data || !personal_data->IsDataLoaded() || !web_contents)
+  if (!personal_data || !personal_data->IsDataLoaded())
     return RespondNow(Error(kErrorDataUnavailable));
 
   // Get the AutofillManager from the web contents. AutofillManager has a
   // pointer to its AutofillClient which owns FormDataImporter.
   autofill::AutofillManager* autofill_manager =
-      autofill::ContentAutofillDriverFactory::FromWebContents(web_contents)
-          ->DriverForFrame(web_contents->GetMainFrame())
-          ->autofill_manager();
+      GetAutofillManager(GetSenderWebContents());
   if (!autofill_manager || !autofill_manager->client())
     return RespondNow(Error(kErrorDataUnavailable));
 
@@ -529,4 +538,36 @@
   return RespondNow(NoArguments());
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// AutofillPrivateSetCreditCardFIDOAuthEnabledStateFunction
+
+AutofillPrivateSetCreditCardFIDOAuthEnabledStateFunction::
+    AutofillPrivateSetCreditCardFIDOAuthEnabledStateFunction()
+    : chrome_details_(this) {}
+
+AutofillPrivateSetCreditCardFIDOAuthEnabledStateFunction::
+    ~AutofillPrivateSetCreditCardFIDOAuthEnabledStateFunction() {}
+
+ExtensionFunction::ResponseAction
+AutofillPrivateSetCreditCardFIDOAuthEnabledStateFunction::Run() {
+  // Getting CreditCardAccessManager from WebContents.
+  autofill::AutofillManager* autofill_manager =
+      GetAutofillManager(GetSenderWebContents());
+  if (!autofill_manager)
+    return RespondNow(Error(kErrorDataUnavailable));
+  autofill::CreditCardAccessManager* credit_card_access_manager =
+      autofill_manager->credit_card_access_manager();
+  if (!credit_card_access_manager)
+    return RespondNow(Error(kErrorDataUnavailable));
+
+  std::unique_ptr<
+      api::autofill_private::SetCreditCardFIDOAuthEnabledState::Params>
+      parameters = api::autofill_private::SetCreditCardFIDOAuthEnabledState::
+          Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(parameters.get());
+
+  credit_card_access_manager->OnSettingsPageFIDOAuthToggled(
+      parameters->enabled);
+  return RespondNow(NoArguments());
+}
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_api.h b/chrome/browser/extensions/api/autofill_private/autofill_private_api.h
index 5b42cd4..0594f14 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_private_api.h
+++ b/chrome/browser/extensions/api/autofill_private/autofill_private_api.h
@@ -208,6 +208,27 @@
   DISALLOW_COPY_AND_ASSIGN(AutofillPrivateLogServerCardLinkClickedFunction);
 };
 
+class AutofillPrivateSetCreditCardFIDOAuthEnabledStateFunction
+    : public ExtensionFunction {
+ public:
+  AutofillPrivateSetCreditCardFIDOAuthEnabledStateFunction();
+  DECLARE_EXTENSION_FUNCTION(
+      "autofillPrivate.setCreditCardFIDOAuthEnabledState",
+      AUTOFILLPRIVATE_SETCREDITCARDFIDOAUTHENABLEDSTATE)
+
+ protected:
+  ~AutofillPrivateSetCreditCardFIDOAuthEnabledStateFunction() override;
+
+  // ExtensionFunction overrides.
+  ResponseAction Run() override;
+
+ private:
+  ChromeExtensionFunctionDetails chrome_details_;
+
+  DISALLOW_COPY_AND_ASSIGN(
+      AutofillPrivateSetCreditCardFIDOAuthEnabledStateFunction);
+};
+
 }  // namespace extensions
 
 #endif  // CHROME_BROWSER_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_PRIVATE_API_H_
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 89f6c0f..a6d8c66 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -151,6 +151,8 @@
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[autofill::prefs::kAutofillCreditCardEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
+  (*s_whitelist)[autofill::prefs::kAutofillCreditCardFIDOAuthEnabled] =
+      settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[payments::kCanMakePaymentEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[bookmarks::prefs::kShowBookmarkBar] =
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index f6a205e0..b4e07f4 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -526,25 +526,31 @@
   }
 };
 
-// TODO(https://crbug.com/1003598): Flake on multiple platforms.
+// Note: this is flaky on multiple platforms (crbug.com/1003598). Temporarily
+// enabled to find flakiness cause.
 IN_PROC_BROWSER_TEST_P(ExtensionWebRequestApiAuthRequiredTest,
-                       DISABLED_WebRequestAuthRequired) {
+                       WebRequestAuthRequired) {
   CancelLoginDialog login_dialog_helper;
 
   ASSERT_TRUE(StartEmbeddedTestServer());
+
+  // Pass "debug" as a custom arg to debug test flakiness.
   ASSERT_TRUE(RunExtensionSubtestWithArgAndFlags(
-      "webrequest", "test_auth_required.html", nullptr, GetFlags()))
+      "webrequest", "test_auth_required.html", "debug", GetFlags()))
       << message_;
 }
 
-// TODO(https://crbug.com/1003598): Flake on multiple platforms.
+// Note: this is flaky on multiple platforms (crbug.com/1003598). Temporarily
+// enabled to find flakiness cause.
 IN_PROC_BROWSER_TEST_P(ExtensionWebRequestApiAuthRequiredTest,
-                       DISABLED_WebRequestAuthRequiredAsync) {
+                       WebRequestAuthRequiredAsync) {
   CancelLoginDialog login_dialog_helper;
 
   ASSERT_TRUE(StartEmbeddedTestServer());
+
+  // Pass "debug" as a custom arg to debug test flakiness.
   ASSERT_TRUE(RunExtensionSubtestWithArgAndFlags(
-      "webrequest", "test_auth_required_async.html", nullptr, GetFlags()))
+      "webrequest", "test_auth_required_async.html", "debug", GetFlags()))
       << message_;
 }
 
diff --git a/chrome/browser/file_select_helper.cc b/chrome/browser/file_select_helper.cc
index e005ae13..8b0be6cfe5 100644
--- a/chrome/browser/file_select_helper.cc
+++ b/chrome/browser/file_select_helper.cc
@@ -30,7 +30,6 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
@@ -150,8 +149,7 @@
       select_file_dialog_(),
       select_file_types_(),
       dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE),
-      dialog_mode_(FileChooserParams::Mode::kOpen),
-      observer_(this) {}
+      dialog_mode_(FileChooserParams::Mode::kOpen) {}
 
 FileSelectHelper::~FileSelectHelper() {
   // There may be pending file dialogs, we need to tell them that we've gone
@@ -197,7 +195,7 @@
        base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
       base::BindOnce(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
 #else
-  NotifyRenderFrameHostAndEnd(files);
+  ConvertToFileChooserFileInfoList(files);
 #endif  // defined(OS_MACOSX)
 }
 
@@ -226,7 +224,7 @@
        base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
       base::BindOnce(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
 #else
-  NotifyRenderFrameHostAndEnd(files);
+  ConvertToFileChooserFileInfoList(files);
 #endif  // defined(OS_MACOSX)
 }
 
@@ -257,7 +255,7 @@
     std::vector<ui::SelectedFileInfo> selected_files) {
   ShowFolderUploadConfirmationDialog(
       path,
-      base::BindOnce(&FileSelectHelper::NotifyRenderFrameHostAndEnd, this),
+      base::BindOnce(&FileSelectHelper::ConvertToFileChooserFileInfoList, this),
       std::move(selected_files), web_contents_);
 }
 
@@ -297,12 +295,10 @@
   }
 }
 
-void FileSelectHelper::NotifyRenderFrameHostAndEnd(
+void FileSelectHelper::ConvertToFileChooserFileInfoList(
     const std::vector<ui::SelectedFileInfo>& files) {
-  if (!render_frame_host_) {
-    RunFileChooserEnd();
+  if (AbortIfWebContentsDestroyed())
     return;
-  }
 
 #if defined(OS_CHROMEOS)
   if (!files.empty()) {
@@ -319,9 +315,8 @@
             ->GetFileSystemContext();
     file_manager::util::ConvertSelectedFileInfoListToFileChooserFileInfoList(
         file_system_context, site_instance->GetSiteURL(), files,
-        base::BindOnce(
-            &FileSelectHelper::NotifyRenderFrameHostAndEndAfterConversion,
-            this));
+        base::BindOnce(&FileSelectHelper::PerformSafeBrowsingDeepScanIfNeeded,
+                       this));
     return;
   }
 #endif  // defined(OS_CHROMEOS)
@@ -334,11 +329,62 @@
             base::FilePath(file.display_name).AsUTF16Unsafe())));
   }
 
-  NotifyRenderFrameHostAndEndAfterConversion(std::move(chooser_files));
+  PerformSafeBrowsingDeepScanIfNeeded(std::move(chooser_files));
 }
 
-void FileSelectHelper::NotifyRenderFrameHostAndEndAfterConversion(
+void FileSelectHelper::PerformSafeBrowsingDeepScanIfNeeded(
     std::vector<FileChooserFileInfoPtr> list) {
+  if (AbortIfWebContentsDestroyed())
+    return;
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  safe_browsing::DeepScanningDialogDelegate::Data data;
+  if (safe_browsing::DeepScanningDialogDelegate::IsEnabled(
+          profile_, render_frame_host_->GetLastCommittedURL(), &data)) {
+    data.paths.reserve(list.size());
+    for (const auto& file : list)
+      data.paths.push_back(file->get_native_file()->file_path);
+
+    safe_browsing::DeepScanningDialogDelegate::ShowForWebContents(
+        web_contents_, std::move(data),
+        base::BindOnce(&FileSelectHelper::DeepScanCompletionCallback, this,
+                       std::move(list)));
+  } else {
+    NotifyListenerAndEnd(std::move(list));
+  }
+#else
+  NotifyListenerAndEnd(std::move(list));
+#endif  // BUILDFLAG(FULL_SAFE_BROWSING)
+}
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+void FileSelectHelper::DeepScanCompletionCallback(
+    std::vector<blink::mojom::FileChooserFileInfoPtr> list,
+    const safe_browsing::DeepScanningDialogDelegate::Data& data,
+    const safe_browsing::DeepScanningDialogDelegate::Result& result) {
+  if (AbortIfWebContentsDestroyed())
+    return;
+
+  DCHECK_EQ(data.text.size(), result.text_results.size());
+  DCHECK_EQ(data.paths.size(), result.paths_results.size());
+  DCHECK_EQ(list.size(), result.paths_results.size());
+
+  // Remove any files that did not pass the deep scan.
+  size_t i = 0;
+  for (auto it = list.begin(); it != list.end(); ++i) {
+    if (!result.paths_results[i]) {
+      it = list.erase(it);
+    } else {
+      ++it;
+    }
+  }
+
+  NotifyListenerAndEnd(std::move(list));
+}
+#endif  // BUILDFLAG(FULL_SAFE_BROWSING)
+
+void FileSelectHelper::NotifyListenerAndEnd(
+    std::vector<blink::mojom::FileChooserFileInfoPtr> list) {
   listener_->FileSelected(std::move(list), base_dir_, dialog_mode_);
   listener_.reset();
 
@@ -365,11 +411,24 @@
 }
 
 bool FileSelectHelper::AbortIfWebContentsDestroyed() {
-  if (render_frame_host_ && web_contents_)
-    return false;
+  if (abort_on_missing_web_contents_in_tests_ &&
+      (render_frame_host_ == nullptr || web_contents_ == nullptr)) {
+    RunFileChooserEnd();
+    return true;
+  }
 
-  RunFileChooserEnd();
-  return true;
+  return false;
+}
+
+void FileSelectHelper::SetFileSelectListenerForTesting(
+    std::unique_ptr<content::FileSelectListener> listener) {
+  DCHECK(listener);
+  DCHECK(!listener_);
+  listener_ = std::move(listener);
+}
+
+void FileSelectHelper::DontAbortOnMissingWebContentsForTesting() {
+  abort_on_missing_web_contents_in_tests_ = false;
 }
 
 std::unique_ptr<ui::SelectFileDialog::FileTypeInfo>
diff --git a/chrome/browser/file_select_helper.h b/chrome/browser/file_select_helper.h
index e02b4da..dbd6f9c 100644
--- a/chrome/browser/file_select_helper.h
+++ b/chrome/browser/file_select_helper.h
@@ -16,12 +16,17 @@
 #include "build/build_config.h"
 #include "components/safe_browsing/buildflags.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_observer.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "net/base/directory_lister.h"
 #include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
 #include "ui/shell_dialogs/select_file_dialog.h"
 
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h"
+#endif
+
 class Profile;
 
 namespace content {
@@ -71,6 +76,17 @@
   FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest, ZipPackage);
   FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest, GetSanitizedFileName);
   FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest, LastSelectedDirectory);
+  FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest,
+                           DeepScanCompletionCallback_NoFiles);
+  FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest,
+                           DeepScanCompletionCallback_OneOKFile);
+  FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest,
+                           DeepScanCompletionCallback_TwoOKFiles);
+  FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest,
+                           DeepScanCompletionCallback_TwoBadFiles);
+  FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest,
+                           DeepScanCompletionCallback_OKBadFiles);
+
   explicit FileSelectHelper(Profile* profile);
   ~FileSelectHelper() override;
 
@@ -158,15 +174,55 @@
   static base::FilePath ZipPackage(const base::FilePath& path);
 #endif  // defined(OS_MACOSX)
 
-  // Utility method that passes |files| to the FileSelectListener, and ends the
-  // file chooser.
-  // TODO(tkent): Remove 'RenderFrameHost' from the name.
-  void NotifyRenderFrameHostAndEnd(
+  // This function is the start of a call chain that may or may not be async
+  // depending on the platform and features enabled.  The call to this method
+  // is made after the user has chosen the file(s) in the UI in order to
+  // process and filter the list before returning the final result to the
+  // caller.  The call chain is as follows:
+  //
+  // ConvertToFileChooserFileInfoList: converts a vector of SelectedFileInfo
+  // into a vector of FileChooserFileInfoPtr and then calls
+  // PerformSafeBrowsingDeepScanIfNeeded().  On chromeos, the conversion is
+  // performed asynchronously.
+  //
+  // PerformSafeBrowsingDeepScanIfNeeded: if the deep scanning feature is
+  // enabled and it is determined by enterprise policy that scans are required,
+  // starts the scans and sets DeepScanCompletionCallback() as the async
+  // callback.  If deep scanning is not enabled or is not supported on the
+  // platform, this function calls NotifyListenerAndEnd() directly.
+  //
+  // DeepScanCompletionCallback: processes the results of the deep scan.  Any
+  // files that did not pass the scan are removed from the list.  Ends by
+  // calling NotifyListenerAndEnd().
+  //
+  // NotifyListenerAndEnd: Informs the listener of the final list of files to
+  // use and performs any required cleanup.
+  //
+  // Because the state of the web contents may change at each asynchronous
+  // step, calls are make to AbortIfWebContentsDestroyed() to check if, for
+  // example, the tab has been closed or the contents navigated.  In these
+  // cases the file selection is aborted and the state cleaned up.
+  void ConvertToFileChooserFileInfoList(
       const std::vector<ui::SelectedFileInfo>& files);
 
-  // Sends the result to the FileSelectListener, and call |RunFileChooserEnd|.
-  // TODO(tkent): Remove 'RenderFrameHost' from the name.
-  void NotifyRenderFrameHostAndEndAfterConversion(
+  // Checks to see if scans are required for the specified files.
+  void PerformSafeBrowsingDeepScanIfNeeded(
+      std::vector<blink::mojom::FileChooserFileInfoPtr> list);
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  // Callback used to receive the results of a deep scan.
+  void DeepScanCompletionCallback(
+      std::vector<blink::mojom::FileChooserFileInfoPtr> list,
+      const safe_browsing::DeepScanningDialogDelegate::Data& data,
+      const safe_browsing::DeepScanningDialogDelegate::Result& result);
+#endif
+
+  // Finish the PerformSafeBrowsingDeepScanIfNeeded() handling after the
+  // deep scanning checks have been performed.  Deep scanning may change the
+  // list of files chosen by the user, so the list of files passed here may be
+  // a subset of of the files passed to
+  // PerformSafeBrowsingDeepScanIfNeeded().
+  void NotifyListenerAndEnd(
       std::vector<blink::mojom::FileChooserFileInfoPtr> list);
 
   // Schedules the deletion of the files in |temporary_files_| and clears the
@@ -180,6 +236,11 @@
   // if the file chooser operation shouldn't proceed.
   bool AbortIfWebContentsDestroyed();
 
+  void SetFileSelectListenerForTesting(
+      std::unique_ptr<content::FileSelectListener> listener);
+
+  void DontAbortOnMissingWebContentsForTesting();
+
   // Helper method to get allowed extensions for select file dialog from
   // the specified accept types as defined in the spec:
   //   http://whatwg.org/html/number-state.html#attr-input-accept
@@ -241,12 +302,15 @@
   std::unique_ptr<ActiveDirectoryEnumeration> directory_enumeration_;
 
   ScopedObserver<content::RenderWidgetHost, content::RenderWidgetHostObserver>
-      observer_;
+      observer_{this};
 
   // Temporary files only used on OSX. This class is responsible for deleting
   // these files when they are no longer needed.
   std::vector<base::FilePath> temporary_files_;
 
+  // Set to false in unit tests since there is no WebContents.
+  bool abort_on_missing_web_contents_in_tests_ = true;
+
   DISALLOW_COPY_AND_ASSIGN(FileSelectHelper);
 };
 
diff --git a/chrome/browser/file_select_helper_contacts_android.cc b/chrome/browser/file_select_helper_contacts_android.cc
index 63c0844..a42f7da0 100644
--- a/chrome/browser/file_select_helper_contacts_android.cc
+++ b/chrome/browser/file_select_helper_contacts_android.cc
@@ -54,7 +54,7 @@
   std::vector<ui::SelectedFileInfo> files;
 
   if (temp_file.empty()) {
-    NotifyRenderFrameHostAndEnd(files);
+    ConvertToFileChooserFileInfoList(files);
     return;
   }
 
@@ -72,5 +72,5 @@
     return;
   }
 
-  NotifyRenderFrameHostAndEnd(files);
+  ConvertToFileChooserFileInfoList(files);
 }
diff --git a/chrome/browser/file_select_helper_mac.mm b/chrome/browser/file_select_helper_mac.mm
index 18f463a..b4e18f5 100644
--- a/chrome/browser/file_select_helper_mac.mm
+++ b/chrome/browser/file_select_helper_mac.mm
@@ -142,5 +142,5 @@
     }
   }
 
-  NotifyRenderFrameHostAndEnd(files);
+  ConvertToFileChooserFileInfoList(files);
 }
diff --git a/chrome/browser/file_select_helper_unittest.cc b/chrome/browser/file_select_helper_unittest.cc
index 921f0144..8ea1aafd 100644
--- a/chrome/browser/file_select_helper_unittest.cc
+++ b/chrome/browser/file_select_helper_unittest.cc
@@ -17,11 +17,61 @@
 #include "chrome/browser/file_select_helper.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/file_select_listener.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/shell_dialogs/selected_file_info.h"
 
 using blink::mojom::FileChooserParams;
 
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+namespace {
+
+// A listener that remembers the list of files chosen.  The |files| argument
+// to the ctor must outlive the listener.
+class TestFileSelectListener : public content::FileSelectListener {
+ public:
+  explicit TestFileSelectListener(
+      std::vector<blink::mojom::FileChooserFileInfoPtr>* files)
+      : files_(files) {}
+
+ private:
+  // content::FileSelectListener overrides.
+  void FileSelected(std::vector<blink::mojom::FileChooserFileInfoPtr> files,
+                    const base::FilePath& base_dir,
+                    blink::mojom::FileChooserParams::Mode mode) override {
+    *files_ = std::move(files);
+  }
+  void FileSelectionCanceled() override {}
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr>* files_;
+};
+
+// Fill in the arguments to be passed to the DeepScanCompletionCallback()
+// method based on a list of paths and the desired result for each path.
+// This function simulates a path either passing the deep scan (status of true)
+// or failing (status of false).
+void PrepareDeepScanCompletionCallbackArgs(
+    std::vector<base::FilePath> paths,
+    std::vector<bool> status,
+    std::vector<blink::mojom::FileChooserFileInfoPtr>* orig_files,
+    safe_browsing::DeepScanningDialogDelegate::Data* data,
+    safe_browsing::DeepScanningDialogDelegate::Result* result) {
+  DCHECK_EQ(status.size(), paths.size());
+
+  for (auto& path : paths) {
+    orig_files->push_back(blink::mojom::FileChooserFileInfo::NewNativeFile(
+        blink::mojom::NativeFileInfo::New(path,
+                                          path.BaseName().AsUTF16Unsafe())));
+  }
+
+  data->paths = std::move(paths);
+  result->paths_results = std::move(status);
+}
+
+}  // namespace
+#endif  // BUILDFLAG(FULL_SAFE_BROWSING)
+
 class FileSelectHelperTest : public testing::Test {
  public:
   FileSelectHelperTest() {}
@@ -182,3 +232,130 @@
   file_select_helper->MultiFilesSelected(dirs, params);
   EXPECT_EQ(dir_path_1, profile.last_selected_directory());
 }
+
+// The following tests depend on the full safe browsing feature set.
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+
+TEST_F(FileSelectHelperTest, DeepScanCompletionCallback_NoFiles) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+  scoped_refptr<FileSelectHelper> file_select_helper =
+      new FileSelectHelper(&profile);
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> files;
+  auto listener = std::make_unique<TestFileSelectListener>(&files);
+  file_select_helper->SetFileSelectListenerForTesting(std::move(listener));
+  file_select_helper->DontAbortOnMissingWebContentsForTesting();
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> orig_files;
+  safe_browsing::DeepScanningDialogDelegate::Data data;
+  safe_browsing::DeepScanningDialogDelegate::Result result;
+  file_select_helper->AddRef();  // Normally called by RunFileChooser().
+  file_select_helper->DeepScanCompletionCallback(std::move(orig_files), data,
+                                                 result);
+
+  EXPECT_EQ(0u, files.size());
+}
+
+TEST_F(FileSelectHelperTest, DeepScanCompletionCallback_OneOKFile) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+  scoped_refptr<FileSelectHelper> file_select_helper =
+      new FileSelectHelper(&profile);
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> files;
+  auto listener = std::make_unique<TestFileSelectListener>(&files);
+  file_select_helper->SetFileSelectListenerForTesting(std::move(listener));
+  file_select_helper->DontAbortOnMissingWebContentsForTesting();
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> orig_files;
+  safe_browsing::DeepScanningDialogDelegate::Data data;
+  safe_browsing::DeepScanningDialogDelegate::Result result;
+  PrepareDeepScanCompletionCallbackArgs({data_dir_.AppendASCII("foo")}, {true},
+                                        &orig_files, &data, &result);
+
+  file_select_helper->AddRef();  // Normally called by RunFileChooser().
+  file_select_helper->DeepScanCompletionCallback(std::move(orig_files), data,
+                                                 result);
+
+  EXPECT_EQ(1u, files.size());
+}
+
+TEST_F(FileSelectHelperTest, DeepScanCompletionCallback_TwoOKFiles) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+  scoped_refptr<FileSelectHelper> file_select_helper =
+      new FileSelectHelper(&profile);
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> files;
+  auto listener = std::make_unique<TestFileSelectListener>(&files);
+  file_select_helper->SetFileSelectListenerForTesting(std::move(listener));
+  file_select_helper->DontAbortOnMissingWebContentsForTesting();
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> orig_files;
+  safe_browsing::DeepScanningDialogDelegate::Data data;
+  safe_browsing::DeepScanningDialogDelegate::Result result;
+  PrepareDeepScanCompletionCallbackArgs(
+      {data_dir_.AppendASCII("foo"), data_dir_.AppendASCII("bar")},
+      {true, true}, &orig_files, &data, &result);
+
+  file_select_helper->AddRef();  // Normally called by RunFileChooser().
+  file_select_helper->DeepScanCompletionCallback(std::move(orig_files), data,
+                                                 result);
+
+  EXPECT_EQ(2u, files.size());
+}
+
+TEST_F(FileSelectHelperTest, DeepScanCompletionCallback_TwoBadFiles) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+  scoped_refptr<FileSelectHelper> file_select_helper =
+      new FileSelectHelper(&profile);
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> files;
+  auto listener = std::make_unique<TestFileSelectListener>(&files);
+  file_select_helper->SetFileSelectListenerForTesting(std::move(listener));
+  file_select_helper->DontAbortOnMissingWebContentsForTesting();
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> orig_files;
+  safe_browsing::DeepScanningDialogDelegate::Data data;
+  safe_browsing::DeepScanningDialogDelegate::Result result;
+  PrepareDeepScanCompletionCallbackArgs(
+      {data_dir_.AppendASCII("foo"), data_dir_.AppendASCII("bar")},
+      {false, false}, &orig_files, &data, &result);
+
+  file_select_helper->AddRef();  // Normally called by RunFileChooser().
+  file_select_helper->DeepScanCompletionCallback(std::move(orig_files), data,
+                                                 result);
+
+  EXPECT_EQ(0u, files.size());
+}
+
+TEST_F(FileSelectHelperTest, DeepScanCompletionCallback_OKBadFiles) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+  scoped_refptr<FileSelectHelper> file_select_helper =
+      new FileSelectHelper(&profile);
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> files;
+  auto listener = std::make_unique<TestFileSelectListener>(&files);
+  file_select_helper->SetFileSelectListenerForTesting(std::move(listener));
+  file_select_helper->DontAbortOnMissingWebContentsForTesting();
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> orig_files;
+  safe_browsing::DeepScanningDialogDelegate::Data data;
+  safe_browsing::DeepScanningDialogDelegate::Result result;
+  PrepareDeepScanCompletionCallbackArgs(
+      {data_dir_.AppendASCII("foo"), data_dir_.AppendASCII("bar")},
+      {false, true}, &orig_files, &data, &result);
+
+  file_select_helper->AddRef();  // Normally called by RunFileChooser().
+  file_select_helper->DeepScanCompletionCallback(std::move(orig_files), data,
+                                                 result);
+
+  ASSERT_EQ(1u, files.size());
+  EXPECT_EQ(data_dir_.AppendASCII("bar"),
+            files[0]->get_native_file()->file_path);
+}
+
+#endif  // BUILDFLAG(FULL_SAFE_BROWSING)
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 35f8a2ff..be915391 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -807,6 +807,11 @@
     "expiry_milestone": 89
   },
   {
+    "name": "enable-accessibility-expose-display-none",
+    "owners": [ "adettenb@microsoft.com", "//third_party/blink/renderer/modules/accessibility/OWNERS", "//ui/accessibility/OWNERS" ],
+    "expiry_milestone": 82
+  },
+  {
     "name": "enable-accessibility-image-descriptions",
     "owners": [ "//ui/accessibility/OWNERS" ],
     "expiry_milestone": 77
@@ -1822,11 +1827,6 @@
     "expiry_milestone": 82
   },
   {
-    "name": "enable-url-loader-lite-page-server-previews",
-    "owners": [ "//components/data_reduction_proxy/OWNERS" ],
-    "expiry_milestone": 79
-  },
-  {
     "name": "enable-usbguard",
     "owners": [ "allenwebb", "mnissler", "jorgelo" ],
     "expiry_milestone": 80
@@ -2734,6 +2734,11 @@
     "expiry_milestone": 85
   },
   {
+    "name": "omnibox-zero-suggestions-on-serp",
+    "owners": [ "tommycli", "pnoland" ],
+    "expiry_milestone": 85
+  },
+  {
     "name": "on-the-fly-mhtml-hash-computation",
     "owners": [ "mtlieuu", "offline-dev@chromium.org" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 929544e4..ed244f6 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -22,6 +22,12 @@
 const char kAcceleratedVideoEncodeDescription[] =
     "Hardware-accelerated video encode where available.";
 
+const char kAccessibilityExposeDisplayNoneName[] =
+    "Expose 'display: none' nodes for accessibility";
+const char kAccessibilityExposeDisplayNoneDescription[] =
+    "Expose 'display: none' nodes that have an HTML ID to the browser process "
+    "accessibility tree.";
+
 const char kAccessibilityInternalsPageImprovementsName[] =
     "Accessibility internals page improvements";
 const char kAccessibilityInternalsPageImprovementsDescription[] =
@@ -697,13 +703,6 @@
     "to a Google-owned domain that serves a pre-rendered version of the "
     "original page. Also known as Lite Page Redirect Previews.";
 
-const char kEnableURLLoaderLitePageServerPreviewsName[] =
-    "Lite Page Server Previews using URL Loader";
-const char kEnableURLLoaderLitePageServerPreviewsDescription[] =
-    "Enable using a network service URL Loader for Lite Page Server Previews. "
-    "This requires enable-lite-page-server-previews to be enabled along with "
-    "network-service.";
-
 const char kEnablePreviewsCoinFlipName[] = "Enable Previews Coin Flip";
 const char kEnablePreviewsCoinFlipDescription[] =
     "Enable coin flip experimentation of Previews.";
@@ -1476,6 +1475,12 @@
 const char kOmniboxZeroSuggestionsOnNTPRealboxDescription[] =
     "Offer suggestions when the real search box on New Tab Page is focused.";
 
+const char kOmniboxZeroSuggestionsOnSERPName[] =
+    "Omnibox Zero Suggestions on SERP / On-Focus Query Refinement";
+const char kOmniboxZeroSuggestionsOnSERPDescription[] =
+    "Offer query refinement suggestions when the URL bar (omnibox) is focused "
+    "on the default search provider's search results page (SERP).";
+
 const char kOnlyNewPasswordFormParsingName[] =
     "Use only new password form parsing";
 const char kOnlyNewPasswordFormParsingDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index dc94503..6992e056 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -53,6 +53,9 @@
 extern const char kAcceleratedVideoEncodeName[];
 extern const char kAcceleratedVideoEncodeDescription[];
 
+extern const char kAccessibilityExposeDisplayNoneName[];
+extern const char kAccessibilityExposeDisplayNoneDescription[];
+
 extern const char kAccessibilityInternalsPageImprovementsName[];
 extern const char kAccessibilityInternalsPageImprovementsDescription[];
 
@@ -422,9 +425,6 @@
 extern const char kEnableLitePageServerPreviewsName[];
 extern const char kEnableLitePageServerPreviewsDescription[];
 
-extern const char kEnableURLLoaderLitePageServerPreviewsName[];
-extern const char kEnableURLLoaderLitePageServerPreviewsDescription[];
-
 extern const char kBuiltInModuleAllName[];
 extern const char kBuiltInModuleAllDescription[];
 
@@ -891,6 +891,9 @@
 extern const char kOmniboxZeroSuggestionsOnNTPRealboxName[];
 extern const char kOmniboxZeroSuggestionsOnNTPRealboxDescription[];
 
+extern const char kOmniboxZeroSuggestionsOnSERPName[];
+extern const char kOmniboxZeroSuggestionsOnSERPDescription[];
+
 extern const char kOnTheFlyMhtmlHashComputationName[];
 extern const char kOnTheFlyMhtmlHashComputationDescription[];
 
diff --git a/chrome/browser/google/google_update_settings_posix.cc b/chrome/browser/google/google_update_settings_posix.cc
index 7671541..76cd73c 100644
--- a/chrome/browser/google/google_update_settings_posix.cc
+++ b/chrome/browser/google/google_update_settings_posix.cc
@@ -13,10 +13,7 @@
 #include "base/task/lazy_task_runner.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_paths.h"
-
-#if defined(OS_MACOSX)
 #include "components/crash/content/app/crashpad.h"
-#endif
 
 namespace {
 
@@ -81,6 +78,10 @@
 bool GoogleUpdateSettings::SetCollectStatsConsent(bool consented) {
 #if defined(OS_MACOSX)
   crash_reporter::SetUploadConsent(consented);
+#elif defined(OS_LINUX)
+  if (crash_reporter::IsCrashpadEnabled()) {
+    crash_reporter::SetUploadConsent(consented);
+  }
 #endif
 
   base::FilePath consent_dir;
diff --git a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
index 5f0f9800..0fa5aa3 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
+++ b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "build/build_config.h"
 #include "chrome/browser/media/webrtc/fake_desktop_media_picker_factory.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "content/public/browser/browser_context.h"
@@ -22,6 +23,10 @@
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
+#if defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#endif
+
 class DisplayMediaAccessHandlerTest : public ChromeRenderViewHostTestHarness {
  public:
   DisplayMediaAccessHandlerTest() {}
@@ -96,6 +101,16 @@
   ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
                                          content::DesktopMediaID::kFakeId),
                  &result, &devices, false /* request_audio */);
+#if defined(OS_MACOSX)
+  // Starting from macOS 10.15, screen capture requires system permissions
+  // that are disabled by default.
+  if (base::mac::IsAtLeastOS10_15()) {
+    EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+              result);
+    return;
+  }
+#endif
+
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
   EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
@@ -110,6 +125,15 @@
                                         content::DesktopMediaID::kFakeId,
                                         true /* audio_share */);
   ProcessRequest(fake_media_id, &result, &devices, true /* request_audio */);
+#if defined(OS_MACOSX)
+  // Starting from macOS 10.15, screen capture requires system permissions
+  // that are disabled by default.
+  if (base::mac::IsAtLeastOS10_15()) {
+    EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+              result);
+    return;
+  }
+#endif
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(2u, devices.size());
   EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
@@ -232,6 +256,16 @@
   wait_loop[0].Run();
   EXPECT_TRUE(test_flags[0].picker_created);
   EXPECT_TRUE(test_flags[0].picker_deleted);
+#if defined(OS_MACOSX)
+  // Starting from macOS 10.15, screen capture requires system permissions
+  // that are disabled by default.
+  if (base::mac::IsAtLeastOS10_15()) {
+    EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
+              result);
+    access_handler_.reset();
+    return;
+  }
+#endif
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
   EXPECT_EQ(1u, devices.size());
   EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
diff --git a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
index 2f94ddd57..85d08ce 100644
--- a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
@@ -13,6 +13,10 @@
 #include "content/public/test/browser_test_utils.h"
 #include "media/base/media_switches.h"
 
+#if defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#endif
+
 namespace {
 
 static const char kMainHtmlPage[] = "/webrtc/webrtc_getdisplaymedia_test.html";
@@ -39,7 +43,14 @@
         tab->GetMainFrame(),
         base::StringPrintf("runGetDisplayMedia(%s);", constraints.c_str()),
         &result));
+#if defined(OS_MACOSX)
+    // Starting from macOS 10.15, screen capture requires system permissions
+    // that are disabled by default.
+    EXPECT_EQ(result, base::mac::IsAtMostOS10_14() ? "getdisplaymedia-success"
+                                                   : "getdisplaymedia-failure");
+#else
     EXPECT_EQ(result, "getdisplaymedia-success");
+#endif
   }
 };
 
diff --git a/chrome/browser/metrics/browser_window_histogram_helper.cc b/chrome/browser/metrics/browser_window_histogram_helper.cc
index 86e17c3..b37677f 100644
--- a/chrome/browser/metrics/browser_window_histogram_helper.cc
+++ b/chrome/browser/metrics/browser_window_histogram_helper.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/metrics/browser_window_histogram_helper.h"
 
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
-#include "ui/compositor/compositor.h"
 
 BrowserWindowHistogramHelper::~BrowserWindowHistogramHelper() {}
 
@@ -24,8 +23,7 @@
 }
 
 BrowserWindowHistogramHelper::BrowserWindowHistogramHelper(
-    ui::Compositor* compositor)
-    : scoped_observer_(this) {
+    ui::Compositor* compositor) {
   startup_metric_utils::RecordBrowserWindowFirstPaint(base::TimeTicks::Now());
 
 #if defined(OS_MACOSX)
diff --git a/chrome/browser/metrics/browser_window_histogram_helper.h b/chrome/browser/metrics/browser_window_histogram_helper.h
index c0bd5aac..648f3823 100644
--- a/chrome/browser/metrics/browser_window_histogram_helper.h
+++ b/chrome/browser/metrics/browser_window_histogram_helper.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 #include "base/time/time.h"
+#include "ui/compositor/compositor.h"
 #include "ui/compositor/compositor_observer.h"
 
 // Class that encapsulates logic of recording
@@ -37,7 +38,7 @@
   void OnCompositingEnded(ui::Compositor* compositor) override;
   void OnCompositingShuttingDown(ui::Compositor* compositor) override;
 
-  ScopedObserver<ui::Compositor, ui::CompositorObserver> scoped_observer_;
+  ScopedObserver<ui::Compositor, ui::CompositorObserver> scoped_observer_{this};
 
   DISALLOW_COPY_AND_ASSIGN(BrowserWindowHistogramHelper);
 };
diff --git a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
index 2854f1f3..b9a94ee1 100644
--- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
+++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
@@ -553,7 +553,15 @@
                                                             "Disabled"
 #endif
                                                             );
+  // Log once here at browser start rather than at each renderer launch.
+  ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial("ChromeWinMultiDll",
+#if defined(CHROME_MULTIPLE_DLL_BROWSER)
+                                                            "Enabled"
+#else
+                                                            "Disabled"
 #endif
+                                                            );
+#endif  // defined(OS_WIN)
 }
 
 void ChromeBrowserMainExtraPartsMetrics::PostBrowserStart() {
diff --git a/chrome/browser/metrics/subprocess_metrics_provider.cc b/chrome/browser/metrics/subprocess_metrics_provider.cc
index e966058..c41867f4 100644
--- a/chrome/browser/metrics/subprocess_metrics_provider.cc
+++ b/chrome/browser/metrics/subprocess_metrics_provider.cc
@@ -20,7 +20,6 @@
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
-#include "content/public/browser/render_process_host.h"
 
 namespace {
 
@@ -30,8 +29,7 @@
 
 }  // namespace
 
-SubprocessMetricsProvider::SubprocessMetricsProvider()
-    : scoped_observer_(this) {
+SubprocessMetricsProvider::SubprocessMetricsProvider() {
   base::StatisticsRecorder::RegisterHistogramProvider(
       weak_ptr_factory_.GetWeakPtr());
   content::BrowserChildProcessObserver::Add(this);
diff --git a/chrome/browser/metrics/subprocess_metrics_provider.h b/chrome/browser/metrics/subprocess_metrics_provider.h
index b995346..4401c69 100644
--- a/chrome/browser/metrics/subprocess_metrics_provider.h
+++ b/chrome/browser/metrics/subprocess_metrics_provider.h
@@ -8,8 +8,8 @@
 #include <memory>
 #include <set>
 
-#include "base/gtest_prod_util.h"
 #include "base/containers/id_map.h"
+#include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/statistics_recorder.h"
 #include "base/scoped_observer.h"
@@ -18,6 +18,7 @@
 #include "content/public/browser/browser_child_process_observer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_process_host_observer.h"
 
 namespace base {
@@ -105,8 +106,8 @@
   AllocatorByIdMap allocators_by_id_;
 
   // Track all observed render processes to un-observe them on exit.
-  ScopedObserver<content::RenderProcessHost, SubprocessMetricsProvider>
-      scoped_observer_;
+  ScopedObserver<content::RenderProcessHost, content::RenderProcessHostObserver>
+      scoped_observer_{this};
 
   base::WeakPtrFactory<SubprocessMetricsProvider> weak_ptr_factory_{this};
 
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.cc b/chrome/browser/navigation_predictor/navigation_predictor.cc
index d2e3f0a..c1fe980 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor.cc
@@ -14,6 +14,8 @@
 #include "base/optional.h"
 #include "base/rand_util.h"
 #include "base/system/sys_info.h"
+#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h"
+#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h"
 #include "chrome/browser/predictors/loading_predictor.h"
 #include "chrome/browser/predictors/loading_predictor_factory.h"
 #include "chrome/browser/prerender/prerender_handle.h"
@@ -189,10 +191,10 @@
           blink::features::kNavigationPredictor,
           "viewport_width_scale",
           0)),
-      sum_link_scales_(
-          ratio_area_scale_ + is_in_iframe_scale_ + is_same_host_scale_ +
-          contains_image_scale_ + is_url_incremented_scale_ +
-          area_rank_scale_ + ratio_distance_root_top_scale_),
+      sum_link_scales_(ratio_area_scale_ + is_in_iframe_scale_ +
+                       is_same_host_scale_ + contains_image_scale_ +
+                       is_url_incremented_scale_ + area_rank_scale_ +
+                       ratio_distance_root_top_scale_),
       sum_page_scales_(link_total_scale_ + iframe_link_total_scale_ +
                        increment_link_total_scale_ +
                        same_origin_link_total_scale_ + image_link_total_scale_ +
@@ -219,14 +221,19 @@
       normalize_navigation_scores_(base::GetFieldTrialParamByFeatureAsBool(
           blink::features::kNavigationPredictor,
           "normalize_scores",
-          true)) {
+          true)),
+      render_frame_host_(render_frame_host) {
   DCHECK(browser_context_);
   DETACH_FROM_SEQUENCE(sequence_checker_);
   DCHECK_LE(0, preconnect_origin_score_threshold_);
+  DCHECK(render_frame_host_);
 
   if (!IsMainFrame(render_frame_host))
     return;
 
+  if (browser_context_->IsOffTheRecord())
+    return;
+
   ukm_recorder_ = ukm::UkmRecorder::Get();
 
   content::WebContents* web_contents =
@@ -476,8 +483,9 @@
     anchor_element_builder.SetIsURLIncrementedByOne(
         navigation_score->is_url_incremented_by_one);
     anchor_element_builder.SetContainsImage(navigation_score->contains_image);
-    anchor_element_builder.SetSameOrigin(navigation_score->url.GetOrigin() ==
-                                         document_origin_.GetURL());
+    anchor_element_builder.SetSameOrigin(
+        url::Origin::Create(navigation_score->url) ==
+        url::Origin::Create(document_url_));
 
     // Convert the ratio area and ratio distance from [0,1] to [0,100].
     int percent_ratio_area =
@@ -837,8 +845,8 @@
   std::sort(navigation_scores.begin(), navigation_scores.end(),
             [](const auto& a, const auto& b) { return a->score > b->score; });
 
-  document_origin_ = url::Origin::Create(metrics[0]->source_url);
-  MaybeTakeActionOnLoad(document_origin_, navigation_scores);
+  document_url_ = metrics[0]->source_url;
+  MaybeTakeActionOnLoad(document_url_, navigation_scores);
 
   // Store navigation scores in |navigation_scores_map_| for fast look up upon
   // clicks.
@@ -938,8 +946,23 @@
   }
 }
 
+void NavigationPredictor::NotifyPredictionUpdated(
+    const std::vector<std::unique_ptr<NavigationScore>>&
+        sorted_navigation_scores) {
+  NavigationPredictorKeyedService* service =
+      NavigationPredictorKeyedServiceFactory::GetForProfile(
+          Profile::FromBrowserContext(browser_context_));
+  DCHECK(service);
+  std::vector<GURL> top_urls;
+  top_urls.reserve(sorted_navigation_scores.size());
+  for (const auto& nav_score : sorted_navigation_scores) {
+    top_urls.push_back(nav_score->url);
+  }
+  service->OnPredictionUpdated(render_frame_host_, document_url_, top_urls);
+}
+
 void NavigationPredictor::MaybeTakeActionOnLoad(
-    const url::Origin& document_origin,
+    const GURL& document_url,
     const std::vector<std::unique_ptr<NavigationScore>>&
         sorted_navigation_scores) {
   DCHECK(!browser_context_->IsOffTheRecord());
@@ -952,10 +975,12 @@
   DCHECK(!preconnect_origin_.has_value());
   DCHECK(!prefetch_url_.has_value());
 
+  NotifyPredictionUpdated(sorted_navigation_scores);
+
   // Try prefetch first.
-  prefetch_url_ = GetUrlToPrefetch(document_origin, sorted_navigation_scores);
+  prefetch_url_ = GetUrlToPrefetch(document_url, sorted_navigation_scores);
   if (prefetch_url_.has_value()) {
-    DCHECK_EQ(document_origin.host(), prefetch_url_->host());
+    DCHECK_EQ(document_url.host(), prefetch_url_->host());
     MaybePreconnectNow(Action::kPrefetch);
     MaybePrefetch();
     return;
@@ -963,9 +988,9 @@
 
   // Compute preconnect origin only if there is no valid prefetch URL.
   preconnect_origin_ =
-      GetOriginToPreconnect(document_origin, sorted_navigation_scores);
+      GetOriginToPreconnect(document_url, sorted_navigation_scores);
   if (preconnect_origin_.has_value()) {
-    DCHECK_EQ(document_origin.host(), preconnect_origin_->host());
+    DCHECK_EQ(document_url.host(), preconnect_origin_->host());
     MaybePreconnectNow(Action::kPreconnect);
     return;
   }
@@ -1006,7 +1031,7 @@
 }
 
 base::Optional<GURL> NavigationPredictor::GetUrlToPrefetch(
-    const url::Origin& document_origin,
+    const GURL& document_url,
     const std::vector<std::unique_ptr<NavigationScore>>&
         sorted_navigation_scores) const {
   // Currently, prefetch is disabled on low-end devices since prefetch may
@@ -1033,7 +1058,8 @@
   // Only the same origin URLs are eligible for prefetching. If the URL with
   // the highest score is from a different origin, then we skip prefetching
   // since same origin URLs are not likely to be clicked.
-  if (url::Origin::Create(url_to_prefetch) != document_origin) {
+  if (url::Origin::Create(url_to_prefetch) !=
+      url::Origin::Create(document_url)) {
     return base::nullopt;
   }
 
@@ -1046,7 +1072,7 @@
 }
 
 base::Optional<url::Origin> NavigationPredictor::GetOriginToPreconnect(
-    const url::Origin& document_origin,
+    const GURL& document_url,
     const std::vector<std::unique_ptr<NavigationScore>>&
         sorted_navigation_scores) const {
   // On search engine results page, next navigation is likely to be a different
@@ -1058,7 +1084,7 @@
   if (base::GetFieldTrialParamByFeatureAsBool(
           blink::features::kNavigationPredictor, "preconnect_skip_link_scores",
           true)) {
-    return document_origin;
+    return url::Origin::Create(document_url);
   }
 
   // Compute preconnect score for each origins: Multiple anchor elements on
@@ -1117,8 +1143,9 @@
 
   // Connect to the origin with highest score provided the origin is same
   // as the document origin.
-  if (sorted_preconnect_scores[0].origin != document_origin)
+  if (sorted_preconnect_scores[0].origin != url::Origin::Create(document_url)) {
     return base::nullopt;
+  }
 
   // If the prediction score of the highest scoring origin is less than the
   // threshold, then return.
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.h b/chrome/browser/navigation_predictor/navigation_predictor.h
index 9df4558..f3037c2 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.h
+++ b/chrome/browser/navigation_predictor/navigation_predictor.h
@@ -150,7 +150,7 @@
   // action to take, or decide not to do anything. Example actions including
   // preresolve, preload, prerendering, etc.
   void MaybeTakeActionOnLoad(
-      const url::Origin& document_origin,
+      const GURL& document_url,
       const std::vector<std::unique_ptr<NavigationScore>>&
           sorted_navigation_scores);
 
@@ -162,12 +162,12 @@
   virtual void Prefetch(prerender::PrerenderManager* prerender_manager);
 
   base::Optional<GURL> GetUrlToPrefetch(
-      const url::Origin& document_origin,
+      const GURL& document_url,
       const std::vector<std::unique_ptr<NavigationScore>>&
           sorted_navigation_scores) const;
 
   base::Optional<url::Origin> GetOriginToPreconnect(
-      const url::Origin& document_origin,
+      const GURL& document_url,
       const std::vector<std::unique_ptr<NavigationScore>>&
           sorted_navigation_scores) const;
 
@@ -208,6 +208,11 @@
   // |ratio_area|.
   int GetLinearBucketForRatioArea(int value) const;
 
+  // Notifies the keyed service of the updated predicted navigation.
+  void NotifyPredictionUpdated(
+      const std::vector<std::unique_ptr<NavigationScore>>&
+          sorted_navigation_scores);
+
   // Used to get keyed services.
   content::BrowserContext* const browser_context_;
 
@@ -309,8 +314,11 @@
   // UKM recorder
   ukm::UkmRecorder* ukm_recorder_ = nullptr;
 
-  // The origin of the current page.
-  url::Origin document_origin_;
+  // The URL of the current page.
+  GURL document_url_;
+
+  // Render frame host of the current page.
+  const content::RenderFrameHost* render_frame_host_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc b/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
index b17881b..2eb423c 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
@@ -9,6 +9,8 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/metrics/subprocess_metrics_provider.h"
 #include "chrome/browser/navigation_predictor/navigation_predictor.h"
+#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h"
+#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -73,6 +75,17 @@
   }
 }
 
+// Verifies that all URLs specified in |expected_urls| are present in
+// |urls_from_observed_prediction|. Ordering of URLs is NOT verified.
+void VerifyURLsPresent(const std::vector<GURL>& urls_from_observed_prediction,
+                       const std::vector<std::string>& expected_urls) {
+  for (const auto& expected_url : expected_urls) {
+    EXPECT_NE(urls_from_observed_prediction.end(),
+              std::find(urls_from_observed_prediction.begin(),
+                        urls_from_observed_prediction.end(), expected_url));
+  }
+}
+
 }  // namespace
 
 class NavigationPredictorBrowserTest
@@ -135,6 +148,57 @@
   DISALLOW_COPY_AND_ASSIGN(NavigationPredictorBrowserTest);
 };
 
+class TestObserver : public NavigationPredictorKeyedService::Observer {
+ public:
+  TestObserver() {}
+  ~TestObserver() override {}
+
+  base::Optional<NavigationPredictorKeyedService::Prediction> last_prediction()
+      const {
+    return last_prediction_;
+  }
+
+  size_t count_predictions() const { return count_predictions_; }
+
+  // Waits until the count if received notifications is at least
+  // |expected_notifications_count|.
+  void WaitUntilNotificationsCountReached(size_t expected_notifications_count) {
+    // Ensure that |wait_loop_| is null implying there is no ongoing wait.
+    ASSERT_FALSE(!!wait_loop_);
+
+    if (count_predictions_ >= expected_notifications_count)
+      return;
+    expected_notifications_count_ = expected_notifications_count;
+    wait_loop_ = std::make_unique<base::RunLoop>();
+    wait_loop_->Run();
+    wait_loop_.reset();
+  }
+
+ private:
+  void OnPredictionUpdated(
+      const base::Optional<NavigationPredictorKeyedService::Prediction>&
+          prediction) override {
+    ++count_predictions_;
+    last_prediction_ = prediction;
+    if (wait_loop_ && count_predictions_ >= expected_notifications_count_) {
+      wait_loop_->Quit();
+    }
+  }
+
+  // Count of prediction notifications received so far.
+  size_t count_predictions_ = 0u;
+
+  // last prediction received.
+  base::Optional<NavigationPredictorKeyedService::Prediction> last_prediction_;
+
+  // If |wait_loop_| is non-null, then it quits as soon as count of received
+  // notifications are at least |expected_notifications_count_|.
+  std::unique_ptr<base::RunLoop> wait_loop_;
+  base::Optional<size_t> expected_notifications_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestObserver);
+};
+
 IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest, Pipeline) {
   base::HistogramTester histogram_tester;
 
@@ -855,3 +919,158 @@
   histogram_tester.ExpectUniqueSample(
       "AnchorElementMetrics.Visible.NumberOfAnchorElementsAfterMerge", 3, 1);
 }
+
+// Test that navigation score of anchor elements can be calculated on page load
+// and the predicted URLs for the next navigation are dispatched to the single
+// observer.
+IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
+                       NavigationScoreSingleObserver) {
+  TestObserver observer;
+
+  NavigationPredictorKeyedService* service =
+      NavigationPredictorKeyedServiceFactory::GetForProfile(
+          browser()->profile());
+  EXPECT_NE(nullptr, service);
+  service->AddObserver(&observer);
+
+  base::HistogramTester histogram_tester;
+
+  const GURL& url = GetTestURL("/simple_page_with_anchors.html");
+  ui_test_utils::NavigateToURL(browser(), url);
+  WaitForLayout();
+  observer.WaitUntilNotificationsCountReached(1);
+
+  histogram_tester.ExpectTotalCount(
+      "AnchorElementMetrics.Visible.HighestNavigationScore", 1);
+  service->RemoveObserver(&observer);
+
+  EXPECT_EQ(1u, observer.count_predictions());
+  EXPECT_EQ(url, observer.last_prediction()->source_document_url());
+  EXPECT_EQ(2u, observer.last_prediction()->sorted_predicted_urls().size());
+  EXPECT_NE(
+      observer.last_prediction()->sorted_predicted_urls().end(),
+      std::find(observer.last_prediction()->sorted_predicted_urls().begin(),
+                observer.last_prediction()->sorted_predicted_urls().end(),
+                "https://google.com/"));
+  EXPECT_NE(
+      observer.last_prediction()->sorted_predicted_urls().end(),
+      std::find(observer.last_prediction()->sorted_predicted_urls().begin(),
+                observer.last_prediction()->sorted_predicted_urls().end(),
+                "https://example.com/"));
+
+  // Doing another navigation after removing the observer should not cause a
+  // crash.
+  ui_test_utils::NavigateToURL(browser(), url);
+  WaitForLayout();
+  EXPECT_EQ(1u, observer.count_predictions());
+}
+
+// Same as NavigationScoreSingleObserver test but with more than one observer.
+IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
+                       NavigationScore_TwoObservers) {
+  TestObserver observer_1;
+  TestObserver observer_2;
+
+  NavigationPredictorKeyedService* service =
+      NavigationPredictorKeyedServiceFactory::GetForProfile(
+          browser()->profile());
+  service->AddObserver(&observer_1);
+  service->AddObserver(&observer_2);
+
+  base::HistogramTester histogram_tester;
+
+  const GURL& url = GetTestURL("/simple_page_with_anchors.html");
+  ui_test_utils::NavigateToURL(browser(), url);
+  WaitForLayout();
+  observer_1.WaitUntilNotificationsCountReached(1);
+  observer_2.WaitUntilNotificationsCountReached(1);
+
+  histogram_tester.ExpectTotalCount(
+      "AnchorElementMetrics.Visible.HighestNavigationScore", 1);
+  service->RemoveObserver(&observer_1);
+
+  EXPECT_EQ(1u, observer_1.count_predictions());
+  EXPECT_EQ(url, observer_1.last_prediction()->source_document_url());
+  EXPECT_EQ(2u, observer_1.last_prediction()->sorted_predicted_urls().size());
+  VerifyURLsPresent(observer_1.last_prediction()->sorted_predicted_urls(),
+                    {"https://google.com/", "https://example.com/"});
+  EXPECT_EQ(1u, observer_2.count_predictions());
+  EXPECT_EQ(url, observer_2.last_prediction()->source_document_url());
+
+  // Only |observer_2| should get the notification since |observer_1| has
+  // been removed from receiving the notifications.
+  ui_test_utils::NavigateToURL(browser(), url);
+  WaitForLayout();
+  observer_2.WaitUntilNotificationsCountReached(2);
+  EXPECT_EQ(1u, observer_1.count_predictions());
+  EXPECT_EQ(2u, observer_2.count_predictions());
+  VerifyURLsPresent(observer_2.last_prediction()->sorted_predicted_urls(),
+                    {"https://google.com/", "https://example.com/"});
+}
+
+// Test that the navigation predictor keyed service is null for incognito
+// profiles.
+IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest, Incognito) {
+  Browser* incognito = CreateIncognitoBrowser();
+  NavigationPredictorKeyedService* incognito_service =
+      NavigationPredictorKeyedServiceFactory::GetForProfile(
+          incognito->profile());
+  EXPECT_EQ(nullptr, incognito_service);
+}
+
+// Verify that the observers are notified of predictions on search results page.
+IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
+                       DISABLE_ON_CHROMEOS(ObserverNotifiedOnSearchPage)) {
+  TestObserver observer;
+
+  NavigationPredictorKeyedService* service =
+      NavigationPredictorKeyedServiceFactory::GetForProfile(
+          browser()->profile());
+  service->AddObserver(&observer);
+
+  static const char kShortName[] = "test";
+  static const char kSearchURL[] =
+      "/anchors_different_area.html?q={searchTerms}";
+
+  // Force Preconnect on
+  std::map<std::string, std::string> parameters;
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(
+      blink::features::kNavigationPredictor, parameters);
+
+  // Set up default search engine.
+  TemplateURLService* model =
+      TemplateURLServiceFactory::GetForProfile(browser()->profile());
+  ASSERT_TRUE(model);
+  search_test_utils::WaitForTemplateURLServiceToLoad(model);
+  ASSERT_TRUE(model->loaded());
+
+  TemplateURLData data;
+  data.SetShortName(base::ASCIIToUTF16(kShortName));
+  data.SetKeyword(data.short_name());
+  data.SetURL(GetTestURL(kSearchURL).spec());
+
+  TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
+  ASSERT_TRUE(template_url);
+  model->SetUserSelectedDefaultSearchProvider(template_url);
+
+  base::HistogramTester histogram_tester;
+
+  EXPECT_EQ(0u, observer.count_predictions());
+
+  // This page only has non-same host links.
+  const GURL& url = GetTestURL("/anchors_different_area.html?q=cats");
+  ui_test_utils::NavigateToURL(browser(), url);
+  WaitForLayout();
+  observer.WaitUntilNotificationsCountReached(1u);
+
+  histogram_tester.ExpectUniqueSample("NavigationPredictor.OnDSE.ActionTaken",
+                                      NavigationPredictor::Action::kNone, 1);
+  EXPECT_EQ(1u, observer.count_predictions());
+  EXPECT_EQ(url, observer.last_prediction()->source_document_url());
+  EXPECT_EQ(5u, observer.last_prediction()->sorted_predicted_urls().size());
+  VerifyURLsPresent(
+      observer.last_prediction()->sorted_predicted_urls(),
+      {"https://example.com/2", "https://google.com/", "https://example.com/1",
+       "https://example.com/", "https://dummy.com/"});
+}
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.cc b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.cc
new file mode 100644
index 0000000..61ef5570
--- /dev/null
+++ b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.cc
@@ -0,0 +1,73 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h"
+
+#include "base/compiler_specific.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+
+NavigationPredictorKeyedService::Prediction::Prediction(
+    const content::RenderFrameHost* render_frame_host,
+    const GURL& source_document_url,
+    const std::vector<GURL>& sorted_predicted_urls)
+    : render_frame_host_(render_frame_host),
+      source_document_url_(source_document_url),
+      sorted_predicted_urls_(sorted_predicted_urls) {
+  // |render_frame_host_| will be used by consumers in future.
+  ALLOW_UNUSED_LOCAL(render_frame_host_);
+}
+
+NavigationPredictorKeyedService::Prediction::Prediction(
+    const NavigationPredictorKeyedService::Prediction& other) = default;
+
+NavigationPredictorKeyedService::Prediction&
+NavigationPredictorKeyedService::Prediction::operator=(
+    const NavigationPredictorKeyedService::Prediction& other) = default;
+
+NavigationPredictorKeyedService::Prediction::~Prediction() = default;
+
+GURL NavigationPredictorKeyedService::Prediction::source_document_url() const {
+  return source_document_url_;
+}
+
+std::vector<GURL>
+NavigationPredictorKeyedService::Prediction::sorted_predicted_urls() const {
+  return sorted_predicted_urls_;
+}
+
+NavigationPredictorKeyedService::NavigationPredictorKeyedService(
+    content::BrowserContext* browser_context) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(!browser_context->IsOffTheRecord());
+}
+
+NavigationPredictorKeyedService::~NavigationPredictorKeyedService() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
+
+void NavigationPredictorKeyedService::OnPredictionUpdated(
+    const content::RenderFrameHost* render_frame_host,
+    const GURL& document_url,
+    const std::vector<GURL>& sorted_predicted_urls) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  last_prediction_ =
+      Prediction(render_frame_host, document_url, sorted_predicted_urls);
+  for (auto& observer : observer_list_) {
+    observer.OnPredictionUpdated(last_prediction_);
+  }
+}
+
+void NavigationPredictorKeyedService::AddObserver(Observer* observer) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  observer_list_.AddObserver(observer);
+  if (last_prediction_.has_value()) {
+    observer->OnPredictionUpdated(last_prediction_);
+  }
+}
+
+void NavigationPredictorKeyedService::RemoveObserver(Observer* observer) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  observer_list_.RemoveObserver(observer);
+}
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h
new file mode 100644
index 0000000..538b381a
--- /dev/null
+++ b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h
@@ -0,0 +1,95 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NAVIGATION_PREDICTOR_NAVIGATION_PREDICTOR_KEYED_SERVICE_H_
+#define CHROME_BROWSER_NAVIGATION_PREDICTOR_NAVIGATION_PREDICTOR_KEYED_SERVICE_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/optional.h"
+#include "base/single_thread_task_runner.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "url/gurl.h"
+
+namespace content {
+class BrowserContext;
+class RenderFrameHost;
+}  // namespace content
+
+// Keyed service that can be used to receive notifications about the URLs for
+// the next predicted navigation.
+class NavigationPredictorKeyedService : public KeyedService {
+ public:
+  // Stores the next set of URLs that the user is expected to navigate to.
+  class Prediction {
+   public:
+    Prediction(const content::RenderFrameHost* render_frame_host,
+               const GURL& source_document_url,
+               const std::vector<GURL>& sorted_predicted_urls);
+    Prediction(const Prediction& other);
+    Prediction& operator=(const Prediction& other);
+    ~Prediction();
+    GURL source_document_url() const;
+    std::vector<GURL> sorted_predicted_urls() const;
+
+   private:
+    // |render_frame_host_| from where the navigation may happen. May be
+    // nullptr.
+    const content::RenderFrameHost* render_frame_host_;
+
+    // Current URL of the document from where the navigtion may happen.
+    GURL source_document_url_;
+
+    // Ordered set of URLs that the user is expected to navigate to next. The
+    // URLs are in the decreasing order of click probability.
+    std::vector<GURL> sorted_predicted_urls_;
+  };
+
+  // Observer class can be implemented to receive notifications about the
+  // predicted URLs for the next navigation. OnPredictionUpdated() is called
+  // every time a new prediction is available. Prediction includes the source
+  // document as well as the ordered list of URLs that the user may navigate to
+  // next. OnPredictionUpdated() may be called multiple times for the same
+  // source document URL.
+  class Observer {
+   public:
+    virtual void OnPredictionUpdated(
+        const base::Optional<Prediction>& prediction) = 0;
+
+   protected:
+    Observer() {}
+    virtual ~Observer() {}
+  };
+
+  explicit NavigationPredictorKeyedService(
+      content::BrowserContext* browser_context);
+  ~NavigationPredictorKeyedService() override;
+
+  // |document_url| may be invalid. Called by navigation predictor.
+  void OnPredictionUpdated(const content::RenderFrameHost* render_frame_host,
+                           const GURL& document_url,
+                           const std::vector<GURL>& sorted_predicted_urls);
+
+  // Adds |observer| as the observer for next predicted navigation. When
+  // |observer| is added via AddObserver, it's immediately notified of the last
+  // known prediction.
+  void AddObserver(Observer* observer);
+
+  // Removes |observer| as the observer for next predicted navigation.
+  void RemoveObserver(Observer* observer);
+
+ private:
+  // List of observers are currently registered to receive notifications for the
+  // next predicted navigations.
+  base::ObserverList<Observer>::Unchecked observer_list_;
+
+  // Last known prediction.
+  base::Optional<Prediction> last_prediction_;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigationPredictorKeyedService);
+};
+
+#endif  // CHROME_BROWSER_NAVIGATION_PREDICTOR_NAVIGATION_PREDICTOR_KEYED_SERVICE_H_
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.cc b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.cc
new file mode 100644
index 0000000..35281fd2
--- /dev/null
+++ b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.cc
@@ -0,0 +1,43 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h"
+
+#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/browser_context.h"
+
+namespace {
+
+base::LazyInstance<NavigationPredictorKeyedServiceFactory>::DestructorAtExit
+    g_navigation_predictor_keyed_service_factory = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+// static
+NavigationPredictorKeyedService*
+NavigationPredictorKeyedServiceFactory::GetForProfile(Profile* profile) {
+  return static_cast<NavigationPredictorKeyedService*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+NavigationPredictorKeyedServiceFactory*
+NavigationPredictorKeyedServiceFactory::GetInstance() {
+  return g_navigation_predictor_keyed_service_factory.Pointer();
+}
+
+NavigationPredictorKeyedServiceFactory::NavigationPredictorKeyedServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "NavigationPredictorKeyedService",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+NavigationPredictorKeyedServiceFactory::
+    ~NavigationPredictorKeyedServiceFactory() {}
+
+KeyedService* NavigationPredictorKeyedServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new NavigationPredictorKeyedService(context);
+}
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h
new file mode 100644
index 0000000..89a4c9a
--- /dev/null
+++ b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NAVIGATION_PREDICTOR_NAVIGATION_PREDICTOR_KEYED_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_NAVIGATION_PREDICTOR_NAVIGATION_PREDICTOR_KEYED_SERVICE_FACTORY_H_
+
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+}
+
+class NavigationPredictorKeyedService;
+class Profile;
+
+// LazyInstance that owns all NavigationPredictorKeyedServices and associates
+// them with Profiles.
+class NavigationPredictorKeyedServiceFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  // Gets the NavigationPredictorKeyedService instance for |profile|.
+  static NavigationPredictorKeyedService* GetForProfile(Profile* profile);
+
+  // Gets the LazyInstance that owns all NavigationPredictorKeyedServices.
+  static NavigationPredictorKeyedServiceFactory* GetInstance();
+
+ private:
+  friend struct base::LazyInstanceTraitsBase<
+      NavigationPredictorKeyedServiceFactory>;
+
+  NavigationPredictorKeyedServiceFactory();
+  ~NavigationPredictorKeyedServiceFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigationPredictorKeyedServiceFactory);
+};
+
+#endif  // CHROME_BROWSER_NAVIGATION_PREDICTOR_NAVIGATION_PREDICTOR_KEYED_SERVICE_FACTORY_H_
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index a0dd2bd1..8861d30 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -31,7 +31,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "components/certificate_transparency/pref_names.h"
-#include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/language/core/browser/pref_names.h"
@@ -129,9 +128,7 @@
 }  // namespace
 
 ProfileNetworkContextService::ProfileNetworkContextService(Profile* profile)
-    : profile_(profile),
-      proxy_config_monitor_(profile),
-      cookie_settings_observer_(this) {
+    : profile_(profile), proxy_config_monitor_(profile) {
   PrefService* profile_prefs = profile->GetPrefs();
   quic_allowed_.Init(
       prefs::kQuicAllowed, profile_prefs,
diff --git a/chrome/browser/net/profile_network_context_service.h b/chrome/browser/net/profile_network_context_service.h
index ad43b67..6ebf38e 100644
--- a/chrome/browser/net/profile_network_context_service.h
+++ b/chrome/browser/net/profile_network_context_service.h
@@ -132,8 +132,9 @@
   PrefChangeRegistrar pref_change_registrar_;
 
   scoped_refptr<content_settings::CookieSettings> cookie_settings_;
-  ScopedObserver<content_settings::CookieSettings, ProfileNetworkContextService>
-      cookie_settings_observer_;
+  ScopedObserver<content_settings::CookieSettings,
+                 content_settings::CookieSettings::Observer>
+      cookie_settings_observer_{this};
 
   // Used to post schedule CT policy updates
   base::OneShotTimer ct_policy_update_timer_;
diff --git a/chrome/browser/notifications/notification_system_observer.cc b/chrome/browser/notifications/notification_system_observer.cc
index 48cf5ca9..a2e7ef8f 100644
--- a/chrome/browser/notifications/notification_system_observer.cc
+++ b/chrome/browser/notifications/notification_system_observer.cc
@@ -11,12 +11,11 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "content/public/browser/notification_service.h"
-#include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
 
 NotificationSystemObserver::NotificationSystemObserver(
     NotificationUIManager* ui_manager)
-    : ui_manager_(ui_manager), extension_registry_observer_(this) {
+    : ui_manager_(ui_manager) {
   DCHECK(ui_manager_);
   registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
                  content::NotificationService::AllSources());
diff --git a/chrome/browser/notifications/notification_system_observer.h b/chrome/browser/notifications/notification_system_observer.h
index f206c37..fc6319b 100644
--- a/chrome/browser/notifications/notification_system_observer.h
+++ b/chrome/browser/notifications/notification_system_observer.h
@@ -9,16 +9,13 @@
 #include "base/scoped_observer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
 
 namespace content {
 class BrowserContext;
 }
 
-namespace extensions {
-class ExtensionRegistry;
-}
-
 class NotificationUIManager;
 
 // The content::NotificationObserver observes system status change and sends
@@ -49,7 +46,7 @@
 
   ScopedObserver<extensions::ExtensionRegistry,
                  extensions::ExtensionRegistryObserver>
-      extension_registry_observer_;
+      extension_registry_observer_{this};
 
   DISALLOW_COPY_AND_ASSIGN(NotificationSystemObserver);
 };
diff --git a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
index d1d8c0e5..9d33ac2 100644
--- a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
+++ b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
@@ -133,6 +133,9 @@
           WebFeature::kBaseWithCrossOriginHref,
           WebFeature::kWakeLockAcquireScreenLock,
           WebFeature::kWakeLockAcquireSystemLock,
+          WebFeature::kThirdPartyServiceWorker,
+          WebFeature::kThirdPartySharedWorker,
+          WebFeature::kThirdPartyBroadcastChannel,
       }));
   return *opt_in_features;
 }
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 78686cf6..b0a2dbd 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -201,7 +201,6 @@
       password_feature_manager_(
           ProfileSyncServiceFactory::GetForProfile(profile_)),
       httpauth_manager_(this),
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
       password_reuse_detection_manager_(this),
 #endif
@@ -640,7 +639,6 @@
   if (!navigation_handle->IsSameDocument())
     content_credential_manager_.DisconnectBinding();
 
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   password_reuse_detection_manager_.DidNavigateMainFrame(GetMainFrameURL());
 #endif  // defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.h b/chrome/browser/password_manager/chrome_password_manager_client.h
index d260d99..ce4cc700 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.h
+++ b/chrome/browser/password_manager/chrome_password_manager_client.h
@@ -250,13 +250,14 @@
       content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
+// TODO(crbug.com/1006430): Paste event is not captured on Android.
 #if !defined(OS_ANDROID)
   void OnPaste() override;
 #endif
 
   void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
 
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
+// Key events are triggered by Ime text committed event on Android.
 #if !defined(OS_ANDROID)
   // content::RenderWidgetHost::InputEventObserver overrides.
   void OnInputEvent(const blink::WebInputEvent&) override;
@@ -300,7 +301,6 @@
   const password_manager::PasswordFeatureManagerImpl password_feature_manager_;
   password_manager::HttpAuthManagerImpl httpauth_manager_;
 
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   password_manager::PasswordReuseDetectionManager
       password_reuse_detection_manager_;
diff --git a/chrome/browser/policy/e2e_test/.vpython b/chrome/browser/policy/e2e_test/.vpython
index 3b319d1..bc935fa 100644
--- a/chrome/browser/policy/e2e_test/.vpython
+++ b/chrome/browser/policy/e2e_test/.vpython
@@ -11,7 +11,8 @@
 
 wheel: <
   name: "infra/celab/celab/windows-amd64"
-  version: "MxUQe6cwcaLhKIoFnrYyH5Nn6mq0qMD7lxaPld0bqgoC"
+  # Source: https://ci.chromium.org/p/celab/builders/ci/Windows/b8902045658105865392
+  version: "mHamQ8UpCgqQTQZyuKIFVlvYy3KSkDCsUSGXQtL5E-YC"
 >
 
 # googleapiclient
diff --git a/chrome/browser/policy/e2e_test/infra/chrome_ent_test_case.py b/chrome/browser/policy/e2e_test/infra/chrome_ent_test_case.py
index 27554cb..fec0453a 100644
--- a/chrome/browser/policy/e2e_test/infra/chrome_ent_test_case.py
+++ b/chrome/browser/policy/e2e_test/infra/chrome_ent_test_case.py
@@ -32,6 +32,7 @@
     - mini_installer.exe, and
     - *.msi
     """
+    self.RunCommand(instance_name, r'md -Force c:\temp')
     file_name = self.UploadFile(instance_name, FLAGS.chrome_installer,
                                 r'c:\temp')
 
@@ -96,32 +97,24 @@
              "-Key %s -ValueName %s") % (key, policy_name)
       self.clients[instance_name].RunPowershell(cmd)
 
-  def _installChocolatey(self, instance_name):
-    cmd = "Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))"
-    self.clients[instance_name].RunPowershell(cmd)
-
-  def InstallPackage(self, instance_name, package_name, package_version):
-    cmd = r'c:\ProgramData\chocolatey\bin\choco install %s -y --version %s' % (
-        package_name, package_version)
-    self.RunCommand(instance_name, cmd)
-
   def InstallWebDriver(self, instance_name):
-    self._installChocolatey(instance_name)
-    self.InstallPackage(instance_name, 'python2', '2.7.15')
-    self.RunCommand(
-        instance_name,
-        r'c:\Python27\python.exe -m pip install selenium absl-py pywin32')
+    self.InstallPipPackagesLatest(instance_name,
+                                  ['selenium', 'absl-py', 'pywin32'])
+
+    temp_dir = 'C:\\temp\\'
     if FLAGS.chromedriver is None:
-      # chromedriver flag is not specified. In this case, install the chocolatey
-      # package
-      self.InstallPackage(instance_name, 'chromedriver', '74.0.3729.60')
+      # chromedriver flag is not specified. Install the chocolatey package.
+      self.InstallChocolateyPackage(instance_name, 'chromedriver',
+                                    '74.0.3729.60')
+      self.RunCommand(
+          instance_name, "copy %s %s" %
+          (r"C:\ProgramData\chocolatey\lib\chromedriver\tools\chromedriver.exe",
+           temp_dir))
     else:
-      self.UploadFile(instance_name, FLAGS.chromedriver,
-                      'C:/ProgramData/chocolatey/lib/chromedriver/tools/')
+      self.UploadFile(instance_name, FLAGS.chromedriver, temp_dir)
 
     dir = os.path.dirname(os.path.abspath(__file__))
-    self.UploadFile(instance_name, os.path.join(dir, 'test_util.py'),
-                    r'c:\temp')
+    self.UploadFile(instance_name, os.path.join(dir, 'test_util.py'), temp_dir)
 
   def RunWebDriverTest(self, instance_name, test_file, args=[]):
     """Runs a python webdriver test on an instance.
@@ -133,6 +126,8 @@
 
     Returns:
       the output."""
+    self.EnsurePythonInstalled(instance_name)
+
     # upload the test
     file_name = self.UploadFile(instance_name, test_file, r'c:\temp')
 
@@ -153,6 +148,8 @@
 
     Returns:
       the output."""
+    self.EnsurePythonInstalled(instance_name)
+
     # upload the test
     file_name = self.UploadFile(instance_name, test_file, r'c:\temp')
 
@@ -184,10 +181,8 @@
   def EnableUITest(self, instance_name):
     """Configures the instance so that UI tests can be run on it."""
     self.InstallWebDriver(instance_name)
-    self.InstallPackage(instance_name, 'sysinternals', '2019.9.5')
-    self.RunCommand(
-        instance_name,
-        r'c:\Python27\python.exe -m pip install pywinauto requests')
+    self.InstallChocolateyPackageLatest(instance_name, 'sysinternals')
+    self.InstallPipPackagesLatest(instance_name, ['pywinauto', 'requests'])
 
     password = self._generatePassword()
     user_name = 'ui_user'
diff --git a/chrome/browser/policy/e2e_test/infra/test_util.py b/chrome/browser/policy/e2e_test/infra/test_util.py
index 50e93d2..f08eed66 100644
--- a/chrome/browser/policy/e2e_test/infra/test_util.py
+++ b/chrome/browser/policy/e2e_test/infra/test_util.py
@@ -63,6 +63,6 @@
   os.environ["CHROME_LOG_FILE"] = r"c:\temp\chrome_log.txt"
 
   return webdriver.Chrome(
-      "C:/ProgramData/chocolatey/lib/chromedriver/tools/chromedriver.exe",
+      executable_path=r"C:\temp\chromedriver.exe",
       service_args=["--verbose", r"--log-path=c:\temp\chromedriver.log"],
       chrome_options=chrome_options)
diff --git a/chrome/browser/policy/e2e_test/tests/bookmarkbar_enabled/bookmarkbar_webdriver.py b/chrome/browser/policy/e2e_test/tests/bookmarkbar_enabled/bookmarkbar_webdriver.py
index 500f0aa..76d089f 100644
--- a/chrome/browser/policy/e2e_test/tests/bookmarkbar_enabled/bookmarkbar_webdriver.py
+++ b/chrome/browser/policy/e2e_test/tests/bookmarkbar_enabled/bookmarkbar_webdriver.py
@@ -2,20 +2,16 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from selenium import webdriver
 from pywinauto.application import Application
 from pywinauto.findwindows import ElementNotFoundError
-import os
+from selenium import webdriver
+
+import test_util
 
 options = webdriver.ChromeOptions()
 options.add_argument("--force-renderer-accessibility")
 
-os.environ["CHROME_LOG_FILE"] = r"c:\temp\chrome_log.txt"
-
-driver = webdriver.Chrome(
-    "C:/ProgramData/chocolatey/lib/chromedriver/tools/chromedriver.exe",
-    chrome_options=options,
-    service_args=["--verbose", r"--log-path=c:\temp\chromedriver.log"])
+driver = test_util.create_chrome_webdriver(chrome_options=options)
 
 try:
   app = Application(backend="uia")
diff --git a/chrome/browser/policy/e2e_test/tests/default_search_provider/default_search_provider_webdriver.py b/chrome/browser/policy/e2e_test/tests/default_search_provider/default_search_provider_webdriver.py
index aa0bbda..6a7684d 100644
--- a/chrome/browser/policy/e2e_test/tests/default_search_provider/default_search_provider_webdriver.py
+++ b/chrome/browser/policy/e2e_test/tests/default_search_provider/default_search_provider_webdriver.py
@@ -2,20 +2,16 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from selenium import webdriver
 from pywinauto.application import Application
-import os
+from selenium import webdriver
+
+import test_util
 
 # Set up ChromeDriver
 options = webdriver.ChromeOptions()
 options.add_argument("--force-renderer-accessibility")
 
-os.environ["CHROME_LOG_FILE"] = r"c:\temp\chrome_log.txt"
-
-driver = webdriver.Chrome(
-    "C:/ProgramData/chocolatey/lib/chromedriver/tools/chromedriver.exe",
-    chrome_options=options,
-    service_args=["--verbose", r"--log-path=c:\temp\chromedriver.log"])
+driver = test_util.create_chrome_webdriver(chrome_options=options)
 
 try:
   app = Application(backend="uia")
diff --git a/chrome/browser/policy/e2e_test/tests/force_google_safe_search/force_google_safe_search_webdriver_test.py b/chrome/browser/policy/e2e_test/tests/force_google_safe_search/force_google_safe_search_webdriver_test.py
index eba78c53..078225e 100644
--- a/chrome/browser/policy/e2e_test/tests/force_google_safe_search/force_google_safe_search_webdriver_test.py
+++ b/chrome/browser/policy/e2e_test/tests/force_google_safe_search/force_google_safe_search_webdriver_test.py
@@ -2,18 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import os
-
-from selenium import webdriver
 from selenium.webdriver.common.by import By
 from selenium.webdriver.support import expected_conditions as EC
 from selenium.webdriver.support.ui import WebDriverWait
 
-os.environ["CHROME_LOG_FILE"] = r"c:\temp\chrome_log.txt"
+import test_util
 
-driver = webdriver.Chrome(
-    "C:/ProgramData/chocolatey/lib/chromedriver/tools/chromedriver.exe",
-    service_args=["--verbose", r"--log-path=c:\temp\chromedriver.log"])
+driver = test_util.create_chrome_webdriver()
 driver.get('http://www.google.com/xhtml')
 
 # wait for page to be loaded
diff --git a/chrome/browser/policy/e2e_test/tests/homepage/get_homepage_url.py b/chrome/browser/policy/e2e_test/tests/homepage/get_homepage_url.py
index 7bb3f5e..af07d707 100644
--- a/chrome/browser/policy/e2e_test/tests/homepage/get_homepage_url.py
+++ b/chrome/browser/policy/e2e_test/tests/homepage/get_homepage_url.py
@@ -2,12 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from selenium import webdriver
-import os
 from pywinauto.application import Application
 
-driver = webdriver.Chrome(
-    "C:/ProgramData/chocolatey/lib/chromedriver/tools/chromedriver.exe")
+import test_util
+
+driver = test_util.create_chrome_webdriver()
 
 try:
   app = Application(backend="uia")
diff --git a/chrome/browser/policy/e2e_test/tests/install_extension.py b/chrome/browser/policy/e2e_test/tests/install_extension.py
index 4188ddc..fd22bdb 100644
--- a/chrome/browser/policy/e2e_test/tests/install_extension.py
+++ b/chrome/browser/policy/e2e_test/tests/install_extension.py
@@ -2,10 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from selenium import webdriver
-import os
 import time
+
 from absl import app, flags
+from selenium import webdriver
+
+import test_util
 
 FLAGS = flags.FLAGS
 
@@ -35,12 +37,7 @@
   #https://bugs.chromium.org/p/chromedriver/issues/detail?id=2930
   chrome_options.add_experimental_option("useAutomationExtension", False)
 
-  os.environ["CHROME_LOG_FILE"] = r"c:\temp\chrome_log.txt"
-
-  driver = webdriver.Chrome(
-      "C:/ProgramData/chocolatey/lib/chromedriver/tools/chromedriver.exe",
-      service_args=["--verbose", r"--log-path=c:\temp\chromedriver.log"],
-      chrome_options=chrome_options)
+  driver = test_util.create_chrome_webdriver(chrome_options=chrome_options)
   driver.implicitly_wait(FLAGS.wait)
   driver.get(FLAGS.url)
 
diff --git a/chrome/browser/policy/e2e_test/tests/popups_allowed/popup_allowed_webdriver_test.py b/chrome/browser/policy/e2e_test/tests/popups_allowed/popup_allowed_webdriver_test.py
index 5029678..57df013 100644
--- a/chrome/browser/policy/e2e_test/tests/popups_allowed/popup_allowed_webdriver_test.py
+++ b/chrome/browser/policy/e2e_test/tests/popups_allowed/popup_allowed_webdriver_test.py
@@ -4,14 +4,12 @@
 
 from selenium import webdriver
 
+import test_util
+
 testSite = "http://www.popuptest.com/popuptest1.html"
 options = webdriver.ChromeOptions()
 options.add_experimental_option('excludeSwitches', ['disable-popup-blocking'])
-exe_path = "C:/ProgramData/chocolatey/lib/chromedriver/tools/chromedriver.exe"
-driver = webdriver.Chrome(
-    exe_path,
-    chrome_options=options,
-    service_args=["--verbose", r"--log-path=c:\temp\chromedriver.log"])
+driver = test_util.create_chrome_webdriver(chrome_options=options)
 driver.implicitly_wait(5)
 driver.get(testSite)
 handles = driver.window_handles
diff --git a/chrome/browser/policy/e2e_test/tests/restore_on_startup/restore_on_startup_webdriver_test.py b/chrome/browser/policy/e2e_test/tests/restore_on_startup/restore_on_startup_webdriver_test.py
index 76a78d3..2127046 100644
--- a/chrome/browser/policy/e2e_test/tests/restore_on_startup/restore_on_startup_webdriver_test.py
+++ b/chrome/browser/policy/e2e_test/tests/restore_on_startup/restore_on_startup_webdriver_test.py
@@ -2,13 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import time
-import test_util
 import json
+import time
+
 from absl import app, flags
-from selenium import webdriver
 from selenium.webdriver.chrome.options import Options
 
+import test_util
+
 FLAGS = flags.FLAGS
 
 flags.DEFINE_enum(
@@ -26,12 +27,7 @@
 def _create_driver():
   chrome_options = Options()
   chrome_options.add_argument(r'user-data-dir=%s' % FLAGS.user_data_dir)
-  driver = webdriver.Chrome(
-      executable_path=
-      "C:/ProgramData/chocolatey/lib/chromedriver/tools/chromedriver.exe",
-      chrome_options=chrome_options,
-      service_args=["--verbose", r"--log-path=c:\temp\chromedriver.log"])
-  return driver
+  return test_util.create_chrome_webdriver(chrome_options=chrome_options)
 
 
 def _get_urls(driver):
diff --git a/chrome/browser/policy/e2e_test/tests/user_data_dir/user_data_dir_webdriver.py b/chrome/browser/policy/e2e_test/tests/user_data_dir/user_data_dir_webdriver.py
index 756b27a..77f8386 100644
--- a/chrome/browser/policy/e2e_test/tests/user_data_dir/user_data_dir_webdriver.py
+++ b/chrome/browser/policy/e2e_test/tests/user_data_dir/user_data_dir_webdriver.py
@@ -2,9 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from selenium import webdriver
 import os
+
 from absl import app, flags
+from selenium import webdriver
+
+import test_util
 
 FLAGS = flags.FLAGS
 flags.DEFINE_string('user_data_dir', None, 'Need specify user data dir to test')
@@ -25,12 +28,8 @@
   options = webdriver.ChromeOptions()
   # Add option for connecting chromedriver with Chrome
   options.add_experimental_option("debuggerAddress", "localhost:9222")
-  os.environ["CHROME_LOG_FILE"] = r"c:\temp\chrome_log.txt"
 
-  driver = webdriver.Chrome(
-      "C:/ProgramData/chocolatey/lib/chromedriver/tools/chromedriver.exe",
-      chrome_options=options,
-      service_args=["--verbose", r"--log-path=c:\temp\chromedriver.log"])
+  driver = test_util.create_chrome_webdriver(chrome_options=options)
 
   try:
     # Verify User Data Dir in chrome://policy page
diff --git a/chrome/browser/predictors/autocomplete_action_predictor.cc b/chrome/browser/predictors/autocomplete_action_predictor.cc
index 2e1391a..cb7d23a 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor.cc
+++ b/chrome/browser/predictors/autocomplete_action_predictor.cc
@@ -25,7 +25,6 @@
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/in_memory_database.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_result.h"
@@ -74,8 +73,7 @@
     : profile_(profile),
       main_profile_predictor_(NULL),
       incognito_predictor_(NULL),
-      initialized_(false),
-      history_service_observer_(this) {
+      initialized_(false) {
   if (profile_->IsOffTheRecord()) {
     main_profile_predictor_ = AutocompleteActionPredictorFactory::GetForProfile(
         profile_->GetOriginalProfile());
diff --git a/chrome/browser/predictors/autocomplete_action_predictor.h b/chrome/browser/predictors/autocomplete_action_predictor.h
index 085a6677..fbaffeb 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor.h
+++ b/chrome/browser/predictors/autocomplete_action_predictor.h
@@ -18,6 +18,7 @@
 #include "base/scoped_observer.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/predictors/autocomplete_action_predictor_table.h"
+#include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -40,7 +41,6 @@
 }
 
 namespace history {
-class HistoryService;
 class URLDatabase;
 }
 
@@ -280,7 +280,7 @@
   bool initialized_;
 
   ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
-      history_service_observer_;
+      history_service_observer_{this};
 
   DISALLOW_COPY_AND_ASSIGN(AutocompleteActionPredictor);
 };
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.cc b/chrome/browser/predictors/resource_prefetch_predictor.cc
index 451e0e7..1ccbca4 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/predictors/predictors_features.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/history/core/browser/history_database.h"
-#include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/url_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
@@ -223,8 +222,7 @@
       config_(config),
       initialization_state_(NOT_INITIALIZED),
       tables_(PredictorDatabaseFactory::GetForProfile(profile)
-                  ->resource_prefetch_tables()),
-      history_service_observer_(this) {
+                  ->resource_prefetch_tables()) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.h b/chrome/browser/predictors/resource_prefetch_predictor.h
index d05c0246..09a8d0ed 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor.h
+++ b/chrome/browser/predictors/resource_prefetch_predictor.h
@@ -25,6 +25,7 @@
 #include "chrome/browser/predictors/navigation_id.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor_tables.h"
 #include "components/history/core/browser/history_db_task.h"
+#include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -276,7 +277,7 @@
   std::unique_ptr<OriginDataMap> origin_data_;
 
   ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
-      history_service_observer_;
+      history_service_observer_{this};
 
   // Indicates if all predictors data should be deleted after the
   // initialization is completed.
diff --git a/chrome/browser/previews/previews_content_util.cc b/chrome/browser/previews/previews_content_util.cc
index 828f0fc5..90562de 100644
--- a/chrome/browser/previews/previews_content_util.cc
+++ b/chrome/browser/previews/previews_content_util.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
 #include "chrome/browser/previews/previews_lite_page_decider.h"
-#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
 #include "chrome/browser/previews/previews_lite_page_url_loader_interceptor.h"
 #include "chrome/browser/previews/previews_offline_helper.h"
 #include "chrome/browser/previews/previews_service.h"
@@ -99,8 +98,7 @@
           Profile::FromBrowserContext(web_contents->GetBrowserContext()))
           .get();
   ContentSetting setting;
-  GURL previews_url =
-      PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(url);
+  GURL previews_url = GetLitePageRedirectURLForURL(url);
   cookie_settings->GetCookieSetting(previews_url, previews_url, nullptr,
                                     &setting);
   if (!content_settings::CookieSettingsBase::IsAllowed(setting)) {
@@ -162,7 +160,7 @@
   // This should always be last.
   if (previews::params::IsInLitePageRedirectControl()) {
     previews::PreviewsUserData::ServerLitePageInfo* info =
-        PreviewsLitePageNavigationThrottle::GetOrCreateServerLitePageInfo(
+        PreviewsLitePageURLLoaderInterceptor::GetOrCreateServerLitePageInfo(
             navigation_handle, decider);
     info->status = previews::ServerLitePageStatus::kControl;
     return false;
@@ -432,9 +430,7 @@
   // Check if a LITE_PAGE_REDIRECT preview was actually served.
   if (previews_state & content::LITE_PAGE_REDIRECT_ON) {
     if (IsLitePageRedirectPreviewURL(url)) {
-      if (navigation_handle &&
-          base::FeatureList::IsEnabled(
-              previews::features::kHTTPSServerPreviewsUsingURLLoader)) {
+      if (navigation_handle) {
         previews_data->set_server_lite_page_info(
             CreateServerLitePageInfoFromNavigationHandle(navigation_handle));
       }
diff --git a/chrome/browser/previews/previews_lite_page_browsertest.cc b/chrome/browser/previews/previews_lite_page_browsertest.cc
index b73af598..8f5ed51 100644
--- a/chrome/browser/previews/previews_lite_page_browsertest.cc
+++ b/chrome/browser/previews/previews_lite_page_browsertest.cc
@@ -36,7 +36,6 @@
 #include "chrome/browser/infobars/mock_infobar_service.h"
 #include "chrome/browser/metrics/subprocess_metrics_provider.h"
 #include "chrome/browser/previews/previews_lite_page_decider.h"
-#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
 #include "chrome/browser/previews/previews_lite_page_url_loader_interceptor.h"
 #include "chrome/browser/previews/previews_service.h"
 #include "chrome/browser/previews/previews_service_factory.h"
@@ -307,7 +306,6 @@
 
     scoped_feature_list_.InitWithFeatures(
         {previews::features::kPreviews,
-         previews::features::kHTTPSServerPreviewsUsingURLLoader,
          optimization_guide::features::kOptimizationHints,
          previews::features::kResourceLoadingHints,
          data_reduction_proxy::features::
@@ -437,9 +435,7 @@
     const GURL virtual_url = entry->GetVirtualURL();
 
     // The loaded url should be the previews version of the virtual url.
-    EXPECT_EQ(
-        loaded_url,
-        PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(virtual_url));
+    EXPECT_EQ(loaded_url, previews::GetLitePageRedirectURLForURL(virtual_url));
 
     EXPECT_FALSE(virtual_url.DomainIs(previews_server_url().host()) &&
                  virtual_url.EffectiveIntPort() ==
@@ -1017,8 +1013,8 @@
     // server.
     base::HistogramTester histogram_tester;
     ui_test_utils::NavigateToURL(
-        browser(), PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(
-                       HttpsLitePageURL(kSuccess)));
+        browser(),
+        previews::GetLitePageRedirectURLForURL(HttpsLitePageURL(kSuccess)));
     VerifyPreviewNotLoaded();
   }
 
@@ -1427,8 +1423,7 @@
   {
     base::RunLoop loop;
     history_service->QueryURL(
-        PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(
-            HttpsLitePageURL(kSuccess)),
+        previews::GetLitePageRedirectURLForURL(HttpsLitePageURL(kSuccess)),
         false /* want_visits */,
         base::BindLambdaForTesting([&](history::QueryURLResult result) {
           EXPECT_FALSE(result.success);
@@ -1559,8 +1554,7 @@
           }
         ]
       )text",
-      PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(
-          HttpsLitePageURL(kSuccess))
+      previews::GetLitePageRedirectURLForURL(HttpsLitePageURL(kSuccess))
           .spec()
           .c_str()));
 
diff --git a/chrome/browser/previews/previews_lite_page_decider.cc b/chrome/browser/previews/previews_lite_page_decider.cc
index 1be6316..fa883e07 100644
--- a/chrome/browser/previews/previews_lite_page_decider.cc
+++ b/chrome/browser/previews/previews_lite_page_decider.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/predictors/loading_predictor.h"
 #include "chrome/browser/predictors/loading_predictor_factory.h"
 #include "chrome/browser/previews/previews_lite_page_infobar_delegate.h"
-#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
 #include "chrome/browser/previews/previews_service.h"
 #include "chrome/browser/previews/previews_service_factory.h"
 #include "chrome/browser/previews/previews_ui_tab_helper.h"
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
deleted file mode 100644
index 0446820f..0000000
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
-
-#include <stdint.h>
-#include <string>
-
-#include "base/metrics/histogram_macros.h"
-#include "base/optional.h"
-#include "base/strings/safe_sprintf.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/time/time.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
-#include "chrome/browser/previews/previews_lite_page_decider.h"
-#include "chrome/browser/previews/previews_ui_tab_helper.h"
-#include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
-#include "components/base32/base32.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
-#include "components/previews/core/previews_experiments.h"
-#include "components/previews/core/previews_lite_page_redirect.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/web_contents.h"
-#include "crypto/sha2.h"
-#include "net/base/escape.h"
-#include "net/base/url_util.h"
-#include "net/http/http_util.h"
-
-bool HandlePreviewsLitePageURLRewrite(
-    GURL* url,
-    content::BrowserContext* browser_context) {
-  // Don't change the |url|, just register our interest in reversing it before
-  // it is displayed to the user in |HandlePreviewsLitePageURLRewriteReverse|.
-  // Without returning true here, |HandlePreviewsLitePageURLRewriteReverse|
-  // would not be called.
-
-  auto* data_reduction_proxy_settings =
-      DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
-          browser_context);
-
-  if (!data_reduction_proxy_settings)
-    return false;
-
-  return data_reduction_proxy_settings->IsDataReductionProxyEnabled() &&
-         previews::params::IsLitePageServerPreviewsEnabled();
-}
-
-bool HandlePreviewsLitePageURLRewriteReverse(
-    GURL* url,
-    content::BrowserContext* browser_context) {
-  std::string original_url;
-  if (previews::ExtractOriginalURLFromLitePageRedirectURL(*url,
-                                                          &original_url)) {
-    *url = GURL(original_url);
-    return true;
-  }
-  return false;
-}
-
-// static
-GURL PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(
-    const GURL& original_url) {
-  DCHECK(original_url.is_valid());
-  const std::string experiment_id =
-      data_reduction_proxy::params::GetDataSaverServerExperiments();
-
-  std::string experiment_query;
-  if (!experiment_id.empty()) {
-    experiment_query =
-        "&x=" + net::EscapeQueryParamValue(experiment_id, true /* use_plus */);
-  }
-  std::string fragment;
-  if (original_url.has_ref()) {
-    fragment = "#" + original_url.ref();
-  }
-
-  // Strip out the fragment so that it is not sent to the server.
-  std::string origin_hash = base::ToLowerASCII(base32::Base32Encode(
-      crypto::SHA256HashString(
-          original_url.scheme() + "://" + original_url.host() + ":" +
-          base::NumberToString(original_url.EffectiveIntPort())),
-      base32::Base32EncodePolicy::OMIT_PADDING));
-  GURL previews_host = previews::params::GetLitePagePreviewsDomainURL();
-  GURL previews_url = GURL(
-      previews_host.scheme() + "://" + origin_hash + "." +
-      previews_host.host() +
-      (previews_host.has_port() ? (":" + previews_host.port()) : "") + "/p?u=" +
-      net::EscapeQueryParamValue(original_url.GetAsReferrer().spec(),
-                                 true /* use_plus */) +
-      experiment_query + fragment);
-  DCHECK(previews_url.is_valid());
-  DCHECK_EQ(previews_host.scheme(), previews_url.scheme());
-  return previews_url;
-}
-
-// static
-previews::PreviewsUserData::ServerLitePageInfo*
-PreviewsLitePageNavigationThrottle::GetOrCreateServerLitePageInfo(
-    content::NavigationHandle* navigation_handle,
-    PreviewsLitePageDecider* decider) {
-  PreviewsUITabHelper* ui_tab_helper =
-      PreviewsUITabHelper::FromWebContents(navigation_handle->GetWebContents());
-  if (!ui_tab_helper)
-    return nullptr;
-
-  previews::PreviewsUserData* previews_data =
-      ui_tab_helper->GetPreviewsUserData(navigation_handle);
-  if (!previews_data)
-    return nullptr;
-
-  if (previews_data->server_lite_page_info()) {
-    return previews_data->server_lite_page_info();
-  }
-
-  previews_data->set_server_lite_page_info(
-      std::make_unique<previews::PreviewsUserData::ServerLitePageInfo>());
-
-  DataReductionProxyChromeSettings* drp_settings =
-      DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
-          navigation_handle->GetWebContents()->GetBrowserContext());
-  base::Optional<std::string> session_id;
-  if (drp_settings) {
-    session_id = data_reduction_proxy::DataReductionProxyRequestOptions::
-        GetSessionKeyFromRequestHeaders(drp_settings->GetProxyRequestHeaders());
-  }
-
-  previews::PreviewsUserData::ServerLitePageInfo* info =
-      previews_data->server_lite_page_info();
-  info->original_navigation_start = navigation_handle->NavigationStart();
-  if (session_id.has_value())
-    info->drp_session_key = session_id.value();
-
-  const ChromeNavigationUIData* chrome_navigation_ui_data =
-      static_cast<const ChromeNavigationUIData*>(
-          navigation_handle->GetNavigationUIData());
-  if (chrome_navigation_ui_data)
-    info->page_id = chrome_navigation_ui_data->data_reduction_proxy_page_id();
-  // The page id may not be set in some corner cases (like forward navigation),
-  // so make sure it gets set here.
-  if (info->page_id == 0U)
-    info->page_id = decider->GeneratePageID();
-
-  return info;
-}
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.h b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
deleted file mode 100644
index 8c76215..0000000
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_H_
-#define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_H_
-
-#include "components/previews/content/previews_user_data.h"
-#include "content/public/browser/navigation_handle.h"
-#include "url/gurl.h"
-
-namespace content {
-class BrowserContext;
-}  // namespace content
-
-class PreviewsLitePageDecider;
-
-// If the given URL is a LitePage Preview URL, this returns true but does not
-// change the |url|. This will set |update_virtual_url_with_url| on
-// NavigationEntry so that |HandlePreviewsLitePageURLRewriteReverse| is called
-// when the navigation finishes.
-// Note: This means the virtual URL will not be set during the navigation load.
-// This is handled separately in UI on Android.
-bool HandlePreviewsLitePageURLRewrite(GURL* url,
-                                      content::BrowserContext* browser_context);
-
-// Handles translating the given Lite Page URL to the original URL. Returns true
-// if the given |url| was a preview, otherwise returns false and does not change
-// |url|.
-bool HandlePreviewsLitePageURLRewriteReverse(
-    GURL* url,
-    content::BrowserContext* browser_context);
-
-class PreviewsLitePageNavigationThrottle {
- public:
-  // Returns the URL for a preview given by the url.
-  static GURL GetPreviewsURLForURL(const GURL& original_url);
-
-  // Gets the ServerLitePageInfo struct from an existing attempted lite page
-  // navigation, if there is one. If not, returns a new ServerLitePageInfo
-  // initialized with metadata from navigation_handle() and |this| that is owned
-  // by the PreviewsUserData associated with navigation_handle().
-  static previews::PreviewsUserData::ServerLitePageInfo*
-  GetOrCreateServerLitePageInfo(content::NavigationHandle* navigation_handle,
-                                PreviewsLitePageDecider* manager);
-
-  DISALLOW_COPY_AND_ASSIGN(PreviewsLitePageNavigationThrottle);
-};
-
-#endif  // CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle_unittest.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle_unittest.cc
deleted file mode 100644
index 286078e..0000000
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle_unittest.cc
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
-
-#include <map>
-#include <memory>
-
-#include "base/command_line.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "chrome/browser/previews/previews_lite_page_decider.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
-#include "components/previews/core/previews_features.h"
-#include "components/variations/variations_associated_data.h"
-#include "content/public/browser/navigation_handle.h"
-#include "net/http/http_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-TEST(PreviewsLitePageNavigationThrottleTest, TestGetPreviewsURL) {
-  struct TestCase {
-    std::string previews_host;
-    std::string original_url;
-    std::string expected_previews_url;
-    std::string experiment_variation;
-    std::string experiment_cmd_line;
-  };
-  const TestCase kTestCases[]{
-      // Use https://play.golang.org/p/HUM2HxmUTOW to compute
-      // |expected_previews_url|.
-      {
-          "https://previews.host.com",
-          "https://original.host.com/path/path/path?query=yes",
-          "https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
-          "previews.host.com/p?u="
-          "https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
-          "",
-          "",
-      },
-      {
-          "https://previews.host.com",
-          "http://original.host.com/path/path/path?query=yes",
-          "https://6p7dar4ju6r4ynz7x3pucmlcltuqsf7z5auhvckzln7voglkt56q."
-          "previews.host.com/p?u="
-          "http%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
-          "",
-      },
-      {
-          "https://previews.host.com",
-          "https://original.host.com:1443/path/path/path?query=yes",
-          "https://mil6oxtqb4zpsbmutm4d7wrx5nlr6tzlxjp7y44u55zqhzsdzjpq."
-          "previews.host.com/p?u=https%3A%2F%2Foriginal.host.com%3A1443"
-          "%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
-          "",
-          "",
-      },
-      {
-          "https://previews.host.com:1443",
-          "http://original.host.com/path/path/path?query=yes",
-          "https://6p7dar4ju6r4ynz7x3pucmlcltuqsf7z5auhvckzln7voglkt56q."
-          "previews.host.com:1443/p?u="
-          "http%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
-          "",
-          "",
-      },
-      {
-          "https://previews.host.com:1443",
-          "https://original.host.com:1443/path/path/path?query=yes",
-          "https://mil6oxtqb4zpsbmutm4d7wrx5nlr6tzlxjp7y44u55zqhzsdzjpq."
-          "previews.host.com:1443/p?u=https%3A%2F%2Foriginal.host.com%3A1443"
-          "%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
-          "",
-          "",
-      },
-      {
-          "https://previews.host.com",
-          "https://original.host.com/path/path/path?query=yes#fragment",
-          "https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
-          "previews.host.com/p?u="
-          "https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes"
-          "#fragment",
-          "",
-          "",
-      },
-      {
-          "https://previews.host.com",
-          "https://original.host.com/path/path/path?query=yes",
-          "https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
-          "previews.host.com/p?u="
-          "https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes"
-          "&x=variation_experiment",
-          "variation_experiment",
-          "",
-      },
-      {
-          // Ensure that the command line experiment takes precedence over the
-          // one provided by variations.
-          "https://previews.host.com",
-          "https://original.host.com/path/path/path?query=yes",
-          "https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
-          "previews.host.com/p?u="
-          "https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes"
-          "&x=cmdline_experiment",
-          "variation_experiment",
-          "cmdline_experiment",
-      },
-      {
-          "https://previews.host.com",
-          "https://[::1]:12345",
-          "https://2ikmbopbfxagkb7uer2vgfxmbzu2vw4qq3d3ixe3h2hfhgcabvua."
-          "previews.host.com/p?u=https%3A%2F%2F%5B%3A%3A1%5D%3A12345%2F",
-          "",
-          "",
-      },
-  };
-
-  for (const TestCase& test_case : kTestCases) {
-    variations::testing::ClearAllVariationParams();
-
-    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-        data_reduction_proxy::switches::kDataReductionProxyExperiment,
-        test_case.experiment_cmd_line);
-
-    base::test::ScopedFeatureList scoped_feature_list;
-    std::map<std::string, std::string> server_experiment_params;
-    server_experiment_params[data_reduction_proxy::params::
-                                 GetDataSaverServerExperimentsOptionName()] =
-        test_case.experiment_variation;
-
-    scoped_feature_list.InitWithFeaturesAndParameters(
-        {{data_reduction_proxy::features::kDataReductionProxyServerExperiments,
-          {server_experiment_params}},
-         {previews::features::kLitePageServerPreviews,
-          {{"previews_host", test_case.previews_host}}}},
-        {});
-
-    EXPECT_EQ(PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(
-                  GURL(test_case.original_url)),
-              GURL(test_case.expected_previews_url));
-  }
-}
diff --git a/chrome/browser/previews/previews_lite_page_predictor.cc b/chrome/browser/previews/previews_lite_page_predictor.cc
index 57eb008bb..0159478 100644
--- a/chrome/browser/previews/previews_lite_page_predictor.cc
+++ b/chrome/browser/previews/previews_lite_page_predictor.cc
@@ -15,7 +15,7 @@
 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
 #include "chrome/browser/predictors/loading_predictor.h"
 #include "chrome/browser/predictors/loading_predictor_factory.h"
-#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
+#include "chrome/browser/previews/previews_lite_page_url_loader_interceptor.h"
 #include "chrome/browser/previews/previews_service.h"
 #include "chrome/browser/previews/previews_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -157,7 +157,7 @@
   if (previews::ExtractOriginalURLFromLitePageRedirectURL(url, &original_url))
     return GURL(original_url);
 
-  return PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(url);
+  return previews::GetLitePageRedirectURLForURL(url);
 }
 
 void PreviewsLitePagePredictor::MaybeToggleTimer(
diff --git a/chrome/browser/previews/previews_lite_page_predictor_unittest.cc b/chrome/browser/previews/previews_lite_page_predictor_unittest.cc
index 96f618f..cb38bb2 100644
--- a/chrome/browser/previews/previews_lite_page_predictor_unittest.cc
+++ b/chrome/browser/previews/previews_lite_page_predictor_unittest.cc
@@ -8,7 +8,7 @@
 
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
-#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
+#include "chrome/browser/previews/previews_lite_page_url_loader_interceptor.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/previews/core/previews_features.h"
 #include "content/public/browser/web_contents.h"
@@ -143,8 +143,7 @@
 
   content::WebContentsTester::For(web_contents())
       ->NavigateAndCommit(
-          PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(
-              GURL(kTestUrl)));
+          previews::GetLitePageRedirectURLForURL(GURL(kTestUrl)));
 
   EXPECT_TRUE(predictor()->ShouldActOnPage(nullptr));
   histogram_tester.ExpectUniqueSample(
@@ -193,8 +192,7 @@
 
   content::WebContentsTester::For(web_contents())
       ->NavigateAndCommit(
-          PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(
-              GURL(kTestUrl)));
+          previews::GetLitePageRedirectURLForURL(GURL(kTestUrl)));
 
   EXPECT_TRUE(predictor()->ShouldActOnPage(nullptr));
 }
diff --git a/chrome/browser/previews/previews_lite_page_redirect_url_loader.cc b/chrome/browser/previews/previews_lite_page_redirect_url_loader.cc
index 024cf52..ddcb548 100644
--- a/chrome/browser/previews/previews_lite_page_redirect_url_loader.cc
+++ b/chrome/browser/previews/previews_lite_page_redirect_url_loader.cc
@@ -10,7 +10,7 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
-#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
+#include "chrome/browser/previews/previews_lite_page_url_loader_interceptor.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/previews/core/previews_experiments.h"
 #include "components/previews/core/previews_lite_page_redirect.h"
@@ -132,8 +132,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   GURL original_url = modified_resource_request_.url;
-  GURL lite_page_url = PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(
-      modified_resource_request_.url);
+  GURL lite_page_url =
+      GetLitePageRedirectURLForURL(modified_resource_request_.url);
 
   CreateRedirectInformation(lite_page_url);
 
diff --git a/chrome/browser/previews/previews_lite_page_url_loader_interceptor.cc b/chrome/browser/previews/previews_lite_page_url_loader_interceptor.cc
index 8eaf20d..270bd7126d 100644
--- a/chrome/browser/previews/previews_lite_page_url_loader_interceptor.cc
+++ b/chrome/browser/previews/previews_lite_page_url_loader_interceptor.cc
@@ -4,25 +4,45 @@
 
 #include "chrome/browser/previews/previews_lite_page_url_loader_interceptor.h"
 
+#include <stdint.h>
 #include <string>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/optional.h"
 #include "base/stl_util.h"
+#include "base/strings/safe_sprintf.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
+#include "chrome/browser/previews/previews_lite_page_decider.h"
+#include "chrome/browser/previews/previews_ui_tab_helper.h"
 #include "chrome/browser/profiles/profile_io_data.h"
+#include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
+#include "components/base32/base32.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
+#include "components/previews/core/previews_experiments.h"
 #include "components/previews/core/previews_features.h"
 #include "components/previews/core/previews_lite_page_redirect.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/common/resource_type.h"
+#include "crypto/sha2.h"
+#include "net/base/escape.h"
+#include "net/base/url_util.h"
 #include "net/http/http_request_headers.h"
+#include "net/http/http_util.h"
 #include "net/nqe/effective_connection_type.h"
 
 namespace previews {
@@ -83,6 +103,71 @@
                             reason);
 }
 
+bool HandlePreviewsLitePageURLRewrite(
+    GURL* url,
+    content::BrowserContext* browser_context) {
+  // Don't change the |url|, just register our interest in reversing it before
+  // it is displayed to the user in |HandlePreviewsLitePageURLRewriteReverse|.
+  // Without returning true here, |HandlePreviewsLitePageURLRewriteReverse|
+  // would not be called.
+
+  auto* data_reduction_proxy_settings =
+      DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
+          browser_context);
+
+  if (!data_reduction_proxy_settings)
+    return false;
+
+  return data_reduction_proxy_settings->IsDataReductionProxyEnabled() &&
+         previews::params::IsLitePageServerPreviewsEnabled();
+}
+
+bool HandlePreviewsLitePageURLRewriteReverse(
+    GURL* url,
+    content::BrowserContext* browser_context) {
+  std::string original_url;
+  if (previews::ExtractOriginalURLFromLitePageRedirectURL(*url,
+                                                          &original_url)) {
+    *url = GURL(original_url);
+    return true;
+  }
+  return false;
+}
+
+GURL GetLitePageRedirectURLForURL(const GURL& original_url) {
+  DCHECK(original_url.is_valid());
+  const std::string experiment_id =
+      data_reduction_proxy::params::GetDataSaverServerExperiments();
+
+  std::string experiment_query;
+  if (!experiment_id.empty()) {
+    experiment_query =
+        "&x=" + net::EscapeQueryParamValue(experiment_id, true /* use_plus */);
+  }
+  std::string fragment;
+  if (original_url.has_ref()) {
+    fragment = "#" + original_url.ref();
+  }
+
+  // Strip out the fragment so that it is not sent to the server.
+  std::string origin_hash = base::ToLowerASCII(base32::Base32Encode(
+      crypto::SHA256HashString(
+          original_url.scheme() + "://" + original_url.host() + ":" +
+          base::NumberToString(original_url.EffectiveIntPort())),
+      base32::Base32EncodePolicy::OMIT_PADDING));
+  GURL previews_host = previews::params::GetLitePagePreviewsDomainURL();
+  GURL previews_url = GURL(
+      previews_host.scheme() + "://" + origin_hash + "." +
+      previews_host.host() +
+      (previews_host.has_port() ? (":" + previews_host.port()) : "") + "/p?u=" +
+      net::EscapeQueryParamValue(original_url.GetAsReferrer().spec(),
+                                 true /* use_plus */) +
+      experiment_query + fragment);
+  DCHECK(previews_url.is_valid());
+  DCHECK_EQ(previews_host.scheme(), previews_url.scheme());
+  return previews_url;
+}
+
 PreviewsLitePageURLLoaderInterceptor::PreviewsLitePageURLLoaderInterceptor(
     const scoped_refptr<network::SharedURLLoaderFactory>&
         network_loader_factory,
@@ -94,6 +179,56 @@
 
 PreviewsLitePageURLLoaderInterceptor::~PreviewsLitePageURLLoaderInterceptor() {}
 
+// static
+PreviewsUserData::ServerLitePageInfo*
+PreviewsLitePageURLLoaderInterceptor::GetOrCreateServerLitePageInfo(
+    content::NavigationHandle* navigation_handle,
+    PreviewsLitePageDecider* decider) {
+  PreviewsUITabHelper* ui_tab_helper =
+      PreviewsUITabHelper::FromWebContents(navigation_handle->GetWebContents());
+  if (!ui_tab_helper)
+    return nullptr;
+
+  previews::PreviewsUserData* previews_data =
+      ui_tab_helper->GetPreviewsUserData(navigation_handle);
+  if (!previews_data)
+    return nullptr;
+
+  if (previews_data->server_lite_page_info()) {
+    return previews_data->server_lite_page_info();
+  }
+
+  previews_data->set_server_lite_page_info(
+      std::make_unique<previews::PreviewsUserData::ServerLitePageInfo>());
+
+  DataReductionProxyChromeSettings* drp_settings =
+      DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
+          navigation_handle->GetWebContents()->GetBrowserContext());
+  base::Optional<std::string> session_id;
+  if (drp_settings) {
+    session_id = data_reduction_proxy::DataReductionProxyRequestOptions::
+        GetSessionKeyFromRequestHeaders(drp_settings->GetProxyRequestHeaders());
+  }
+
+  previews::PreviewsUserData::ServerLitePageInfo* info =
+      previews_data->server_lite_page_info();
+  info->original_navigation_start = navigation_handle->NavigationStart();
+  if (session_id.has_value())
+    info->drp_session_key = session_id.value();
+
+  const ChromeNavigationUIData* chrome_navigation_ui_data =
+      static_cast<const ChromeNavigationUIData*>(
+          navigation_handle->GetNavigationUIData());
+  if (chrome_navigation_ui_data)
+    info->page_id = chrome_navigation_ui_data->data_reduction_proxy_page_id();
+  // The page id may not be set in some corner cases (like forward navigation),
+  // so make sure it gets set here.
+  if (info->page_id == 0U)
+    info->page_id = decider->GeneratePageID();
+
+  return info;
+}
+
 void PreviewsLitePageURLLoaderInterceptor::MaybeCreateLoader(
     const network::ResourceRequest& tentative_resource_request,
     content::BrowserContext* browser_context,
diff --git a/chrome/browser/previews/previews_lite_page_url_loader_interceptor.h b/chrome/browser/previews/previews_lite_page_url_loader_interceptor.h
index 7d2a0f3..9ecd2f46 100644
--- a/chrome/browser/previews/previews_lite_page_url_loader_interceptor.h
+++ b/chrome/browser/previews/previews_lite_page_url_loader_interceptor.h
@@ -14,11 +14,19 @@
 #include "base/sequence_checker.h"
 #include "chrome/browser/previews/previews_lite_page_redirect_url_loader.h"
 #include "chrome/browser/previews/previews_lite_page_serving_url_loader.h"
+#include "components/previews/content/previews_user_data.h"
 #include "content/public/browser/url_loader_request_interceptor.h"
 #include "net/http/http_request_headers.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "url/gurl.h"
 
+namespace content {
+class BrowserContext;
+class NavigationHandle;
+}  // namespace content
+
+class PreviewsLitePageDecider;
+
 namespace previews {
 
 // Reasons that a navigation is blacklisted from a lite page redirect preview.
@@ -100,6 +108,25 @@
 void LogLitePageRedirectIneligibleReason(
     LitePageRedirectIneligibleReason reason);
 
+// If the given URL is a LitePage Preview URL, this returns true but does not
+// change the |url|. This will set |update_virtual_url_with_url| on
+// NavigationEntry so that |HandlePreviewsLitePageURLRewriteReverse| is called
+// when the navigation finishes.
+// Note: This means the virtual URL will not be set during the navigation load.
+// This is handled separately in UI on Android.
+bool HandlePreviewsLitePageURLRewrite(GURL* url,
+                                      content::BrowserContext* browser_context);
+
+// Handles translating the given Lite Page URL to the original URL. Returns true
+// if the given |url| was a preview, otherwise returns false and does not change
+// |url|.
+bool HandlePreviewsLitePageURLRewriteReverse(
+    GURL* url,
+    content::BrowserContext* browser_context);
+
+// Returns the URL for a preview given by the url.
+GURL GetLitePageRedirectURLForURL(const GURL& original_url);
+
 // A class that attempts to intercept requests and fetch the Lite Page version
 // of the request. Its lifetime matches that of the content/ navigation loader
 // code. Currently, not fully implemented.
@@ -113,6 +140,14 @@
       int frame_tree_node_id);
   ~PreviewsLitePageURLLoaderInterceptor() override;
 
+  // Gets the ServerLitePageInfo struct from an existing attempted lite page
+  // navigation, if there is one. If not, returns a new ServerLitePageInfo
+  // initialized with metadata from navigation_handle() and |this| that is owned
+  // by the PreviewsUserData associated with navigation_handle().
+  static PreviewsUserData::ServerLitePageInfo* GetOrCreateServerLitePageInfo(
+      content::NavigationHandle* navigation_handle,
+      PreviewsLitePageDecider* manager);
+
   // content::URLLaoderRequestInterceptor:
   void MaybeCreateLoader(
       const network::ResourceRequest& tentative_resource_request,
diff --git a/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc b/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc
index 6c750a4..4667d59 100644
--- a/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc
+++ b/chrome/browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc
@@ -4,27 +4,40 @@
 
 #include "chrome/browser/previews/previews_lite_page_url_loader_interceptor.h"
 
+#include <map>
 #include <memory>
 #include <string>
 
 #include "base/bind.h"
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
-#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
+#include "base/test/task_environment.h"
+#include "chrome/browser/previews/previews_lite_page_decider.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "components/previews/core/previews_features.h"
+#include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/url_loader_request_interceptor.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/common/resource_type.h"
 #include "content/public/test/browser_task_environment.h"
 #include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
 #include "net/url_request/url_request_status.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
 
 // TODO(crbug.com/961073): Fix memory leaks in tests and re-enable on LSAN.
 #ifdef LEAK_SANITIZER
@@ -144,9 +157,8 @@
   request.resource_type = static_cast<int>(content::ResourceType::kMainFrame);
   request.method = "GET";
 
-  SetFakeResponse(
-      PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(request.url),
-      "Fake Body", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+  SetFakeResponse(GetLitePageRedirectURLForURL(request.url), "Fake Body",
+                  net::HTTP_OK, net::URLRequestStatus::SUCCESS);
   SetProbeResponse(request.url.GetOrigin(), net::HTTP_OK,
                    net::URLRequestStatus::SUCCESS);
 
@@ -175,9 +187,8 @@
   request.resource_type = static_cast<int>(content::ResourceType::kMainFrame);
   request.method = "GET";
 
-  SetFakeResponse(
-      PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(request.url),
-      "Fake Body", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+  SetFakeResponse(GetLitePageRedirectURLForURL(request.url), "Fake Body",
+                  net::HTTP_OK, net::URLRequestStatus::SUCCESS);
   SetProbeResponse(request.url.GetOrigin(), net::HTTP_OK,
                    net::URLRequestStatus::FAILED);
 
@@ -203,10 +214,8 @@
   request.resource_type = static_cast<int>(content::ResourceType::kMainFrame);
   request.method = "GET";
   request.previews_state = content::LITE_PAGE_REDIRECT_ON;
-  SetFakeResponse(
-      PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(request.url),
-      "Fake Body", net::HTTP_TEMPORARY_REDIRECT,
-      net::URLRequestStatus::SUCCESS);
+  SetFakeResponse(GetLitePageRedirectURLForURL(request.url), "Fake Body",
+                  net::HTTP_TEMPORARY_REDIRECT, net::URLRequestStatus::SUCCESS);
   SetProbeResponse(request.url.GetOrigin(), net::HTTP_OK,
                    net::URLRequestStatus::SUCCESS);
 
@@ -230,10 +239,9 @@
   request.resource_type = static_cast<int>(content::ResourceType::kMainFrame);
   request.method = "GET";
   request.previews_state = content::LITE_PAGE_REDIRECT_ON;
-  SetFakeResponse(
-      PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(request.url),
-      "Fake Body", net::HTTP_SERVICE_UNAVAILABLE,
-      net::URLRequestStatus::SUCCESS);
+  SetFakeResponse(GetLitePageRedirectURLForURL(request.url), "Fake Body",
+                  net::HTTP_SERVICE_UNAVAILABLE,
+                  net::URLRequestStatus::SUCCESS);
   SetProbeResponse(request.url.GetOrigin(), net::HTTP_OK,
                    net::URLRequestStatus::SUCCESS);
 
@@ -258,9 +266,8 @@
   request.resource_type = static_cast<int>(content::ResourceType::kMainFrame);
   request.method = "GET";
   request.previews_state = content::LITE_PAGE_REDIRECT_ON;
-  SetFakeResponse(
-      PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(request.url),
-      "Fake Body", net::HTTP_FORBIDDEN, net::URLRequestStatus::SUCCESS);
+  SetFakeResponse(GetLitePageRedirectURLForURL(request.url), "Fake Body",
+                  net::HTTP_FORBIDDEN, net::URLRequestStatus::SUCCESS);
   SetProbeResponse(request.url.GetOrigin(), net::HTTP_OK,
                    net::URLRequestStatus::SUCCESS);
 
@@ -284,9 +291,8 @@
   request.resource_type = static_cast<int>(content::ResourceType::kMainFrame);
   request.method = "GET";
   request.previews_state = content::LITE_PAGE_REDIRECT_ON;
-  SetFakeResponse(
-      PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(request.url),
-      "Fake Body", net::HTTP_OK, net::URLRequestStatus::FAILED);
+  SetFakeResponse(GetLitePageRedirectURLForURL(request.url), "Fake Body",
+                  net::HTTP_OK, net::URLRequestStatus::FAILED);
   SetProbeResponse(request.url.GetOrigin(), net::HTTP_OK,
                    net::URLRequestStatus::SUCCESS);
 
@@ -303,6 +309,128 @@
   EXPECT_TRUE(callback_was_empty().value());
 }
 
+TEST(PreviewsGetLitePageRedirectURL, TestGetPreviewsURL) {
+  struct TestCase {
+    std::string previews_host;
+    std::string original_url;
+    std::string expected_previews_url;
+    std::string experiment_variation;
+    std::string experiment_cmd_line;
+  };
+  const TestCase kTestCases[]{
+      // Use https://play.golang.org/p/HUM2HxmUTOW to compute
+      // |expected_previews_url|.
+      {
+          "https://previews.host.com",
+          "https://original.host.com/path/path/path?query=yes",
+          "https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
+          "previews.host.com/p?u="
+          "https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
+          "",
+          "",
+      },
+      {
+          "https://previews.host.com",
+          "http://original.host.com/path/path/path?query=yes",
+          "https://6p7dar4ju6r4ynz7x3pucmlcltuqsf7z5auhvckzln7voglkt56q."
+          "previews.host.com/p?u="
+          "http%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
+          "",
+      },
+      {
+          "https://previews.host.com",
+          "https://original.host.com:1443/path/path/path?query=yes",
+          "https://mil6oxtqb4zpsbmutm4d7wrx5nlr6tzlxjp7y44u55zqhzsdzjpq."
+          "previews.host.com/p?u=https%3A%2F%2Foriginal.host.com%3A1443"
+          "%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
+          "",
+          "",
+      },
+      {
+          "https://previews.host.com:1443",
+          "http://original.host.com/path/path/path?query=yes",
+          "https://6p7dar4ju6r4ynz7x3pucmlcltuqsf7z5auhvckzln7voglkt56q."
+          "previews.host.com:1443/p?u="
+          "http%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
+          "",
+          "",
+      },
+      {
+          "https://previews.host.com:1443",
+          "https://original.host.com:1443/path/path/path?query=yes",
+          "https://mil6oxtqb4zpsbmutm4d7wrx5nlr6tzlxjp7y44u55zqhzsdzjpq."
+          "previews.host.com:1443/p?u=https%3A%2F%2Foriginal.host.com%3A1443"
+          "%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
+          "",
+          "",
+      },
+      {
+          "https://previews.host.com",
+          "https://original.host.com/path/path/path?query=yes#fragment",
+          "https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
+          "previews.host.com/p?u="
+          "https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes"
+          "#fragment",
+          "",
+          "",
+      },
+      {
+          "https://previews.host.com",
+          "https://original.host.com/path/path/path?query=yes",
+          "https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
+          "previews.host.com/p?u="
+          "https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes"
+          "&x=variation_experiment",
+          "variation_experiment",
+          "",
+      },
+      {
+          // Ensure that the command line experiment takes precedence over the
+          // one provided by variations.
+          "https://previews.host.com",
+          "https://original.host.com/path/path/path?query=yes",
+          "https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
+          "previews.host.com/p?u="
+          "https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes"
+          "&x=cmdline_experiment",
+          "variation_experiment",
+          "cmdline_experiment",
+      },
+      {
+          "https://previews.host.com",
+          "https://[::1]:12345",
+          "https://2ikmbopbfxagkb7uer2vgfxmbzu2vw4qq3d3ixe3h2hfhgcabvua."
+          "previews.host.com/p?u=https%3A%2F%2F%5B%3A%3A1%5D%3A12345%2F",
+          "",
+          "",
+      },
+  };
+
+  for (const TestCase& test_case : kTestCases) {
+    variations::testing::ClearAllVariationParams();
+
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        data_reduction_proxy::switches::kDataReductionProxyExperiment,
+        test_case.experiment_cmd_line);
+
+    base::test::ScopedFeatureList scoped_feature_list;
+    std::map<std::string, std::string> server_experiment_params;
+    server_experiment_params[data_reduction_proxy::params::
+                                 GetDataSaverServerExperimentsOptionName()] =
+        test_case.experiment_variation;
+
+    scoped_feature_list.InitWithFeaturesAndParameters(
+        {{data_reduction_proxy::features::kDataReductionProxyServerExperiments,
+          {server_experiment_params}},
+         {previews::features::kLitePageServerPreviews,
+          {{"previews_host", test_case.previews_host}}}},
+        {});
+
+    EXPECT_EQ(GetLitePageRedirectURLForURL(GURL(test_case.original_url)),
+              GURL(test_case.expected_previews_url));
+  }
+}
+
 }  // namespace
 
 }  // namespace previews
diff --git a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
index c2866ab..565fc31 100644
--- a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
+++ b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
 #include "chrome/browser/previews/previews_https_notification_infobar_decider.h"
-#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
+#include "chrome/browser/previews/previews_lite_page_url_loader_interceptor.h"
 #include "chrome/browser/previews/previews_service.h"
 #include "chrome/browser/previews/previews_service_factory.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
@@ -89,9 +89,7 @@
     previews_user_data->SetCommittedPreviewsType(previews_type);
   }
 
-  void SimulateWillProcessResponse() {
-    SimulateCommit();
-  }
+  void SimulateWillProcessResponse() { SimulateCommit(); }
 
   void SimulateCommit() {
     test_handle_->set_has_committed(true);
@@ -326,8 +324,7 @@
   base::RunLoop().RunUntilIdle();
 
   GURL original_url("https://porgs.com");
-  GURL previews_url =
-      PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(original_url);
+  GURL previews_url = previews::GetLitePageRedirectURLForURL(original_url);
   content::WebContentsTester::For(web_contents())
       ->NavigateAndCommit(previews_url);
 
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc
index 735c183..4db5771 100644
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc
+++ b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h"
 #include "chrome/browser/resource_coordinator/local_site_characteristics_data_writer.h"
 #include "chrome/browser/resource_coordinator/tab_manager_features.h"
-#include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/url_row.h"
 #include "url/gurl.h"
 
@@ -35,7 +34,7 @@
 
 LocalSiteCharacteristicsDataStore::LocalSiteCharacteristicsDataStore(
     Profile* profile)
-    : history_observer_(this), profile_(profile) {
+    : profile_(profile) {
   DCHECK(base::FeatureList::IsEnabled(features::kSiteCharacteristicsDatabase));
 
   database_ = std::make_unique<LevelDBSiteCharacteristicsDatabase>(
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.h
index 913a82f6..de68f331 100644
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.h
+++ b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.h
@@ -14,6 +14,7 @@
 #include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h"
 #include "chrome/browser/resource_coordinator/site_characteristics_data_store.h"
 #include "chrome/browser/resource_coordinator/site_characteristics_data_writer.h"
+#include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_service_observer.h"
 
 class Profile;
@@ -97,8 +98,8 @@
   // pointer.
   LocalSiteCharacteristicsMap origin_data_map_;
 
-  ScopedObserver<history::HistoryService, LocalSiteCharacteristicsDataStore>
-      history_observer_;
+  ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
+      history_observer_{this};
 
   std::unique_ptr<LocalSiteCharacteristicsDatabase> database_;
 
diff --git a/chrome/browser/resources/bookmarks/bookmarks.html b/chrome/browser/resources/bookmarks/bookmarks.html
index 077405e..5e8532d2 100644
--- a/chrome/browser/resources/bookmarks/bookmarks.html
+++ b/chrome/browser/resources/bookmarks/bookmarks.html
@@ -1,5 +1,6 @@
 <!doctype html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}" class="loading">
+<html dir="$i18n{textdirection}" lang="$i18n{language}" class="loading"
+    $i18n{a11yenhanced}>
 <head>
   <meta charset="utf8">
   <title>$i18n{title}</title>
diff --git a/chrome/browser/resources/bookmarks/item.html b/chrome/browser/resources/bookmarks/item.html
index 9191430e..6543b57 100644
--- a/chrome/browser/resources/bookmarks/item.html
+++ b/chrome/browser/resources/bookmarks/item.html
@@ -29,6 +29,10 @@
         background-color: var(--highlight-color);
       }
 
+      :host([is-selected-item_]) cr-icon-button {
+        --cr-icon-button-focus-color: var(--highlight-color);
+      }
+
       @media (prefers-color-scheme: dark) {
         :host([is-selected-item_]),
         :host([is-selected-item_]) .folder-icon {
@@ -103,7 +107,7 @@
     <div id="website-url" class="elided-text" title="[[item_.url]]">
       [[item_.url]]
     </div>
-    <cr-icon-button class$="[[crIcon_]]"
+    <cr-icon-button class="icon-more-vert"
         id="menuButton"
         tabindex="[[ironListTabIndex]]"
         title="$i18n{moreActionsButtonTitle}"
diff --git a/chrome/browser/resources/bookmarks/item.js b/chrome/browser/resources/bookmarks/item.js
index e2f0aa7..667e5de 100644
--- a/chrome/browser/resources/bookmarks/item.js
+++ b/chrome/browser/resources/bookmarks/item.js
@@ -17,11 +17,6 @@
 
     ironListTabIndex: Number,
 
-    crIcon_: {
-      type: String,
-      value: 'icon-more-vert',
-    },
-
     /** @private {BookmarkNode} */
     item_: {
       type: Object,
@@ -32,7 +27,6 @@
     isSelectedItem_: {
       type: Boolean,
       reflectToAttribute: true,
-      observer: 'onIsSelectedItemChanged_',
     },
 
     /** @private */
@@ -139,12 +133,6 @@
   },
 
   /** @private */
-  onIsSelectedItemChanged_: function() {
-    this.crIcon_ = this.isSelectedItem_ ? 'icon-more-vert-light-mode' :
-        'icon-more-vert';
-  },
-
-  /** @private */
   onItemIdChanged_: function() {
     // TODO(tsergeant): Add a histogram to measure whether this assertion fails
     // for real users.
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
index ddee7a20..972d1fb 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
@@ -1014,6 +1014,15 @@
           "keyCode": [65, 70]
         }
       }
+    },
+    {
+      "command": "readPhoneticPronunciation",
+      "sequence": {
+        "cvoxModifier": true,
+        "keys": {
+          "keyCode": [65, 67]
+        }
+      }
     }
   ]
 }
diff --git a/chrome/browser/resources/chromeos/chromevox/common/command_store.js b/chrome/browser/resources/chromeos/chromevox/common/command_store.js
index e0df7ed..957cbb8 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/command_store.js
+++ b/chrome/browser/resources/chromeos/chromevox/common/command_store.js
@@ -132,11 +132,8 @@
  * false.
  */
 cvox.CommandStore.CMD_WHITELIST = {
-  'toggleStickyMode': {
-    announce: false,
-    msgId: 'toggle_sticky_mode',
-    category: 'modifier_keys'
-  },
+  'toggleStickyMode':
+      {announce: false, msgId: 'toggle_sticky_mode', category: 'modifier_keys'},
   'passThroughMode': {
     announce: false,
     msgId: 'pass_through_key_description',
@@ -819,6 +816,12 @@
   'pauseAllMedia':
       {announce: false, msgId: 'pause_all_media', category: 'information'},
 
+  'readPhoneticPronunciation': {
+    announce: true,
+    msgId: 'read_phonetic_pronunciation',
+    category: 'information'
+  },
+
   // Scrolling actions.
   'scrollBackward': {msgId: 'action_scroll_backward_description'},
   'scrollForward': {msgId: 'action_scroll_forward_description'},
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
index bf2d058..97b10d5 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
@@ -1990,7 +1990,6 @@
       var button = document.getElementById('button');
       var expanded = false;
       button.addEventListener('click', function(e) {
-        console.error('Heelooo');
         if (expanded)
           button.setAttribute('aria-expanded', false);
         else
@@ -2018,3 +2017,47 @@
       .replay();
   });
 });
+
+TEST_F('ChromeVoxBackgroundTest', 'ReadPhoneticPronunciationTest', function() {
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(function() {/*!
+   <button>This is a button</button>
+   <input type="text"></input>
+  */}, function(root) {
+    root.find({role: RoleType.BUTTON}).focus();
+    mockFeedback.call(doCmd('readPhoneticPronunciation'))
+      .expectSpeech('T')
+      .expectSpeech('tango')
+      .expectSpeech('h')
+      .expectSpeech('hotel')
+      .expectSpeech('i')
+      .expectSpeech('india')
+      .expectSpeech('s')
+      .expectSpeech('sierra')
+      .call(doCmd('nextWord'))
+      .call(doCmd('readPhoneticPronunciation'))
+      .expectSpeech('i')
+      .expectSpeech('india')
+      .expectSpeech('s')
+      .expectSpeech('sierra')
+      .call(doCmd('nextWord'))
+      .call(doCmd('nextWord'))
+      .call(doCmd('readPhoneticPronunciation'))
+      .expectSpeech('b')
+      .expectSpeech('bravo')
+      .expectSpeech('u')
+      .expectSpeech('uniform')
+      .expectSpeech('t')
+      .expectSpeech('tango')
+      .expectSpeech('t')
+      .expectSpeech('tango')
+      .expectSpeech('o')
+      .expectSpeech('oscar')
+      .expectSpeech('n')
+      .expectSpeech('november')
+      .call(doCmd('nextEditText'))
+      .call(doCmd('readPhoneticPronunciation'))
+      .expectSpeech('No available text for this item');
+    mockFeedback.replay();
+  });
+});
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
index ce199d0..5a5c944 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
@@ -9,15 +9,16 @@
 goog.provide('CommandHandler');
 
 goog.require('ChromeVoxState');
+goog.require('Color');
 goog.require('CustomAutomationEvent');
 goog.require('LogStore');
 goog.require('Output');
+goog.require('PhoneticData');
 goog.require('TreeDumper');
 goog.require('cvox.ChromeVoxBackground');
 goog.require('cvox.ChromeVoxKbHandler');
 goog.require('cvox.ChromeVoxPrefs');
 goog.require('cvox.CommandStore');
-goog.require('Color');
 
 goog.scope(function() {
 var AutomationEvent = chrome.automation.AutomationEvent;
@@ -896,6 +897,65 @@
           .withQueueMode(cvox.QueueMode.CATEGORY_FLUSH)
           .go();
       return false;
+    case 'readPhoneticPronunciation':
+      // Get node info.
+      var node = ChromeVoxState.instance.currentRange.start.node;
+      var index = ChromeVoxState.instance.currentRange.start.index;
+      var text = node.name;
+      // If there is no text to speak, inform the user and return early.
+      if (!text) {
+        new Output()
+            .withString(Msgs.getMsg('empty_name'))
+            .withQueueMode(cvox.QueueMode.CATEGORY_FLUSH)
+            .go();
+        return false;
+      }
+
+      // Get word start and end indices.
+      var wordStarts, wordEnds;
+      if (node.role == RoleType.INLINE_TEXT_BOX) {
+        wordStarts = node.wordStarts;
+        wordEnds = node.wordEnds;
+      } else {
+        wordStarts = node.nonInlineTextWordStarts;
+        wordEnds = node.nonInlineTextWordEnds;
+      }
+      // Find the word we want to speak phonetically. If index === -1, then the
+      // index represents an entire node. If that is the case, we want to find
+      // the first word in the node's name. We do this by setting index to 0.
+      if (index === -1)
+        index = 0;
+      var word = '';
+      for (var z = 0; z < wordStarts.length; ++z) {
+        if (wordStarts[z] <= index && wordEnds[z] >= index) {
+          word = text.substring(wordStarts[z], wordEnds[z]);
+          break;
+        }
+      }
+
+      // Get unicode-aware array of characters.
+      var characterArray = [...word];
+      // We currently only load phonetic data for the browser UI language.
+      var language = chrome.i18n.getUILanguage();
+      for (var i = 0; i < characterArray.length; ++i) {
+        var character = characterArray[i];
+        var phoneticText =
+            PhoneticData.getPhoneticDisambiguation(language, character);
+        // Speak the character followed by its phonetic disambiguation, if it
+        // was found.
+        new Output()
+            .withString(character)
+            .withQueueMode(
+                i === 0 ? cvox.QueueMode.CATEGORY_FLUSH : cvox.QueueMode.QUEUE)
+            .go();
+        if (phoneticText) {
+          new Output()
+              .withString(phoneticText)
+              .withQueueMode(cvox.QueueMode.QUEUE)
+              .go();
+        }
+      }
+      return false;
     default:
       return true;
   }
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/phonetic_data.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/phonetic_data.js
index 55e6b91c..7bd54868 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/phonetic_data.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/phonetic_data.js
@@ -49,9 +49,10 @@
  * @return {string}
  */
 PhoneticData.getPhoneticDisambiguation = function(language, character) {
-  if (!language)
+  if (!language || !character)
     return '';
   language = language.toLowerCase();
+  character = character.toLowerCase();
   // If language isn't in the map, try stripping extra information, such as the
   // country and/or script codes (e.g. "en-us" or "zh-hant-hk") and use only the
   // language code to do a lookup.
diff --git a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
index f9a2d552..8665039 100644
--- a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
+++ b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -3709,6 +3709,12 @@
       <message desc="Appends language in front of content." name="IDS_CHROMEVOX_LANGUAGE_SWITCH">
         <ph name="language">$1<ex>English</ex></ph>: <ph name="content">$2<ex>This is example content</ex></ph>
       </message>
+      <message desc="The description of the readPhoneticPronunciation key. Displayed in the ChromeVox menu." name="IDS_CHROMEVOX_READ_PHONETIC_PRONUNCIATION">
+        Announce phonetic pronunciation for word
+      </message>
+      <message desc="Spoken to inform the user that the node's name is empty" name="IDS_CHROMEVOX_EMPTY_NAME">
+        No available text for this item
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/browser/resources/chromeos/network_ui/network_ui.js b/chrome/browser/resources/chromeos/network_ui/network_ui.js
index 25e9371b..f2178ab 100644
--- a/chrome/browser/resources/chromeos/network_ui/network_ui.js
+++ b/chrome/browser/resources/chromeos/network_ui/network_ui.js
@@ -388,7 +388,9 @@
    */
   const handleDeviceDetail = function(state, selectedId, detailCell) {
     if (selectedId == 'shill') {
-      chrome.send('getShillDeviceProperties', [state.type]);
+      chrome.send(
+          'getShillDeviceProperties',
+          [OncMojo.getNetworkTypeString(state.type)]);
     } else {
       showDetail(detailCell, state);
     }
diff --git a/chrome/browser/resources/downloads/downloads.html b/chrome/browser/resources/downloads/downloads.html
index ad88272..5e09e70 100644
--- a/chrome/browser/resources/downloads/downloads.html
+++ b/chrome/browser/resources/downloads/downloads.html
@@ -1,5 +1,6 @@
 <!doctype html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}" class="loading">
+<html dir="$i18n{textdirection}" lang="$i18n{language}" class="loading"
+    $i18n{a11yenhanced}>
 <head>
   <meta charset="utf-8">
 <if expr="not optimize_webui">
diff --git a/chrome/browser/resources/extensions/extensions.html b/chrome/browser/resources/extensions/extensions.html
index 83ea4fe72..c94fc08 100644
--- a/chrome/browser/resources/extensions/extensions.html
+++ b/chrome/browser/resources/extensions/extensions.html
@@ -1,6 +1,6 @@
 <!doctype html>
 <html dir="$i18n{textdirection}" lang="$i18n{language}"
-    class="loading $i18n{loadTimeClasses}">
+    class="loading $i18n{loadTimeClasses}" $i18n{a11yenhanced}>
 <head>
   <meta charset="utf8">
   <title>$i18n{title}</title>
diff --git a/chrome/browser/resources/history/history.html b/chrome/browser/resources/history/history.html
index af0c23e..5fd5f3c 100644
--- a/chrome/browser/resources/history/history.html
+++ b/chrome/browser/resources/history/history.html
@@ -1,5 +1,5 @@
 <!doctype html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<html dir="$i18n{textdirection}" lang="$i18n{language}" $i18n{a11yenhanced}>
 <head>
   <meta charset="utf8">
   <title>$i18n{title}</title>
diff --git a/chrome/browser/resources/history/shared_vars.html b/chrome/browser/resources/history/shared_vars.html
index ba04f03..ade6397 100644
--- a/chrome/browser/resources/history/shared_vars.html
+++ b/chrome/browser/resources/history/shared_vars.html
@@ -23,8 +23,6 @@
     --history-item-time-color: #646464;
     --interactive-color: var(--google-blue-500);
     --item-height: 44px;
-    --iron-icon-height: var(--cr-icon-size);
-    --iron-icon-width: var(--cr-icon-size);
     --separator-color: rgba(0, 0, 0, 0.08);
     --side-bar-width: 256px;
     --sidebar-footer-text-color: var(--google-grey-refresh-700);
diff --git a/chrome/browser/resources/pdf/elements/viewer-zoom-button.html b/chrome/browser/resources/pdf/elements/viewer-zoom-button.html
index 000d098..b104fb7 100644
--- a/chrome/browser/resources/pdf/elements/viewer-zoom-button.html
+++ b/chrome/browser/resources/pdf/elements/viewer-zoom-button.html
@@ -31,11 +31,12 @@
       }
 
       cr-icon-button {
-        --cr-icon-button-size: 36px;
+        --cr-icon-button-color: var(--paper-grey-700);
         --cr-icon-button-icon-size: 20px;
+        --cr-icon-button-size: 36px;
+        --cr-icon-button-focus-color: rgb(242, 242, 242);
         background-color: rgb(242, 242, 242);
         border-radius: 50%;
-        color: var(--paper-grey-700);
         overflow: visible;
         @apply --shadow-elevation-2dp;
       }
@@ -49,9 +50,10 @@
       }
 
       :host([new-print-preview]) cr-icon-button {
+        --cr-icon-button-color: white;
+        --cr-icon-button-focus-color: var(--google-grey-600);
         --cr-icon-button-size: 32px;
         background-color: var(--google-grey-600);
-        color: white;
       }
 
       @media (prefers-color-scheme: light) {
@@ -62,8 +64,9 @@
 
       @media (prefers-color-scheme: dark) {
         :host([new-print-preview]) cr-icon-button {
+          --cr-icon-button-color: var(--google-grey-200);
+          --cr-icon-button-focus-color: var(--google-grey-900);
           background-color: var(--google-grey-900);
-          color: var(--google-grey-200);
         }
       }
 
diff --git a/chrome/browser/resources/pdf/index.html b/chrome/browser/resources/pdf/index.html
index 00d9643..3ddf279 100644
--- a/chrome/browser/resources/pdf/index.html
+++ b/chrome/browser/resources/pdf/index.html
@@ -1,5 +1,5 @@
 <!doctype html>
-<html>
+<html $i18n{a11yenhanced}>
 <head>
   <meta charset="utf-8">
   <script src="chrome://resources/polymer/v1_0/html-imports/html-imports.min.js">
diff --git a/chrome/browser/resources/print_preview/print_preview.html b/chrome/browser/resources/print_preview/print_preview.html
index d0e4cf1..b29f602 100644
--- a/chrome/browser/resources/print_preview/print_preview.html
+++ b/chrome/browser/resources/print_preview/print_preview.html
@@ -1,6 +1,6 @@
 <!doctype html>
 <html dir="$i18n{textdirection}" lang="$i18n{language}" class="loading"
-    $i18n{newprintpreviewlayout}>
+    $i18n{newprintpreviewlayout} $i18n{a11yenhanced}>
 <head>
   <meta charset="utf-8">
 <if expr="not optimize_webui">
diff --git a/chrome/browser/resources/print_preview/ui/link_container.html b/chrome/browser/resources/print_preview/ui/link_container.html
index 3268cb4..2f9274b 100644
--- a/chrome/browser/resources/print_preview/ui/link_container.html
+++ b/chrome/browser/resources/print_preview/ui/link_container.html
@@ -62,7 +62,7 @@
          hidden$="[[!shouldShowSystemDialogLink_]]"
          on-click="onSystemDialogClick_">
       <div class="label">$i18n{systemDialogOption}</div>
-      <cr-icon-button actionable class="icon-external"
+      <cr-icon-button class="icon-external"
           hidden$="[[openingSystemDialog_]]"
           disabled="[[systemDialogLinkDisabled_]]"
           aria-label="$i18n{systemDialogOption}"></cr-icon-button>
@@ -73,7 +73,7 @@
     <div class="link" id="openPdfInPreviewLink" actionable$="[[!disabled]]"
         on-click="onOpenInPreviewClick_">
       <div class="label">$i18n{openPdfInPreviewOption}</div>
-      <cr-icon-button actionable class="icon-external"
+      <cr-icon-button class="icon-external"
           hidden$="[[openingInPreview_]]" disabled="[[disabled]]"
           aria-label="$i18n{openPdfInPreviewOption}"></cr-icon-button>
       <div id="openPdfInPreviewThrobber" hidden$="[[!openingInPreview_]]"
diff --git a/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html b/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html
index fd217ed..cf7801b 100644
--- a/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html
+++ b/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html
@@ -22,6 +22,10 @@
     --destination-item-height: 32px;
     --preview-area-background-color: var(--google-grey-200);
     --preview-area-background-color-new: var(--google-grey-refresh-300);
+
+    --iron-icon-fill-color: var(--google-grey-refresh-700);
+    --iron-icon-height: var(--cr-icon-size);
+    --iron-icon-width: var(--cr-icon-size);
   }
 
   @media (prefers-color-scheme: dark) {
@@ -32,6 +36,7 @@
         opacity: var(--cr-disabled-opacity);
       }
       --print-preview-settings-border: var(--cr-separator-line);
+      --iron-icon-fill-color: var(--google-grey-refresh-500);
     }
   }
 </style>
diff --git a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
index 92bf3fb3..61c356c 100644
--- a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
@@ -241,7 +241,7 @@
             $i18n{a11yWebStore}
           </div>
         </div>
-        <cr-icon-button actionable class="icon-external"
+        <cr-icon-button class="icon-external"
             aria-label="$i18n{additionalFeaturesTitle}"
             aria-describedby="moreFeaturesSecondary"></cr-icon-button>
       </a>
diff --git a/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html b/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html
index b7df5505..601a93b 100644
--- a/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html
+++ b/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html
@@ -16,10 +16,8 @@
     <style include="settings-shared"></style>
 
     <template is="dom-if" if="[[androidAppsInfo.settingsAppAvailable]]" restamp>
-      <cr-link-row id="manageApps" icon-class="icon-external"
-          label="$i18n{androidAppsManageApps}"
-          on-click="onManageAndroidAppsTap_" external>
-      </cr-link-row>
+      <cr-link-row id="manageApps" label="$i18n{androidAppsManageApps}"
+          on-click="onManageAndroidAppsTap_" external></cr-link-row>
     </template>
 
     <template is="dom-if" if="[[allowRemove_(prefs.arc.enabled.*)]]">
diff --git a/chrome/browser/resources/settings/autofill_page/autofill_section.html b/chrome/browser/resources/settings/autofill_page/autofill_section.html
index d781462..e5dc1e7a 100644
--- a/chrome/browser/resources/settings/autofill_page/autofill_section.html
+++ b/chrome/browser/resources/settings/autofill_page/autofill_section.html
@@ -73,7 +73,7 @@
               </cr-icon-button>
             </template>
             <template is="dom-if" if="[[!item.metadata.isLocal]]">
-              <cr-icon-button actionable class="icon-external"
+              <cr-icon-button class="icon-external"
                   on-click="onRemoteEditAddressTap_"></cr-icon-button>
             </template>
           </div>
diff --git a/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html b/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html
index 4f62b13e..1d3bbd05 100644
--- a/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html
+++ b/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html
@@ -47,9 +47,8 @@
           </cr-icon-button>
         </template>
         <template is="dom-if" if="[[!showDots_(creditCard.metadata)]]">
-          <cr-icon-button actionable class="icon-external"
-              id="remoteCreditCardLink" on-click="onRemoteEditClick_">
-          </cr-icon-button>
+          <cr-icon-button class="icon-external" id="remoteCreditCardLink"
+              on-click="onRemoteEditClick_"></cr-icon-button>
         </template>
       </div>
     </div>
diff --git a/chrome/browser/resources/settings/autofill_page/payments_section.html b/chrome/browser/resources/settings/autofill_page/payments_section.html
index 5836b23..1de86d2a 100644
--- a/chrome/browser/resources/settings/autofill_page/payments_section.html
+++ b/chrome/browser/resources/settings/autofill_page/payments_section.html
@@ -42,6 +42,19 @@
         pref="{{prefs.autofill.credit_card_enabled}}">
     </settings-toggle-button>
     <template is="dom-if"
+        if="[[isUserFIDOVerifiable_(
+            prefs.autofill.credit_card_enabled.value)]]">
+      <settings-toggle-button
+          class="settings-box first"
+          id="autofillCreditCardFIDOAuthToggle"
+          aria-label="$i18n{creditCards}" no-extension-indicator
+          label="$i18n{enableCreditCardFIDOAuthLabel}"
+          sub-label="$i18n{enableCreditCardFIDOAuthSublabel}"
+          pref="{{prefs.autofill.credit_card_fido_auth_enabled}}"
+          on-click="setFIDOAuthenticationEnabledState_">
+      </settings-toggle-button>
+    </template>
+    <template is="dom-if"
         if="[[prefs.autofill.credit_card_enabled.extensionId]]">
       <div class="settings-box continuation">
         <extension-controlled-indicator class="start"
diff --git a/chrome/browser/resources/settings/autofill_page/payments_section.js b/chrome/browser/resources/settings/autofill_page/payments_section.js
index 03d51d2..ef245525 100644
--- a/chrome/browser/resources/settings/autofill_page/payments_section.js
+++ b/chrome/browser/resources/settings/autofill_page/payments_section.js
@@ -51,6 +51,11 @@
    * Logs that the server cards edit link was clicked.
    */
   logServerCardLinkClicked() {}
+
+  /**
+   * Enables FIDO authentication for card unmasking.
+   */
+  setCreditCardFIDOAuthEnabledState(enabled) {}
 }
 
 /** @typedef {chrome.autofillPrivate.CreditCardEntry} */
@@ -100,6 +105,11 @@
   logServerCardLinkClicked() {
     chrome.autofillPrivate.logServerCardLinkClicked();
   }
+
+  /** @override */
+  setCreditCardFIDOAuthEnabledState(enabled) {
+    chrome.autofillPrivate.setCreditCardFIDOAuthEnabledState(enabled);
+  }
 }
 
 cr.addSingletonGetter(PaymentsManagerImpl);
@@ -126,6 +136,18 @@
     },
 
     /**
+     * Set to true if user can be verified through FIDO authentication.
+     * @private
+     */
+    userIsFIDOVerifiable_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('userIsFIDOVerifiable');
+      },
+      readOnly: true,
+    },
+
+    /**
      * The model for any credit card related action menus or dialogs.
      * @private {?chrome.autofillPrivate.CreditCardEntry}
      */
@@ -311,6 +333,25 @@
   },
 
   /**
+   * @param {boolean} creditCardEnabled
+   * @return {boolean} Whether or not the user is verifiable through FIDO
+   *     authentication.
+   * @private
+   */
+  isUserFIDOVerifiable_: function(creditCardEnabled) {
+    return creditCardEnabled && this.userIsFIDOVerifiable_;
+  },
+
+  /**
+   * Listens for the enable-authentication event, and calls the private API.
+   * @private
+   */
+  setFIDOAuthenticationEnabledState_: function() {
+    this.paymentsManager_.setCreditCardFIDOAuthEnabledState(
+        this.$$('#autofillCreditCardFIDOAuthToggle').checked);
+  },
+
+  /**
    * @param {!Array<!PaymentsManager.CreditCardEntry>} creditCards
    * @param {boolean} creditCardEnabled
    * @return {boolean} Whether to show the migration button.
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index 51b1cf1..6e3caabc 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -230,7 +230,7 @@
             <div class="start settings-box-text">
               $i18n{personalizeGoogleServicesTitle}
             </div>
-            <cr-icon-button actionable class="icon-external"></cr-icon-button>
+            <cr-icon-button class="icon-external"></cr-icon-button>
           </a>
 
           <a id="syncDashboardLink"
@@ -241,7 +241,7 @@
             <div class="start settings-box-text">
               $i18n{manageSyncedDataTitle}
             </div>
-            <cr-icon-button actionable class="icon-external"></cr-icon-button>
+            <cr-icon-button class="icon-external"></cr-icon-button>
           </a>
 
           <div id="encryptionDescription"
diff --git a/chrome/browser/resources/settings/printing_page/cloud_printers.html b/chrome/browser/resources/settings/printing_page/cloud_printers.html
index 21bcb4b..fb6f35b 100644
--- a/chrome/browser/resources/settings/printing_page/cloud_printers.html
+++ b/chrome/browser/resources/settings/printing_page/cloud_printers.html
@@ -24,7 +24,7 @@
       <div class="start">
         $i18n{printingManageCloudPrintDevices}
       </div>
-      <cr-icon-button actionable class="icon-external"
+      <cr-icon-button class="icon-external"
           aria-label="$i18n{printingManageCloudPrintDevices}"></cr-icon-button>
     </a>
   </template>
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index 11c6038..16dd75c 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -445,7 +445,7 @@
               target="_blank"
               href="https://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager07.html">
             <div class="start">$i18n{adobeFlashStorage}</div>
-            <cr-icon-button actionable class="icon-external"
+            <cr-icon-button class="icon-external"
                 aria-label="$i18n{adobeFlashStorage}"></cr-icon-button>
           </a>
 </if>
diff --git a/chrome/browser/resources/settings/settings.html b/chrome/browser/resources/settings/settings.html
index 7842e79..55f5804 100644
--- a/chrome/browser/resources/settings/settings.html
+++ b/chrome/browser/resources/settings/settings.html
@@ -1,5 +1,6 @@
 <!doctype html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}" class="loading">
+<html dir="$i18n{textdirection}" lang="$i18n{language}" class="loading"
+    $i18n{a11yenhanced}>
 <head>
   <meta charset="utf-8">
   <title>$i18n{settings}</title>
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.html b/chrome/browser/resources/settings/settings_menu/settings_menu.html
index 0bcf848..5f7a88b 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.html
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.html
@@ -231,7 +231,7 @@
           on-click="onExtensionsLinkClick_"
           title="$i18n{extensionsLinkTooltip}">
         <span>$i18n{extensionsPageTitle}</span>
-        <iron-icon class="cr-icon icon-external" actionable></iron-icon>
+        <iron-icon icon="cr:open-in-new"></iron-icon>
       </a>
       <a id="about-menu" href="/help">$i18n{aboutPageTitle}</a>
     </iron-selector>
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
index b65889b..57afb03b 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
@@ -193,7 +193,6 @@
       <cr-link-row
           class="hr two-line"
           data-route="SITE_SETTINGS_SERIAL_PORTS"
-          icon-class="subpage-arrow"
           id="serial-ports"
           label="$i18n{siteSettingsSerialPorts}"
           on-click="onTapNavigate_"
@@ -208,7 +207,7 @@
     <template is="dom-if" if="[[enableNativeFileSystemWriteContentSetting_]]">
       <cr-link-row class="hr two-line"
           data-route="SITE_SETTINGS_NATIVE_FILE_SYSTEM_WRITE"
-          icon-class="subpage-arrow" id="native-file-system-write"
+          id="native-file-system-write"
           label="$i18n{siteSettingsNativeFileSystemWrite}"
           on-click="onTapNavigate_" start-icon="settings:save-original"
           sub-label="[[defaultSettingLabel_(
diff --git a/chrome/browser/resources/tab_strip/tab.html b/chrome/browser/resources/tab_strip/tab.html
index 8e3c886..2cd9c6d 100644
--- a/chrome/browser/resources/tab_strip/tab.html
+++ b/chrome/browser/resources/tab_strip/tab.html
@@ -38,13 +38,47 @@
     padding-inline-start: 12px;
   }
 
-  #favicon {
+  #faviconContainer {
     flex-shrink: 0;
     height: 16px;
     margin-inline-end: 8px;
+    position: relative;
     width: 16px;
   }
 
+  #loading,
+  #favicon {
+    height: 100%;
+    position: absolute;
+    width: 100%;
+  }
+
+  #loading {
+    -webkit-mask-image: url(chrome://resources/images/throbber_small.svg);
+    background-color: var(--tabstrip-primary-text-color);
+    display: none;
+  }
+
+  #favicon {
+    background-size: contain;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    transition: border-radius 250ms;
+  }
+
+  :host([loading]) #loading,
+  :host([loading]) #favicon {
+    display: block;
+  }
+
+  :host([loading]) #favicon {
+    border-radius: 50%;
+    height: calc(100% - 6px);
+    overflow: hidden;
+    width: calc(100% - 6px);
+  }
+
   #titleText {
     font-size: 100%;
     font-weight: normal;
@@ -135,7 +169,10 @@
 
 <div id="dragImage">
   <header id="title">
-    <span id="favicon"></span>
+    <div id="faviconContainer">
+      <div id="loading"></div>
+      <div id="favicon"></div>
+    </div>
     <h2 id="titleText"></h2>
     <button id="close">
       <span id="closeIcon"></span>
diff --git a/chrome/browser/resources/tab_strip/tab.js b/chrome/browser/resources/tab_strip/tab.js
index 77bbc41d..e302a2eb2 100644
--- a/chrome/browser/resources/tab_strip/tab.js
+++ b/chrome/browser/resources/tab_strip/tab.js
@@ -9,6 +9,12 @@
 
 export const DEFAULT_ANIMATION_DURATION = 125;
 
+/** @const @enum {string} */
+const STATUS = {
+  LOADING: 'loading',
+  COMPLETE: 'complete',
+};
+
 export class TabElement extends CustomElement {
   static get template() {
     return `{__html_template__}`;
@@ -65,18 +71,29 @@
   /** @param {!Tab} tab */
   set tab(tab) {
     this.toggleAttribute('active', tab.active);
+    // TODO(johntlee): Update loading status to also be based on whether
+    // the tab is navigating to a new document.
+    this.toggleAttribute('loading', tab.status === STATUS.LOADING);
     this.toggleAttribute('pinned', tab.pinned);
 
     if (!this.tab_ || this.tab_.title !== tab.title) {
       this.titleTextEl_.textContent = tab.title;
     }
 
-    if (tab.favIconUrl &&
-        (!this.tab_ || this.tab_.favIconUrl !== tab.favIconUrl)) {
-      this.faviconEl_.style.backgroundImage = getFavicon(tab.favIconUrl);
-    } else if (!this.tab_ || this.tab_.url !== tab.url) {
-      this.faviconEl_.style.backgroundImage =
-          getFaviconForPageURL(tab.url, false);
+    if (tab.favIconUrl) {
+      if (!this.tab_ || this.tab_.favIconUrl !== tab.favIconUrl) {
+        this.faviconEl_.style.backgroundImage = getFavicon(tab.favIconUrl);
+      }
+    } else {
+      if (tab.status === STATUS.COMPLETE) {
+        // If the tab has finished loading and there still is no favicon,
+        // fallback to a favicon generated by the page URL. This guarantees
+        // there is always some favicon, even if it's the default icon.
+        this.faviconEl_.style.backgroundImage =
+            getFaviconForPageURL(tab.url, false);
+      } else {
+        this.faviconEl_.style.backgroundImage = 'none';
+      }
     }
 
     // Expose the ID to an attribute to allow easy querySelector use
diff --git a/chrome/browser/resources/tab_strip/tab_list.js b/chrome/browser/resources/tab_strip/tab_list.js
index dd05244d..3b98eaa 100644
--- a/chrome/browser/resources/tab_strip/tab_list.js
+++ b/chrome/browser/resources/tab_strip/tab_list.js
@@ -369,6 +369,15 @@
 
     const tabElement = this.findTabElement_(tabId);
     if (tabElement) {
+      // While a tab may go in and out of a loading state, the Extensions API
+      // only dispatches |onUpdated| events up until the first time a tab
+      // reaches a non-loading state. Therefore, the UI should ignore any
+      // updates to a |status| of a tab unless the API specifically has
+      // dispatched an event indicating the status has changed.
+      if (!changeInfo.status) {
+        tab.status = tabElement.tab.status;
+      }
+
       tabElement.tab = tab;
 
       if (changeInfo.pinned !== undefined) {
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
index 9f4ea6a..1270337 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
@@ -26,7 +26,6 @@
 #include "chrome/common/safe_browsing/download_type_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "components/history/core/browser/download_constants.h"
-#include "components/history/core/browser/history_service.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/language/core/common/locale_util.h"
 #include "components/prefs/pref_service.h"
@@ -257,15 +256,13 @@
   return finder;
 }
 
-LastDownloadFinder::LastDownloadFinder() : history_service_observer_(this) {}
+LastDownloadFinder::LastDownloadFinder() = default;
 
 LastDownloadFinder::LastDownloadFinder(
     const DownloadDetailsGetter& download_details_getter,
     const std::vector<Profile*>& profiles,
     const LastDownloadCallback& callback)
-    : download_details_getter_(download_details_getter),
-      callback_(callback),
-      history_service_observer_(this) {
+    : download_details_getter_(download_details_getter), callback_(callback) {
   // Observe profile lifecycle events so that the finder can begin or abandon
   // the search in profiles while it is running.
   notification_registrar_.Add(this,
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.h b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.h
index bf06622..9b19395 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.h
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.h
@@ -16,6 +16,7 @@
 #include "base/scoped_observer.h"
 #include "chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.h"
 #include "components/history/core/browser/download_row.h"
+#include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_service_observer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
@@ -27,10 +28,6 @@
 class NotificationSource;
 }
 
-namespace history {
-class HistoryService;
-}
-
 namespace safe_browsing {
 
 class ClientIncidentReport_DownloadDetails;
@@ -149,7 +146,7 @@
 
   // HistoryServiceObserver
   ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
-      history_service_observer_;
+      history_service_observer_{this};
 
   // A factory for asynchronous operations on profiles' HistoryService.
   base::WeakPtrFactory<LastDownloadFinder> weak_ptr_factory_{this};
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
index 20f17e9..8360f38 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/ui/page_info/page_info_ui.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
@@ -107,8 +106,7 @@
     : content::WebContentsObserver(contents),
       manager_(manager),
       has_user_gesture_(false),
-      last_user_gesture_timestamp_(base::Time()),
-      content_settings_observer_(this) {
+      last_user_gesture_timestamp_(base::Time()) {
   content_settings_observer_.Add(HostContentSettingsMapFactory::GetForProfile(
       Profile::FromBrowserContext(web_contents()->GetBrowserContext())));
 }
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
index d3bcfbe0..45f3542 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
@@ -8,6 +8,7 @@
 #include "base/scoped_observer.h"
 #include "base/supports_user_data.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/safe_browsing/proto/csd.pb.h"
 #include "components/sessions/core/session_id.h"
@@ -18,8 +19,6 @@
 class NavigationHandle;
 }
 
-class HostContentSettingsMap;
-
 namespace safe_browsing {
 class SafeBrowsingNavigationObserverManager;
 
@@ -159,7 +158,7 @@
 
   base::Time last_user_gesture_timestamp_;
   ScopedObserver<HostContentSettingsMap, content_settings::Observer>
-      content_settings_observer_;
+      content_settings_observer_{this};
 
   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingNavigationObserver);
 };
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index c04aa723..49e945e 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/image_fetcher/image_decoder_impl.h"
 #include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/background/ntp_background_service.h"
 #include "chrome/browser/search/background/ntp_background_service_factory.h"
 #include "chrome/browser/search/chrome_colors/chrome_colors_service.h"
 #include "chrome/browser/search/instant_io_context.h"
@@ -179,8 +178,6 @@
     : profile_(profile),
       most_visited_info_(std::make_unique<InstantMostVisitedInfo>()),
       pref_service_(profile_->GetPrefs()),
-      theme_observer_(this),
-      background_service_observer_(this),
       native_theme_(ui::NativeTheme::GetInstanceForNativeUi()),
       background_updated_timestamp_(base::TimeTicks::Now()),
       clock_(base::DefaultClock::GetInstance()) {
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h
index 8bbfbf6..a2de87f 100644
--- a/chrome/browser/search/instant_service.h
+++ b/chrome/browser/search/instant_service.h
@@ -17,6 +17,7 @@
 #include "base/observer_list.h"
 #include "base/optional.h"
 #include "build/build_config.h"
+#include "chrome/browser/search/background/ntp_background_service.h"
 #include "chrome/browser/search/background/ntp_background_service_observer.h"
 #include "chrome/browser/search/search_provider_observer.h"
 #include "components/history/core/browser/history_types.h"
@@ -38,7 +39,6 @@
 
 class InstantIOContext;
 class InstantServiceObserver;
-class NtpBackgroundService;
 class Profile;
 struct CollectionImage;
 struct InstantMostVisitedInfo;
@@ -314,10 +314,11 @@
 
   PrefService* pref_service_;
 
-  ScopedObserver<ui::NativeTheme, InstantService> theme_observer_;
+  ScopedObserver<ui::NativeTheme, ui::NativeThemeObserver> theme_observer_{
+      this};
 
   ScopedObserver<NtpBackgroundService, NtpBackgroundServiceObserver>
-      background_service_observer_;
+      background_service_observer_{this};
 
   ui::NativeTheme* native_theme_;
 
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc
index 1a7423b..0890a24 100644
--- a/chrome/browser/search/local_ntp_source.cc
+++ b/chrome/browser/search/local_ntp_source.cc
@@ -27,20 +27,17 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/background/ntp_background_data.h"
-#include "chrome/browser/search/background/ntp_background_service.h"
 #include "chrome/browser/search/background/ntp_background_service_factory.h"
 #include "chrome/browser/search/instant_io_context.h"
 #include "chrome/browser/search/local_ntp_js_integrity.h"
 #include "chrome/browser/search/ntp_features.h"
 #include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
-#include "chrome/browser/search/one_google_bar/one_google_bar_service.h"
 #include "chrome/browser/search/one_google_bar/one_google_bar_service_factory.h"
 #include "chrome/browser/search/promos/promo_data.h"
 #include "chrome/browser/search/promos/promo_service.h"
 #include "chrome/browser/search/promos/promo_service_factory.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/search/search_suggest/search_suggest_data.h"
-#include "chrome/browser/search/search_suggest/search_suggest_service.h"
 #include "chrome/browser/search/search_suggest/search_suggest_service_factory.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/search_provider_logos/logo_service_factory.h"
@@ -771,15 +768,11 @@
     : profile_(profile),
       ntp_background_service_(
           NtpBackgroundServiceFactory::GetForProfile(profile_)),
-      ntp_background_service_observer_(this),
       one_google_bar_service_(
           OneGoogleBarServiceFactory::GetForProfile(profile_)),
-      one_google_bar_service_observer_(this),
       promo_service_(PromoServiceFactory::GetForProfile(profile_)),
-      promo_service_observer_(this),
       search_suggest_service_(
           SearchSuggestServiceFactory::GetForProfile(profile_)),
-      search_suggest_service_observer_(this),
       logo_service_(nullptr) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
diff --git a/chrome/browser/search/local_ntp_source.h b/chrome/browser/search/local_ntp_source.h
index 91c6264..ad6ecead 100644
--- a/chrome/browser/search/local_ntp_source.h
+++ b/chrome/browser/search/local_ntp_source.h
@@ -15,9 +15,13 @@
 #include "base/scoped_observer.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "chrome/browser/search/background/ntp_background_service.h"
 #include "chrome/browser/search/background/ntp_background_service_observer.h"
+#include "chrome/browser/search/one_google_bar/one_google_bar_service.h"
 #include "chrome/browser/search/one_google_bar/one_google_bar_service_observer.h"
+#include "chrome/browser/search/promos/promo_service.h"
 #include "chrome/browser/search/promos/promo_service_observer.h"
+#include "chrome/browser/search/search_suggest/search_suggest_service.h"
 #include "chrome/browser/search/search_suggest/search_suggest_service_observer.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "content/public/browser/url_data_source.h"
@@ -28,10 +32,6 @@
 
 struct OneGoogleBarData;
 struct PromoData;
-class NtpBackgroundService;
-class OneGoogleBarService;
-class PromoService;
-class SearchSuggestService;
 class Profile;
 
 namespace search_provider_logos {
@@ -137,7 +137,7 @@
   NtpBackgroundService* ntp_background_service_;
 
   ScopedObserver<NtpBackgroundService, NtpBackgroundServiceObserver>
-      ntp_background_service_observer_;
+      ntp_background_service_observer_{this};
 
   base::Optional<base::TimeTicks> pending_one_google_bar_request_;
   std::vector<content::URLDataSource::GotDataCallback>
@@ -146,21 +146,22 @@
   OneGoogleBarService* one_google_bar_service_;
 
   ScopedObserver<OneGoogleBarService, OneGoogleBarServiceObserver>
-      one_google_bar_service_observer_;
+      one_google_bar_service_observer_{this};
 
   base::Optional<base::TimeTicks> pending_promo_request_;
   std::vector<content::URLDataSource::GotDataCallback> promo_callbacks_;
 
   PromoService* promo_service_;
 
-  ScopedObserver<PromoService, PromoServiceObserver> promo_service_observer_;
+  ScopedObserver<PromoService, PromoServiceObserver> promo_service_observer_{
+      this};
 
   base::Optional<base::TimeTicks> pending_search_suggest_request_;
 
   SearchSuggestService* search_suggest_service_;
 
   ScopedObserver<SearchSuggestService, SearchSuggestServiceObserver>
-      search_suggest_service_observer_;
+      search_suggest_service_observer_{this};
 
   search_provider_logos::LogoService* logo_service_;
   std::unique_ptr<DesktopLogoObserver> logo_observer_;
diff --git a/chrome/browser/search/search_engine_base_url_tracker.cc b/chrome/browser/search/search_engine_base_url_tracker.cc
index becc1ab..108e6f6 100644
--- a/chrome/browser/search/search_engine_base_url_tracker.cc
+++ b/chrome/browser/search/search_engine_base_url_tracker.cc
@@ -6,7 +6,6 @@
 
 
 #include "components/search_engines/search_terms_data.h"
-#include "components/search_engines/template_url_service.h"
 
 SearchEngineBaseURLTracker::SearchEngineBaseURLTracker(
     TemplateURLService* template_url_service,
@@ -15,7 +14,6 @@
     : template_url_service_(template_url_service),
       search_terms_data_(std::move(search_terms_data)),
       base_url_changed_callback_(base_url_changed_callback),
-      observer_(this),
       previous_google_base_url_(search_terms_data_->GoogleBaseURLValue()) {
   DCHECK(template_url_service_);
 
diff --git a/chrome/browser/search/search_engine_base_url_tracker.h b/chrome/browser/search/search_engine_base_url_tracker.h
index 81845969..78d34c6 100644
--- a/chrome/browser/search/search_engine_base_url_tracker.h
+++ b/chrome/browser/search/search_engine_base_url_tracker.h
@@ -12,11 +12,11 @@
 #include "base/optional.h"
 #include "base/scoped_observer.h"
 #include "components/search_engines/template_url_data.h"
+#include "components/search_engines/template_url_service.h"
 #include "components/search_engines/template_url_service_observer.h"
 #include "url/gurl.h"
 
 class SearchTermsData;
-class TemplateURLService;
 
 // A helper class that watches for changes to the base URL of the default search
 // engine. Typically this changes when a different DSE is selected. For Google,
@@ -48,7 +48,8 @@
   std::unique_ptr<SearchTermsData> search_terms_data_;
   BaseURLChangedCallback base_url_changed_callback_;
 
-  ScopedObserver<TemplateURLService, TemplateURLServiceObserver> observer_;
+  ScopedObserver<TemplateURLService, TemplateURLServiceObserver> observer_{
+      this};
 
   // Used to check whether notifications from TemplateURLService indicate a
   // change that affects the default search provider.
diff --git a/chrome/browser/search_engines/chrome_template_url_service_client.cc b/chrome/browser/search_engines/chrome_template_url_service_client.cc
index 8de531d..8edade1 100644
--- a/chrome/browser/search_engines/chrome_template_url_service_client.cc
+++ b/chrome/browser/search_engines/chrome_template_url_service_client.cc
@@ -4,13 +4,11 @@
 
 #include "chrome/browser/search_engines/chrome_template_url_service_client.h"
 
-#include "components/history/core/browser/history_service.h"
 #include "components/search_engines/template_url_service.h"
 
 ChromeTemplateURLServiceClient::ChromeTemplateURLServiceClient(
     history::HistoryService* history_service)
     : owner_(NULL),
-      history_service_observer_(this),
       history_service_(history_service) {
   // TODO(sky): bug 1166191. The keywords should be moved into the history
   // db, which will mean we no longer need this notification and the history
diff --git a/chrome/browser/search_engines/chrome_template_url_service_client.h b/chrome/browser/search_engines/chrome_template_url_service_client.h
index 8d849393a..8505a559 100644
--- a/chrome/browser/search_engines/chrome_template_url_service_client.h
+++ b/chrome/browser/search_engines/chrome_template_url_service_client.h
@@ -7,13 +7,10 @@
 
 #include "base/macros.h"
 #include "base/scoped_observer.h"
+#include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/search_engines/template_url_service_client.h"
 
-namespace history {
-class HistoryService;
-}
-
 // ChromeTemplateURLServiceClient provides keyword related history
 // functionality for TemplateURLService.
 class ChromeTemplateURLServiceClient : public TemplateURLServiceClient,
@@ -42,7 +39,7 @@
  private:
   TemplateURLService* owner_;
   ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
-      history_service_observer_;
+      history_service_observer_{this};
   history::HistoryService* history_service_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeTemplateURLServiceClient);
diff --git a/chrome/browser/sessions/session_restore_stats_collector.cc b/chrome/browser/sessions/session_restore_stats_collector.cc
index 7ea3a86..eb1d69f7 100644
--- a/chrome/browser/sessions/session_restore_stats_collector.cc
+++ b/chrome/browser/sessions/session_restore_stats_collector.cc
@@ -15,7 +15,6 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
-#include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 
@@ -125,8 +124,7 @@
       loading_tab_count_(0u),
       deferred_tab_count_(0u),
       tick_clock_(new base::DefaultTickClock()),
-      reporting_delegate_(std::move(reporting_delegate)),
-      observer_(this) {
+      reporting_delegate_(std::move(reporting_delegate)) {
   this_retainer_ = this;
 }
 
diff --git a/chrome/browser/sessions/session_restore_stats_collector.h b/chrome/browser/sessions/session_restore_stats_collector.h
index d75d443..b3c1009b 100644
--- a/chrome/browser/sessions/session_restore_stats_collector.h
+++ b/chrome/browser/sessions/session_restore_stats_collector.h
@@ -19,11 +19,11 @@
 #include "chrome/browser/sessions/session_restore_delegate.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_observer.h"
 
 namespace content {
 class NavigationController;
-class RenderWidgetHost;
 }
 
 // SessionRestoreStatsCollector observes SessionRestore events ands records UMA
@@ -282,8 +282,8 @@
   // has deferred tabs remaining from an interrupted session restore.
   scoped_refptr<SessionRestoreStatsCollector> this_retainer_;
 
-  ScopedObserver<content::RenderWidgetHost, SessionRestoreStatsCollector>
-      observer_;
+  ScopedObserver<content::RenderWidgetHost, content::RenderWidgetHostObserver>
+      observer_{this};
 
   DISALLOW_COPY_AND_ASSIGN(SessionRestoreStatsCollector);
 };
diff --git a/chrome/browser/sessions/session_service_unittest.cc b/chrome/browser/sessions/session_service_unittest.cc
index 81a5d4f..da53915 100644
--- a/chrome/browser/sessions/session_service_unittest.cc
+++ b/chrome/browser/sessions/session_service_unittest.cc
@@ -35,6 +35,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "components/sessions/content/content_serialized_navigation_builder.h"
 #include "components/sessions/content/content_test_helper.h"
 #include "components/sessions/core/serialized_navigation_entry_test_helper.h"
 #include "components/sessions/core/session_command.h"
@@ -873,26 +874,30 @@
   SessionID tab_id = SessionID::NewUnique();
   ASSERT_NE(window_id, tab_id);
 
-  // Create a page state representing a HTTP body without posted passwords.
-  content::PageState page_state =
-      content::PageState::CreateForTesting(GURL(), false, "data", NULL);
-
   // Create a TabNavigation containing page_state and representing a POST
   // request.
+  std::string post_data = "data";
+  std::unique_ptr<content::NavigationEntry> entry1 =
+      content::NavigationEntry::Create();
+  entry1->SetURL(GURL("http://google.com"));
+  entry1->SetTitle(base::UTF8ToUTF16("title1"));
+  entry1->SetHasPostData(true);
+  entry1->SetPostData(network::ResourceRequestBody::CreateFromBytes(
+      post_data.data(), post_data.size()));
   SerializedNavigationEntry nav1 =
-      ContentTestHelper::CreateNavigation("http://google.com", "title");
-  SerializedNavigationEntryTestHelper::SetEncodedPageState(
-      page_state.ToEncodedData(), &nav1);
-  SerializedNavigationEntryTestHelper::SetHasPostData(true, &nav1);
-  nav1.set_index(0);
+      sessions::ContentSerializedNavigationBuilder::FromNavigationEntry(
+          0 /* == index*/, entry1.get());
 
   // Create a TabNavigation containing page_state and representing a normal
   // request.
+  std::unique_ptr<content::NavigationEntry> entry2 =
+      content::NavigationEntry::Create();
+  entry2->SetURL(GURL("http://google.com/nopost"));
+  entry2->SetTitle(base::UTF8ToUTF16("title2"));
+  entry2->SetHasPostData(false);
   SerializedNavigationEntry nav2 =
-      ContentTestHelper::CreateNavigation("http://google.com/nopost", "title");
-  SerializedNavigationEntryTestHelper::SetEncodedPageState(
-      page_state.ToEncodedData(), &nav2);
-  nav2.set_index(1);
+      sessions::ContentSerializedNavigationBuilder::FromNavigationEntry(
+          1 /* == index*/, entry2.get());
 
   helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
   UpdateNavigation(window_id, tab_id, nav1, true);
diff --git a/chrome/browser/signin/signin_profile_attributes_updater.cc b/chrome/browser/signin/signin_profile_attributes_updater.cc
index 076df05d..5417b5fc 100644
--- a/chrome/browser/signin/signin_profile_attributes_updater.cc
+++ b/chrome/browser/signin/signin_profile_attributes_updater.cc
@@ -20,9 +20,7 @@
     : identity_manager_(identity_manager),
       signin_error_controller_(signin_error_controller),
       profile_attributes_storage_(profile_attributes_storage),
-      profile_path_(profile_path),
-      identity_manager_observer_(this),
-      signin_error_controller_observer_(this) {
+      profile_path_(profile_path) {
   DCHECK(identity_manager_);
   DCHECK(signin_error_controller_);
   DCHECK(profile_attributes_storage_);
diff --git a/chrome/browser/signin/signin_profile_attributes_updater.h b/chrome/browser/signin/signin_profile_attributes_updater.h
index 0c77301c..dc368db5 100644
--- a/chrome/browser/signin/signin_profile_attributes_updater.h
+++ b/chrome/browser/signin/signin_profile_attributes_updater.h
@@ -51,10 +51,10 @@
   SigninErrorController* signin_error_controller_;
   ProfileAttributesStorage* profile_attributes_storage_;
   const base::FilePath profile_path_;
-  ScopedObserver<signin::IdentityManager, SigninProfileAttributesUpdater>
-      identity_manager_observer_;
-  ScopedObserver<SigninErrorController, SigninProfileAttributesUpdater>
-      signin_error_controller_observer_;
+  ScopedObserver<signin::IdentityManager, signin::IdentityManager::Observer>
+      identity_manager_observer_{this};
+  ScopedObserver<SigninErrorController, SigninErrorController::Observer>
+      signin_error_controller_observer_{this};
 
   DISALLOW_COPY_AND_ASSIGN(SigninProfileAttributesUpdater);
 };
diff --git a/chrome/browser/signin/signin_util.cc b/chrome/browser/signin/signin_util.cc
index 1c0966c..78baf7f 100644
--- a/chrome/browser/signin/signin_util.cc
+++ b/chrome/browser/signin/signin_util.cc
@@ -39,6 +39,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_list_observer.h"
 #include "chrome/browser/ui/browser_window.h"
+#define CAN_DELETE_PROFILE
 #endif
 
 namespace signin_util {
@@ -46,10 +47,6 @@
 
 constexpr char kSignoutSettingKey[] = "signout_setting";
 
-#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_MACOSX)
-#define CAN_DELETE_PROFILE
-#endif
-
 #if defined(CAN_DELETE_PROFILE)
 // Manager that presents the profile will be deleted dialog on the first active
 // browser window.
@@ -67,12 +64,12 @@
                              Delegate* delegate)
       : profile_(profile),
         primary_account_email_(primary_account_email),
-        delegate_(delegate),
-        browser_observer_(this) {}
-  ~DeleteProfileDialogManager() override {}
+        delegate_(delegate) {}
+
+  ~DeleteProfileDialogManager() override { BrowserList::RemoveObserver(this); }
 
   void PresentDialogOnAllBrowserWindows() {
-    browser_observer_.Add(BrowserList::GetInstance());
+    BrowserList::AddObserver(this);
     Browser* active_browser = chrome::FindLastActiveWithProfile(profile_);
     if (active_browser)
       OnBrowserSetLastActive(active_browser);
@@ -104,7 +101,6 @@
   Profile* profile_;
   std::string primary_account_email_;
   Delegate* delegate_;
-  ScopedObserver<BrowserList, DeleteProfileDialogManager> browser_observer_;
 
   DISALLOW_COPY_AND_ASSIGN(DeleteProfileDialogManager);
 };
diff --git a/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.cc b/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.cc
index 256bd83..aaa90cbe 100644
--- a/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.cc
@@ -16,7 +16,6 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
-#include "components/history/core/browser/history_service.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "url/gurl.h"
 
@@ -39,8 +38,7 @@
 
 SubresourceFilterContentSettingsManager::
     SubresourceFilterContentSettingsManager(Profile* profile)
-    : history_observer_(this),
-      settings_map_(HostContentSettingsMapFactory::GetForProfile(profile)),
+    : settings_map_(HostContentSettingsMapFactory::GetForProfile(profile)),
       clock_(std::make_unique<base::DefaultClock>(base::DefaultClock())),
       should_use_smart_ui_(ShouldUseSmartUI()) {
   DCHECK(profile);
diff --git a/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.h b/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.h
index e515d92..ff13379 100644
--- a/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.h
+++ b/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.h
@@ -13,6 +13,7 @@
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "components/content_settings/core/common/content_settings.h"
+#include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_service_observer.h"
 
 class GURL;
@@ -23,10 +24,6 @@
 class DictionaryValue;
 }  // namespace base
 
-namespace history {
-class HistoryService;
-}  // namespace history
-
 // This class contains helpers to get/set content and website settings related
 // to subresource filtering.
 // TODO(crbug.com/706061): Once observing changes to content settings is robust
@@ -83,7 +80,7 @@
                        std::unique_ptr<base::DictionaryValue> dict);
 
   ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
-      history_observer_;
+      history_observer_{this};
 
   HostContentSettingsMap* settings_map_;
 
diff --git a/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc b/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc
new file mode 100644
index 0000000..ba2a47f
--- /dev/null
+++ b/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc
@@ -0,0 +1,232 @@
+// 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 <ostream>
+#include <string>
+
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/integration/status_change_checker.h"
+#include "chrome/browser/sync/test/integration/sync_test.h"
+#include "chrome/browser/ui/browser.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/base/sync_prefs.h"
+#include "components/sync/base/time.h"
+#include "components/sync/driver/sync_driver_switches.h"
+#include "components/sync/protocol/proto_value_conversions.h"
+#include "components/sync/protocol/sync.pb.h"
+#include "components/sync/test/fake_server/fake_server.h"
+#include "components/sync_device_info/device_info_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+
+using testing::ElementsAre;
+using testing::IsSupersetOf;
+
+MATCHER_P(HasCacheGuid, expected_cache_guid, "") {
+  return arg.specifics().device_info().cache_guid() == expected_cache_guid;
+}
+
+std::string CacheGuidForSuffix(int suffix) {
+  return base::StringPrintf("cache guid %d", suffix);
+}
+
+std::string ClientNameForSuffix(int suffix) {
+  return base::StringPrintf("client name %d", suffix);
+}
+
+std::string SyncUserAgentForSuffix(int suffix) {
+  return base::StringPrintf("sync user agent %d", suffix);
+}
+
+std::string ChromeVersionForSuffix(int suffix) {
+  return base::StringPrintf("chrome version %d", suffix);
+}
+
+std::string SigninScopedDeviceIdForSuffix(int suffix) {
+  return base::StringPrintf("signin scoped device id %d", suffix);
+}
+
+sync_pb::DeviceInfoSpecifics CreateSpecifics(int suffix) {
+  sync_pb::DeviceInfoSpecifics specifics;
+  specifics.set_cache_guid(CacheGuidForSuffix(suffix));
+  specifics.set_client_name(ClientNameForSuffix(suffix));
+  specifics.set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_LINUX);
+  specifics.set_sync_user_agent(SyncUserAgentForSuffix(suffix));
+  specifics.set_chrome_version(ChromeVersionForSuffix(suffix));
+  specifics.set_signin_scoped_device_id(SigninScopedDeviceIdForSuffix(suffix));
+  specifics.set_last_updated_timestamp(
+      syncer::TimeToProtoTime(base::Time::Now()));
+  return specifics;
+}
+
+class ServerDeviceInfoMatchChecker : public StatusChangeChecker,
+                                     fake_server::FakeServer::Observer {
+ public:
+  using Matcher = testing::Matcher<std::vector<sync_pb::SyncEntity>>;
+
+  ServerDeviceInfoMatchChecker(fake_server::FakeServer* fake_server,
+                               const Matcher& matcher)
+      : fake_server_(fake_server), matcher_(matcher) {
+    fake_server->AddObserver(this);
+  }
+
+  ~ServerDeviceInfoMatchChecker() override {
+    fake_server_->RemoveObserver(this);
+  }
+
+  // FakeServer::Observer overrides.
+  void OnCommit(const std::string& committer_id,
+                syncer::ModelTypeSet committed_model_types) override {
+    if (committed_model_types.Has(syncer::DEVICE_INFO)) {
+      CheckExitCondition();
+    }
+  }
+
+  // StatusChangeChecker overrides.
+  bool IsExitConditionSatisfied() override {
+    std::vector<sync_pb::SyncEntity> entities =
+        fake_server_->GetSyncEntitiesByModelType(syncer::DEVICE_INFO);
+    return testing::Matches(matcher_)(entities);
+  }
+
+  std::string GetDebugMessage() const override {
+    std::vector<sync_pb::SyncEntity> entities =
+        fake_server_->GetSyncEntitiesByModelType(syncer::DEVICE_INFO);
+
+    testing::StringMatchResultListener result_listener;
+    testing::ExplainMatchResult(matcher_, entities, &result_listener);
+    return result_listener.str();
+  }
+
+ private:
+  fake_server::FakeServer* const fake_server_;
+  const Matcher matcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServerDeviceInfoMatchChecker);
+};
+
+class SingleClientDeviceInfoSyncTest : public SyncTest {
+ public:
+  SingleClientDeviceInfoSyncTest() : SyncTest(SINGLE_CLIENT) {}
+
+  ~SingleClientDeviceInfoSyncTest() override {}
+
+  std::string GetLocalCacheGuid() {
+    syncer::SyncPrefs prefs(GetProfile(0)->GetPrefs());
+    return prefs.GetCacheGuid();
+  }
+
+  // Injects a test DeviceInfo entity to the fake server.
+  void InjectDeviceInfoEntityToServer(int suffix) {
+    sync_pb::EntitySpecifics specifics;
+    *specifics.mutable_device_info() = CreateSpecifics(suffix);
+    GetFakeServer()->InjectEntity(
+        syncer::PersistentUniqueClientEntity::CreateFromSpecificsForTesting(
+            /*non_unique_name=*/"",
+            /*client_tag=*/
+            syncer::DeviceInfoUtil::SpecificsToTag(specifics.device_info()),
+            specifics,
+            /*creation_time=*/0, /*last_modified_time=*/0));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SingleClientDeviceInfoSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SingleClientDeviceInfoSyncTest, CommitLocalDevice) {
+  ASSERT_TRUE(SetupSync());
+
+  // The local device should eventually be committed to the server.
+  EXPECT_TRUE(
+      ServerDeviceInfoMatchChecker(
+          GetFakeServer(), ElementsAre(HasCacheGuid(GetLocalCacheGuid())))
+          .Wait());
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientDeviceInfoSyncTest, DownloadRemoteDevices) {
+  InjectDeviceInfoEntityToServer(/*suffix=*/1);
+  InjectDeviceInfoEntityToServer(/*suffix=*/2);
+
+  ASSERT_TRUE(SetupSync());
+
+  // The local device may or may not already be committed at this point.
+  EXPECT_THAT(fake_server_->GetSyncEntitiesByModelType(syncer::DEVICE_INFO),
+              IsSupersetOf({HasCacheGuid(CacheGuidForSuffix(1)),
+                            HasCacheGuid(CacheGuidForSuffix(2))}));
+}
+
+class SingleClientDeviceInfoWithTransportModeSyncTest
+    : public SingleClientDeviceInfoSyncTest {
+ public:
+  SingleClientDeviceInfoWithTransportModeSyncTest() {
+    scoped_list_.InitAndEnableFeature(switches::kSyncDeviceInfoInTransportMode);
+  }
+
+  ~SingleClientDeviceInfoWithTransportModeSyncTest() override {}
+
+ private:
+  base::test::ScopedFeatureList scoped_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(SingleClientDeviceInfoWithTransportModeSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SingleClientDeviceInfoWithTransportModeSyncTest,
+                       CommitLocalDevice) {
+  ASSERT_TRUE(SetupClients());
+
+#if defined(OS_CHROMEOS)
+  // On ChromeOS, Sync-the-feature gets started automatically once a primary
+  // account is signed in. To prevent that, explicitly set SyncRequested to
+  // false.
+  GetSyncService(0)->GetUserSettings()->SetSyncRequested(false);
+#endif  // defined(OS_CHROMEOS)
+
+  // Setup a primary account, but don't actually enable Sync-the-feature (so
+  // that Sync will start in transport mode).
+  ASSERT_TRUE(GetClient(0)->SignInPrimaryAccount());
+  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
+
+  ASSERT_FALSE(GetSyncService(0)->IsSyncFeatureEnabled());
+  ASSERT_TRUE(GetSyncService(0)->GetActiveDataTypes().Has(syncer::DEVICE_INFO));
+
+  // The local device should eventually be committed to the server.
+  EXPECT_TRUE(
+      ServerDeviceInfoMatchChecker(
+          GetFakeServer(), ElementsAre(HasCacheGuid(GetLocalCacheGuid())))
+          .Wait());
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientDeviceInfoWithTransportModeSyncTest,
+                       DownloadRemoteDevices) {
+  InjectDeviceInfoEntityToServer(/*suffix=*/1);
+  InjectDeviceInfoEntityToServer(/*suffix=*/2);
+
+  ASSERT_TRUE(SetupClients());
+
+#if defined(OS_CHROMEOS)
+  // On ChromeOS, Sync-the-feature gets started automatically once a primary
+  // account is signed in. To prevent that, explicitly set SyncRequested to
+  // false.
+  GetSyncService(0)->GetUserSettings()->SetSyncRequested(false);
+#endif  // defined(OS_CHROMEOS)
+
+  // Setup a primary account, but don't actually enable Sync-the-feature (so
+  // that Sync will start in transport mode).
+  ASSERT_TRUE(GetClient(0)->SignInPrimaryAccount());
+  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
+
+  ASSERT_FALSE(GetSyncService(0)->IsSyncFeatureEnabled());
+  ASSERT_TRUE(GetSyncService(0)->GetActiveDataTypes().Has(syncer::DEVICE_INFO));
+
+  EXPECT_THAT(fake_server_->GetSyncEntitiesByModelType(syncer::DEVICE_INFO),
+              IsSupersetOf({HasCacheGuid(CacheGuidForSuffix(1)),
+                            HasCacheGuid(CacheGuidForSuffix(2))}));
+}
+
+}  // namespace
diff --git a/chrome/browser/themes/theme_properties.h b/chrome/browser/themes/theme_properties.h
index 75cf802..c3078d9 100644
--- a/chrome/browser/themes/theme_properties.h
+++ b/chrome/browser/themes/theme_properties.h
@@ -163,9 +163,13 @@
     COLOR_OMNIBOX_RESULTS_BG,
     COLOR_OMNIBOX_RESULTS_BG_HOVERED,
     COLOR_OMNIBOX_RESULTS_BG_SELECTED,
+    COLOR_OMNIBOX_RESULTS_TEXT_SELECTED,
     COLOR_OMNIBOX_RESULTS_TEXT_DIMMED,
+    COLOR_OMNIBOX_RESULTS_TEXT_DIMMED_SELECTED,
     COLOR_OMNIBOX_RESULTS_ICON,
+    COLOR_OMNIBOX_RESULTS_ICON_SELECTED,
     COLOR_OMNIBOX_RESULTS_URL,
+    COLOR_OMNIBOX_RESULTS_URL_SELECTED,
     COLOR_OMNIBOX_BUBBLE_OUTLINE,
     COLOR_OMNIBOX_BUBBLE_OUTLINE_EXPERIMENTAL_KEYWORD_MODE,
     COLOR_OMNIBOX_SECURITY_CHIP_DEFAULT,
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index e23230b..2cd05dcf 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -1105,16 +1105,16 @@
     bool custom;
   };
 
-  auto get_base_color = [&](int id) -> OmniboxColor {
-    SkColor color;
-    if (theme_supplier_ && theme_supplier_->GetColor(id, &color))
-      return {color, true};
-    return {GetDefaultColor(id, incognito), false};
-  };
-  // These are the only base colors.
-  const OmniboxColor fg = get_base_color(TP::COLOR_OMNIBOX_TEXT);
-  const OmniboxColor bg = get_base_color(TP::COLOR_OMNIBOX_BACKGROUND);
-  const bool dark = IsDark(bg.value);
+  const bool high_contrast =
+      theme_supplier_ && theme_supplier_->get_theme_type() ==
+                             CustomThemeSupplier::ThemeType::INCREASED_CONTRAST;
+
+  const bool invert =
+      high_contrast && (id == TP::COLOR_OMNIBOX_RESULTS_BG_SELECTED ||
+                        id == TP::COLOR_OMNIBOX_RESULTS_TEXT_SELECTED ||
+                        id == TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED_SELECTED ||
+                        id == TP::COLOR_OMNIBOX_RESULTS_ICON_SELECTED ||
+                        id == TP::COLOR_OMNIBOX_RESULTS_URL_SELECTED);
 
   // Some utilities from color_utils are reimplemented here to plumb the custom
   // bit through.
@@ -1130,6 +1130,8 @@
   };
   auto blend_for_min_contrast = [&](OmniboxColor fg, OmniboxColor bg,
                                     base::Optional<OmniboxColor> hc_fg =
+                                        base::nullopt,
+                                    base::Optional<float> contrast_ratio =
                                         base::nullopt) -> OmniboxColor {
     base::Optional<SkColor> hc_fg_arg;
     bool custom = fg.custom || bg.custom;
@@ -1137,17 +1139,37 @@
       hc_fg_arg = hc_fg.value().value;
       custom |= hc_fg.value().custom;
     }
-    const bool high_contrast =
-        theme_supplier_ &&
-        theme_supplier_->get_theme_type() ==
-            CustomThemeSupplier::ThemeType::INCREASED_CONTRAST;
-    const float contrast_ratio =
-        high_contrast ? 6.0f : kMinimumReadableContrastRatio;
-    return {BlendForMinContrast(fg.value, bg.value, hc_fg_arg, contrast_ratio)
+    return {BlendForMinContrast(
+                fg.value, bg.value, hc_fg_arg,
+                contrast_ratio.value_or(
+                    high_contrast ? 6.0f : kMinimumReadableContrastRatio))
                 .color,
             custom};
   };
 
+  auto get_base_color = [&](int id) -> OmniboxColor {
+    SkColor color;
+    if (theme_supplier_ && theme_supplier_->GetColor(id, &color))
+      return {color, true};
+    return {GetDefaultColor(id, incognito), false};
+  };
+  // These are the only base colors.
+  OmniboxColor fg = get_base_color(TP::COLOR_OMNIBOX_TEXT);
+  OmniboxColor bg = get_base_color(TP::COLOR_OMNIBOX_BACKGROUND);
+  if (invert) {
+    // Given a color with some contrast against the opposite endpoint, returns a
+    // color with that same contrast against the nearby endpoint.
+    auto invert_color = [&](OmniboxColor fg) -> OmniboxColor {
+      const auto bg = get_color_with_max_contrast(fg);
+      const auto inverted_bg = get_color_with_max_contrast(bg);
+      const float contrast = GetContrastRatio(fg.value, bg.value);
+      return blend_for_min_contrast(fg, inverted_bg, base::nullopt, contrast);
+    };
+    fg = invert_color(fg);
+    bg = invert_color(bg);
+  }
+  const bool dark = IsDark(bg.value);
+
   auto results_bg_color = [&]() { return get_color_with_max_contrast(fg); };
   auto security_chip_color = [&](OmniboxColor color) {
     return blend_for_min_contrast(color, bg);
@@ -1167,6 +1189,7 @@
   auto get_omnibox_color_impl = [&](int id) -> base::Optional<OmniboxColor> {
     switch (id) {
       case TP::COLOR_OMNIBOX_TEXT:
+      case TP::COLOR_OMNIBOX_RESULTS_TEXT_SELECTED:
         return fg;
       case TP::COLOR_OMNIBOX_BACKGROUND:
         return bg;
@@ -1189,12 +1212,20 @@
       case TP::COLOR_OMNIBOX_TEXT_DIMMED:
         return blend_with_clamped_contrast(bg);
       case TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED:
+        return blend_with_clamped_contrast(results_bg_color());
+      case TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED_SELECTED:
         return blend_with_clamped_contrast(results_bg_selected_color());
       case TP::COLOR_OMNIBOX_RESULTS_ICON:
-        return derive_default_icon_color(fg);
+        return blend_for_min_contrast(derive_default_icon_color(fg),
+                                      results_bg_color());
+      case TP::COLOR_OMNIBOX_RESULTS_ICON_SELECTED:
+        return blend_for_min_contrast(derive_default_icon_color(fg),
+                                      results_bg_selected_color());
       case TP::COLOR_OMNIBOX_RESULTS_BG_HOVERED:
         return blend_toward_max_contrast(results_bg_color(), 0x1A);
       case TP::COLOR_OMNIBOX_RESULTS_URL:
+        return url_color(results_bg_color());
+      case TP::COLOR_OMNIBOX_RESULTS_URL_SELECTED:
         return url_color(results_bg_selected_color());
       case TP::COLOR_OMNIBOX_SECURITY_CHIP_DEFAULT:
       case TP::COLOR_OMNIBOX_SECURITY_CHIP_SECURE:
diff --git a/chrome/browser/themes/theme_service_unittest.cc b/chrome/browser/themes/theme_service_unittest.cc
index 8eb8e64..68974d7 100644
--- a/chrome/browser/themes/theme_service_unittest.cc
+++ b/chrome/browser/themes/theme_service_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/files/file_util.h"
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
@@ -15,6 +16,7 @@
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/extensions/unpacked_installer.h"
 #include "chrome/browser/themes/custom_theme_supplier.h"
+#include "chrome/browser/themes/increased_contrast_theme_supplier.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/common/buildflags.h"
@@ -31,6 +33,8 @@
 #include "extensions/browser/uninstall_reason.h"
 #include "extensions/common/extension.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/color_utils.h"
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 #include "chrome/browser/supervised_user/supervised_user_service.h"
@@ -112,6 +116,22 @@
     return theme_service->get_theme_supplier();
   }
 
+  void set_theme_supplier(ThemeService* theme_service,
+                          scoped_refptr<CustomThemeSupplier> theme_supplier) {
+    theme_service->theme_supplier_ = theme_supplier;
+  }
+
+  SkColor GetOmniboxColor(ThemeService* theme_service,
+                          int id,
+                          bool incognito) const {
+    bool has_custom_color;
+    base::Optional<SkColor> color =
+        theme_service->GetOmniboxColor(id, incognito, &has_custom_color);
+    EXPECT_FALSE(has_custom_color);
+    EXPECT_TRUE(color);
+    return color.value_or(gfx::kPlaceholderColor);
+  }
+
   void WaitForThemeInstall() {
     content::WindowedNotificationObserver theme_change_observer(
         chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
@@ -517,4 +537,83 @@
   EXPECT_FALSE(service_->IsExtensionEnabled(extension1_id));
 }
 
+TEST_F(ThemeServiceTest, OmniboxContrast) {
+  using TP = ThemeProperties;
+  ThemeService* theme_service =
+      ThemeServiceFactory::GetForProfile(profile_.get());
+  for (bool dark : {false, true}) {
+    for (bool high_contrast : {false, true}) {
+      set_theme_supplier(
+          theme_service,
+          high_contrast
+              ? base::MakeRefCounted<IncreasedContrastThemeSupplier>(dark)
+              : nullptr);
+      constexpr int contrasting_ids[][2] = {
+          {TP::COLOR_OMNIBOX_TEXT, TP::COLOR_OMNIBOX_BACKGROUND},
+          {TP::COLOR_OMNIBOX_TEXT, TP::COLOR_OMNIBOX_BACKGROUND_HOVERED},
+          {TP::COLOR_OMNIBOX_TEXT, TP::COLOR_OMNIBOX_RESULTS_BG},
+          {TP::COLOR_OMNIBOX_TEXT, TP::COLOR_OMNIBOX_RESULTS_BG_HOVERED},
+          {TP::COLOR_OMNIBOX_TEXT_DIMMED, TP::COLOR_OMNIBOX_BACKGROUND},
+          {TP::COLOR_OMNIBOX_SELECTED_KEYWORD, TP::COLOR_OMNIBOX_RESULTS_BG},
+          {TP::COLOR_OMNIBOX_RESULTS_TEXT_SELECTED,
+           TP::COLOR_OMNIBOX_RESULTS_BG_SELECTED},
+          {TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED, TP::COLOR_OMNIBOX_RESULTS_BG},
+          {TP::COLOR_OMNIBOX_RESULTS_TEXT_SELECTED,
+           TP::COLOR_OMNIBOX_RESULTS_BG_SELECTED},
+          {TP::COLOR_OMNIBOX_RESULTS_ICON, TP::COLOR_OMNIBOX_RESULTS_BG},
+          {TP::COLOR_OMNIBOX_RESULTS_ICON,
+           TP::COLOR_OMNIBOX_RESULTS_BG_HOVERED},
+          {TP::COLOR_OMNIBOX_RESULTS_ICON_SELECTED,
+           TP::COLOR_OMNIBOX_RESULTS_BG_SELECTED},
+          {TP::COLOR_OMNIBOX_RESULTS_URL, TP::COLOR_OMNIBOX_RESULTS_BG},
+          {TP::COLOR_OMNIBOX_RESULTS_URL_SELECTED,
+           TP::COLOR_OMNIBOX_RESULTS_BG_SELECTED},
+          {TP::COLOR_OMNIBOX_BUBBLE_OUTLINE, TP::COLOR_OMNIBOX_RESULTS_BG},
+          {TP::COLOR_OMNIBOX_BUBBLE_OUTLINE_EXPERIMENTAL_KEYWORD_MODE,
+           TP::COLOR_OMNIBOX_RESULTS_BG},
+          {TP::COLOR_OMNIBOX_SECURITY_CHIP_DEFAULT,
+           TP::COLOR_OMNIBOX_BACKGROUND},
+          {TP::COLOR_OMNIBOX_SECURITY_CHIP_DEFAULT,
+           TP::COLOR_OMNIBOX_BACKGROUND_HOVERED},
+          {TP::COLOR_OMNIBOX_SECURITY_CHIP_DEFAULT,
+           TP::COLOR_OMNIBOX_RESULTS_BG},
+          {TP::COLOR_OMNIBOX_SECURITY_CHIP_SECURE,
+           TP::COLOR_OMNIBOX_BACKGROUND},
+          {TP::COLOR_OMNIBOX_SECURITY_CHIP_SECURE,
+           TP::COLOR_OMNIBOX_BACKGROUND_HOVERED},
+          {TP::COLOR_OMNIBOX_SECURITY_CHIP_SECURE,
+           TP::COLOR_OMNIBOX_RESULTS_BG},
+          {TP::COLOR_OMNIBOX_SECURITY_CHIP_DANGEROUS,
+           TP::COLOR_OMNIBOX_BACKGROUND},
+          {TP::COLOR_OMNIBOX_SECURITY_CHIP_DANGEROUS,
+           TP::COLOR_OMNIBOX_RESULTS_BG},
+          // TODO(thomasanderson): Because colors are computed relative to
+          // non-hovered backgrounds, some colors over hovered backgrounds do
+          // not have sufficient contrast in all configurations.  Computing the
+          // non-contrasty colors here relative to hovered backgrounds should
+          // fix this and not reduce contrast of non-hovered backgrounds.
+          // {TP::COLOR_OMNIBOX_TEXT_DIMMED,
+          //  TP::COLOR_OMNIBOX_BACKGROUND_HOVERED},
+          // {TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED,
+          //  TP::COLOR_OMNIBOX_RESULTS_BG_HOVERED},
+          // {TP::COLOR_OMNIBOX_RESULTS_URL,
+          //  TP::COLOR_OMNIBOX_RESULTS_BG_HOVERED},
+          // {TP::COLOR_OMNIBOX_SECURITY_CHIP_DANGEROUS,
+          //  TP::COLOR_OMNIBOX_BACKGROUND_HOVERED},
+      };
+      auto check_sufficient_contrast = [&](int id1, int id2) {
+        const float contrast = color_utils::GetContrastRatio(
+            GetOmniboxColor(theme_service, id1, dark),
+            GetOmniboxColor(theme_service, id2, dark));
+        EXPECT_GE(contrast, color_utils::kMinimumReadableContrastRatio);
+      };
+      for (const int* ids : contrasting_ids)
+        check_sufficient_contrast(ids[0], ids[1]);
+      if (high_contrast)
+        check_sufficient_contrast(TP::COLOR_OMNIBOX_RESULTS_BG_SELECTED,
+                                  TP::COLOR_OMNIBOX_RESULTS_BG);
+    }
+  }
+}
+
 }  // namespace theme_service_internal
diff --git a/chrome/browser/touch_to_fill/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
index fef5aae42..d1bedc9 100644
--- a/chrome/browser/touch_to_fill/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
@@ -22,6 +22,8 @@
     "java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java",
     "java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java",
     "java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java",
+    "java/src/org/chromium/chrome/browser/touch_to_fill/helper/ListViewAdapter.java",
+    "java/src/org/chromium/chrome/browser/touch_to_fill/helper/SimpleListViewMcp.java",
   ]
 
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_credential_item.xml b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_credential_item.xml
new file mode 100644
index 0000000..6460150
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_credential_item.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:descendantFocusability="blocksDescendants"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="70dp"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:paddingStart="10dp"
+    android:paddingEnd="10dp">
+    <ImageView
+        android:id="@+id/favicon"
+        android:layout_width="20dp"
+        android:layout_height="20dp"
+        android:importantForAccessibility="no"
+        android:layout_gravity="center"/>
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_margin="8dp"
+        android:layout_marginStart="16dp"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <TextView
+            android:id="@+id/username"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.BlackTitle1" />
+        <TextView
+            android:id="@+id/password"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.BlackBody" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java
index f03bc4e..d6e9c681 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java
@@ -4,11 +4,15 @@
 
 package org.chromium.chrome.browser.touch_to_fill;
 
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CREDENTIAL_LIST;
+
 import android.content.Context;
 
 import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.touch_to_fill.data.Credential;
+import org.chromium.chrome.browser.touch_to_fill.helper.ListViewAdapter;
+import org.chromium.chrome.browser.touch_to_fill.helper.SimpleListViewMcp;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
@@ -44,5 +48,9 @@
     @VisibleForTesting
     static void setUpModelChangeProcessors(PropertyModel model, TouchToFillView view) {
         PropertyModelChangeProcessor.create(model, view, TouchToFillViewBinder::bind);
+        view.setCredentialListAdapter(
+                new ListViewAdapter<>(new SimpleListViewMcp<>(model.get(CREDENTIAL_LIST),
+                                              TouchToFillViewBinder::bindCredentialView),
+                        TouchToFillViewBinder::createCredentialView));
     }
 }
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
index c28fdc9..8d297be6 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
@@ -8,7 +8,10 @@
 import android.support.annotation.Nullable;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.AdapterView;
 import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
 import android.widget.TextView;
 
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
@@ -24,6 +27,7 @@
 class TouchToFillView implements BottomSheet.BottomSheetContent {
     private final Context mContext;
     private final BottomSheetController mBottomSheetController;
+    private final ListView mCredentialListView;
     private final LinearLayout mContentView;
     private TouchToFillProperties.ViewEventListener mEventListener;
 
@@ -47,6 +51,8 @@
         mBottomSheetController = bottomSheetController;
         mContentView = (LinearLayout) LayoutInflater.from(mContext).inflate(
                 R.layout.touch_to_fill_sheet, null);
+        mCredentialListView = mContentView.findViewById(R.id.credential_list);
+        mCredentialListView.setOnItemClickListener(this::onItemSelected);
     }
 
     /**
@@ -83,6 +89,16 @@
                 mContext.getString(R.string.touch_to_fill_sheet_subtitle), formattedUrl));
     }
 
+    void setCredentialListAdapter(ListAdapter adapter) {
+        mCredentialListView.setAdapter(adapter);
+    }
+
+    private void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
+        assert adapterView == mCredentialListView : "Use this click handler only for credentials!";
+        assert mEventListener != null;
+        mEventListener.onSelectItemAt(i);
+    }
+
     @Override
     public void destroy() {
         mBottomSheetController.getBottomSheet().removeObserver(mBottomSheetObserver);
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
index 33d3469..f9d7ee7 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
@@ -9,6 +9,13 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.VIEW_EVENT_LISTENER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.VISIBLE;
 
+import android.text.method.PasswordTransformationMethod;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.chromium.chrome.browser.touch_to_fill.data.Credential;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -18,6 +25,31 @@
  */
 class TouchToFillViewBinder {
     /**
+     * Factory used to create a new View inside the ListView inside the TouchToFillView.
+     * @param parent The parent {@link ViewGroup} of the new item.
+     */
+    static View createCredentialView(ViewGroup parent) {
+        return LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.touch_to_fill_credential_item, parent, false);
+    }
+
+    /**
+     * Called whenever a credential is bound to this view holder. Please note that this method
+     * might be called on the same list entry repeatedly, so make sure to always set a default for
+     * unused fields.
+     * @param view The view to be bound.
+     * @param credential The {@link Credential} whose data needs to be displayed.
+     */
+    static void bindCredentialView(View view, Credential credential) {
+        TextView usernameText = view.findViewById(R.id.username);
+        usernameText.setText(credential.getUsername());
+
+        TextView passwordText = view.findViewById(R.id.password);
+        passwordText.setText(credential.getPassword());
+        passwordText.setTransformationMethod(new PasswordTransformationMethod());
+    }
+
+    /**
      * Called whenever a property in the given model changes. It updates the given view accordingly.
      * @param model The observed {@link PropertyModel}. Its data need to be reflected in the view.
      * @param view The {@link TouchToFillView} to update.
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/helper/ListViewAdapter.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/helper/ListViewAdapter.java
new file mode 100644
index 0000000..ae745a1c
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/helper/ListViewAdapter.java
@@ -0,0 +1,170 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.touch_to_fill.helper;
+
+import android.database.DataSetObserver;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListAdapter;
+
+import org.chromium.ui.modelutil.ListObservable;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This Adapter creates Views of type {@link V} for a {@link android.widget.ListView} and binds
+ * items of type {@link T} to each of the created views. It defers most tasks to a {@link Delegate}.
+ *
+ * Construct with a {@link SimpleListViewMcp} to update a ListView according to a list model.
+ *
+ * @param <T> The item type inside the observed list. It's passed to the {@link Delegate}.
+ * @param <V> The view type of a single list entry as created by the {@link ViewFactory}.
+ */
+public class ListViewAdapter<T, V extends View>
+        implements ListObservable.ListObserver<Void>, ListAdapter {
+    private final Delegate<T, V> mDelegate;
+    private final ViewFactory<V> mViewFactory;
+    private final Set<DataSetObserver> mObservers = new HashSet<>();
+
+    /**
+     * This delegates decouples the ListViewAdapter from the underlying model and handles all tasks
+     * for which the model might be necessary.
+     * @param <T> The item type inside the observed list.
+     * @param <V> The view type of a single list entry as created by the {@link ViewFactory}.
+     */
+    public interface Delegate<T, V extends View> {
+        /**
+         * Returns the list item at the given position.
+         * @param pos The position inside the list to retrieve the item for.
+         * @return An item of type {@link T}.
+         */
+        T getItemAt(int pos);
+
+        /**
+         * @return The total number of items in the observed list.
+         */
+        int getItemCount();
+
+        /**
+         * Binds the data of the given item to the given View.
+         * @param view A View inside the ListView of type {@link V}.
+         * @param item An Item inside the observed list of type {@link T}.
+         */
+        void onBindView(V view, T item);
+    }
+
+    /**
+     * Subclasses of this interface create the View that represents an item in a ListView.
+     * @param <V> The type of {@link View} this factory creates.
+     */
+    public interface ViewFactory<V extends View> {
+        /**
+         * Creates a View used in a ListView and attaches it to the given parent.
+         * @param parent The parent of the newly created View.
+         * @return A {@link View} that represents an item in a ListView.
+         */
+        V create(ViewGroup parent);
+    }
+
+    /**
+     * Creates a new ListViewAdapter.
+     * @param delegate The {@link Delegate} to bind views and query for list information.
+     * @param viewFactory The {@link ViewFactory} used to create new views for the ListView.
+     */
+    public ListViewAdapter(Delegate<T, V> delegate, ViewFactory<V> viewFactory) {
+        mDelegate = delegate;
+        mViewFactory = viewFactory;
+    }
+
+    @Override
+    public void onItemMoved(ListObservable source, int curIndex, int newIndex) {
+        notifyObserversChanged();
+    }
+
+    @Override
+    public void onItemRangeChanged(
+            ListObservable source, int index, int count, @Nullable Void payload) {
+        notifyObserversChanged();
+    }
+
+    @Override
+    public void onItemRangeInserted(ListObservable source, int index, int count) {
+        notifyObserversChanged();
+    }
+
+    @Override
+    public void onItemRangeRemoved(ListObservable source, int index, int count) {
+        notifyObserversChanged();
+    }
+
+    private void notifyObserversChanged() {
+        for (DataSetObserver observer : mObservers) observer.onChanged();
+    }
+
+    @Override
+    public void registerDataSetObserver(DataSetObserver dataSetObserver) {
+        mObservers.add(dataSetObserver);
+    }
+
+    @Override
+    public void unregisterDataSetObserver(DataSetObserver dataSetObserver) {
+        mObservers.remove(dataSetObserver);
+    }
+
+    @Override
+    public int getCount() {
+        return mDelegate.getItemCount();
+    }
+
+    @Override
+    public Object getItem(int i) {
+        return mDelegate.getItemAt(i);
+    }
+
+    @Override
+    public long getItemId(int i) {
+        return i;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public View getView(int pos, View view, ViewGroup parent) {
+        if (view == null) view = mViewFactory.create(parent);
+        mDelegate.onBindView((V) view, mDelegate.getItemAt(pos));
+        return view;
+    }
+
+    @Override
+    public int getItemViewType(int i) {
+        return 0; // Always the same view type.
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        return 1; // Always the same view type.
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return mDelegate.getItemCount() == 0;
+    }
+
+    @Override
+    public boolean areAllItemsEnabled() {
+        return true; // There are no disabled items yet.
+    }
+
+    @Override
+    public boolean isEnabled(int i) {
+        return true; // There are no disabled items yet.
+    }
+}
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/helper/SimpleListViewMcp.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/helper/SimpleListViewMcp.java
new file mode 100644
index 0000000..0bbfccc9f
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/helper/SimpleListViewMcp.java
@@ -0,0 +1,66 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.touch_to_fill.helper;
+
+import android.view.View;
+
+import org.chromium.ui.modelutil.ForwardingListObservable;
+import org.chromium.ui.modelutil.ListModel;
+
+// TODO(fhorschig): Ask org.chromium.ui.modelutil OWNERS whether this should be shared.
+/**
+ * Observes a {@link ListModel} and forwards any changes. It implements the
+ * {@link ListViewAdapter.Delegate} so it can be used to bind views as needed by a
+ * {@link android.widget.ListView} that ends up displaying the observed Entries.
+ *
+ * @param <T> The item type inside the passed ListModel. It's passed to the {@link ViewBinder}.
+ * @param <V> The view type of a single list entry.
+ */
+public class SimpleListViewMcp<T, V extends View>
+        extends ForwardingListObservable<Void> implements ListViewAdapter.Delegate<T, V> {
+    private final ListModel<T> mModel;
+    private final ViewBinder<T, V> mViewBinder;
+
+    /**
+     * Subclasses of this interface bind a given item to a given view.
+     * @param <T> The item type of a list entry.
+     * @param <V> The view type of a single list entry.
+     */
+    public interface ViewBinder<T, V extends View> {
+        /**
+         * Binds a given item to a given view. That means, the item properties are mapped to view
+         * properties. No logic should happen in here.
+         * @param view The view that holds the data of a list entry.
+         * @param item The item describing a single list entry.
+         */
+        void bind(V view, T item);
+    }
+
+    /**
+     * This creates a new Model Change Processor that observes the given model.
+     * @param model The {@Link ListModel} to be observed.
+     * @param viewBinder The {@link ViewBinder} used to bind items to views inside the ListView.
+     */
+    public SimpleListViewMcp(ListModel<T> model, ViewBinder<T, V> viewBinder) {
+        mModel = model;
+        mViewBinder = viewBinder;
+        model.addObserver(this);
+    }
+
+    @Override
+    public void onBindView(V view, T item) {
+        mViewBinder.bind(view, item);
+    }
+
+    @Override
+    public T getItemAt(int pos) {
+        return mModel.get(pos);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mModel.size();
+    }
+}
\ No newline at end of file
diff --git a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
index 2c9d86ec..aa2b3c53 100644
--- a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
+++ b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
@@ -4,15 +4,21 @@
 
 package org.chromium.chrome.browser.touch_to_fill;
 
+import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CREDENTIAL_LIST;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FORMATTED_URL;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.VISIBLE;
 import static org.chromium.content_public.browser.test.util.CriteriaHelper.pollUiThread;
 
 import android.support.test.filters.MediumTest;
+import android.text.method.PasswordTransformationMethod;
+import android.widget.ListView;
 import android.widget.TextView;
 
 import org.junit.Before;
@@ -23,14 +29,20 @@
 import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.ScalableTimeout;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.touch_to_fill.data.Credential;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.SheetState;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.content_public.browser.test.util.TouchCommon;
 import org.chromium.ui.modelutil.PropertyModel;
 
+import java.util.Arrays;
+
 /**
  * View tests for the Touch To Fill component ensure that model changes are reflected in the sheet.
  */
@@ -95,6 +107,46 @@
 
     @Test
     @MediumTest
+    public void testCredentialsChangedByModel() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mTouchToFillView.setVisible(true);
+            mModel.get(CREDENTIAL_LIST)
+                    .addAll(Arrays.asList(new Credential("Ana", "S3cr3t", null, false),
+                            new Credential("Bob", "***", "m.example.xyz", true)));
+        });
+
+        pollUiThread(() -> getBottomSheetState() == SheetState.FULL);
+        assertThat(getCredentials().getChildCount(), is(2));
+        assertThat(getCredentialNameAt(0).getText(), is("Ana"));
+        assertThat(getCredentialPasswordAt(0).getText(), is("S3cr3t"));
+        assertThat(getCredentialPasswordAt(0).getTransformationMethod(),
+                instanceOf(PasswordTransformationMethod.class));
+        assertThat(getCredentialNameAt(1).getText(), is("Bob"));
+        assertThat(getCredentialPasswordAt(1).getText(), is("***"));
+        assertThat(getCredentialPasswordAt(1).getTransformationMethod(),
+                instanceOf(PasswordTransformationMethod.class));
+    }
+
+    @Test
+    @MediumTest
+    public void testCredentialsAreClickable() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mModel.get(CREDENTIAL_LIST)
+                    .addAll(Arrays.asList(new Credential("Carl", "G3h3!m", "", false),
+                            new Credential("Bob", "***", "m.example.xyz", true)));
+            mModel.set(VISIBLE, true);
+        });
+        pollUiThread(() -> getBottomSheetState() == SheetState.FULL);
+
+        assertNotNull(getCredentials().getChildAt(1));
+
+        TouchCommon.singleClickView(getCredentials().getChildAt(1));
+
+        waitForEvent().onSelectItemAt(1);
+    }
+
+    @Test
+    @MediumTest
     public void testDismissesWhenHidden() {
         TestThreadUtils.runOnUiThreadBlocking(() -> mModel.set(VISIBLE, true));
         pollUiThread(() -> getBottomSheetState() == SheetState.FULL);
@@ -115,4 +167,21 @@
         pollUiThread(() -> getActivity().getBottomSheet() != null);
         return getActivity().getBottomSheet().getSheetState();
     }
+
+    private ListView getCredentials() {
+        return mTouchToFillView.getContentView().findViewById(R.id.credential_list);
+    }
+
+    private TextView getCredentialNameAt(int index) {
+        return getCredentials().getChildAt(index).findViewById(R.id.username);
+    }
+
+    private TextView getCredentialPasswordAt(int index) {
+        return getCredentials().getChildAt(index).findViewById(R.id.password);
+    }
+
+    TouchToFillProperties.ViewEventListener waitForEvent() {
+        return verify(mMockListener,
+                timeout(ScalableTimeout.scaleTimeout(CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL)));
+    }
 }
\ No newline at end of file
diff --git a/chrome/browser/ui/android/widget/BUILD.gn b/chrome/browser/ui/android/widget/BUILD.gn
index 4051c34..fe306dc 100644
--- a/chrome/browser/ui/android/widget/BUILD.gn
+++ b/chrome/browser/ui/android/widget/BUILD.gn
@@ -20,6 +20,7 @@
     "java/src/org/chromium/chrome/browser/ui/widget/animation/AnimatorProperties.java",
     "java/src/org/chromium/chrome/browser/ui/widget/animation/CancelAwareAnimatorListener.java",
     "java/src/org/chromium/chrome/browser/ui/widget/animation/FocusAnimator.java",
+    "java/src/org/chromium/chrome/browser/ui/widget/animation/Interpolators.java",
     "java/src/org/chromium/chrome/browser/ui/widget/displaystyle/DisplayStyleObserver.java",
     "java/src/org/chromium/chrome/browser/ui/widget/displaystyle/DisplayStyleObserverAdapter.java",
     "java/src/org/chromium/chrome/browser/ui/widget/displaystyle/HorizontalDisplayStyle.java",
@@ -35,6 +36,7 @@
   deps = [
     ":ui_widget_java_resources",
     "//base:base_java",
+    "//third_party/android_deps:android_support_v4_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:com_android_support_design_java",
     "//third_party/android_deps:com_android_support_interpolator_java",
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/FocusAnimator.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/FocusAnimator.java
index 27b4a1de..4c900e8a 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/FocusAnimator.java
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/FocusAnimator.java
@@ -9,7 +9,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
@@ -139,7 +138,7 @@
         // Set up and kick off the animation.
         AnimatorSet animator = new AnimatorSet();
         animator.setDuration(ANIMATION_LENGTH_MS);
-        animator.setInterpolator(new LinearOutSlowInInterpolator());
+        animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
         animator.playTogether(animators);
         animator.addListener(new AnimatorListenerAdapter() {
             @Override
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/Interpolators.java b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/Interpolators.java
new file mode 100644
index 0000000..96f8bd12
--- /dev/null
+++ b/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/animation/Interpolators.java
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ui.widget.animation;
+
+import android.support.v4.view.animation.FastOutLinearInInterpolator;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.support.v4.view.animation.LinearOutSlowInInterpolator;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+
+/** Reference to one of each standard interpolator to avoid allocations. */
+public class Interpolators {
+    public static final AccelerateInterpolator ACCELERATE_INTERPOLATOR =
+            new AccelerateInterpolator();
+    public static final DecelerateInterpolator DECELERATE_INTERPOLATOR =
+            new DecelerateInterpolator();
+    public static final FastOutLinearInInterpolator FAST_OUT_LINEAR_IN_INTERPOLATOR =
+            new FastOutLinearInInterpolator();
+    public static final FastOutSlowInInterpolator FAST_OUT_SLOW_IN_INTERPOLATOR =
+            new FastOutSlowInInterpolator();
+    public static final LinearInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+    public static final LinearOutSlowInInterpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR =
+            new LinearOutSlowInInterpolator();
+}
diff --git a/chrome/browser/ui/ash/launcher/launcher_app_service_app_updater.cc b/chrome/browser/ui/ash/launcher/launcher_app_service_app_updater.cc
index a061653..8d406ed 100644
--- a/chrome/browser/ui/ash/launcher/launcher_app_service_app_updater.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_app_service_app_updater.cc
@@ -7,8 +7,8 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/services/app_service/public/cpp/app_registry_cache.h"
 #include "chrome/services/app_service/public/cpp/app_update.h"
+#include "chrome/services/app_service/public/mojom/types.mojom.h"
 
 LauncherAppServiceAppUpdater::LauncherAppServiceAppUpdater(
     Delegate* delegate,
@@ -17,6 +17,10 @@
   apps::AppServiceProxy* proxy = apps::AppServiceProxyFactory::GetForProfile(
       Profile::FromBrowserContext(browser_context));
   if (proxy) {
+    proxy->AppRegistryCache().ForEachApp([this](const apps::AppUpdate& update) {
+      if (update.Readiness() == apps::mojom::Readiness::kReady)
+        this->installed_apps_.insert(update.AppId());
+    });
     Observe(&proxy->AppRegistryCache());
   }
 }
diff --git a/chrome/browser/ui/blocked_content/popup_blocker.cc b/chrome/browser/ui/blocked_content/popup_blocker.cc
index 950bc0db..1c1646b82 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/blocked_content/popup_blocker.h"
 
+#include <string>
+
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -16,6 +18,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/safe_browsing/triggers/ad_popup_trigger.h"
 #include "content/public/browser/page_navigator.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 
 namespace {
@@ -23,7 +26,7 @@
 // If the popup should be blocked, returns the reason why it was blocked.
 // Otherwise returns kNotBlocked.
 PopupBlockType ShouldBlockPopup(content::WebContents* web_contents,
-                                const base::Optional<GURL>& opener_url,
+                                const GURL* opener_url,
                                 bool user_gesture,
                                 const content::OpenURLParams* open_url_params) {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -38,7 +41,7 @@
   // the active entry is the page to be loaded as we navigate away from the
   // unloading page.
   const GURL& url =
-      opener_url ? opener_url.value() : web_contents->GetLastCommittedURL();
+      opener_url ? *opener_url : web_contents->GetLastCommittedURL();
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   if (url.is_valid() &&
@@ -68,41 +71,8 @@
   return PopupBlockType::kNotBlocked;
 }
 
-}  // namespace
-
-bool ConsiderForPopupBlocking(WindowOpenDisposition disposition) {
-  return disposition == WindowOpenDisposition::NEW_POPUP ||
-         disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
-         disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
-         disposition == WindowOpenDisposition::NEW_WINDOW;
-}
-
-bool MaybeBlockPopup(content::WebContents* web_contents,
-                     const base::Optional<GURL>& opener_url,
-                     NavigateParams* params,
-                     const content::OpenURLParams* open_url_params,
-                     const blink::mojom::WindowFeatures& window_features) {
-  DCHECK(web_contents);
-  DCHECK(!open_url_params ||
-         open_url_params->user_gesture == params->user_gesture);
-  PopupBlockerTabHelper::LogAction(PopupBlockerTabHelper::Action::kInitiated);
-
-  PopupBlockType block_type = ShouldBlockPopup(
-      web_contents, opener_url, params->user_gesture, open_url_params);
-  auto* popup_blocker = PopupBlockerTabHelper::FromWebContents(web_contents);
-  if (popup_blocker && block_type != PopupBlockType::kNotBlocked) {
-    popup_blocker->AddBlockedPopup(params, window_features, block_type);
-    if (safe_browsing::AdPopupTrigger::FromWebContents(web_contents)) {
-      content::RenderFrameHost* source_frame =
-          GetSourceFrameForPopup(params, open_url_params, web_contents);
-      safe_browsing::AdPopupTrigger::FromWebContents(web_contents)
-          ->PopupWasBlocked(source_frame);
-    }
-    return true;
-  }
-  return false;
-}
-
+// Tries to get the opener from either the |params| or |open_url_params|,
+// otherwise uses the focused frame from |web_contents| as a proxy.
 content::RenderFrameHost* GetSourceFrameForPopup(
     NavigateParams* params,
     const content::OpenURLParams* open_url_params,
@@ -122,3 +92,42 @@
   // and is used as a fallback in case opener information is not available.
   return web_contents->GetFocusedFrame();
 }
+
+}  // namespace
+
+bool ConsiderForPopupBlocking(WindowOpenDisposition disposition) {
+  return disposition == WindowOpenDisposition::NEW_POPUP ||
+         disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
+         disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
+         disposition == WindowOpenDisposition::NEW_WINDOW;
+}
+
+bool MaybeBlockPopup(content::WebContents* web_contents,
+                     const GURL* opener_url,
+                     NavigateParams* params,
+                     const content::OpenURLParams* open_url_params,
+                     const blink::mojom::WindowFeatures& window_features) {
+  DCHECK(web_contents);
+  DCHECK(!open_url_params ||
+         open_url_params->user_gesture == params->user_gesture);
+  PopupBlockerTabHelper::LogAction(PopupBlockerTabHelper::Action::kInitiated);
+
+  // Check |popup_blocker| first since it is cheaper than ShouldBlockPopup().
+  auto* popup_blocker = PopupBlockerTabHelper::FromWebContents(web_contents);
+  if (!popup_blocker)
+    return false;
+
+  PopupBlockType block_type = ShouldBlockPopup(
+      web_contents, opener_url, params->user_gesture, open_url_params);
+  if (block_type == PopupBlockType::kNotBlocked)
+    return false;
+
+  popup_blocker->AddBlockedPopup(params, window_features, block_type);
+  auto* trigger = safe_browsing::AdPopupTrigger::FromWebContents(web_contents);
+  if (trigger) {
+    content::RenderFrameHost* source_frame =
+        GetSourceFrameForPopup(params, open_url_params, web_contents);
+    trigger->PopupWasBlocked(source_frame);
+  }
+  return true;
+}
diff --git a/chrome/browser/ui/blocked_content/popup_blocker.h b/chrome/browser/ui/blocked_content/popup_blocker.h
index c926be6..cbebb84e 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker.h
+++ b/chrome/browser/ui/blocked_content/popup_blocker.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_UI_BLOCKED_CONTENT_POPUP_BLOCKER_H_
 #define CHROME_BROWSER_UI_BLOCKED_CONTENT_POPUP_BLOCKER_H_
 
-#include "base/optional.h"
-#include "content/public/browser/render_frame_host.h"
 #include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
 #include "ui/base/window_open_disposition.h"
 #include "url/gurl.h"
@@ -38,22 +36,15 @@
 // |blocked_popups_| container.
 //
 // |opener_url| is an optional parameter used to compute how the popup
-// permission will behave. If it is not set the current committed URL will be
+// permission will behave. If it is nullptr, the current committed URL will be
 // used instead.
 //
 // If this function returns true, then the contents of |params| is moved to
 // |blocked_popups_|.
 bool MaybeBlockPopup(content::WebContents* web_contents,
-                     const base::Optional<GURL>& opener_url,
+                     const GURL* opener_url,
                      NavigateParams* params,
                      const content::OpenURLParams* open_url_params,
                      const blink::mojom::WindowFeatures& window_features);
 
-// Tries to get the opener from either the |params| or |open_url_params|,
-// otherwise uses the focused frame from |web_contents| as a proxy.
-content::RenderFrameHost* GetSourceFrameForPopup(
-    NavigateParams* params,
-    const content::OpenURLParams* open_url_params,
-    content::WebContents* web_contents);
-
 #endif  // CHROME_BROWSER_UI_BLOCKED_CONTENT_POPUP_BLOCKER_H_
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
index 73d4a641..cda6776 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
@@ -242,7 +242,7 @@
   nav_params.FillNavigateParamsFromOpenURLParams(params);
   nav_params.source_contents = web_contents();
   nav_params.user_gesture = true;
-  MaybeBlockPopup(web_contents(), base::nullopt, &nav_params, &params,
+  MaybeBlockPopup(web_contents(), nullptr, &nav_params, &params,
                   blink::mojom::WindowFeatures());
 
   EXPECT_EQ(1u, PopupBlockerTabHelper::FromWebContents(web_contents())
@@ -270,7 +270,7 @@
   nav_params.FillNavigateParamsFromOpenURLParams(params);
   nav_params.source_contents = web_contents();
   nav_params.user_gesture = true;
-  MaybeBlockPopup(web_contents(), base::nullopt, &nav_params, &params,
+  MaybeBlockPopup(web_contents(), nullptr, &nav_params, &params,
                   blink::mojom::WindowFeatures());
 
   EXPECT_EQ(0u, PopupBlockerTabHelper::FromWebContents(web_contents())
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 24515ab..3d5d9ac0 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1415,8 +1415,8 @@
   nav_params.blob_url_loader_factory = params.blob_url_loader_factory;
   nav_params.href_translate = params.href_translate;
   bool is_popup = source && ConsiderForPopupBlocking(params.disposition);
-  if (is_popup && MaybeBlockPopup(source, base::Optional<GURL>(), &nav_params,
-                                  &params, blink::mojom::WindowFeatures())) {
+  if (is_popup && MaybeBlockPopup(source, nullptr, &nav_params, &params,
+                                  blink::mojom::WindowFeatures())) {
     return nullptr;
   }
 
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
index 738b00a..0260cc80 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -1245,7 +1245,7 @@
   for (size_t i = 1; i <= kItemCount; i++) {
     NavigateParams navigate_params =
         params.CreateNavigateParams(web_contents());
-    EXPECT_TRUE(MaybeBlockPopup(web_contents(), url, &navigate_params,
+    EXPECT_TRUE(MaybeBlockPopup(web_contents(), &url, &navigate_params,
                                 nullptr /*=open_url_params*/,
                                 params.features()));
     EXPECT_EQ(i, list_items.size());
diff --git a/chrome/browser/ui/login/login_handler_browsertest.cc b/chrome/browser/ui/login/login_handler_browsertest.cc
index 796ed3d9..ac929c60 100644
--- a/chrome/browser/ui/login/login_handler_browsertest.cc
+++ b/chrome/browser/ui/login/login_handler_browsertest.cc
@@ -1694,6 +1694,52 @@
   EXPECT_EQ(expected_title, auth_supplied_title_watcher.WaitAndGetTitle());
 }
 
+// Tests that when HTTP Auth committed interstitials are enabled, showing a
+// login prompt in a new window opened from window.open() does not
+// crash. Regression test for https://crbug.com/1005096.
+IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, PromptWithNoVisibleEntry) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+
+  // Open a new window via JavaScript and navigate it to a page that delivers an
+  // auth prompt.
+  GURL test_page = embedded_test_server()->GetURL(kAuthBasicPage);
+  ASSERT_NE(false, content::EvalJs(contents, "w = window.open();"));
+  content::WebContents* opened_contents =
+      browser()->tab_strip_model()->GetWebContentsAt(1);
+  NavigationController* opened_controller = &opened_contents->GetController();
+  ASSERT_FALSE(opened_controller->GetVisibleEntry());
+  LoginPromptBrowserTestObserver observer;
+  observer.Register(content::Source<NavigationController>(opened_controller));
+  WindowedAuthNeededObserver auth_needed_waiter(opened_controller);
+  ASSERT_NE(false, content::EvalJs(contents, "w.location.href = '" +
+                                                 test_page.spec() + "';"));
+
+  // Test that the login prompt displays above an empty page.
+  EXPECT_EQ(
+      "<head></head><body></body>",
+      content::EvalJs(opened_contents, "document.documentElement.innerHTML"));
+
+  auth_needed_waiter.Wait();
+  ASSERT_EQ(1u, observer.handlers().size());
+
+  // Test that credentials are handled correctly.
+  WindowedAuthSuppliedObserver auth_supplied_waiter(opened_controller);
+  LoginHandler* handler = *observer.handlers().begin();
+  SetAuthFor(handler);
+  auth_supplied_waiter.Wait();
+
+  base::string16 expected_title = ExpectedTitleFromAuth(
+      base::ASCIIToUTF16("basicuser"), base::ASCIIToUTF16("secret"));
+  content::TitleWatcher auth_supplied_title_watcher(opened_contents,
+                                                    expected_title);
+  EXPECT_EQ(expected_title, auth_supplied_title_watcher.WaitAndGetTitle());
+}
+
 // Tests that FTP auth challenges appear over a blank committed interstitial.
 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, FtpAuth) {
   net::SpawnedTestServer ftp_server(
diff --git a/chrome/browser/ui/login/login_tab_helper.cc b/chrome/browser/ui/login/login_tab_helper.cc
index 9e6df4c..c0d6c23 100644
--- a/chrome/browser/ui/login/login_tab_helper.cc
+++ b/chrome/browser/ui/login/login_tab_helper.cc
@@ -159,8 +159,17 @@
     content::NavigationHandle* navigation_handle) {
   // If the user has just cancelled the auth prompt for this navigation, then
   // the page is being refreshed to retrieve the 401 body from the server, so
-  // allow the refresh to proceed.
-  if (web_contents()->GetController().GetVisibleEntry()->GetUniqueID() ==
+  // allow the refresh to proceed. The entry to compare against is the pending
+  // entry, because while refreshing after cancelling the prompt, the page that
+  // showed the prompt will be the pending entry until the refresh
+  // commits. Comparing against GetVisibleEntry() would also work, but it's less
+  // specific and not guaranteed to exist in all cases (e.g., in the case of
+  // navigating a window just opened via window.open()).
+  //
+  // TODO(https://crbug.com/1006955): if this line is crashing, the assumption
+  // that GetPendingEntry() must be non-null is incorrect, in which case a null
+  // check should be added here.
+  if (web_contents()->GetController().GetPendingEntry()->GetUniqueID() ==
       navigation_entry_id_with_cancelled_prompt_) {
     // Note the navigation handle ID so that when this refresh navigation
     // finishes, DidFinishNavigation declines to show another login prompt. We
diff --git a/chrome/browser/ui/omnibox/omnibox_theme.cc b/chrome/browser/ui/omnibox/omnibox_theme.cc
index a35ba09..57330f1 100644
--- a/chrome/browser/ui/omnibox/omnibox_theme.cc
+++ b/chrome/browser/ui/omnibox/omnibox_theme.cc
@@ -18,6 +18,7 @@
 namespace {
 
 int GetThemePropertyId(OmniboxPart part, OmniboxPartState state) {
+  const bool selected = state == OmniboxPartState::SELECTED;
   switch (part) {
     case OmniboxPart::LOCATION_BAR_BACKGROUND:
       return state == OmniboxPartState::HOVERED
@@ -39,16 +40,21 @@
       }
     case OmniboxPart::LOCATION_BAR_CLEAR_ALL:
     case OmniboxPart::LOCATION_BAR_TEXT_DEFAULT:
-    case OmniboxPart::RESULTS_TEXT_DEFAULT:
       return TP::COLOR_OMNIBOX_TEXT;
+    case OmniboxPart::RESULTS_TEXT_DEFAULT:
+      return selected ? TP::COLOR_OMNIBOX_RESULTS_TEXT_SELECTED
+                      : TP::COLOR_OMNIBOX_TEXT;
     case OmniboxPart::LOCATION_BAR_TEXT_DIMMED:
       return TP::COLOR_OMNIBOX_TEXT_DIMMED;
     case OmniboxPart::RESULTS_TEXT_DIMMED:
-      return TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED;
+      return selected ? TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED_SELECTED
+                      : TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED;
     case OmniboxPart::RESULTS_ICON:
-      return TP::COLOR_OMNIBOX_RESULTS_ICON;
+      return selected ? TP::COLOR_OMNIBOX_RESULTS_ICON_SELECTED
+                      : TP::COLOR_OMNIBOX_RESULTS_ICON;
     case OmniboxPart::RESULTS_TEXT_URL:
-      return TP::COLOR_OMNIBOX_RESULTS_URL;
+      return selected ? TP::COLOR_OMNIBOX_RESULTS_URL_SELECTED
+                      : TP::COLOR_OMNIBOX_RESULTS_URL;
     case OmniboxPart::LOCATION_BAR_BUBBLE_OUTLINE:
       return OmniboxFieldTrial::IsExperimentalKeywordModeEnabled()
                  ? TP::COLOR_OMNIBOX_BUBBLE_OUTLINE_EXPERIMENTAL_KEYWORD_MODE
diff --git a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.cc b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.cc
index e48a9da..4aa8693 100644
--- a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.cc
+++ b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.cc
@@ -162,7 +162,7 @@
                                                 int index) {
   ValidDeviceItem item(device_name, cache_guid);
   int command_id =
-      (menu_type_ == kTab) ? kShareTabCommandId : kShareLinkCommandId + index;
+      ((menu_type_ == kTab) ? kShareTabCommandId : kShareLinkCommandId) + index;
   InsertItemAt(index, command_id, base::UTF8ToUTF16(device_name));
   valid_device_items_.push_back(item);
 }
diff --git a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model_unittest.cc b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model_unittest.cc
new file mode 100644
index 0000000..03bd063
--- /dev/null
+++ b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model_unittest.cc
@@ -0,0 +1,121 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.h"
+
+#include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
+#include "components/send_tab_to_self/test_send_tab_to_self_model.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace send_tab_to_self {
+
+namespace {
+
+using testing::_;
+using testing::Return;
+using testing::SaveArg;
+
+class SendTabToSelfModelMock : public TestSendTabToSelfModel {
+ public:
+  SendTabToSelfModelMock() = default;
+  ~SendTabToSelfModelMock() override = default;
+
+  MOCK_METHOD0(GetTargetDeviceInfoSortedList, std::vector<TargetDeviceInfo>());
+
+  MOCK_METHOD4(AddEntry,
+               const SendTabToSelfEntry*(const GURL&,
+                                         const std::string&,
+                                         base::Time,
+                                         const std::string&));
+
+  bool IsReady() override { return true; }
+};
+
+class TestSendTabToSelfSyncService : public SendTabToSelfSyncService {
+ public:
+  TestSendTabToSelfSyncService() = default;
+  ~TestSendTabToSelfSyncService() override = default;
+
+  SendTabToSelfModel* GetSendTabToSelfModel() override {
+    return &send_tab_to_self_model_mock_;
+  }
+
+ protected:
+  SendTabToSelfModelMock send_tab_to_self_model_mock_;
+};
+
+std::unique_ptr<KeyedService> BuildTestSendTabToSelfSyncService(
+    content::BrowserContext* context) {
+  return std::make_unique<TestSendTabToSelfSyncService>();
+}
+
+TargetDeviceInfo BuildTargetDeviceInfo(const std::string& device_name,
+                                       const std::string& cache_guid) {
+  return TargetDeviceInfo(device_name, cache_guid,
+                          sync_pb::SyncEnums_DeviceType_TYPE_OTHER,
+                          base::Time());
+}
+
+class SendTabToSelfSubMenuModelTest : public BrowserWithTestWindowTest {
+ public:
+  SendTabToSelfSubMenuModelTest() = default;
+  ~SendTabToSelfSubMenuModelTest() override = default;
+
+  void SetUp() override {
+    BrowserWithTestWindowTest::SetUp();
+
+    // Set up all test conditions to let ShouldOfferFeature() return true.
+    GURL url("https://www.test.com");
+    AddTab(browser(), url);
+    NavigateAndCommitActiveTabWithTitle(browser(), url,
+                                        base::ASCIIToUTF16("test"));
+  }
+
+  void SetUpTestService() {
+    SendTabToSelfSyncServiceFactory::GetInstance()->SetTestingFactory(
+        browser()->profile(),
+        base::BindRepeating(&BuildTestSendTabToSelfSyncService));
+  }
+
+  SendTabToSelfModelMock* GetSendTabToSelfModelMock() {
+    return static_cast<SendTabToSelfModelMock*>(
+        SendTabToSelfSyncServiceFactory::GetForProfile(browser()->profile())
+            ->GetSendTabToSelfModel());
+  }
+};
+
+TEST_F(SendTabToSelfSubMenuModelTest, ExecuteCommandTab) {
+  SetUpTestService();
+
+  SendTabToSelfModelMock* model_mock = GetSendTabToSelfModelMock();
+  std::vector<TargetDeviceInfo> devices = {
+      BuildTargetDeviceInfo("device0", "0"),
+      BuildTargetDeviceInfo("device1", "1"),
+      BuildTargetDeviceInfo("device2", "2")};
+
+  EXPECT_CALL(*model_mock, GetTargetDeviceInfoSortedList())
+      .WillOnce(Return(devices));
+  SendTabToSelfSubMenuModel sub_menu_model(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      send_tab_to_self::SendTabToSelfMenuType::kTab);
+
+  std::string device_guid;
+  EXPECT_CALL(*model_mock, AddEntry(_, _, _, _))
+      .WillRepeatedly(
+          DoAll(SaveArg<3>(&device_guid), testing::Return(nullptr)));
+
+  // Check that all devices can be selected.
+  for (int i = 0; i < (int)devices.size(); i++) {
+    device_guid = std::string();
+    sub_menu_model.ExecuteCommand(SendTabToSelfSubMenuModel::kMinCommandId + i,
+                                  -1);
+    EXPECT_EQ(devices[i].cache_guid, device_guid) << "for index: " << i;
+  }
+}
+
+}  // namespace
+
+}  // namespace send_tab_to_self
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index ed269047..63f9f4ae8 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -883,17 +883,12 @@
 }
 
 void AppMenuModel::CreateZoomMenu() {
-  // WARNING: Mac does not use the ButtonMenuItemModel, but instead defines the
-  // layout for this menu item in AppMenu.xib. It does, however, use the
-  // command_id value from AddButtonItem() to identify this special item.
   zoom_menu_item_model_.reset(new ui::ButtonMenuItemModel(IDS_ZOOM_MENU, this));
   zoom_menu_item_model_->AddGroupItemWithStringId(IDC_ZOOM_MINUS,
                                                   IDS_ZOOM_MINUS2);
   zoom_menu_item_model_->AddGroupItemWithStringId(IDC_ZOOM_PLUS,
                                                   IDS_ZOOM_PLUS2);
-  // TODO(https://crbug.com/957391): Remove the former IDR_ parameter here once
-  // the change to remove it from the model (crrev.com/1816118) is in.
-  zoom_menu_item_model_->AddItemWithImage(IDC_FULLSCREEN, -1);
+  zoom_menu_item_model_->AddImageItem(IDC_FULLSCREEN);
   AddButtonItem(IDC_ZOOM_MENU, zoom_menu_item_model_.get());
 }
 
diff --git a/chrome/browser/ui/views/first_run_dialog.cc b/chrome/browser/ui/views/first_run_dialog.cc
index d3e55388..6b0d433 100644
--- a/chrome/browser/ui/views/first_run_dialog.cc
+++ b/chrome/browser/ui/views/first_run_dialog.cc
@@ -17,6 +17,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/crash/content/app/crashpad.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
@@ -37,8 +38,14 @@
 namespace {
 
 void InitCrashReporterIfEnabled(bool enabled) {
+#if defined(OS_WIN)
   if (enabled)
     breakpad::InitCrashReporter(std::string());
+#elif defined(OS_LINUX)
+  if (!crash_reporter::IsCrashpadEnabled() && enabled) {
+    breakpad::InitCrashReporter(std::string());
+  }
+#endif
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc b/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc
index 7846131..2dfdfc6 100644
--- a/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc
@@ -68,14 +68,18 @@
 
 void MediaToolbarButtonView::Enable() {
   SetEnabled(true);
-  UpdateIcon();
   InformIPHOfButtonEnabled();
+#if defined(OS_MACOSX)
+  UpdateIcon();
+#endif  // defined(OS_MACOSX)
 }
 
 void MediaToolbarButtonView::Disable() {
   SetEnabled(false);
-  UpdateIcon();
   InformIPHOfButtonDisabledorHidden();
+#if defined(OS_MACOSX)
+  UpdateIcon();
+#endif  // defined(OS_MACOSX)
 }
 
 SkColor MediaToolbarButtonView::GetInkDropBaseColor() const {
@@ -85,6 +89,9 @@
 }
 
 void MediaToolbarButtonView::UpdateIcon() {
+  if (!GetWidget())
+    return;
+
   const gfx::VectorIcon& icon = ::vector_icons::kQueueMusicIcon;
 
   const SkColor normal_color =
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
index 6b4ae8f..3a9ef14 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
@@ -10,17 +10,23 @@
 #include <string>
 
 #include "base/bind.h"
+#include "base/environment.h"
+#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/nix/xdg_util.h"
 #include "base/numerics/checked_math.h"
 #include "base/process/process.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "base/task_runner_util.h"
 #include "components/dbus/menu/menu.h"
 #include "components/dbus/properties/dbus_properties.h"
 #include "components/dbus/properties/success_barrier_callback.h"
 #include "components/dbus/properties/types.h"
 #include "components/dbus/thread_linux/dbus_thread_linux.h"
+#include "content/public/browser/browser_thread.h"
 #include "dbus/bus.h"
 #include "dbus/exported_object.h"
 #include "dbus/message.h"
@@ -71,6 +77,7 @@
 const char kPropertyAttentionMovieName[] = "AttentionMovieName";
 const char kPropertyCategory[] = "Category";
 const char kPropertyIconName[] = "IconName";
+const char kPropertyIconThemePath[] = "IconThemePath";
 const char kPropertyId[] = "Id";
 const char kPropertyOverlayIconName[] = "OverlayIconName";
 const char kPropertyStatus[] = "Status";
@@ -82,6 +89,7 @@
 
 // Signals.
 const char kSignalNewIcon[] = "NewIcon";
+const char kSignalNewIconThemePath[] = "NewIconThemePath";
 const char kSignalNewToolTip[] = "NewToolTip";
 
 // Property values.
@@ -131,9 +139,56 @@
       DbusString(text), DbusString(""));
 }
 
+bool ShouldWriteIconToFile() {
+  auto env = base::Environment::Create();
+  switch (base::nix::GetDesktopEnvironment(env.get())) {
+    case base::nix::DESKTOP_ENVIRONMENT_GNOME:
+      // gnome-shell-extension-appindicator doesn't downsize icons when they're
+      // given as DBus pixmaps.  But it does when icons are given as files.
+    case base::nix::DESKTOP_ENVIRONMENT_PANTHEON:
+      // wingpanel-indicator-ayatana only supports file icons.
+      return true;
+    case base::nix::DESKTOP_ENVIRONMENT_OTHER:
+    case base::nix::DESKTOP_ENVIRONMENT_CINNAMON:
+    case base::nix::DESKTOP_ENVIRONMENT_KDE3:
+    case base::nix::DESKTOP_ENVIRONMENT_KDE4:
+    case base::nix::DESKTOP_ENVIRONMENT_KDE5:
+    case base::nix::DESKTOP_ENVIRONMENT_UNITY:
+    case base::nix::DESKTOP_ENVIRONMENT_XFCE:
+      return false;
+  }
+  NOTREACHED();
+  return false;
+}
+
+base::FilePath WriteIconFile(size_t icon_file_id,
+                             scoped_refptr<base::RefCountedMemory> data) {
+  // Some StatusNotifierHosts require both the theme directory and icon name to
+  // change in order to update, so we need a new temporary directory and a
+  // unique base name for the file.
+  base::FilePath temp_dir;
+  if (!base::CreateNewTempDirectory("", &temp_dir))
+    return {};
+
+  base::FilePath file_path = temp_dir.Append(
+      "status_icon_" + base::NumberToString(icon_file_id) + ".png");
+  if (!base::WriteFile(file_path, data->front_as<char>(), data->size())) {
+    base::DeleteFile(temp_dir, true);
+    return {};
+  }
+
+  return file_path;
+}
+
 }  // namespace
 
-StatusIconLinuxDbus::StatusIconLinuxDbus() {
+StatusIconLinuxDbus::StatusIconLinuxDbus()
+    : should_write_icon_to_file_(ShouldWriteIconToFile()),
+      icon_task_runner_(base::CreateSequencedTaskRunner(
+          {base::ThreadPool(), base::MayBlock(),
+           base::TaskPriority::USER_VISIBLE,
+           base::TaskShutdownBehavior::BLOCK_SHUTDOWN})) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   dbus::Bus::Options bus_options;
   bus_options.bus_type = dbus::Bus::SESSION;
   bus_options.connection_type = dbus::Bus::PRIVATE;
@@ -142,22 +197,13 @@
   CheckStatusNotifierWatcherHasOwner();
 }
 
-StatusIconLinuxDbus::~StatusIconLinuxDbus() {
-  bus_->GetDBusTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus_));
-}
-
 void StatusIconLinuxDbus::SetIcon(const gfx::ImageSkia& image) {
-  if (!properties_)
-    return;
-
-  properties_->SetProperty(kInterfaceStatusNotifierItem, kPropertyIconPixmap,
-                           MakeDbusImage(image), true, false);
-  dbus::Signal signal(kInterfaceStatusNotifierItem, kSignalNewIcon);
-  item_->SendSignal(&signal);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  SetIconImpl(image, true);
 }
 
 void StatusIconLinuxDbus::SetToolTip(const base::string16& tool_tip) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!properties_)
     return;
 
@@ -171,10 +217,12 @@
 }
 
 void StatusIconLinuxDbus::UpdatePlatformContextMenu(ui::MenuModel* model) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   UpdateMenuImpl(model, true);
 }
 
 void StatusIconLinuxDbus::RefreshPlatformContextMenu() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // This codepath gets called for property changes like changed labels or
   // icons, but also for layout changes like deleted items.
   // TODO(thomasanderson): Split this into two methods so we can avoid
@@ -183,11 +231,20 @@
 }
 
 void StatusIconLinuxDbus::ExecuteCommand(int command_id, int event_flags) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_EQ(command_id, 0);
   delegate_->OnClick();
 }
 
+StatusIconLinuxDbus::~StatusIconLinuxDbus() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  bus_->GetDBusTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus_));
+  CleanupIconFile();
+}
+
 void StatusIconLinuxDbus::CheckStatusNotifierWatcherHasOwner() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   dbus::ObjectProxy* bus_proxy =
       bus_->GetObjectProxy(DBUS_SERVICE_DBUS, dbus::ObjectPath(DBUS_PATH_DBUS));
   dbus::MethodCall method_call(DBUS_INTERFACE_DBUS, kMethodNameHasOwner);
@@ -200,6 +257,7 @@
 }
 
 void StatusIconLinuxDbus::OnNameHasOwnerResponse(dbus::Response* response) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   dbus::MessageReader reader(response);
   bool owned = false;
   if (!response || !reader.PopBool(&owned) || !owned) {
@@ -221,6 +279,7 @@
 }
 
 void StatusIconLinuxDbus::OnHostRegisteredResponse(dbus::Response* response) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!response) {
     delegate_->OnImplInitializationFailed();
     return;
@@ -242,6 +301,7 @@
 
 void StatusIconLinuxDbus::OnOwnership(const std::string& service_name,
                                       bool success) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!success) {
     delegate_->OnImplInitializationFailed();
     return;
@@ -291,27 +351,28 @@
   set_property(kPropertyAttentionIconName, DbusString(""));
   set_property(kPropertyAttentionMovieName, DbusString(""));
   set_property(kPropertyCategory, DbusString(kPropertyValueCategory));
-  set_property(kPropertyIconName, DbusString(""));
   set_property(kPropertyId, DbusString(PropertyIdFromId(service_id_)));
   set_property(kPropertyOverlayIconName, DbusString(""));
   set_property(kPropertyStatus, DbusString(kPropertyValueStatus));
   set_property(kPropertyTitle, DbusString(""));
   set_property(kPropertyAttentionIconPixmap,
                DbusArray<DbusStruct<DbusInt32, DbusInt32, DbusByteArray>>());
-  set_property(kPropertyIconPixmap, MakeDbusImage(delegate_->GetImage()));
   set_property(kPropertyOverlayIconPixmap,
                DbusArray<DbusStruct<DbusInt32, DbusInt32, DbusByteArray>>());
   set_property(kPropertyToolTip,
                MakeDbusToolTip(base::UTF16ToUTF8(delegate_->GetToolTip())));
+  SetIconImpl(delegate_->GetImage(), false);
 }
 
 void StatusIconLinuxDbus::OnExported(const std::string& interface_name,
                                      const std::string& method_name,
                                      bool success) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   barrier_.Run(success);
 }
 
 void StatusIconLinuxDbus::OnInitialized(bool success) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!success) {
     delegate_->OnImplInitializationFailed();
     return;
@@ -327,6 +388,7 @@
 }
 
 void StatusIconLinuxDbus::OnRegistered(dbus::Response* response) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!response)
     delegate_->OnImplInitializationFailed();
 }
@@ -334,6 +396,7 @@
 void StatusIconLinuxDbus::OnActivate(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender sender) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   delegate_->OnClick();
   sender.Run(dbus::Response::FromMethodCall(method_call));
 }
@@ -341,6 +404,7 @@
 void StatusIconLinuxDbus::OnContextMenu(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender sender) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   dbus::MessageReader reader(method_call);
   int32_t x;
   int32_t y;
@@ -364,6 +428,7 @@
 void StatusIconLinuxDbus::OnScroll(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender sender) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // Ignore scroll events.
   sender.Run(dbus::Response::FromMethodCall(method_call));
 }
@@ -371,6 +436,7 @@
 void StatusIconLinuxDbus::OnSecondaryActivate(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender sender) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // Intentionally ignore secondary activations.  In the future, we may decide
   // to run the same handler as regular activations.
   sender.Run(dbus::Response::FromMethodCall(method_call));
@@ -378,6 +444,7 @@
 
 void StatusIconLinuxDbus::UpdateMenuImpl(ui::MenuModel* model,
                                          bool send_signal) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!menu_)
     return;
 
@@ -398,3 +465,56 @@
   menu_->SetModel(concat_menu_.get(), send_signal);
   menu_runner_.reset();
 }
+
+void StatusIconLinuxDbus::SetIconImpl(const gfx::ImageSkia& image,
+                                      bool send_signals) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!properties_)
+    return;
+
+  if (should_write_icon_to_file_) {
+    base::PostTaskAndReplyWithResult(
+        icon_task_runner_.get(), FROM_HERE,
+        base::BindOnce(WriteIconFile, icon_file_id_++,
+                       gfx::Image(image).As1xPNGBytes()),
+        base::BindOnce(&StatusIconLinuxDbus::OnIconFileWritten, this));
+  } else {
+    properties_->SetProperty(kInterfaceStatusNotifierItem, kPropertyIconPixmap,
+                             MakeDbusImage(image), send_signals, false);
+    if (send_signals) {
+      dbus::Signal signal(kInterfaceStatusNotifierItem, kSignalNewIcon);
+      item_->SendSignal(&signal);
+    }
+  }
+}
+
+void StatusIconLinuxDbus::OnIconFileWritten(const base::FilePath& icon_file) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  CleanupIconFile();
+  icon_file_ = icon_file;
+  if (icon_file_.empty())
+    return;
+
+  properties_->SetProperty(kInterfaceStatusNotifierItem, kPropertyIconThemePath,
+                           DbusString(icon_file_.DirName().value()), false);
+  properties_->SetProperty(
+      kInterfaceStatusNotifierItem, kPropertyIconName,
+      DbusString(icon_file_.BaseName().RemoveExtension().value()), false);
+
+  dbus::Signal new_icon_theme_path_signal(kInterfaceStatusNotifierItem,
+                                          kSignalNewIconThemePath);
+  dbus::MessageWriter writer(&new_icon_theme_path_signal);
+  writer.AppendString(icon_file_.DirName().value());
+  item_->SendSignal(&new_icon_theme_path_signal);
+  dbus::Signal new_icon_signal(kInterfaceStatusNotifierItem, kSignalNewIcon);
+  item_->SendSignal(&new_icon_signal);
+}
+
+void StatusIconLinuxDbus::CleanupIconFile() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!icon_file_.empty()) {
+    icon_task_runner_->PostTask(
+        FROM_HERE, (base::BindOnce(base::IgnoreResult(&base::DeleteFile),
+                                   icon_file_.DirName(), true)));
+  }
+}
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h
index 67121638..5e686af33 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h
@@ -8,8 +8,11 @@
 #include <string>
 
 #include "base/callback_forward.h"
+#include "base/files/file_path.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
 #include "chrome/browser/ui/views/status_icons/concat_menu_model.h"
 #include "dbus/bus.h"
 #include "dbus/exported_object.h"
@@ -29,10 +32,10 @@
 // A status icon following the StatusNotifierItem specification.
 // https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem/
 class StatusIconLinuxDbus : public views::StatusIconLinux,
-                            public ui::SimpleMenuModel::Delegate {
+                            public ui::SimpleMenuModel::Delegate,
+                            public base::RefCounted<StatusIconLinuxDbus> {
  public:
   StatusIconLinuxDbus();
-  ~StatusIconLinuxDbus() override;
 
   // StatusIcon:
   void SetIcon(const gfx::ImageSkia& image) override;
@@ -44,6 +47,10 @@
   void ExecuteCommand(int command_id, int event_flags) override;
 
  private:
+  friend class base::RefCounted<StatusIconLinuxDbus>;
+
+  ~StatusIconLinuxDbus() override;
+
   // Step 0: send the request to verify that the StatusNotifierWatcher service
   // is owned.
   void CheckStatusNotifierWatcherHasOwner();
@@ -85,6 +92,12 @@
 
   void UpdateMenuImpl(ui::MenuModel* model, bool send_signal);
 
+  void SetIconImpl(const gfx::ImageSkia& image, bool send_signals);
+
+  void OnIconFileWritten(const base::FilePath& icon_file);
+
+  void CleanupIconFile();
+
   scoped_refptr<dbus::Bus> bus_;
 
   int service_id_ = 0;
@@ -111,6 +124,11 @@
   // our own menu.
   std::unique_ptr<views::MenuRunner> menu_runner_;
 
+  const bool should_write_icon_to_file_;
+  const scoped_refptr<base::SequencedTaskRunner> icon_task_runner_;
+  size_t icon_file_id_ = 0;
+  base::FilePath icon_file_;
+
   base::WeakPtrFactory<StatusIconLinuxDbus> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(StatusIconLinuxDbus);
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
index 1e1ca5c..070f1cf 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
@@ -8,6 +8,7 @@
 
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "ui/message_center/public/cpp/notifier_id.h"
 #include "ui/views/linux_ui/linux_ui.h"
@@ -42,11 +43,11 @@
 }  // namespace
 
 StatusIconLinuxWrapper::StatusIconLinuxWrapper(
-    std::unique_ptr<views::StatusIconLinux> status_icon,
+    views::StatusIconLinux* status_icon,
     StatusIconType status_icon_type,
     const gfx::ImageSkia& image,
     const base::string16& tool_tip)
-    : status_icon_(std::move(status_icon)),
+    : status_icon_(status_icon),
       status_icon_type_(status_icon_type),
       image_(GetBestImageRep(image)),
       tool_tip_(tool_tip),
@@ -54,6 +55,28 @@
   status_icon_->SetDelegate(this);
 }
 
+#if defined(USE_DBUS)
+StatusIconLinuxWrapper::StatusIconLinuxWrapper(
+    scoped_refptr<StatusIconLinuxDbus> status_icon,
+    const gfx::ImageSkia& image,
+    const base::string16& tool_tip)
+    : StatusIconLinuxWrapper(status_icon.get(), kTypeDbus, image, tool_tip) {
+  status_icon_dbus_ = status_icon;
+}
+#endif
+
+StatusIconLinuxWrapper::StatusIconLinuxWrapper(
+    std::unique_ptr<views::StatusIconLinux> status_icon,
+    StatusIconType status_icon_type,
+    const gfx::ImageSkia& image,
+    const base::string16& tool_tip)
+    : StatusIconLinuxWrapper(status_icon.get(),
+                             status_icon_type,
+                             image,
+                             tool_tip) {
+  status_icon_linux_ = std::move(status_icon);
+}
+
 StatusIconLinuxWrapper::~StatusIconLinuxWrapper() {
   if (menu_model_)
     menu_model_->RemoveObserver(this);
@@ -103,7 +126,9 @@
   switch (status_icon_type_) {
     case kTypeDbus:
 #if defined(USE_X11)
-      status_icon_ = std::make_unique<StatusIconLinuxX11>();
+      status_icon_dbus_.reset();
+      status_icon_linux_ = std::make_unique<StatusIconLinuxX11>();
+      status_icon_ = status_icon_linux_.get();
       status_icon_type_ = kTypeX11;
       status_icon_->SetDelegate(this);
       return;
@@ -113,7 +138,8 @@
       FALLTHROUGH;
 #endif
     case kTypeX11:
-      status_icon_.reset();
+      status_icon_linux_.reset();
+      status_icon_ = nullptr;
       status_icon_type_ = kTypeOther;
       if (menu_model_)
         menu_model_->RemoveObserver(this);
@@ -136,7 +162,7 @@
   if (base::FeatureList::IsEnabled(features::kEnableDbusAndX11StatusIcons)) {
 #if defined(USE_DBUS)
     return base::WrapUnique(new StatusIconLinuxWrapper(
-        std::make_unique<StatusIconLinuxDbus>(), kTypeDbus, image, tool_tip));
+        base::MakeRefCounted<StatusIconLinuxDbus>(), image, tool_tip));
 #elif defined(USE_X11)
     return base::WrapUnique(new StatusIconLinuxWrapper(
         std::make_unique<StatusIconLinuxX11>(), kTypeX11, image, tool_tip));
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
index 6542b57e4..3e6feee6 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
@@ -8,10 +8,13 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "chrome/browser/status_icons/desktop_notification_balloon.h"
 #include "chrome/browser/status_icons/status_icon.h"
 #include "ui/views/linux_ui/status_icon_linux.h"
 
+class StatusIconLinuxDbus;
+
 // Wrapper class for StatusIconLinux that implements the standard StatusIcon
 // interface. Also handles callbacks from StatusIconLinux.
 class StatusIconLinuxWrapper : public StatusIcon,
@@ -59,6 +62,15 @@
 
   // A status icon wrapper should only be created by calling
   // CreateWrappedStatusIcon().
+  StatusIconLinuxWrapper(views::StatusIconLinux* status_icon,
+                         StatusIconType status_icon_type,
+                         const gfx::ImageSkia& image,
+                         const base::string16& tool_tip);
+#if defined(USE_DBUS)
+  StatusIconLinuxWrapper(scoped_refptr<StatusIconLinuxDbus> status_icon,
+                         const gfx::ImageSkia& image,
+                         const base::string16& tool_tip);
+#endif
   StatusIconLinuxWrapper(std::unique_ptr<views::StatusIconLinux> status_icon,
                          StatusIconType status_icon_type,
                          const gfx::ImageSkia& image,
@@ -67,7 +79,14 @@
   // Notification balloon.
   DesktopNotificationBalloon notification_;
 
-  std::unique_ptr<views::StatusIconLinux> status_icon_;
+  // The status icon may be ref-counted (via |status_icon_dbus_|) or owned by
+  // |this| (via |status_icon_linux_|).  Either way, |status_icon_| points to
+  // the underlying object.
+#if defined(USE_DBUS)
+  scoped_refptr<StatusIconLinuxDbus> status_icon_dbus_;
+#endif
+  std::unique_ptr<views::StatusIconLinux> status_icon_linux_;
+  views::StatusIconLinux* status_icon_;
   StatusIconType status_icon_type_;
 
   gfx::ImageSkia image_;
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.cc b/chrome/browser/ui/views/tabs/new_tab_button.cc
index a59048b..89e1946 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.cc
+++ b/chrome/browser/ui/views/tabs/new_tab_button.cc
@@ -132,11 +132,6 @@
   return kClassName;
 }
 
-void NewTabButton::Layout() {
-  views::ImageButton::Layout();
-  ink_drop_container_->SetBoundsRect(GetLocalBounds());
-}
-
 void NewTabButton::AddLayerBeneathView(ui::Layer* new_layer) {
   ink_drop_container_->AddLayerBeneathView(new_layer);
 }
@@ -145,6 +140,14 @@
   ink_drop_container_->RemoveLayerBeneathView(old_layer);
 }
 
+void NewTabButton::OnBoundsChanged(const gfx::Rect& previous_bounds) {
+  ImageButton::OnBoundsChanged(previous_bounds);
+  ink_drop_container_->SetBoundsRect(GetLocalBounds());
+  SetProperty(
+      views::kHighlightPathKey,
+      new SkPath(GetBorderPath(GetContentsBounds().origin(), 1.0f, false)));
+}
+
 #if defined(OS_WIN)
 void NewTabButton::OnMouseReleased(const ui::MouseEvent& event) {
   if (!event.IsOnlyRightMouseButton()) {
@@ -184,13 +187,6 @@
   PaintPlusIcon(canvas);
 }
 
-void NewTabButton::OnBoundsChanged(const gfx::Rect& previous_bounds) {
-  ImageButton::OnBoundsChanged(previous_bounds);
-  SetProperty(
-      views::kHighlightPathKey,
-      new SkPath(GetBorderPath(GetContentsBounds().origin(), 1.0f, false)));
-}
-
 gfx::Size NewTabButton::CalculatePreferredSize() const {
   gfx::Size size = kButtonSize;
   const auto insets = GetInsets();
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.h b/chrome/browser/ui/views/tabs/new_tab_button.h
index b53bac3e..20e2de9 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.h
+++ b/chrome/browser/ui/views/tabs/new_tab_button.h
@@ -61,10 +61,13 @@
 
   // views::View:
   const char* GetClassName() const override;
-  void Layout() override;
   void AddLayerBeneathView(ui::Layer* new_layer) override;
   void RemoveLayerBeneathView(ui::Layer* old_layer) override;
 
+ protected:
+  // views::View:
+  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
+
  private:
 // views::ImageButton:
 #if defined(OS_WIN)
@@ -73,7 +76,6 @@
   void OnGestureEvent(ui::GestureEvent* event) override;
   void NotifyClick(const ui::Event& event) override;
   void PaintButtonContents(gfx::Canvas* canvas) override;
-  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   gfx::Size CalculatePreferredSize() const override;
 
   // views::MaskedTargeterDelegate:
diff --git a/chrome/browser/ui/views/tabs/tab_close_button.cc b/chrome/browser/ui/views/tabs/tab_close_button.cc
index 767f5ab..0c99bea 100644
--- a/chrome/browser/ui/views/tabs/tab_close_button.cc
+++ b/chrome/browser/ui/views/tabs/tab_close_button.cc
@@ -24,6 +24,7 @@
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/animation/ink_drop.h"
 #include "ui/views/animation/ink_drop_mask.h"
+#include "ui/views/layout/layout_provider.h"
 #include "ui/views/rect_based_targeting_utils.h"
 #include "ui/views/view_class_properties.h"
 
@@ -71,6 +72,10 @@
       color_utils::GetColorWithMaxContrast(background_color));
 }
 
+const char* TabCloseButton::GetClassName() const {
+  return "TabCloseButton";
+}
+
 views::View* TabCloseButton::GetTooltipHandlerForPoint(
     const gfx::Point& point) {
   // Tab close button has no children, so tooltip handler should be the same
@@ -90,16 +95,16 @@
   return !event.IsMiddleMouseButton() && handled;
 }
 
-void TabCloseButton::OnMouseMoved(const ui::MouseEvent& event) {
-  mouse_event_callback_.Run(this, event);
-  Button::OnMouseMoved(event);
-}
-
 void TabCloseButton::OnMouseReleased(const ui::MouseEvent& event) {
   mouse_event_callback_.Run(this, event);
   Button::OnMouseReleased(event);
 }
 
+void TabCloseButton::OnMouseMoved(const ui::MouseEvent& event) {
+  mouse_event_callback_.Run(this, event);
+  Button::OnMouseMoved(event);
+}
+
 void TabCloseButton::OnGestureEvent(ui::GestureEvent* event) {
   // Consume all gesture events here so that the parent (Tab) does not
   // start consuming gestures.
@@ -107,18 +112,6 @@
   event->SetHandled();
 }
 
-const char* TabCloseButton::GetClassName() const {
-  return "TabCloseButton";
-}
-
-void TabCloseButton::Layout() {
-  ImageButton::Layout();
-  auto path = std::make_unique<SkPath>();
-  gfx::Point center = GetContentsBounds().CenterPoint();
-  path->addCircle(center.x(), center.y(), GetWidth() / 2);
-  SetProperty(views::kHighlightPathKey, path.release());
-}
-
 gfx::Size TabCloseButton::CalculatePreferredSize() const {
   int width = GetWidth();
   gfx::Size size(width, width);
@@ -127,10 +120,23 @@
   return size;
 }
 
+void TabCloseButton::OnBoundsChanged(const gfx::Rect& previous_bounds) {
+  ImageButton::OnBoundsChanged(previous_bounds);
+  auto path = std::make_unique<SkPath>();
+  const gfx::Rect bounds = GetContentsBounds();
+  const gfx::Point center = bounds.CenterPoint();
+  const int radius = views::LayoutProvider::Get()->GetCornerRadiusMetric(
+      views::EMPHASIS_MAXIMUM, bounds.size());
+  path->addCircle(center.x(), center.y(), radius);
+  SetProperty(views::kHighlightPathKey, path.release());
+}
+
 std::unique_ptr<views::InkDropMask> TabCloseButton::CreateInkDropMask() const {
+  const gfx::Rect bounds = GetContentsBounds();
+  const int radius = views::LayoutProvider::Get()->GetCornerRadiusMetric(
+      views::EMPHASIS_MAXIMUM, bounds.size());
   return std::make_unique<views::CircleInkDropMask>(
-      size(), GetMirroredRect(GetContentsBounds()).CenterPoint(),
-      GetWidth() / 2);
+      size(), GetMirroredRect(bounds).CenterPoint(), radius);
 }
 
 void TabCloseButton::PaintButtonContents(gfx::Canvas* canvas) {
diff --git a/chrome/browser/ui/views/tabs/tab_close_button.h b/chrome/browser/ui/views/tabs/tab_close_button.h
index 9aa7a1a..ccf1e6b 100644
--- a/chrome/browser/ui/views/tabs/tab_close_button.h
+++ b/chrome/browser/ui/views/tabs/tab_close_button.h
@@ -38,17 +38,18 @@
   void SetIconColors(SkColor foreground_color, SkColor background_color);
 
   // views::ImageButton:
+  const char* GetClassName() const override;
   View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
-  void OnMouseMoved(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
+  void OnMouseMoved(const ui::MouseEvent& event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
-  const char* GetClassName() const override;
-  void Layout() override;
-  gfx::Size CalculatePreferredSize() const override;
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
 
  protected:
+  // views::ImageButton:
+  gfx::Size CalculatePreferredSize() const override;
+  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   void PaintButtonContents(gfx::Canvas* canvas) override;
 
  private:
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc
index 2be869c..3d8c942d 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/supervised_user/supervised_user_service.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler_utils.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_metrics_recorder.h"
 #include "chrome/services/app_service/public/cpp/app_registry_cache.h"
@@ -25,17 +26,20 @@
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/web_ui.h"
 #include "google_apis/gaia/gaia_constants.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace chromeos {
 
 AddSupervisionHandler::AddSupervisionHandler(
-    add_supervision::mojom::AddSupervisionHandlerRequest request,
+    mojo::PendingReceiver<add_supervision::mojom::AddSupervisionHandler>
+        receiver,
     content::WebUI* web_ui,
     signin::IdentityManager* identity_manager,
     Delegate* delegate)
     : web_ui_(web_ui),
       identity_manager_(identity_manager),
-      binding_(this, std::move(request)),
+      receiver_(this, std::move(receiver)),
       delegate_(delegate) {}
 
 AddSupervisionHandler::~AddSupervisionHandler() = default;
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h
index f6faafb..8310d5b 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h
@@ -9,7 +9,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace content {
 class WebUI;
@@ -39,7 +40,8 @@
 
   // |delegate| is owned by the caller and its lifetime must outlive |this|.
   AddSupervisionHandler(
-      add_supervision::mojom::AddSupervisionHandlerRequest request,
+      mojo::PendingReceiver<add_supervision::mojom::AddSupervisionHandler>
+          receiver,
       content::WebUI* web_ui,
       signin::IdentityManager* identity_manager,
       Delegate* delegate);
@@ -64,7 +66,7 @@
   signin::IdentityManager* identity_manager_;
   std::unique_ptr<signin::AccessTokenFetcher> oauth2_access_token_fetcher_;
 
-  mojo::Binding<add_supervision::mojom::AddSupervisionHandler> binding_;
+  mojo::Receiver<add_supervision::mojom::AddSupervisionHandler> receiver_;
 
   Delegate* delegate_;
 
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc
index d920540..6579db4 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/supervised_user/supervised_user_service.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/browser/ui/views/chrome_web_dialog_view.h"
+#include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler_utils.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_metrics_recorder.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.h"
@@ -24,7 +25,7 @@
 #include "components/google/core/common/google_util.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/resources/grit/ui_resources.h"
 #include "ui/web_dialogs/web_dialog_delegate.h"
@@ -160,14 +161,15 @@
 }
 
 void AddSupervisionUI::BindAddSupervisionHandler(
-    add_supervision::mojom::AddSupervisionHandlerRequest request) {
+    mojo::PendingReceiver<add_supervision::mojom::AddSupervisionHandler>
+        receiver) {
   signin::IdentityManager* identity_manager =
       test_identity_manager_
           ? test_identity_manager_
           : IdentityManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()));
 
   mojo_api_handler_ = std::make_unique<AddSupervisionHandler>(
-      std::move(request), web_ui(), identity_manager, this);
+      std::move(receiver), web_ui(), identity_manager, this);
 }
 
 void AddSupervisionUI::SetUpResources() {
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.h b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.h
index 1c91c5e..a05b13f 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.h
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.h
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h"
 #include "chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/views/controls/label.h"
@@ -68,7 +69,8 @@
 
  private:
   void BindAddSupervisionHandler(
-      add_supervision::mojom::AddSupervisionHandlerRequest request);
+      mojo::PendingReceiver<add_supervision::mojom::AddSupervisionHandler>
+          receiver);
   void SetUpResources();
   GURL GetAddSupervisionURL();
 
diff --git a/chrome/browser/ui/webui/crashes_ui.cc b/chrome/browser/ui/webui/crashes_ui.cc
index d150c2c..ade6add 100644
--- a/chrome/browser/ui/webui/crashes_ui.cc
+++ b/chrome/browser/ui/webui/crashes_ui.cc
@@ -37,6 +37,10 @@
 #include "chromeos/dbus/debug_daemon_client.h"
 #endif
 
+#if defined(OS_LINUX)
+#include "components/crash/content/app/crashpad.h"
+#endif
+
 using content::WebContents;
 using content::WebUIMessageHandler;
 
@@ -168,20 +172,23 @@
   system_crash_reporter = true;
 #endif
 
-  bool upload_list = crash_reporting_enabled;
-  bool support_manual_uploads = false;
-
+  bool using_crashpad = false;
 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_ANDROID)
-  // Maunal uploads currently are supported only for Crashpad-using platforms
-  // and Android, and only if crash uploads are not disabled by policy.
-  support_manual_uploads =
-      crash_reporting_enabled || !IsMetricsReportingPolicyManaged();
-
-  // Show crash reports regardless of |crash_reporting_enabled| so that users
-  // can manually upload those reports.
-  upload_list = true;
+  using_crashpad = true;
+#elif defined(OS_LINUX)
+  using_crashpad = crash_reporter::IsCrashpadEnabled();
 #endif
 
+  // Manual uploads currently are supported only for Crashpad-using platforms
+  // and only if crash uploads are not disabled by policy.
+  bool support_manual_uploads =
+      using_crashpad &&
+      (crash_reporting_enabled || !IsMetricsReportingPolicyManaged());
+
+  // Show crash reports regardless of |crash_reporting_enabled| when using
+  // Crashpad so that users can manually upload those reports.
+  bool upload_list = using_crashpad || crash_reporting_enabled;
+
   base::ListValue crash_list;
   if (upload_list)
     crash_reporter::UploadListToValue(upload_list_.get(), &crash_list);
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 8740d7a..46dc4431 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
@@ -157,7 +157,8 @@
   html_source->AddResourcePath("app-management/image_info.mojom-lite.js",
                                IDR_APP_MANAGEMENT_IMAGE_INFO_MOJO_LITE_JS);
 
-  ::settings::AddLocalizedStrings(html_source, profile);
+  ::settings::AddLocalizedStrings(html_source, profile,
+                                  web_ui->GetWebContents());
 
   auto plural_string_handler = std::make_unique<PluralStringHandler>();
   plural_string_handler->AddLocalizedString("profileLabel",
diff --git a/chrome/browser/ui/webui/settings/search_engines_handler.cc b/chrome/browser/ui/webui/settings/search_engines_handler.cc
index 37eab48..6f0374d 100644
--- a/chrome/browser/ui/webui/settings/search_engines_handler.cc
+++ b/chrome/browser/ui/webui/settings/search_engines_handler.cc
@@ -108,8 +108,7 @@
       list_controller_.table_model()->IndexOfTemplateURL(default_engine);
 
   // Build the first list (default search engines).
-  std::unique_ptr<base::ListValue> defaults =
-      std::make_unique<base::ListValue>();
+  auto defaults = std::make_unique<base::ListValue>();
   int last_default_engine_index =
       list_controller_.table_model()->last_search_engine_index();
 
@@ -122,7 +121,7 @@
   }
 
   // Build the second list (other search engines).
-  std::unique_ptr<base::ListValue> others = std::make_unique<base::ListValue>();
+  auto others = std::make_unique<base::ListValue>();
   int last_other_engine_index =
       list_controller_.table_model()->last_other_engine_index();
 
@@ -135,8 +134,7 @@
   }
 
   // Build the third list (omnibox extensions).
-  std::unique_ptr<base::ListValue> extensions =
-      std::make_unique<base::ListValue>();
+  auto extensions = std::make_unique<base::ListValue>();
   int engine_count = list_controller_.table_model()->RowCount();
 
   // Sanity check for https://crbug.com/781703.
@@ -146,8 +144,7 @@
     extensions->Append(CreateDictionaryForEngine(i, i == default_index));
   }
 
-  std::unique_ptr<base::DictionaryValue> search_engines_info(
-      new base::DictionaryValue);
+  auto search_engines_info = std::make_unique<base::DictionaryValue>();
   search_engines_info->Set("defaults", std::move(defaults));
   search_engines_info->Set("others", std::move(others));
   search_engines_info->Set("extensions", std::move(extensions));
@@ -185,7 +182,7 @@
   // chrome/browser/resources/settings/search_engines_page/
   // in @typedef for SearchEngine. Please update it whenever you add or remove
   // any keys here.
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  auto dict = std::make_unique<base::DictionaryValue>();
   dict->SetInteger("id", template_url->id());
   dict->SetString("name", template_url->short_name());
   dict->SetString("displayName",
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 c006d1ef..1c69a5fa 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -33,7 +33,11 @@
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/locale_settings.h"
+#include "components/autofill/content/browser/content_autofill_driver.h"
+#include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/autofill/core/browser/autofill_experiments.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/payments/credit_card_access_manager.h"
 #include "components/autofill/core/browser/payments/payments_service_url.h"
 #include "components/autofill/core/browser/payments/payments_util.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
@@ -1701,8 +1705,37 @@
                           base::size(kLocalizedStrings));
 }
 
+bool isUserFIDOVerifiable(autofill::PersonalDataManager* personal_data,
+                          content::WebContents* web_contents) {
+  if (personal_data->GetSyncSigninState() !=
+          autofill::AutofillSyncSigninState::
+              kSignedInAndWalletSyncTransportEnabled &&
+      personal_data->GetSyncSigninState() !=
+          autofill::AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled) {
+    return false;
+  }
+
+  autofill::ContentAutofillDriverFactory* autofill_driver_factory =
+      autofill::ContentAutofillDriverFactory::FromWebContents(web_contents);
+  if (!autofill_driver_factory)
+    return false;
+  autofill::ContentAutofillDriver* autofill_driver =
+      autofill_driver_factory->DriverForFrame(web_contents->GetMainFrame());
+  if (!autofill_driver)
+    return false;
+  autofill::AutofillManager* autofill_manager =
+      autofill_driver->autofill_manager();
+  if (!autofill_manager)
+    return false;
+
+  return autofill_manager->credit_card_access_manager()
+      ->GetOrCreateFIDOAuthenticator()
+      ->IsUserVerifiable();
+}
+
 void AddAutofillStrings(content::WebUIDataSource* html_source,
-                        Profile* profile) {
+                        Profile* profile,
+                        content::WebContents* web_contents) {
   static constexpr LocalizedString kLocalizedStrings[] = {
       {"autofillPageTitle", IDS_SETTINGS_AUTOFILL},
       {"passwords", IDS_SETTINGS_PASSWORDS},
@@ -1715,6 +1748,9 @@
       {"enableCreditCardsLabel", IDS_AUTOFILL_ENABLE_CREDIT_CARDS_TOGGLE_LABEL},
       {"enableCreditCardsSublabel",
        IDS_AUTOFILL_ENABLE_CREDIT_CARDS_TOGGLE_SUBLABEL},
+      {"enableCreditCardFIDOAuthLabel", IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_LABEL},
+      {"enableCreditCardFIDOAuthSublabel",
+       IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_SUBLABEL},
       {"addresses", IDS_AUTOFILL_ADDRESSES},
       {"addressesTitle", IDS_AUTOFILL_ADDRESSES_SETTINGS_TITLE},
       {"addAddressTitle", IDS_SETTINGS_AUTOFILL_ADDRESSES_ADD_TITLE},
@@ -1822,15 +1858,17 @@
 #else   // !defined(OS_CHROMEOS)
   is_guest_mode = profile->IsOffTheRecord();
 #endif  // defined(OS_CHROMEOS)
+  autofill::PersonalDataManager* personal_data =
+      autofill::PersonalDataManagerFactory::GetForProfile(profile);
   html_source->AddBoolean(
       "migrationEnabled",
-      !is_guest_mode &&
-          autofill::IsCreditCardMigrationEnabled(
-              autofill::PersonalDataManagerFactory::GetForProfile(profile),
-              profile->GetPrefs(),
-              ProfileSyncServiceFactory::GetForProfile(profile),
-              /*is_test_mode=*/false,
-              /*log_manager=*/nullptr));
+      !is_guest_mode && autofill::IsCreditCardMigrationEnabled(
+                            personal_data, profile->GetPrefs(),
+                            ProfileSyncServiceFactory::GetForProfile(profile),
+                            /*is_test_mode=*/false,
+                            /*log_manager=*/nullptr));
+  html_source->AddBoolean("userIsFIDOVerifiable",
+                          isUserFIDOVerifiable(personal_data, web_contents));
 
   html_source->AddBoolean(
       "passwordsLeakDetectionEnabled",
@@ -3339,10 +3377,11 @@
 }  // namespace
 
 void AddLocalizedStrings(content::WebUIDataSource* html_source,
-                         Profile* profile) {
+                         Profile* profile,
+                         content::WebContents* web_contents) {
   AddA11yStrings(html_source);
   AddAboutStrings(html_source);
-  AddAutofillStrings(html_source, profile);
+  AddAutofillStrings(html_source, profile, web_contents);
   AddAppearanceStrings(html_source, profile);
 
 #if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.h b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.h
index 1642eb7..a72290f4 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.h
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.h
@@ -9,6 +9,7 @@
 
 namespace content {
 class WebUIDataSource;
+class WebContents;
 }
 
 namespace settings {
@@ -17,7 +18,8 @@
 // causes |html_source| to expose a strings.js file from its source which
 // contains a mapping from string's name to its translated value.
 void AddLocalizedStrings(content::WebUIDataSource* html_source,
-                         Profile* profile);
+                         Profile* profile,
+                         content::WebContents* web_contents);
 
 }  // namespace settings
 
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index f0337c52..d20ae17 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -47,7 +47,6 @@
 #include "chrome/browser/ui/webui/settings/settings_security_key_handler.h"
 #include "chrome/browser/ui/webui/settings/settings_startup_pages_handler.h"
 #include "chrome/browser/ui/webui/settings/site_settings_handler.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/settings_resources.h"
@@ -271,10 +270,6 @@
       AccountConsistencyModeManager::IsDiceEnabledForProfile(profile));
 #endif  // !defined(OS_CHROMEOS)
 
-  html_source->AddBoolean(
-      "a11yEnhancements",
-      base::FeatureList::IsEnabled(features::kWebUIA11yEnhancements));
-
   html_source->AddBoolean("unifiedConsentEnabled",
                           unified_consent::IsUnifiedConsentFeatureEnabled());
 
@@ -339,7 +334,7 @@
   html_source->SetDefaultResource(IDR_SETTINGS_SETTINGS_HTML);
 #endif
 
-  AddLocalizedStrings(html_source, profile);
+  AddLocalizedStrings(html_source, profile, web_ui->GetWebContents());
 
   ManagedUIHandler::Initialize(web_ui, html_source);
 
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index ffbf1706..46b0cf6 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -763,6 +763,7 @@
       # Tests.
       sources += [
         "webxr_vr_consent_dialog_browser_test.cc",
+        "webxr_vr_frame_loop_browser_test.cc",
         "webxr_vr_frame_pose_browser_test.cc",
         "webxr_vr_indicators_browser_test.cc",
         "webxr_vr_input_browser_test.cc",
diff --git a/chrome/browser/vr/webxr_vr_frame_loop_browser_test.cc b/chrome/browser/vr/webxr_vr_frame_loop_browser_test.cc
new file mode 100644
index 0000000..4fd6200
--- /dev/null
+++ b/chrome/browser/vr/webxr_vr_frame_loop_browser_test.cc
@@ -0,0 +1,108 @@
+// 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 <memory>
+
+#include "base/environment.h"
+#include "base/run_loop.h"
+#include "chrome/browser/vr/test/mock_xr_device_hook_base.h"
+#include "chrome/browser/vr/test/multi_class_browser_test.h"
+#include "chrome/browser/vr/test/ui_utils.h"
+#include "chrome/browser/vr/test/webxr_vr_browser_test.h"
+
+namespace vr {
+
+namespace {
+
+class MyXRMock : public MockXRDeviceHookBase {
+ public:
+  void OnFrameSubmitted(
+      device_test::mojom::SubmittedFrameDataPtr frame_data,
+      device_test::mojom::XRTestHook::OnFrameSubmittedCallback callback) final;
+
+  // The test waits for a submitted frame before returning.
+  void WaitForFrames(int count) {
+    DCHECK(!wait_loop_);
+    wait_frame_count_ = num_frames_submitted_ + count;
+
+    base::RunLoop* wait_loop =
+        new base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed);
+    wait_loop_ = wait_loop;
+    wait_loop->Run();
+    delete wait_loop;
+  }
+
+  int FramesSubmitted() const { return num_frames_submitted_; }
+
+ private:
+  // Set to null on background thread after calling Quit(), so we can ensure we
+  // only call Quit once.
+  base::RunLoop* wait_loop_ = nullptr;
+
+  int wait_frame_count_ = 0;
+  int num_frames_submitted_ = 0;
+};
+
+void MyXRMock::OnFrameSubmitted(
+    device_test::mojom::SubmittedFrameDataPtr frame_data,
+    device_test::mojom::XRTestHook::OnFrameSubmittedCallback callback) {
+  num_frames_submitted_++;
+  if (num_frames_submitted_ >= wait_frame_count_ && wait_frame_count_ > 0 &&
+      wait_loop_) {
+    wait_loop_->Quit();
+    wait_loop_ = nullptr;
+  }
+
+  std::move(callback).Run();
+}
+
+}  // namespace
+
+WEBXR_VR_ALL_RUNTIMES_BROWSER_TEST_F(TestNoStalledFrameLoop) {
+  MyXRMock my_mock;
+
+  // Load the test page, and enter presentation.
+  t->LoadUrlAndAwaitInitialization(
+      t->GetFileUrlForHtmlTestFile("webxr_no_stalled_frame_loop"));
+  t->EnterSessionWithUserGestureOrFail();
+
+  // Wait for 2 frames to be submitted back to the device, but the js frame loop
+  // should've only been called once.
+  my_mock.WaitForFrames(2);
+  ASSERT_TRUE(t->RunJavaScriptAndExtractBoolOrFail("frame_count === 1"));
+
+  // Now restart the frame loop and wait for another frame to get submitted.
+  t->RunJavaScriptOrFail("setBaseLayer()");
+  t->PollJavaScriptBooleanOrFail("frame_count >= 2",
+                                 XrBrowserTestBase::kPollTimeoutMedium);
+
+  t->AssertNoJavaScriptErrors();
+}
+
+WEBXR_VR_ALL_RUNTIMES_BROWSER_TEST_F(TestLateSetOfBaseLayer) {
+  MyXRMock my_mock;
+
+  // Load the test page, and enter presentation.
+  t->LoadUrlAndAwaitInitialization(
+      t->GetFileUrlForHtmlTestFile("webxr_set_base_layer_late"));
+  t->EnterSessionWithUserGestureOrFail();
+
+  // Poll and have the javascript yield for 500 ms.  This should give us enough
+  // time for any frame requests that were going to propagate to propagate.
+  t->RunJavaScriptOrFail("delayMilliseconds(500)");
+  t->PollJavaScriptBooleanOrFail("delay_ended");
+
+  // No frames should have been submitted to either the JS or the runtime.
+  ASSERT_TRUE(t->RunJavaScriptAndExtractBoolOrFail("frame_count === 0"));
+  ASSERT_EQ(my_mock.FramesSubmitted(), 0);
+
+  // Now restart the frame loop and wait for a frame to get submitted.
+  t->RunJavaScriptOrFail("setBaseLayer()");
+  t->PollJavaScriptBooleanOrFail("frame_count >= 1",
+                                 XrBrowserTestBase::kPollTimeoutMedium);
+
+  t->AssertNoJavaScriptErrors();
+}
+
+}  // namespace vr
diff --git a/chrome/browser/web_applications/components/app_registrar.cc b/chrome/browser/web_applications/components/app_registrar.cc
index 63fb5f73a..b5c625e8 100644
--- a/chrome/browser/web_applications/components/app_registrar.cc
+++ b/chrome/browser/web_applications/components/app_registrar.cc
@@ -28,7 +28,7 @@
   observers_.AddObserver(observer);
 }
 
-void AppRegistrar::RemoveObserver(const AppRegistrarObserver* observer) {
+void AppRegistrar::RemoveObserver(AppRegistrarObserver* observer) {
   observers_.RemoveObserver(observer);
 }
 
diff --git a/chrome/browser/web_applications/components/app_registrar.h b/chrome/browser/web_applications/components/app_registrar.h
index 9582181..f4334f4d 100644
--- a/chrome/browser/web_applications/components/app_registrar.h
+++ b/chrome/browser/web_applications/components/app_registrar.h
@@ -94,7 +94,7 @@
   bool IsLocallyInstalled(const GURL& start_url) const;
 
   void AddObserver(AppRegistrarObserver* observer);
-  void RemoveObserver(const AppRegistrarObserver* observer);
+  void RemoveObserver(AppRegistrarObserver* observer);
 
   void NotifyWebAppInstalled(const AppId& app_id);
 
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 5eec67c..4cf3935 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -826,10 +826,6 @@
 // Compress remote-bound WebRTC event logs (if used; see kWebRtcRemoteEventLog).
 const base::Feature kWebRtcRemoteEventLogGzipped{
     "WebRtcRemoteEventLogGzipped", base::FEATURE_ENABLED_BY_DEFAULT};
-
-// Enable WebUI accessibility enhancements for review and testing.
-const base::Feature kWebUIA11yEnhancements{"WebUIA11yEnhancements",
-                                           base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
 // Whether to enable "dark mode" enhancements in Mac Mojave or Windows 10 for
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index dcbdee9..1395ff2 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -518,8 +518,6 @@
 extern const base::Feature kWebRtcRemoteEventLog;
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kWebRtcRemoteEventLogGzipped;
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kWebUIA11yEnhancements;
 #endif
 
 COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kWebUIDarkMode;
diff --git a/chrome/common/extensions/api/autofill_private.idl b/chrome/common/extensions/api/autofill_private.idl
index b41477f7..b2709a5 100644
--- a/chrome/common/extensions/api/autofill_private.idl
+++ b/chrome/common/extensions/api/autofill_private.idl
@@ -239,6 +239,9 @@
 
     // Logs that the server cards edit link was clicked.
     static void logServerCardLinkClicked();
+
+    // Enables or disables FIDO Authentication for credit card unmasking.
+    static void setCreditCardFIDOAuthEnabledState(boolean enabled);
   };
 
   interface Events {
diff --git a/chrome/installer/linux/BUILD.gn b/chrome/installer/linux/BUILD.gn
index 709552f..44a5594 100644
--- a/chrome/installer/linux/BUILD.gn
+++ b/chrome/installer/linux/BUILD.gn
@@ -29,6 +29,7 @@
 packaging_files_executables = [
   "$root_out_dir/chrome",
   "$root_out_dir/chrome_sandbox",
+  "$root_out_dir/crashpad_handler",
 ]
 packaging_files_shlibs = []
 
@@ -352,6 +353,7 @@
     "//chrome:packed_resources",
     "//chrome/browser/resources/media/mei_preload:component",
     "//sandbox/linux:chrome_sandbox",
+    "//third_party/crashpad/crashpad/handler:crashpad_handler",
   ]
   if (enable_nacl) {
     public_deps += [
diff --git a/chrome/installer/linux/common/installer.include b/chrome/installer/linux/common/installer.include
index b145f85..27393586 100644
--- a/chrome/installer/linux/common/installer.include
+++ b/chrome/installer/linux/common/installer.include
@@ -140,6 +140,13 @@
   STRIPPEDFILE="${BUILDDIR}/${PROGNAME}.stripped"
   install -m 755 "${STRIPPEDFILE}" "${STAGEDIR}/${INSTALLDIR}/${PROGNAME}"
 
+  # crashpad
+  buildfile="${BUILDDIR}/crashpad_handler"
+  strippedfile="${buildfile}.stripped"
+  debugfile="${buildfile}.debug"
+  "${BUILDDIR}/installer/common/eu-strip" -o "${strippedfile}" -f "${debugfile}" "${buildfile}"
+  install -m 755 "${strippedfile}" "${STAGEDIR}/${INSTALLDIR}/crashpad_handler"
+
   # resources
   install -m 644 "${BUILDDIR}/resources.pak" "${STAGEDIR}/${INSTALLDIR}/"
   # TODO(mmoss): This has broken a couple times on adding new .pak files. Maybe
diff --git a/chrome/installer/mac/BUILD.gn b/chrome/installer/mac/BUILD.gn
index 121a2db1..8b293d4 100644
--- a/chrome/installer/mac/BUILD.gn
+++ b/chrome/installer/mac/BUILD.gn
@@ -7,6 +7,7 @@
 import("//build/util/branding.gni")
 import("//build/util/version.gni")
 import("//chrome/process_version_rc_template.gni")
+import("mac_signing_sources.gni")
 
 group("mac") {
   public_deps = [
@@ -34,18 +35,6 @@
   ]
 }
 
-_mac_signing_sources = [
-  "signing/__init__.py",
-  "signing/commands.py",
-  "signing/chromium_config.py",
-  "signing/config.py",
-  "signing/model.py",
-  "signing/modification.py",
-  "signing/notarize.py",
-  "signing/pipeline.py",
-  "signing/signing.py",
-]
-
 copy("copy_signing") {
   visibility = [ ":copies" ]
 
@@ -53,7 +42,7 @@
     ":sign_config",
   ]
 
-  sources = _mac_signing_sources
+  sources = mac_signing_sources
 
   if (is_chrome_branded) {
     sources += [ "internal/internal_config.py" ]
@@ -119,7 +108,7 @@
 }
 
 group("mac_signing_tests") {
-  data = _mac_signing_sources + [
+  data = mac_signing_sources + [
            "signing/commands_test.py",
            "signing/model_test.py",
            "signing/modification_test.py",
diff --git a/chrome/installer/mac/mac_signing_sources.gni b/chrome/installer/mac/mac_signing_sources.gni
new file mode 100644
index 0000000..5b86d51
--- /dev/null
+++ b/chrome/installer/mac/mac_signing_sources.gni
@@ -0,0 +1,15 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mac_signing_sources = [
+  "signing/__init__.py",
+  "signing/commands.py",
+  "signing/chromium_config.py",
+  "signing/config.py",
+  "signing/model.py",
+  "signing/modification.py",
+  "signing/notarize.py",
+  "signing/pipeline.py",
+  "signing/signing.py",
+]
diff --git a/chrome/renderer/v8_unwinder_unittest.cc b/chrome/renderer/v8_unwinder_unittest.cc
index 0bf58c8..0d52efc2 100644
--- a/chrome/renderer/v8_unwinder_unittest.cc
+++ b/chrome/renderer/v8_unwinder_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <algorithm>
 #include <memory>
+#include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -178,8 +180,7 @@
 
 // Checks that unwinding from C++ through JavaScript and back into C++ succeeds.
 // NB: unwinding is only supported for 64 bit Windows and OS X.
-#if (defined(OS_WIN) && defined(ARCH_CPU_64_BITS)) || \
-    (defined(OS_MACOSX) && !defined(OS_IOS))
+#if (defined(OS_WIN) && defined(ARCH_CPU_64_BITS)) || defined(OS_MACOSX)
 #define MAYBE_UnwindThroughV8Frames UnwindThroughV8Frames
 #else
 #define MAYBE_UnwindThroughV8Frames DISABLED_UnwindThroughV8Frames
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 76d9c00..f0beace 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3167,7 +3167,6 @@
     "../browser/previews/previews_content_util_unittest.cc",
     "../browser/previews/previews_lite_page_decider_unittest.cc",
     "../browser/previews/previews_lite_page_infobar_delegate_unittest.cc",
-    "../browser/previews/previews_lite_page_navigation_throttle_unittest.cc",
     "../browser/previews/previews_lite_page_predictor_unittest.cc",
     "../browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc",
     "../browser/previews/previews_offline_helper_unittest.cc",
@@ -3846,6 +3845,7 @@
       "../browser/ui/search/search_ipc_router_policy_unittest.cc",
       "../browser/ui/search/search_ipc_router_unittest.cc",
       "../browser/ui/search/search_tab_helper_unittest.cc",
+      "../browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model_unittest.cc",
       "../browser/ui/serial/serial_chooser_controller_unittest.cc",
       "../browser/ui/tab_contents/tab_contents_iterator_unittest.cc",
       "../browser/ui/tabs/pinned_tab_codec_unittest.cc",
@@ -6012,6 +6012,7 @@
       "../browser/sync/test/integration/single_client_autofill_profile_sync_test.cc",
       "../browser/sync/test/integration/single_client_bookmarks_sync_test.cc",
       "../browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc",
+      "../browser/sync/test/integration/single_client_device_info_sync_test.cc",
       "../browser/sync/test/integration/single_client_dictionary_sync_test.cc",
       "../browser/sync/test/integration/single_client_directory_sync_test.cc",
       "../browser/sync/test/integration/single_client_extensions_sync_test.cc",
diff --git a/chrome/test/data/extensions/api_test/webrequest/framework.js b/chrome/test/data/extensions/api_test/webrequest/framework.js
index dd854dd..edfd83db 100644
--- a/chrome/test/data/extensions/api_test/webrequest/framework.js
+++ b/chrome/test/data/extensions/api_test/webrequest/framework.js
@@ -43,7 +43,7 @@
 
 // This is a debugging aid to print all received events as well as the
 // information whether they were expected.
-var logAllRequests = false;
+var debug = false;
 
 // Runs the |tests| using the |tab| as a default tab.
 function runTestsForTab(tests, tab) {
@@ -60,7 +60,14 @@
 // Creates an "about:blank" tab and runs |tests| with this tab as default.
 function runTests(tests) {
   chrome.test.getConfig(function(config) {
+    if (config.customArg == 'debug')
+      debug = true;
+
     var waitForAboutBlank = function(_, info, tab) {
+      if (debug) {
+        console.log("tabs.OnUpdated received in waitForAboutBlank: " +
+          JSON.stringify(info) + " " + JSON.stringify(tab));
+      }
       if (info.status == "complete" && tab.url == "about:blank") {
         chrome.tabs.onUpdated.removeListener(waitForAboutBlank);
         runTestsForTab(tests, tab);
@@ -119,6 +126,10 @@
 function navigateAndWait(url, callback) {
   var done = chrome.test.listenForever(chrome.tabs.onUpdated,
       function (_, info, tab) {
+    if (debug) {
+      console.log("tabs.OnUpdated received in navigateAndWait: " +
+        JSON.stringify(info) + " " + JSON.stringify(tab));
+    }
     if (tab.id == tabId && info.status == "complete") {
       if (callback) callback(tab);
       done();
@@ -372,8 +383,9 @@
   var retval;
   var retval_function;
   if (matchingExpectedEvent) {
-    if (logAllRequests) {
-      console.log("Expected: " + name + ": " + JSON.stringify(details));
+    if (debug) {
+      console.log("Expected event received: " + name + ": " +
+        JSON.stringify(details));
     }
     capturedEventData.push(
         {label: matchingExpectedEvent.label, event: name, details: details});
@@ -387,8 +399,9 @@
     retval = matchingExpectedEvent.retval;
     retval_function = matchingExpectedEvent.retval_function;
   } else {
-    if (logAllRequests) {
-      console.log('NOT Expected: ' + name + ': ' + JSON.stringify(details));
+    if (debug) {
+      console.log('NOT Expected event received: ' + name + ': ' +
+        JSON.stringify(details));
     }
     capturedUnexpectedData.push({event: name, details: details});
   }
diff --git a/chrome/test/data/pdf/basic_test.js b/chrome/test/data/pdf/basic_test.js
index ffe9f34..eba0c6ca0 100644
--- a/chrome/test/data/pdf/basic_test.js
+++ b/chrome/test/data/pdf/basic_test.js
@@ -67,7 +67,7 @@
 
     // Clicking on the plugin should close the bookmarks menu.
     chrome.test.assertFalse(dropdown.dropdownOpen);
-    MockInteractions.tap(dropdown.$.button);
+    dropdown.$.button.click();
     chrome.test.assertTrue(dropdown.dropdownOpen);
     // Generate pointer event manually, as MockInteractions doesn't include
     // this.
@@ -75,7 +75,7 @@
     chrome.test.assertFalse(dropdown.dropdownOpen,
         "Clicking plugin closes dropdown");
 
-    MockInteractions.tap(dropdown.$.button);
+    dropdown.$.button.click();
     chrome.test.assertTrue(dropdown.dropdownOpen);
     MockInteractions.pressAndReleaseKeyOn(document, ESC_KEY);
     chrome.test.assertFalse(dropdown.dropdownOpen,
diff --git a/chrome/test/data/pdf/bookmarks_test.js b/chrome/test/data/pdf/bookmarks_test.js
index f244b15b..cf6ba76 100644
--- a/chrome/test/data/pdf/bookmarks_test.js
+++ b/chrome/test/data/pdf/bookmarks_test.js
@@ -60,7 +60,7 @@
     var rootBookmarks =
         bookmarkContent.shadowRoot.querySelectorAll('viewer-bookmark');
     chrome.test.assertEq(3, rootBookmarks.length, "three root bookmarks");
-    MockInteractions.tap(rootBookmarks[0].$.expand);
+    rootBookmarks[0].$.expand.click();
 
     Polymer.dom.flush();
 
@@ -96,7 +96,7 @@
       lastXChange = undefined;
       lastYChange = undefined;
       lastUriNavigation = undefined;
-      MockInteractions.tap(tapTarget);
+      tapTarget.click();
       chrome.test.assertEq(expectedEvent.page, lastPageChange);
       chrome.test.assertEq(expectedEvent.x, lastXChange);
       chrome.test.assertEq(expectedEvent.y, lastYChange);
diff --git a/chrome/test/data/pdf/material_elements_test.js b/chrome/test/data/pdf/material_elements_test.js
index 6492926..fd5c7fd 100644
--- a/chrome/test/data/pdf/material_elements_test.js
+++ b/chrome/test/data/pdf/material_elements_test.js
@@ -35,8 +35,8 @@
    * past document bounds.
    */
   function testPageSelectorChange() {
-    var selector =
-        Polymer.Base.create('viewer-page-selector', {docLength: 1234});
+    var selector = document.createElement('viewer-page-selector');
+    selector.docLength = 1234;
     document.body.appendChild(selector);
 
     var input = selector.pageSelector;
@@ -75,8 +75,8 @@
    * Test that viewer-page-selector changes in response to setting docLength.
    */
   function testPageSelectorDocLength() {
-    var selector =
-        Polymer.Base.create('viewer-page-selector', {docLength: 1234});
+    var selector = document.createElement('viewer-page-selector');
+    selector.docLength = 1234;
     document.body.appendChild(selector);
     chrome.test.assertEq('1234', selector.$.pagelength.textContent);
     chrome.test.assertEq(
@@ -88,23 +88,22 @@
    * Test that clicking the dropdown icon opens/closes the dropdown.
    */
   function testToolbarDropdownShowHide() {
-    var dropdown = Polymer.Base.create('viewer-toolbar-dropdown', {
-      header: 'Test Menu',
-      closedIcon: 'closedIcon',
-      openIcon: 'openIcon'
-    });
+    var dropdown = document.createElement('viewer-toolbar-dropdown');
+    dropdown.header = 'Test Menu';
+    dropdown.closedIcon = 'closedIcon';
+    dropdown.openIcon = 'openIcon';
     document.body.appendChild(dropdown);
 
     const button = dropdown.$.button;
     chrome.test.assertFalse(dropdown.dropdownOpen);
     chrome.test.assertEq('closedIcon,cr:arrow-drop-down', button.ironIcon);
 
-    MockInteractions.tap(button);
+    button.click();
 
     chrome.test.assertTrue(dropdown.dropdownOpen);
     chrome.test.assertEq('openIcon,cr:arrow-drop-down', button.ironIcon);
 
-    MockInteractions.tap(button);
+    button.click();
 
     chrome.test.assertFalse(dropdown.dropdownOpen);
 
@@ -134,7 +133,7 @@
         bookmarkContent.shadowRoot.querySelectorAll('viewer-bookmark');
     chrome.test.assertEq(1, rootBookmarks.length, "one root bookmark");
     var rootBookmark = rootBookmarks[0];
-    MockInteractions.tap(rootBookmark.$.expand);
+    rootBookmark.$.expand.click();
 
     Polymer.dom.flush();
 
@@ -149,10 +148,10 @@
       lastPageChange = e.detail.page;
     });
 
-    MockInteractions.tap(rootBookmark.$.item);
+    rootBookmark.$.item.click();
     chrome.test.assertEq(1, lastPageChange);
 
-    MockInteractions.tap(subBookmarks[1].$.item);
+    subBookmarks[1].$.item.click();
     chrome.test.assertEq(3, lastPageChange);
 
     chrome.test.succeed();
@@ -180,17 +179,17 @@
     chrome.test.assertTrue(button.ironIcon.endsWith(fitPageIcon));
 
     // Tap 1: Fire fit-to-changed(FIT_TO_PAGE), show fit-to-width.
-    MockInteractions.tap(button);
+    button.click();
     fitToEventChecker.assertEvent(FittingType.FIT_TO_PAGE, true);
     chrome.test.assertTrue(button.ironIcon.endsWith(fitWidthIcon));
 
     // Tap 2: Fire fit-to-changed(FIT_TO_WIDTH), show fit-to-page.
-    MockInteractions.tap(button);
+    button.click();
     fitToEventChecker.assertEvent(FittingType.FIT_TO_WIDTH, true);
     chrome.test.assertTrue(button.ironIcon.endsWith(fitPageIcon));
 
     // Tap 3: Fire fit-to-changed(FIT_TO_PAGE) again.
-    MockInteractions.tap(button);
+    button.click();
     fitToEventChecker.assertEvent(FittingType.FIT_TO_PAGE, true);
     chrome.test.assertTrue(button.ironIcon.endsWith(fitWidthIcon));
 
@@ -206,7 +205,7 @@
     chrome.test.assertTrue(button.ironIcon.endsWith(fitPageIcon));
 
     // Tap 4: Fire fit-to-changed(FIT_TO_PAGE) again.
-    MockInteractions.tap(button);
+    button.click();
     fitToEventChecker.assertEvent(FittingType.FIT_TO_PAGE, true);
     chrome.test.assertTrue(button.ironIcon.endsWith(fitWidthIcon));
 
@@ -233,7 +232,7 @@
     chrome.test.assertTrue(button.ironIcon.endsWith(fitWidthIcon));
 
     // Tap 1: Fire fit-to-changed(FIT_TO_WIDTH).
-    MockInteractions.tap(button);
+    button.click();
     fitToEventChecker.assertEvent(FittingType.FIT_TO_WIDTH, true);
     chrome.test.assertTrue(button.ironIcon.endsWith(fitPageIcon));
 
@@ -248,7 +247,7 @@
     chrome.test.assertTrue(button.ironIcon.endsWith(fitWidthIcon));
 
     // Tap 2: Fire fit-to-changed(FIT_TO_WIDTH).
-    MockInteractions.tap(button);
+    button.click();
     fitToEventChecker.assertEvent(FittingType.FIT_TO_WIDTH, true);
     chrome.test.assertTrue(button.ironIcon.endsWith(fitPageIcon));
 
@@ -275,12 +274,12 @@
     chrome.test.assertTrue(button.ironIcon.endsWith(fitPageIcon));
 
     // Tap 1: Fire fit-to-changed(FIT_TO_PAGE).
-    MockInteractions.tap(button);
+    button.click();
     fitToEventChecker.assertEvent(FittingType.FIT_TO_PAGE, true);
     chrome.test.assertTrue(button.ironIcon.endsWith(fitWidthIcon));
 
     // Tap 2: Fire fit-to-changed(FIT_TO_WIDTH).
-    MockInteractions.tap(button);
+    button.click();
     fitToEventChecker.assertEvent(FittingType.FIT_TO_WIDTH, true);
     chrome.test.assertTrue(button.ironIcon.endsWith(fitPageIcon));
 
@@ -290,7 +289,7 @@
     chrome.test.assertTrue(button.ironIcon.endsWith(fitPageIcon));
 
     // Tap 3: Fire fit-to-changed(FIT_TO_PAGE).
-    MockInteractions.tap(button);
+    button.click();
     fitToEventChecker.assertEvent(FittingType.FIT_TO_PAGE, true);
     chrome.test.assertTrue(button.ironIcon.endsWith(fitWidthIcon));
 
diff --git a/chrome/test/data/pdf/toolbar_manager_test.js b/chrome/test/data/pdf/toolbar_manager_test.js
index 2e2b041..124a600 100644
--- a/chrome/test/data/pdf/toolbar_manager_test.js
+++ b/chrome/test/data/pdf/toolbar_manager_test.js
@@ -123,8 +123,8 @@
    */
   function testToolbarKeyboardNavigation() {
     var mockWindow = new MockWindow(1920, 1080);
-    var toolbar =
-        Polymer.Base.create('viewer-pdf-toolbar', {loadProgress: 100});
+    var toolbar = document.createElement('viewer-pdf-toolbar');
+    toolbar.loadProgress = 100;
     document.body.appendChild(toolbar);
     var zoomToolbar = document.createElement('viewer-zoom-toolbar');
     document.body.appendChild(zoomToolbar);
@@ -218,8 +218,8 @@
    */
   function testToolbarTouchInteraction() {
     var mockWindow = new MockWindow(1920, 1080);
-    var toolbar =
-        Polymer.Base.create('viewer-pdf-toolbar', {loadProgress: 100});
+    var toolbar = document.createElement('viewer-pdf-toolbar');
+    toolbar.loadProgress = 100;
     document.body.appendChild(toolbar);
     var zoomToolbar = document.createElement('viewer-zoom-toolbar');
     document.body.appendChild(zoomToolbar);
diff --git a/chrome/test/data/webui/cr_elements/cr_drawer_tests.js b/chrome/test/data/webui/cr_elements/cr_drawer_tests.js
index 977b8133..e5f7347 100644
--- a/chrome/test/data/webui/cr_elements/cr_drawer_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_drawer_tests.js
@@ -7,7 +7,6 @@
 //
 // #import {eventToPromise} from 'chrome://test/test_util.m.js';
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {tap} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 // clang-format on
 
 suite('cr-drawer', function() {
@@ -37,7 +36,7 @@
           assertTrue(drawer.open);
 
           // Clicking the content does not close the drawer.
-          MockInteractions.tap(document.querySelector('.drawer-content'));
+          document.querySelector('.drawer-content').click();
 
           const whenClosed = test_util.eventToPromise('close', drawer);
           drawer.$.dialog.dispatchEvent(new MouseEvent('click', {
@@ -60,7 +59,7 @@
         });
   });
 
-  test('tapping icon closes drawer', async () => {
+  test('clicking icon closes drawer', async () => {
     // Create a drawer with an icon and open it.
     document.body.innerHTML = `
       <cr-drawer id="drawer" align="ltr" icon-name="menu" icon-title="close">
@@ -71,8 +70,8 @@
     drawer.openDrawer();
     await test_util.eventToPromise('cr-drawer-opened', drawer);
 
-    // Tapping the icon closes the drawer.
-    MockInteractions.tap(drawer.$.iconButton);
+    // Clicking the icon closes the drawer.
+    drawer.$.iconButton.click();
     await test_util.eventToPromise('close', drawer);
     assertFalse(drawer.open);
     assertTrue(drawer.wasCanceled());
diff --git a/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_tests.js b/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_tests.js
index a47fa0c..4d7acdc 100644
--- a/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_tests.js
@@ -63,7 +63,7 @@
           const items = getGridItems();
 
           // Simulate tapping the third avatar.
-          MockInteractions.tap(items[2]);
+          items[2].click();
           assertEquals(
               'chrome://avatar3.png', avatarSelector.selectedAvatar.url);
           assertFalse(items[0].classList.contains('iron-selected'));
diff --git a/chrome/test/data/webui/cr_elements/cr_toggle_test.js b/chrome/test/data/webui/cr_elements/cr_toggle_test.js
index 83aa110..dc058f6 100644
--- a/chrome/test/data/webui/cr_elements/cr_toggle_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_toggle_test.js
@@ -4,7 +4,7 @@
 
 // clang-format off
 // #import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.m.js';
-// #import {keyEventOn, tap} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
+// #import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 // #import {eventToPromise} from 'chrome://test/test_util.m.js';
 // clang-format on
 
@@ -89,7 +89,7 @@
     }
     toggle.dispatchEvent(
         new PointerEvent('pointerup', {pointerId: 1, clientX: xEnd}));
-    MockInteractions.tap(toggle);
+    toggle.click();
   }
 
   // Test that the control is toggled when the |checked| attribute is
diff --git a/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.js b/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.js
index 2d11201..3907a0a1 100644
--- a/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.js
@@ -6,7 +6,7 @@
 // #import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.m.js';
 //
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {blur, pressAndReleaseKeyOn, tap} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
+// #import {blur, pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 // clang-format on
 
 /** @fileoverview Suite of tests for cr-toolbar-search-field. */
@@ -85,7 +85,7 @@
 
     const clearSearch = field.$$('#clearSearch');
     clearSearch.focus();
-    MockInteractions.tap(clearSearch);
+    clearSearch.click();
     assertTrue(field.showingSearch);
     assertEquals('', field.getValue());
     assertEquals(field.$.searchInput, field.root.activeElement);
@@ -98,7 +98,7 @@
     Polymer.dom.flush();
     assertEquals('query1', field.getValue());
 
-    MockInteractions.tap(field.$$('#clearSearch'));
+    field.$$('#clearSearch').click();
     assertTrue(field.showingSearch);
     assertEquals('', field.getValue());
 
diff --git a/chrome/test/data/webui/settings/payments_section_test.js b/chrome/test/data/webui/settings/payments_section_test.js
index 1de01370..5eb49068 100644
--- a/chrome/test/data/webui/settings/payments_section_test.js
+++ b/chrome/test/data/webui/settings/payments_section_test.js
@@ -7,11 +7,17 @@
     test('testAutofillExtensionIndicator', function() {
       // Initializing with fake prefs
       const section = document.createElement('settings-payments-section');
-      section.prefs = {autofill: {credit_card_enabled: {}}};
+      section.prefs = {
+        autofill: {credit_card_enabled: {}, credit_card_fido_auth_enabled: {}}
+      };
       document.body.appendChild(section);
 
       assertFalse(!!section.$$('#autofillExtensionIndicator'));
-      section.set('prefs.autofill.credit_card_enabled.extensionId', 'test-id');
+      section.set(
+          'prefs.autofill.credit_card_enabled.extensionId', 'test-id-1');
+      section.set(
+          'prefs.autofill.credit_card_fido_auth_enabled.extensionId',
+          'test-id-2');
       Polymer.dom.flush();
 
       assertTrue(!!section.$$('#autofillExtensionIndicator'));
@@ -442,5 +448,42 @@
 
       assertFalse(section.$$('#migrateCreditCards').hidden);
     });
+
+    test('verifyFIDOAuthToggleShownIfUserIsVerifiable', function() {
+      // Set |userIsFIDOVerifiable| to true.
+      loadTimeData.overrideValues({userIsFIDOVerifiable: true});
+      const section =
+          createPaymentsSection([], {credit_card_enabled: {value: true}});
+
+      assertTrue(!!section.$$('#autofillCreditCardFIDOAuthToggle'));
+    });
+
+    test('verifyFIDOAuthToggleNotShownIfUserIsNotVerifiable', function() {
+      // Set |userIsFIDOVerifiable| to false.
+      loadTimeData.overrideValues({userIsFIDOVerifiable: false});
+      const section =
+          createPaymentsSection([], {credit_card_enabled: {value: true}});
+      assertFalse(!!section.$$('#autofillCreditCardFIDOAuthToggle'));
+    });
+
+    test('verifyFIDOAuthToggleCheckedIfOptedIn', function() {
+      // Set FIDO auth pref value to true.
+      loadTimeData.overrideValues({userIsFIDOVerifiable: true});
+      const section = createPaymentsSection([], {
+        credit_card_enabled: {value: true},
+        credit_card_fido_auth_enabled: {value: true}
+      });
+      assertTrue(section.$$('#autofillCreditCardFIDOAuthToggle').checked);
+    });
+
+    test('verifyFIDOAuthToggleUncheckedIfOptedOut', function() {
+      // Set FIDO auth pref value to false.
+      loadTimeData.overrideValues({userIsFIDOVerifiable: true});
+      const section = createPaymentsSection([], {
+        credit_card_enabled: {value: true},
+        credit_card_fido_auth_enabled: {value: false}
+      });
+      assertFalse(section.$$('#autofillCreditCardFIDOAuthToggle').checked);
+    });
   });
 });
diff --git a/chrome/test/data/webui/tab_strip/tab_test.js b/chrome/test/data/webui/tab_strip/tab_test.js
index 32df3c0d..6131e3c 100644
--- a/chrome/test/data/webui/tab_strip/tab_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_test.js
@@ -14,6 +14,7 @@
 
   const tab = {
     id: 1001,
+    status: 'complete',
     title: 'My title',
   };
 
@@ -63,6 +64,13 @@
     assertFalse(tabElement.hasAttribute('pinned'));
   });
 
+  test('toggles a [loading] attribute when loading', () => {
+    tabElement.tab = Object.assign({}, tab, {status: 'loading'});
+    assertTrue(tabElement.hasAttribute('loading'));
+    tabElement.tab = Object.assign({}, tab, {status: 'complete'});
+    assertFalse(tabElement.hasAttribute('loading'));
+  });
+
   test('clicking on the element activates the tab', () => {
     tabElement.click();
     return testTabsApiProxy.whenCalled('activateTab', tabId => {
@@ -104,6 +112,15 @@
         getFaviconForPageURL(expectedPageUrl, false));
   });
 
+  test(
+      'removes the favicon if the tab is loading and there is no favicon URL',
+      () => {
+        delete tab.favIconUrl;
+        tabElement.tab = Object.assign({}, tab, {status: 'loading'});
+        const faviconElement = tabElement.shadowRoot.querySelector('#favicon');
+        assertEquals(faviconElement.style.backgroundImage, 'none');
+      });
+
   test('hides the thumbnail if there is no source yet', () => {
     const thumbnailImage = tabElement.shadowRoot.querySelector('#thumbnailImg');
     assertFalse(thumbnailImage.hasAttribute('src'));
diff --git a/chrome/test/data/xr/e2e_test_files/html/webxr_no_stalled_frame_loop.html b/chrome/test/data/xr/e2e_test_files/html/webxr_no_stalled_frame_loop.html
new file mode 100644
index 0000000..5f438c00
--- /dev/null
+++ b/chrome/test/data/xr/e2e_test_files/html/webxr_no_stalled_frame_loop.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<!--
+WebXR page that only submits one frame.
+-->
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css">
+  </head>
+  <body>
+    <canvas id="webgl-canvas"></canvas>
+    <script src="../resources/webxr_e2e.js"></script>
+    <script src="../resources/webxr_boilerplate.js"></script>
+    <script src="../../../../../../third_party/blink/web_tests/resources/testharness.js"></script>
+    <script>
+      var frame_count = 0;
+      var frame_expected = true;
+      onImmersiveXRFrameCallback = function() {
+        assert_true(frame_expected);
+        frame_count++;
+
+        if (frame_count === 1) {
+          // No need to request an animationFrame because it should've been
+          // requested previously.
+          sessionInfos[sessionTypes.IMMERSIVE].session.updateRenderState({ baseLayer: null });
+          frame_expected = false;
+        }
+      };
+
+      function setBaseLayer() {
+        let session = sessionInfos[sessionTypes.IMMERSIVE].session;
+        session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
+        frame_expected = true;
+      }
+    </script>
+  </body>
+</html>
diff --git a/chrome/test/data/xr/e2e_test_files/html/webxr_set_base_layer_late.html b/chrome/test/data/xr/e2e_test_files/html/webxr_set_base_layer_late.html
new file mode 100644
index 0000000..67cb91a
--- /dev/null
+++ b/chrome/test/data/xr/e2e_test_files/html/webxr_set_base_layer_late.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<!--
+WebXR page that only submits one frame.
+-->
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css">
+  </head>
+  <body>
+    <canvas id="webgl-canvas"></canvas>
+    <script src="../resources/webxr_e2e.js"></script>
+    <script src="../resources/webxr_boilerplate.js"></script>
+    <script src="../../../../../../third_party/blink/web_tests/resources/testharness.js"></script>
+    <script>
+      shouldSetBaseLayer = false;
+      var frame_count = 0;
+      var frame_expected = false;
+      var delay_ended = false;
+      onImmersiveXRFrameCallback = function() {
+        assert_true(frame_expected);
+        frame_count++;
+      };
+
+      function setBaseLayer() {
+        let session = sessionInfos[sessionTypes.IMMERSIVE].session;
+        session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
+        frame_expected = true;
+      }
+
+      function delayMilliseconds(waitTime) {
+        delay_ended = false;
+        window.setTimeout(() => { delay_ended = true }, waitTime);
+      }
+    </script>
+  </body>
+</html>
diff --git a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
index 405c5b9..905b52f 100644
--- a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
+++ b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
@@ -24,6 +24,7 @@
 var shouldSubmitFrame = true;
 var hasPresentedFrame = false;
 var arSessionRequestWouldTriggerPermissionPrompt = null;
+var shouldSetBaseLayer = true;
 
 var sessionTypes = Object.freeze({
   IMMERSIVE: 1,
@@ -164,7 +165,10 @@
     onSessionStartedCallback(session);
   }
 
-  session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
+  if (shouldSetBaseLayer) {
+    session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
+  }
+
   session.requestReferenceSpace(referenceSpaceMap[sessionType])
       .then( (refSpace) => {
         sessionInfos[sessionType].currentRefSpace = refSpace;
diff --git a/chrome/tools/build/chromeos/FILES.cfg b/chrome/tools/build/chromeos/FILES.cfg
index 76fe0da0..23ba4d2a 100644
--- a/chrome/tools/build/chromeos/FILES.cfg
+++ b/chrome/tools/build/chromeos/FILES.cfg
@@ -41,6 +41,10 @@
     'buildtype': ['dev', 'official'],
   },
   {
+    'filename': 'crashpad_handler',
+    'buildtype': ['dev', 'official'],
+  },
+  {
     'filename': 'icudtl.dat',
     'buildtype': ['dev', 'official'],
   },
diff --git a/chrome/tools/build/linux/FILES.cfg b/chrome/tools/build/linux/FILES.cfg
index 602432a..ca79c7d6 100644
--- a/chrome/tools/build/linux/FILES.cfg
+++ b/chrome/tools/build/linux/FILES.cfg
@@ -48,6 +48,10 @@
     'buildtype': ['dev', 'official'],
   },
   {
+    'filename': 'crashpad_handler',
+    'buildtype': ['dev', 'official'],
+  },
+  {
     'filename': 'icudtl.dat',
     'buildtype': ['dev', 'official'],
   },
@@ -167,6 +171,12 @@
     'archive': 'breakpad-info.zip',
   },
   {
+    'filename': 'crashpad.breakpad.x64',
+    'arch': ['64bit'],
+    'buildtype': ['official'],
+    'archive': 'breakpad-info.zip',
+  },
+  {
     'filename': 'swiftshader_libegl.breakpad.x64',
     'arch': ['64bit'],
     'buildtype': ['official'],
diff --git a/chromecast/browser/accessibility/accessibility_manager.cc b/chromecast/browser/accessibility/accessibility_manager.cc
index fa3f7dfe..b4480ae 100644
--- a/chromecast/browser/accessibility/accessibility_manager.cc
+++ b/chromecast/browser/accessibility/accessibility_manager.cc
@@ -7,6 +7,7 @@
 #include "chromecast/graphics/accessibility/focus_ring_controller.h"
 #include "chromecast/graphics/accessibility/fullscreen_magnification_controller.h"
 #include "chromecast/graphics/cast_window_manager_aura.h"
+#include "chromecast/graphics/cast_window_tree_host_aura.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/wm/public/activation_client.h"
diff --git a/chromecast/graphics/BUILD.gn b/chromecast/graphics/BUILD.gn
index bb0aaa0..d6d28fc 100644
--- a/chromecast/graphics/BUILD.gn
+++ b/chromecast/graphics/BUILD.gn
@@ -53,6 +53,8 @@
       "cast_touch_event_gate.h",
       "cast_window_manager_aura.cc",
       "cast_window_manager_aura.h",
+      "cast_window_tree_host_aura.cc",
+      "cast_window_tree_host_aura.h",
       "gestures/cast_system_gesture_dispatcher.cc",
       "gestures/cast_system_gesture_dispatcher.h",
       "gestures/cast_system_gesture_event_handler.cc",
diff --git a/chromecast/graphics/cast_external_begin_frame_source.cc b/chromecast/graphics/cast_external_begin_frame_source.cc
index 5d305d76..f38eccc 100644
--- a/chromecast/graphics/cast_external_begin_frame_source.cc
+++ b/chromecast/graphics/cast_external_begin_frame_source.cc
@@ -6,6 +6,7 @@
 
 #include "base/time/time.h"
 #include "chromecast/graphics/cast_window_manager_aura.h"
+#include "chromecast/graphics/cast_window_tree_host_aura.h"
 
 namespace chromecast {
 namespace {
diff --git a/chromecast/graphics/cast_window_manager_aura.cc b/chromecast/graphics/cast_window_manager_aura.cc
index 3ab7e3fd..ea8865a0 100644
--- a/chromecast/graphics/cast_window_manager_aura.cc
+++ b/chromecast/graphics/cast_window_manager_aura.cc
@@ -10,6 +10,7 @@
 #include "chromecast/graphics/cast_focus_client_aura.h"
 #include "chromecast/graphics/cast_touch_activity_observer.h"
 #include "chromecast/graphics/cast_touch_event_gate.h"
+#include "chromecast/graphics/cast_window_tree_host_aura.h"
 #include "chromecast/graphics/gestures/cast_system_gesture_event_handler.h"
 #include "chromecast/graphics/gestures/side_swipe_detector.h"
 #include "ui/aura/client/default_capture_client.h"
@@ -17,9 +18,7 @@
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/layout_manager.h"
-#include "ui/aura/null_window_targeter.h"
 #include "ui/aura/window.h"
-#include "ui/aura/window_tree_host_platform.h"
 #include "ui/base/ime/init/input_method_factory.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/display/display.h"
@@ -81,39 +80,6 @@
   }
 }
 
-}  // namespace
-
-CastWindowTreeHost::CastWindowTreeHost(
-    bool enable_input,
-    ui::PlatformWindowInitProperties properties,
-    bool use_external_frame_control)
-    : WindowTreeHostPlatform(std::move(properties),
-                             nullptr,
-                             nullptr,
-                             use_external_frame_control),
-      enable_input_(enable_input) {
-  if (!enable_input)
-    window()->SetEventTargeter(std::make_unique<aura::NullWindowTargeter>());
-}
-
-CastWindowTreeHost::~CastWindowTreeHost() {}
-
-void CastWindowTreeHost::DispatchEvent(ui::Event* event) {
-  if (!enable_input_) {
-    return;
-  }
-
-  WindowTreeHostPlatform::DispatchEvent(event);
-}
-
-gfx::Rect CastWindowTreeHost::GetTransformedRootWindowBoundsInPixels(
-    const gfx::Size& host_size_in_pixels) const {
-  gfx::RectF new_bounds(WindowTreeHost::GetTransformedRootWindowBoundsInPixels(
-      host_size_in_pixels));
-  new_bounds.set_origin(gfx::PointF());
-  return gfx::ToEnclosingRect(new_bounds);
-}
-
 // A layout manager owned by the root window.
 class CastLayoutManager : public aura::LayoutManager {
  public:
@@ -218,6 +184,8 @@
   SetChildBoundsDirect(child, requested_bounds);
 }
 
+}  // namespace
+
 CastWindowManagerAura::CastWindowManagerAura(bool enable_input)
     : enable_input_(enable_input) {}
 
@@ -248,7 +216,7 @@
 
   LOG(INFO) << "Starting window manager, bounds: " << host_bounds.ToString();
   CHECK(aura::Env::GetInstance());
-  window_tree_host_ = std::make_unique<CastWindowTreeHost>(
+  window_tree_host_ = std::make_unique<CastWindowTreeHostAura>(
       enable_input_, std::move(properties));
   window_tree_host_->InitHost();
   aura::Window* tree_window = window_tree_host_->window();
@@ -296,7 +264,7 @@
   }
 }
 
-CastWindowTreeHost* CastWindowManagerAura::window_tree_host() const {
+CastWindowTreeHostAura* CastWindowManagerAura::window_tree_host() const {
   DCHECK(window_tree_host_);
   return window_tree_host_.get();
 }
diff --git a/chromecast/graphics/cast_window_manager_aura.h b/chromecast/graphics/cast_window_manager_aura.h
index f73a73c6..8b62f6b7 100644
--- a/chromecast/graphics/cast_window_manager_aura.h
+++ b/chromecast/graphics/cast_window_manager_aura.h
@@ -28,27 +28,7 @@
 class CastSystemGestureEventHandler;
 class CastSystemGestureDispatcher;
 class SideSwipeDetector;
-
-// An aura::WindowTreeHost that correctly converts input events.
-class CastWindowTreeHost : public aura::WindowTreeHostPlatform {
- public:
-  CastWindowTreeHost(bool enable_input,
-                     ui::PlatformWindowInitProperties properties,
-                     bool use_external_frame_control = false);
-  ~CastWindowTreeHost() override;
-
-  // aura::WindowTreeHostPlatform implementation:
-  void DispatchEvent(ui::Event* event) override;
-
-  // aura::WindowTreeHost implementation
-  gfx::Rect GetTransformedRootWindowBoundsInPixels(
-      const gfx::Size& size_in_pixels) const override;
-
- private:
-  const bool enable_input_;
-
-  DISALLOW_COPY_AND_ASSIGN(CastWindowTreeHost);
-};
+class CastWindowTreeHostAura;
 
 class CastWindowManagerAura : public CastWindowManager,
                               public aura::client::WindowParentingClient {
@@ -56,10 +36,6 @@
   explicit CastWindowManagerAura(bool enable_input);
   ~CastWindowManagerAura() override;
 
-  aura::client::CaptureClient* capture_client() const {
-    return capture_client_.get();
-  }
-
   void Setup();
   void OnWindowOrderChanged(std::vector<WindowId> window_order);
 
@@ -72,27 +48,26 @@
   void InjectEvent(ui::Event* event) override;
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
-
-  // aura::client::WindowParentingClient implementation:
-  aura::Window* GetDefaultParent(aura::Window* window,
-                                 const gfx::Rect& bounds) override;
-
   void AddGestureHandler(CastGestureHandler* handler) override;
-
   void RemoveGestureHandler(CastGestureHandler* handler) override;
-
-  CastWindowTreeHost* window_tree_host() const;
-
-  CastGestureHandler* GetGestureHandler() const;
-
   void SetTouchInputDisabled(bool disabled) override;
   void AddTouchActivityObserver(CastTouchActivityObserver* observer) override;
   void RemoveTouchActivityObserver(
       CastTouchActivityObserver* observer) override;
 
+  // aura::client::WindowParentingClient implementation:
+  aura::Window* GetDefaultParent(aura::Window* window,
+                                 const gfx::Rect& bounds) override;
+
+  CastWindowTreeHostAura* window_tree_host() const;
+  CastGestureHandler* GetGestureHandler() const;
+  aura::client::CaptureClient* capture_client() const {
+    return capture_client_.get();
+  }
+
  private:
   const bool enable_input_;
-  std::unique_ptr<CastWindowTreeHost> window_tree_host_;
+  std::unique_ptr<CastWindowTreeHostAura> window_tree_host_;
   std::unique_ptr<aura::client::DefaultCaptureClient> capture_client_;
   std::unique_ptr<CastFocusClientAura> focus_client_;
   std::unique_ptr<aura::client::ScreenPositionClient> screen_position_client_;
diff --git a/chromecast/graphics/cast_window_tree_host_aura.cc b/chromecast/graphics/cast_window_tree_host_aura.cc
new file mode 100644
index 0000000..82000ec
--- /dev/null
+++ b/chromecast/graphics/cast_window_tree_host_aura.cc
@@ -0,0 +1,43 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/graphics/cast_window_tree_host_aura.h"
+
+#include "ui/aura/null_window_targeter.h"
+#include "ui/platform_window/platform_window_init_properties.h"
+
+namespace chromecast {
+
+CastWindowTreeHostAura::CastWindowTreeHostAura(
+    bool enable_input,
+    ui::PlatformWindowInitProperties properties,
+    bool use_external_frame_control)
+    : WindowTreeHostPlatform(std::move(properties),
+                             nullptr,
+                             nullptr,
+                             use_external_frame_control),
+      enable_input_(enable_input) {
+  if (!enable_input)
+    window()->SetEventTargeter(std::make_unique<aura::NullWindowTargeter>());
+}
+
+CastWindowTreeHostAura::~CastWindowTreeHostAura() {}
+
+void CastWindowTreeHostAura::DispatchEvent(ui::Event* event) {
+  if (!enable_input_) {
+    return;
+  }
+
+  WindowTreeHostPlatform::DispatchEvent(event);
+}
+
+gfx::Rect CastWindowTreeHostAura::GetTransformedRootWindowBoundsInPixels(
+    const gfx::Size& host_size_in_pixels) const {
+  gfx::RectF new_bounds(WindowTreeHost::GetTransformedRootWindowBoundsInPixels(
+      host_size_in_pixels));
+  new_bounds.set_origin(gfx::PointF());
+  return gfx::ToEnclosingRect(new_bounds);
+}
+
+}  // namespace chromecast
diff --git a/chromecast/graphics/cast_window_tree_host_aura.h b/chromecast/graphics/cast_window_tree_host_aura.h
new file mode 100644
index 0000000..c7adc4c
--- /dev/null
+++ b/chromecast/graphics/cast_window_tree_host_aura.h
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_GRAPHICS_CAST_WINDOW_TREE_HOST_AURA_H_
+#define CHROMECAST_GRAPHICS_CAST_WINDOW_TREE_HOST_AURA_H_
+
+#include "ui/aura/window_tree_host_platform.h"
+
+namespace chromecast {
+
+// An aura::WindowTreeHost that correctly converts input events.
+class CastWindowTreeHostAura : public aura::WindowTreeHostPlatform {
+ public:
+  CastWindowTreeHostAura(bool enable_input,
+                         ui::PlatformWindowInitProperties properties,
+                         bool use_external_frame_control = false);
+  ~CastWindowTreeHostAura() override;
+
+  // aura::WindowTreeHostPlatform implementation:
+  void DispatchEvent(ui::Event* event) override;
+
+  // aura::WindowTreeHost implementation
+  gfx::Rect GetTransformedRootWindowBoundsInPixels(
+      const gfx::Size& size_in_pixels) const override;
+
+ private:
+  const bool enable_input_;
+
+  DISALLOW_COPY_AND_ASSIGN(CastWindowTreeHostAura);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_GRAPHICS_CAST_WINDOW_TREE_HOST_AURA_H_
\ No newline at end of file
diff --git a/chromecast/renderer/media/key_systems_cast.cc b/chromecast/renderer/media/key_systems_cast.cc
index 321dff7..21166af 100644
--- a/chromecast/renderer/media/key_systems_cast.cc
+++ b/chromecast/renderer/media/key_systems_cast.cc
@@ -127,20 +127,20 @@
   codecs |= ::media::EME_CODEC_HEVC;
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
 
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
   codecs |= ::media::EME_CODEC_DOLBY_VISION_AVC;
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
   codecs |= ::media::EME_CODEC_DOLBY_VISION_HEVC;
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
-#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
   codecs |= ::media::EME_CODEC_AC3 | ::media::EME_CODEC_EAC3;
-#endif  // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
 
-#if BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
   codecs |= ::media::EME_CODEC_MPEG_H_AUDIO;
-#endif  // BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
 
   return codecs;
 }
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 0388894..b6de9a7 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -157,6 +157,10 @@
 const base::Feature kReleaseNotes{"ReleaseNotes",
                                   base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables or disables the scrollable shelf.
+const base::Feature kShelfScrollable{"ShelfScrollable",
+                                     base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Enables or disables a toggle to enable Bluetooth debug logs.
 const base::Feature kShowBluetoothDebugLogToggle{
     "ShowBluetoothDebugLogToggle", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 2f3a61c..8fd0609 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -76,6 +76,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kReleaseNotes;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kShelfScrollable;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kShowBluetoothDebugLogToggle;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kShowBluetoothDeviceBattery;
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index 7883c18..5342fe9 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -8,6 +8,7 @@
 
 #include "base/command_line.h"
 #include "base/metrics/field_trial.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "third_party/icu/source/common/unicode/locid.h"
 
 namespace chromeos {
@@ -457,9 +458,6 @@
 // App window previews when hovering over the shelf.
 const char kShelfHoverPreviews[] = "shelf-hover-previews";
 
-// Scrollable list of apps on the shelf.
-const char kShelfScrollable[] = "shelf-scrollable";
-
 // If true, files in Android internal storage will be shown in Files app.
 const char kShowAndroidFilesInFilesApp[] = "show-android-files-in-files-app";
 
@@ -560,7 +558,8 @@
   // If we're showing the new shelf design, also enable scrollable shelf.
   if (ShouldShowShelfHotseat())
     return true;
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(kShelfScrollable);
+
+  return base::FeatureList::IsEnabled(features::kShelfScrollable);
 }
 
 bool ShouldTetherHostScansIgnoreWiredConnections() {
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index fa8c2c29..082b81f0 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -184,7 +184,6 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kSamlPasswordChangeUrl[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShelfHoverPreviews[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShelfHotseat[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShelfScrollable[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kShowAndroidFilesInFilesApp[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShowLoginDevOverlay[];
diff --git a/chromeos/dbus/concierge_client.cc b/chromeos/dbus/concierge_client.cc
index eee532fd..5220b44 100644
--- a/chromeos/dbus/concierge_client.cc
+++ b/chromeos/dbus/concierge_client.cc
@@ -16,9 +16,9 @@
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #include "third_party/cros_system_api/dbus/vm_concierge/dbus-constants.h"
 
-namespace chromeos {
+namespace concierge = vm_tools::concierge;
 
-using namespace vm_tools::concierge;
+namespace chromeos {
 
 class ConciergeClientImpl : public ConciergeClient {
  public:
@@ -66,27 +66,25 @@
     return is_disk_import_progress_signal_connected_;
   }
 
-  void CreateDiskImage(
-      const vm_tools::concierge::CreateDiskImageRequest& request,
-      DBusMethodCallback<vm_tools::concierge::CreateDiskImageResponse> callback)
-      override {
-    CallMethod(kCreateDiskImageMethod, request, std::move(callback));
+  void CreateDiskImage(const concierge::CreateDiskImageRequest& request,
+                       DBusMethodCallback<concierge::CreateDiskImageResponse>
+                           callback) override {
+    CallMethod(concierge::kCreateDiskImageMethod, request, std::move(callback));
   }
 
-  void DestroyDiskImage(
-      const vm_tools::concierge::DestroyDiskImageRequest& request,
-      DBusMethodCallback<vm_tools::concierge::DestroyDiskImageResponse>
-          callback) override {
-    CallMethod(kDestroyDiskImageMethod, request, std::move(callback));
+  void DestroyDiskImage(const concierge::DestroyDiskImageRequest& request,
+                        DBusMethodCallback<concierge::DestroyDiskImageResponse>
+                            callback) override {
+    CallMethod(concierge::kDestroyDiskImageMethod, request,
+               std::move(callback));
   }
 
-  void ImportDiskImage(
-      base::ScopedFD fd,
-      const vm_tools::concierge::ImportDiskImageRequest& request,
-      DBusMethodCallback<vm_tools::concierge::ImportDiskImageResponse> callback)
-      override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kImportDiskImageMethod);
+  void ImportDiskImage(base::ScopedFD fd,
+                       const concierge::ImportDiskImageRequest& request,
+                       DBusMethodCallback<concierge::ImportDiskImageResponse>
+                           callback) override {
+    dbus::MethodCall method_call(concierge::kVmConciergeInterface,
+                                 concierge::kImportDiskImageMethod);
     dbus::MessageWriter writer(&method_call);
 
     if (!writer.AppendProtoAsArrayOfBytes(request)) {
@@ -101,65 +99,64 @@
     concierge_proxy_->CallMethod(
         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
         base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::ImportDiskImageResponse>,
+                           concierge::ImportDiskImageResponse>,
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
   void CancelDiskImageOperation(
-      const vm_tools::concierge::CancelDiskImageRequest& request,
-      DBusMethodCallback<vm_tools::concierge::CancelDiskImageResponse> callback)
+      const concierge::CancelDiskImageRequest& request,
+      DBusMethodCallback<concierge::CancelDiskImageResponse> callback)
       override {
-    CallMethod(kCancelDiskImageMethod, request, std::move(callback));
+    CallMethod(concierge::kCancelDiskImageMethod, request, std::move(callback));
   }
 
-  void DiskImageStatus(
-      const vm_tools::concierge::DiskImageStatusRequest& request,
-      DBusMethodCallback<vm_tools::concierge::DiskImageStatusResponse> callback)
-      override {
-    CallMethod(kDiskImageStatusMethod, request, std::move(callback));
+  void DiskImageStatus(const concierge::DiskImageStatusRequest& request,
+                       DBusMethodCallback<concierge::DiskImageStatusResponse>
+                           callback) override {
+    CallMethod(concierge::kDiskImageStatusMethod, request, std::move(callback));
   }
 
-  void ListVmDisks(const vm_tools::concierge::ListVmDisksRequest& request,
-                   DBusMethodCallback<vm_tools::concierge::ListVmDisksResponse>
-                       callback) override {
-    CallMethod(kListVmDisksMethod, request, std::move(callback));
+  void ListVmDisks(
+      const concierge::ListVmDisksRequest& request,
+      DBusMethodCallback<concierge::ListVmDisksResponse> callback) override {
+    CallMethod(concierge::kListVmDisksMethod, request, std::move(callback));
   }
 
-  void StartTerminaVm(const vm_tools::concierge::StartVmRequest& request,
-                      DBusMethodCallback<vm_tools::concierge::StartVmResponse>
-                          callback) override {
+  void StartTerminaVm(
+      const concierge::StartVmRequest& request,
+      DBusMethodCallback<concierge::StartVmResponse> callback) override {
     // TODO(nverne): revert to TIMEOUT_USE_DEFAULT when StartVm no longer
     // requires unnecessary long running crypto calculations.
     constexpr int kStartVmTimeoutMs = 160 * 1000;
-    CallMethod(kStartVmMethod, request, std::move(callback), kStartVmTimeoutMs);
+    CallMethod(concierge::kStartVmMethod, request, std::move(callback),
+               kStartVmTimeoutMs);
   }
 
-  void StopVm(const vm_tools::concierge::StopVmRequest& request,
-              DBusMethodCallback<vm_tools::concierge::StopVmResponse> callback)
-      override {
-    CallMethod(kStopVmMethod, request, std::move(callback));
+  void StopVm(const concierge::StopVmRequest& request,
+              DBusMethodCallback<concierge::StopVmResponse> callback) override {
+    CallMethod(concierge::kStopVmMethod, request, std::move(callback));
   }
 
-  void GetVmInfo(const vm_tools::concierge::GetVmInfoRequest& request,
-                 DBusMethodCallback<vm_tools::concierge::GetVmInfoResponse>
-                     callback) override {
-    CallMethod(kGetVmInfoMethod, request, std::move(callback));
+  void GetVmInfo(
+      const concierge::GetVmInfoRequest& request,
+      DBusMethodCallback<concierge::GetVmInfoResponse> callback) override {
+    CallMethod(concierge::kGetVmInfoMethod, request, std::move(callback));
   }
 
   void GetVmEnterpriseReportingInfo(
-      const vm_tools::concierge::GetVmEnterpriseReportingInfoRequest& request,
-      DBusMethodCallback<
-          vm_tools::concierge::GetVmEnterpriseReportingInfoResponse> callback)
-      override {
-    CallMethod(kGetVmEnterpriseReportingInfoMethod, request,
+      const concierge::GetVmEnterpriseReportingInfoRequest& request,
+      DBusMethodCallback<concierge::GetVmEnterpriseReportingInfoResponse>
+          callback) override {
+    CallMethod(concierge::kGetVmEnterpriseReportingInfoMethod, request,
                std::move(callback));
   }
 
   void SetVmCpuRestriction(
-      const vm_tools::concierge::SetVmCpuRestrictionRequest& request,
-      DBusMethodCallback<vm_tools::concierge::SetVmCpuRestrictionResponse>
-          callback) override {
-    CallMethod(kSetVmCpuRestrictionMethod, request, std::move(callback));
+      const concierge::SetVmCpuRestrictionRequest& request,
+      DBusMethodCallback<concierge::SetVmCpuRestrictionResponse> callback)
+      override {
+    CallMethod(concierge::kSetVmCpuRestrictionMethod, request,
+               std::move(callback));
   }
 
   void WaitForServiceToBeAvailable(
@@ -169,19 +166,19 @@
   }
 
   void GetContainerSshKeys(
-      const vm_tools::concierge::ContainerSshKeysRequest& request,
-      DBusMethodCallback<vm_tools::concierge::ContainerSshKeysResponse>
-          callback) override {
-    CallMethod(kGetContainerSshKeysMethod, request, std::move(callback));
+      const concierge::ContainerSshKeysRequest& request,
+      DBusMethodCallback<concierge::ContainerSshKeysResponse> callback)
+      override {
+    CallMethod(concierge::kGetContainerSshKeysMethod, request,
+               std::move(callback));
   }
 
-  void AttachUsbDevice(
-      base::ScopedFD fd,
-      const vm_tools::concierge::AttachUsbDeviceRequest& request,
-      DBusMethodCallback<vm_tools::concierge::AttachUsbDeviceResponse> callback)
-      override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kAttachUsbDeviceMethod);
+  void AttachUsbDevice(base::ScopedFD fd,
+                       const concierge::AttachUsbDeviceRequest& request,
+                       DBusMethodCallback<concierge::AttachUsbDeviceResponse>
+                           callback) override {
+    dbus::MethodCall method_call(concierge::kVmConciergeInterface,
+                                 concierge::kAttachUsbDeviceMethod);
     dbus::MessageWriter writer(&method_call);
 
     if (!writer.AppendProtoAsArrayOfBytes(request)) {
@@ -196,64 +193,59 @@
     concierge_proxy_->CallMethod(
         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
         base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::AttachUsbDeviceResponse>,
+                           concierge::AttachUsbDeviceResponse>,
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
-  void DetachUsbDevice(
-      const vm_tools::concierge::DetachUsbDeviceRequest& request,
-      DBusMethodCallback<vm_tools::concierge::DetachUsbDeviceResponse> callback)
-      override {
-    CallMethod(kDetachUsbDeviceMethod, request, std::move(callback));
+  void DetachUsbDevice(const concierge::DetachUsbDeviceRequest& request,
+                       DBusMethodCallback<concierge::DetachUsbDeviceResponse>
+                           callback) override {
+    CallMethod(concierge::kDetachUsbDeviceMethod, request, std::move(callback));
   }
 
   void ListUsbDevices(
-      const vm_tools::concierge::ListUsbDeviceRequest& request,
-      DBusMethodCallback<vm_tools::concierge::ListUsbDeviceResponse> callback)
-      override {
-    CallMethod(kListUsbDeviceMethod, request, std::move(callback));
+      const concierge::ListUsbDeviceRequest& request,
+      DBusMethodCallback<concierge::ListUsbDeviceResponse> callback) override {
+    CallMethod(concierge::kListUsbDeviceMethod, request, std::move(callback));
   }
 
-  void StartArcVm(const vm_tools::concierge::StartArcVmRequest& request,
-                  DBusMethodCallback<vm_tools::concierge::StartVmResponse>
-                      callback) override {
-    CallMethod(kStartArcVmMethod, request, std::move(callback));
+  void StartArcVm(
+      const concierge::StartArcVmRequest& request,
+      DBusMethodCallback<concierge::StartVmResponse> callback) override {
+    CallMethod(concierge::kStartArcVmMethod, request, std::move(callback));
   }
 
  protected:
   void Init(dbus::Bus* bus) override {
     concierge_proxy_ = bus->GetObjectProxy(
-        vm_tools::concierge::kVmConciergeServiceName,
-        dbus::ObjectPath(vm_tools::concierge::kVmConciergeServicePath));
+        concierge::kVmConciergeServiceName,
+        dbus::ObjectPath(concierge::kVmConciergeServicePath));
     if (!concierge_proxy_) {
       LOG(ERROR) << "Unable to get dbus proxy for "
-                 << vm_tools::concierge::kVmConciergeServiceName;
+                 << concierge::kVmConciergeServiceName;
     }
     concierge_proxy_->ConnectToSignal(
-        vm_tools::concierge::kVmConciergeInterface,
-        vm_tools::concierge::kVmStartedSignal,
+        concierge::kVmConciergeInterface, concierge::kVmStartedSignal,
         base::BindRepeating(&ConciergeClientImpl::OnVmStartedSignal,
                             weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&ConciergeClientImpl::OnSignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
     concierge_proxy_->ConnectToSignal(
-        vm_tools::concierge::kVmConciergeInterface,
-        vm_tools::concierge::kVmStoppedSignal,
+        concierge::kVmConciergeInterface, concierge::kVmStoppedSignal,
         base::BindRepeating(&ConciergeClientImpl::OnVmStoppedSignal,
                             weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&ConciergeClientImpl::OnSignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
     concierge_proxy_->ConnectToSignal(
-        vm_tools::concierge::kVmConciergeInterface,
-        vm_tools::concierge::kContainerStartupFailedSignal,
+        concierge::kVmConciergeInterface,
+        concierge::kContainerStartupFailedSignal,
         base::BindRepeating(
             &ConciergeClientImpl::OnContainerStartupFailedSignal,
             weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&ConciergeClientImpl::OnSignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
     concierge_proxy_->ConnectToSignal(
-        vm_tools::concierge::kVmConciergeInterface,
-        vm_tools::concierge::kDiskImageProgressSignal,
+        concierge::kVmConciergeInterface, concierge::kDiskImageProgressSignal,
         base::BindRepeating(&ConciergeClientImpl::OnDiskImageProgress,
                             weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&ConciergeClientImpl::OnSignalConnected,
@@ -266,7 +258,7 @@
                   const RequestProto& request,
                   DBusMethodCallback<ResponseProto> callback,
                   int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) {
-    dbus::MethodCall method_call(kVmConciergeInterface, method_name);
+    dbus::MethodCall method_call(concierge::kVmConciergeInterface, method_name);
     dbus::MessageWriter writer(&method_call);
 
     if (!writer.AppendProtoAsArrayOfBytes(request)) {
@@ -300,11 +292,10 @@
   }
 
   void OnVmStartedSignal(dbus::Signal* signal) {
-    DCHECK_EQ(signal->GetInterface(),
-              vm_tools::concierge::kVmConciergeInterface);
-    DCHECK_EQ(signal->GetMember(), vm_tools::concierge::kVmStartedSignal);
+    DCHECK_EQ(signal->GetInterface(), concierge::kVmConciergeInterface);
+    DCHECK_EQ(signal->GetMember(), concierge::kVmStartedSignal);
 
-    vm_tools::concierge::VmStartedSignal vm_started_signal;
+    concierge::VmStartedSignal vm_started_signal;
     dbus::MessageReader reader(signal);
     if (!reader.PopArrayOfBytesAsProto(&vm_started_signal)) {
       LOG(ERROR) << "Failed to parse proto from DBus Signal";
@@ -316,11 +307,10 @@
   }
 
   void OnVmStoppedSignal(dbus::Signal* signal) {
-    DCHECK_EQ(signal->GetInterface(),
-              vm_tools::concierge::kVmConciergeInterface);
-    DCHECK_EQ(signal->GetMember(), vm_tools::concierge::kVmStoppedSignal);
+    DCHECK_EQ(signal->GetInterface(), concierge::kVmConciergeInterface);
+    DCHECK_EQ(signal->GetMember(), concierge::kVmStoppedSignal);
 
-    vm_tools::concierge::VmStoppedSignal vm_stopped_signal;
+    concierge::VmStoppedSignal vm_stopped_signal;
     dbus::MessageReader reader(signal);
     if (!reader.PopArrayOfBytesAsProto(&vm_stopped_signal)) {
       LOG(ERROR) << "Failed to parse proto from DBus Signal";
@@ -332,12 +322,10 @@
   }
 
   void OnContainerStartupFailedSignal(dbus::Signal* signal) {
-    DCHECK_EQ(signal->GetInterface(),
-              vm_tools::concierge::kVmConciergeInterface);
-    DCHECK_EQ(signal->GetMember(),
-              vm_tools::concierge::kContainerStartupFailedSignal);
+    DCHECK_EQ(signal->GetInterface(), concierge::kVmConciergeInterface);
+    DCHECK_EQ(signal->GetMember(), concierge::kContainerStartupFailedSignal);
 
-    vm_tools::concierge::ContainerStartedSignal container_startup_failed_signal;
+    concierge::ContainerStartedSignal container_startup_failed_signal;
     dbus::MessageReader reader(signal);
     if (!reader.PopArrayOfBytesAsProto(&container_startup_failed_signal)) {
       LOG(ERROR) << "Failed to parse proto from DBus Signal";
@@ -350,13 +338,10 @@
   }
 
   void OnDiskImageProgress(dbus::Signal* signal) {
-    DCHECK_EQ(signal->GetInterface(),
-              vm_tools::concierge::kVmConciergeInterface);
-    DCHECK_EQ(signal->GetMember(),
-              vm_tools::concierge::kDiskImageProgressSignal);
+    DCHECK_EQ(signal->GetInterface(), concierge::kVmConciergeInterface);
+    DCHECK_EQ(signal->GetMember(), concierge::kDiskImageProgressSignal);
 
-    vm_tools::concierge::DiskImageStatusResponse
-        disk_image_status_response_signal;
+    concierge::DiskImageStatusResponse disk_image_status_response_signal;
     dbus::MessageReader reader(signal);
     if (!reader.PopArrayOfBytesAsProto(&disk_image_status_response_signal)) {
       LOG(ERROR) << "Failed to parse proto from DBus Signal";
@@ -371,18 +356,17 @@
   void OnSignalConnected(const std::string& interface_name,
                          const std::string& signal_name,
                          bool is_connected) {
-    DCHECK_EQ(interface_name, vm_tools::concierge::kVmConciergeInterface);
+    DCHECK_EQ(interface_name, concierge::kVmConciergeInterface);
     if (!is_connected)
       LOG(ERROR) << "Failed to connect to signal: " << signal_name;
 
-    if (signal_name == vm_tools::concierge::kVmStartedSignal) {
+    if (signal_name == concierge::kVmStartedSignal) {
       is_vm_started_signal_connected_ = is_connected;
-    } else if (signal_name == vm_tools::concierge::kVmStoppedSignal) {
+    } else if (signal_name == concierge::kVmStoppedSignal) {
       is_vm_stopped_signal_connected_ = is_connected;
-    } else if (signal_name ==
-               vm_tools::concierge::kContainerStartupFailedSignal) {
+    } else if (signal_name == concierge::kContainerStartupFailedSignal) {
       is_container_startup_failed_signal_connected_ = is_connected;
-    } else if (signal_name == vm_tools::concierge::kDiskImageProgressSignal) {
+    } else if (signal_name == concierge::kDiskImageProgressSignal) {
       is_disk_import_progress_signal_connected_ = is_connected;
     } else {
       NOTREACHED();
diff --git a/chromeos/dbus/fake_vm_plugin_dispatcher_client.cc b/chromeos/dbus/fake_vm_plugin_dispatcher_client.cc
index d2d55151..c1507d88 100644
--- a/chromeos/dbus/fake_vm_plugin_dispatcher_client.cc
+++ b/chromeos/dbus/fake_vm_plugin_dispatcher_client.cc
@@ -9,8 +9,6 @@
 #include "base/bind.h"
 #include "base/threading/thread_task_runner_handle.h"
 
-using namespace vm_tools::plugin_dispatcher;
-
 namespace chromeos {
 
 FakeVmPluginDispatcherClient::FakeVmPluginDispatcherClient() = default;
@@ -26,43 +24,48 @@
 }
 
 void FakeVmPluginDispatcherClient::StartVm(
-    const StartVmRequest& request,
-    DBusMethodCallback<StartVmResponse> callback) {
+    const vm_tools::plugin_dispatcher::StartVmRequest& request,
+    DBusMethodCallback<vm_tools::plugin_dispatcher::StartVmResponse> callback) {
   start_vm_called_ = true;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), start_vm_response_));
 }
 
 void FakeVmPluginDispatcherClient::ListVms(
-    const ListVmRequest& request,
-    DBusMethodCallback<ListVmResponse> callback) {
+    const vm_tools::plugin_dispatcher::ListVmRequest& request,
+    DBusMethodCallback<vm_tools::plugin_dispatcher::ListVmResponse> callback) {
   list_vms_called_ = true;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), list_vms_response_));
 }
 
 void FakeVmPluginDispatcherClient::StopVm(
-    const StopVmRequest& request,
-    DBusMethodCallback<StopVmResponse> callback) {
+    const vm_tools::plugin_dispatcher::StopVmRequest& request,
+    DBusMethodCallback<vm_tools::plugin_dispatcher::StopVmResponse> callback) {
   stop_vm_called_ = true;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), StopVmResponse()));
+      FROM_HERE, base::BindOnce(std::move(callback),
+                                vm_tools::plugin_dispatcher::StopVmResponse()));
 }
 
 void FakeVmPluginDispatcherClient::SuspendVm(
-    const SuspendVmRequest& request,
-    DBusMethodCallback<SuspendVmResponse> callback) {
+    const vm_tools::plugin_dispatcher::SuspendVmRequest& request,
+    DBusMethodCallback<vm_tools::plugin_dispatcher::SuspendVmResponse>
+        callback) {
   suspend_vm_called_ = true;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), SuspendVmResponse()));
+      FROM_HERE,
+      base::BindOnce(std::move(callback),
+                     vm_tools::plugin_dispatcher::SuspendVmResponse()));
 }
 
 void FakeVmPluginDispatcherClient::ShowVm(
-    const ShowVmRequest& request,
-    DBusMethodCallback<ShowVmResponse> callback) {
+    const vm_tools::plugin_dispatcher::ShowVmRequest& request,
+    DBusMethodCallback<vm_tools::plugin_dispatcher::ShowVmResponse> callback) {
   show_vm_called_ = true;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), ShowVmResponse()));
+      FROM_HERE, base::BindOnce(std::move(callback),
+                                vm_tools::plugin_dispatcher::ShowVmResponse()));
 }
 
 void FakeVmPluginDispatcherClient::WaitForServiceToBeAvailable(
diff --git a/chromeos/dbus/vm_plugin_dispatcher_client.cc b/chromeos/dbus/vm_plugin_dispatcher_client.cc
index e791779b..1a40e444 100644
--- a/chromeos/dbus/vm_plugin_dispatcher_client.cc
+++ b/chromeos/dbus/vm_plugin_dispatcher_client.cc
@@ -16,9 +16,9 @@
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #include "third_party/cros_system_api/dbus/vm_plugin_dispatcher/dbus-constants.h"
 
-namespace chromeos {
+namespace dispatcher = vm_tools::plugin_dispatcher;
 
-using namespace vm_tools::plugin_dispatcher;
+namespace chromeos {
 
 class VmPluginDispatcherClientImpl : public VmPluginDispatcherClient {
  public:
@@ -34,29 +34,34 @@
     observer_list_.RemoveObserver(observer);
   }
 
-  void StartVm(const StartVmRequest& request,
-               DBusMethodCallback<StartVmResponse> callback) override {
-    CallMethod(kStartVmMethod, request, std::move(callback));
+  void StartVm(
+      const dispatcher::StartVmRequest& request,
+      DBusMethodCallback<dispatcher::StartVmResponse> callback) override {
+    CallMethod(dispatcher::kStartVmMethod, request, std::move(callback));
   }
 
-  void ListVms(const ListVmRequest& request,
-               DBusMethodCallback<ListVmResponse> callback) override {
-    CallMethod(kListVmsMethod, request, std::move(callback));
+  void ListVms(
+      const dispatcher::ListVmRequest& request,
+      DBusMethodCallback<dispatcher::ListVmResponse> callback) override {
+    CallMethod(dispatcher::kListVmsMethod, request, std::move(callback));
   }
 
-  void StopVm(const StopVmRequest& request,
-              DBusMethodCallback<StopVmResponse> callback) override {
-    CallMethod(kStopVmMethod, request, std::move(callback));
+  void StopVm(
+      const dispatcher::StopVmRequest& request,
+      DBusMethodCallback<dispatcher::StopVmResponse> callback) override {
+    CallMethod(dispatcher::kStopVmMethod, request, std::move(callback));
   }
 
-  void SuspendVm(const SuspendVmRequest& request,
-                 DBusMethodCallback<SuspendVmResponse> callback) override {
-    CallMethod(kSuspendVmMethod, request, std::move(callback));
+  void SuspendVm(
+      const dispatcher::SuspendVmRequest& request,
+      DBusMethodCallback<dispatcher::SuspendVmResponse> callback) override {
+    CallMethod(dispatcher::kSuspendVmMethod, request, std::move(callback));
   }
 
-  void ShowVm(const ShowVmRequest& request,
-              DBusMethodCallback<ShowVmResponse> callback) override {
-    CallMethod(kShowVmMethod, request, std::move(callback));
+  void ShowVm(
+      const dispatcher::ShowVmRequest& request,
+      DBusMethodCallback<dispatcher::ShowVmResponse> callback) override {
+    CallMethod(dispatcher::kShowVmMethod, request, std::move(callback));
   }
 
   void WaitForServiceToBeAvailable(
@@ -68,17 +73,17 @@
 
  protected:
   void Init(dbus::Bus* bus) override {
-    vm_plugin_dispatcher_proxy_ =
-        bus->GetObjectProxy(kVmPluginDispatcherServiceName,
-                            dbus::ObjectPath(kVmPluginDispatcherServicePath));
+    vm_plugin_dispatcher_proxy_ = bus->GetObjectProxy(
+        dispatcher::kVmPluginDispatcherServiceName,
+        dbus::ObjectPath(dispatcher::kVmPluginDispatcherServicePath));
     if (!vm_plugin_dispatcher_proxy_) {
       LOG(ERROR) << "Unable to get dbus proxy for "
-                 << kVmPluginDispatcherServiceName;
+                 << dispatcher::kVmPluginDispatcherServiceName;
     }
 
     vm_plugin_dispatcher_proxy_->ConnectToSignal(
-        vm_tools::plugin_dispatcher::kVmPluginDispatcherInterface,
-        vm_tools::plugin_dispatcher::kVmStateChangedSignal,
+        dispatcher::kVmPluginDispatcherInterface,
+        dispatcher::kVmStateChangedSignal,
         base::BindRepeating(
             &VmPluginDispatcherClientImpl::OnVmStateChangedSignal,
             weak_ptr_factory_.GetWeakPtr()),
@@ -91,7 +96,8 @@
   void CallMethod(const std::string& method_name,
                   const RequestProto& request,
                   DBusMethodCallback<ResponseProto> callback) {
-    dbus::MethodCall method_call(kVmPluginDispatcherInterface, method_name);
+    dbus::MethodCall method_call(dispatcher::kVmPluginDispatcherInterface,
+                                 method_name);
     dbus::MessageWriter writer(&method_call);
 
     if (!writer.AppendProtoAsArrayOfBytes(request)) {
@@ -126,12 +132,10 @@
   }
 
   void OnVmStateChangedSignal(dbus::Signal* signal) {
-    DCHECK_EQ(signal->GetInterface(),
-              vm_tools::plugin_dispatcher::kVmPluginDispatcherInterface);
-    DCHECK_EQ(signal->GetMember(),
-              vm_tools::plugin_dispatcher::kVmStateChangedSignal);
+    DCHECK_EQ(signal->GetInterface(), dispatcher::kVmPluginDispatcherInterface);
+    DCHECK_EQ(signal->GetMember(), dispatcher::kVmStateChangedSignal);
 
-    vm_tools::plugin_dispatcher::VmStateChangedSignal vm_state_changed_signal;
+    dispatcher::VmStateChangedSignal vm_state_changed_signal;
     dbus::MessageReader reader(signal);
     if (!reader.PopArrayOfBytesAsProto(&vm_state_changed_signal)) {
       LOG(ERROR) << "Failed to parse proto from DBus Signal";
@@ -146,8 +150,7 @@
   void OnSignalConnected(const std::string& interface_name,
                          const std::string& signal_name,
                          bool is_connected) {
-    DCHECK_EQ(interface_name,
-              vm_tools::plugin_dispatcher::kVmPluginDispatcherInterface);
+    DCHECK_EQ(interface_name, dispatcher::kVmPluginDispatcherInterface);
     if (!is_connected)
       LOG(ERROR) << "Failed to connect to signal: " << signal_name;
   }
diff --git a/chromeos/network/client_cert_util.cc b/chromeos/network/client_cert_util.cc
index d8c2434..0db2405 100644
--- a/chromeos/network/client_cert_util.cc
+++ b/chromeos/network/client_cert_util.cc
@@ -45,15 +45,14 @@
   if (identity)
     cert_config->policy_identity = *identity;
 
-  using namespace ::onc::client_cert;
   const std::string* client_cert_type =
-      dict_with_client_cert.FindStringKey(kClientCertType);
+      dict_with_client_cert.FindStringKey(::onc::client_cert::kClientCertType);
   if (client_cert_type)
     cert_config->client_cert_type = *client_cert_type;
 
-  if (cert_config->client_cert_type == kPattern) {
+  if (cert_config->client_cert_type == ::onc::client_cert::kPattern) {
     const base::Value* pattern_value = dict_with_client_cert.FindKeyOfType(
-        kClientCertPattern, base::Value::Type::DICTIONARY);
+        ::onc::client_cert::kClientCertPattern, base::Value::Type::DICTIONARY);
     if (pattern_value) {
       base::Optional<OncCertificatePattern> pattern =
           OncCertificatePattern::ReadFromONCDictionary(*pattern_value);
@@ -63,9 +62,9 @@
       }
       cert_config->pattern = pattern.value();
     }
-  } else if (cert_config->client_cert_type == kRef) {
+  } else if (cert_config->client_cert_type == ::onc::client_cert::kRef) {
     const base::Value* client_cert_ref_key =
-        dict_with_client_cert.FindKeyOfType(kClientCertRef,
+        dict_with_client_cert.FindKeyOfType(::onc::client_cert::kClientCertRef,
                                             base::Value::Type::STRING);
     if (client_cert_ref_key)
       cert_config->guid = client_cert_ref_key->GetString();
@@ -242,18 +241,16 @@
 void OncToClientCertConfig(::onc::ONCSource onc_source,
                            const base::DictionaryValue& network_config,
                            ClientCertConfig* cert_config) {
-  using namespace ::onc;
-
   *cert_config = ClientCertConfig();
 
   const base::DictionaryValue* dict_with_client_cert = NULL;
 
   const base::DictionaryValue* wifi = NULL;
-  network_config.GetDictionaryWithoutPathExpansion(network_config::kWiFi,
+  network_config.GetDictionaryWithoutPathExpansion(::onc::network_config::kWiFi,
                                                    &wifi);
   if (wifi) {
     const base::DictionaryValue* eap = NULL;
-    wifi->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
+    wifi->GetDictionaryWithoutPathExpansion(::onc::wifi::kEAP, &eap);
     if (!eap)
       return;
 
@@ -262,12 +259,13 @@
   }
 
   const base::DictionaryValue* vpn = NULL;
-  network_config.GetDictionaryWithoutPathExpansion(network_config::kVPN, &vpn);
+  network_config.GetDictionaryWithoutPathExpansion(::onc::network_config::kVPN,
+                                                   &vpn);
   if (vpn) {
     const base::DictionaryValue* openvpn = NULL;
-    vpn->GetDictionaryWithoutPathExpansion(vpn::kOpenVPN, &openvpn);
+    vpn->GetDictionaryWithoutPathExpansion(::onc::vpn::kOpenVPN, &openvpn);
     const base::DictionaryValue* ipsec = NULL;
-    vpn->GetDictionaryWithoutPathExpansion(vpn::kIPsec, &ipsec);
+    vpn->GetDictionaryWithoutPathExpansion(::onc::vpn::kIPsec, &ipsec);
     if (openvpn) {
       dict_with_client_cert = openvpn;
       cert_config->location = CONFIG_TYPE_OPENVPN;
@@ -280,11 +278,11 @@
   }
 
   const base::DictionaryValue* ethernet = NULL;
-  network_config.GetDictionaryWithoutPathExpansion(network_config::kEthernet,
-                                                   &ethernet);
+  network_config.GetDictionaryWithoutPathExpansion(
+      ::onc::network_config::kEthernet, &ethernet);
   if (ethernet) {
     const base::DictionaryValue* eap = NULL;
-    ethernet->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
+    ethernet->GetDictionaryWithoutPathExpansion(::onc::wifi::kEAP, &eap);
     if (!eap)
       return;
     dict_with_client_cert = eap;
diff --git a/chromeos/network/onc/onc_normalizer.cc b/chromeos/network/onc/onc_normalizer.cc
index c51931f..f34a6aee 100644
--- a/chromeos/network/onc/onc_normalizer.cc
+++ b/chromeos/network/onc/onc_normalizer.cc
@@ -93,26 +93,27 @@
 }  // namespace
 
 void Normalizer::NormalizeCertificate(base::DictionaryValue* cert) {
-  using namespace ::onc::certificate;
-
   std::string type;
   cert->GetStringWithoutPathExpansion(::onc::certificate::kType, &type);
-  RemoveEntryUnless(cert, kPKCS12, type == kClient);
-  RemoveEntryUnless(cert, kTrustBits, type == kServer || type == kAuthority);
-  RemoveEntryUnless(cert, kX509, type == kServer || type == kAuthority);
+  RemoveEntryUnless(cert, ::onc::certificate::kPKCS12,
+                    type == ::onc::certificate::kClient);
+  RemoveEntryUnless(cert, ::onc::certificate::kTrustBits,
+                    type == ::onc::certificate::kServer ||
+                        type == ::onc::certificate::kAuthority);
+  RemoveEntryUnless(cert, ::onc::certificate::kX509,
+                    type == ::onc::certificate::kServer ||
+                        type == ::onc::certificate::kAuthority);
 }
 
 void Normalizer::NormalizeEthernet(base::DictionaryValue* ethernet) {
-  using namespace ::onc::ethernet;
-
   std::string auth;
-  ethernet->GetStringWithoutPathExpansion(kAuthentication, &auth);
-  RemoveEntryUnless(ethernet, kEAP, auth == k8021X);
+  ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
+                                          &auth);
+  RemoveEntryUnless(ethernet, ::onc::ethernet::kEAP,
+                    auth == ::onc::ethernet::k8021X);
 }
 
 void Normalizer::NormalizeEAP(base::DictionaryValue* eap) {
-  using namespace ::onc::eap;
-
   std::string clientcert_type;
   eap->GetStringWithoutPathExpansion(::onc::client_cert::kClientCertType,
                                      &clientcert_type);
@@ -124,23 +125,27 @@
                     clientcert_type == ::onc::client_cert::kRef);
 
   std::string outer;
-  eap->GetStringWithoutPathExpansion(kOuter, &outer);
-  RemoveEntryUnless(eap, kAnonymousIdentity,
-                    outer == kPEAP || outer == kEAP_TTLS);
-  RemoveEntryUnless(eap, kInner,
-                    outer == kPEAP || outer == kEAP_TTLS || outer == kEAP_FAST);
+  eap->GetStringWithoutPathExpansion(::onc::eap::kOuter, &outer);
+  RemoveEntryUnless(
+      eap, ::onc::eap::kAnonymousIdentity,
+      outer == ::onc::eap::kPEAP || outer == ::onc::eap::kEAP_TTLS);
+  RemoveEntryUnless(eap, ::onc::eap::kInner,
+                    outer == ::onc::eap::kPEAP ||
+                        outer == ::onc::eap::kEAP_TTLS ||
+                        outer == ::onc::eap::kEAP_FAST);
 }
 
 void Normalizer::NormalizeIPsec(base::DictionaryValue* ipsec) {
-  using namespace ::onc::ipsec;
-
   std::string auth_type;
-  ipsec->GetStringWithoutPathExpansion(kAuthenticationType, &auth_type);
-  RemoveEntryUnless(
-      ipsec, ::onc::client_cert::kClientCertType, auth_type == kCert);
-  RemoveEntryUnless(ipsec, kServerCARef, auth_type == kCert);
-  RemoveEntryUnless(ipsec, kPSK, auth_type == kPSK);
-  RemoveEntryUnless(ipsec, ::onc::vpn::kSaveCredentials, auth_type == kPSK);
+  ipsec->GetStringWithoutPathExpansion(::onc::ipsec::kAuthenticationType,
+                                       &auth_type);
+  RemoveEntryUnless(ipsec, ::onc::client_cert::kClientCertType,
+                    auth_type == ::onc::ipsec::kCert);
+  RemoveEntryUnless(ipsec, ::onc::ipsec::kServerCARef,
+                    auth_type == ::onc::ipsec::kCert);
+  RemoveEntryUnless(ipsec, ::onc::ipsec::kPSK, auth_type == ::onc::ipsec::kPSK);
+  RemoveEntryUnless(ipsec, ::onc::vpn::kSaveCredentials,
+                    auth_type == ::onc::ipsec::kPSK);
 
   std::string clientcert_type;
   ipsec->GetStringWithoutPathExpansion(::onc::client_cert::kClientCertType,
@@ -153,10 +158,11 @@
                     clientcert_type == ::onc::client_cert::kRef);
 
   int ike_version = -1;
-  ipsec->GetIntegerWithoutPathExpansion(kIKEVersion, &ike_version);
-  RemoveEntryUnless(ipsec, kEAP, ike_version == 2);
-  RemoveEntryUnless(ipsec, kGroup, ike_version == 1);
-  RemoveEntryUnless(ipsec, kXAUTH, ike_version == 1);
+  ipsec->GetIntegerWithoutPathExpansion(::onc::ipsec::kIKEVersion,
+                                        &ike_version);
+  RemoveEntryUnless(ipsec, ::onc::ipsec::kEAP, ike_version == 2);
+  RemoveEntryUnless(ipsec, ::onc::ipsec::kGroup, ike_version == 1);
+  RemoveEntryUnless(ipsec, ::onc::ipsec::kXAUTH, ike_version == 1);
 }
 
 void Normalizer::NormalizeNetworkConfiguration(base::DictionaryValue* network) {
@@ -216,35 +222,38 @@
 }
 
 void Normalizer::NormalizeProxySettings(base::DictionaryValue* proxy) {
-  using namespace ::onc::proxy;
-
   std::string type;
   proxy->GetStringWithoutPathExpansion(::onc::proxy::kType, &type);
-  RemoveEntryUnless(proxy, kManual, type == kManual);
-  RemoveEntryUnless(proxy, kExcludeDomains, type == kManual);
-  RemoveEntryUnless(proxy, kPAC, type == kPAC);
+  RemoveEntryUnless(proxy, ::onc::proxy::kManual,
+                    type == ::onc::proxy::kManual);
+  RemoveEntryUnless(proxy, ::onc::proxy::kExcludeDomains,
+                    type == ::onc::proxy::kManual);
+  RemoveEntryUnless(proxy, ::onc::proxy::kPAC, type == ::onc::proxy::kPAC);
 }
 
 void Normalizer::NormalizeVPN(base::DictionaryValue* vpn) {
-  using namespace ::onc::vpn;
-
   std::string type;
   vpn->GetStringWithoutPathExpansion(::onc::vpn::kType, &type);
-  RemoveEntryUnless(vpn, kOpenVPN, type == kOpenVPN);
-  RemoveEntryUnless(vpn, kIPsec, type == kIPsec || type == kTypeL2TP_IPsec);
-  RemoveEntryUnless(vpn, kL2TP, type == kTypeL2TP_IPsec);
-  RemoveEntryUnless(vpn, kThirdPartyVpn, type == kThirdPartyVpn);
-  RemoveEntryUnless(vpn, kArcVpn, type == kArcVpn);
+  RemoveEntryUnless(vpn, ::onc::vpn::kOpenVPN, type == ::onc::vpn::kOpenVPN);
+  RemoveEntryUnless(
+      vpn, ::onc::vpn::kIPsec,
+      type == ::onc::vpn::kIPsec || type == ::onc::vpn::kTypeL2TP_IPsec);
+  RemoveEntryUnless(vpn, ::onc::vpn::kL2TP,
+                    type == ::onc::vpn::kTypeL2TP_IPsec);
+  RemoveEntryUnless(vpn, ::onc::vpn::kThirdPartyVpn,
+                    type == ::onc::vpn::kThirdPartyVpn);
+  RemoveEntryUnless(vpn, ::onc::vpn::kArcVpn, type == ::onc::vpn::kArcVpn);
 }
 
 void Normalizer::NormalizeWiFi(base::DictionaryValue* wifi) {
-  using namespace ::onc::wifi;
-
   std::string security;
   wifi->GetStringWithoutPathExpansion(::onc::wifi::kSecurity, &security);
-  RemoveEntryUnless(wifi, kEAP, security == kWEP_8021X || security == kWPA_EAP);
-  RemoveEntryUnless(wifi, kPassphrase,
-                    security == kWEP_PSK || security == kWPA_PSK);
+  RemoveEntryUnless(
+      wifi, ::onc::wifi::kEAP,
+      security == ::onc::wifi::kWEP_8021X || security == ::onc::wifi::kWPA_EAP);
+  RemoveEntryUnless(
+      wifi, ::onc::wifi::kPassphrase,
+      security == ::onc::wifi::kWEP_PSK || security == ::onc::wifi::kWPA_PSK);
   FillInHexSSIDField(wifi);
 }
 
diff --git a/chromeos/network/onc/onc_utils.cc b/chromeos/network/onc/onc_utils.cc
index a573cbd0..95e3d39 100644
--- a/chromeos/network/onc/onc_utils.cc
+++ b/chromeos/network/onc/onc_utils.cc
@@ -57,8 +57,6 @@
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
-using namespace ::onc;
-
 namespace chromeos {
 namespace onc {
 
@@ -148,8 +146,10 @@
     if (FieldIsCredential(object_signature, field_name)) {
       // If it's the password field and the substitution string is used, don't
       // mask it.
-      if (&object_signature == &kEAPSignature && field_name == eap::kPassword &&
-          onc_value.GetString() == substitutes::kPasswordPlaceholderVerbatim) {
+      if (&object_signature == &kEAPSignature &&
+          field_name == ::onc::eap::kPassword &&
+          onc_value.GetString() ==
+              ::onc::substitutes::kPasswordPlaceholderVerbatim) {
         return Mapper::MapField(field_name, object_signature, onc_value,
                                 found_unknown_field, error);
       }
@@ -175,15 +175,15 @@
     DCHECK(entry_is_dictionary);
 
     std::string guid;
-    cert->GetStringWithoutPathExpansion(certificate::kGUID, &guid);
+    cert->GetStringWithoutPathExpansion(::onc::certificate::kGUID, &guid);
     std::string cert_type;
-    cert->GetStringWithoutPathExpansion(certificate::kType, &cert_type);
-    if (cert_type != certificate::kServer &&
-        cert_type != certificate::kAuthority) {
+    cert->GetStringWithoutPathExpansion(::onc::certificate::kType, &cert_type);
+    if (cert_type != ::onc::certificate::kServer &&
+        cert_type != ::onc::certificate::kAuthority) {
       continue;
     }
     std::string x509_data;
-    cert->GetStringWithoutPathExpansion(certificate::kX509, &x509_data);
+    cert->GetStringWithoutPathExpansion(::onc::certificate::kX509, &x509_data);
 
     std::string der = DecodePEM(x509_data);
     std::string pem;
@@ -335,39 +335,30 @@
                                    const OncValueSignature& signature,
                                    base::DictionaryValue* onc_object) {
   if (&signature == &kCertificatePatternSignature) {
-    if (!ResolveCertRefList(certs_by_guid,
-                            client_cert::kIssuerCARef,
-                            client_cert::kIssuerCAPEMs,
-                            onc_object)) {
+    if (!ResolveCertRefList(certs_by_guid, ::onc::client_cert::kIssuerCARef,
+                            ::onc::client_cert::kIssuerCAPEMs, onc_object)) {
       return false;
     }
   } else if (&signature == &kEAPSignature) {
-    if (!ResolveCertRefsOrRefToList(certs_by_guid,
-                                    eap::kServerCARefs,
-                                    eap::kServerCARef,
-                                    eap::kServerCAPEMs,
-                                    onc_object)) {
+    if (!ResolveCertRefsOrRefToList(certs_by_guid, ::onc::eap::kServerCARefs,
+                                    ::onc::eap::kServerCARef,
+                                    ::onc::eap::kServerCAPEMs, onc_object)) {
       return false;
     }
   } else if (&signature == &kIPsecSignature) {
-    if (!ResolveCertRefsOrRefToList(certs_by_guid,
-                                    ipsec::kServerCARefs,
-                                    ipsec::kServerCARef,
-                                    ipsec::kServerCAPEMs,
-                                    onc_object)) {
+    if (!ResolveCertRefsOrRefToList(certs_by_guid, ::onc::ipsec::kServerCARefs,
+                                    ::onc::ipsec::kServerCARef,
+                                    ::onc::ipsec::kServerCAPEMs, onc_object)) {
       return false;
     }
   } else if (&signature == &kIPsecSignature ||
              &signature == &kOpenVPNSignature) {
-    if (!ResolveSingleCertRef(certs_by_guid,
-                              openvpn::kServerCertRef,
-                              openvpn::kServerCertPEM,
-                              onc_object) ||
-        !ResolveCertRefsOrRefToList(certs_by_guid,
-                                    openvpn::kServerCARefs,
-                                    openvpn::kServerCARef,
-                                    openvpn::kServerCAPEMs,
-                                    onc_object)) {
+    if (!ResolveSingleCertRef(certs_by_guid, ::onc::openvpn::kServerCertRef,
+                              ::onc::openvpn::kServerCertPEM, onc_object) ||
+        !ResolveCertRefsOrRefToList(
+            certs_by_guid, ::onc::openvpn::kServerCARefs,
+            ::onc::openvpn::kServerCARef, ::onc::openvpn::kServerCAPEMs,
+            onc_object)) {
       return false;
     }
   }
@@ -683,22 +674,23 @@
   int iterations;
   std::string ciphertext;
 
-  if (!GetString(root, encrypted::kCiphertext, &ciphertext) ||
-      !GetString(root, encrypted::kCipher, &cipher) ||
-      !GetString(root, encrypted::kHMAC, &hmac) ||
-      !GetString(root, encrypted::kHMACMethod, &hmac_method) ||
-      !GetString(root, encrypted::kIV, &initial_vector) ||
-      !GetInt(root, encrypted::kIterations, &iterations) ||
-      !GetString(root, encrypted::kSalt, &salt) ||
-      !GetString(root, encrypted::kStretch, &stretch_method) ||
-      !GetString(root, toplevel_config::kType, &onc_type) ||
-      onc_type != toplevel_config::kEncryptedConfiguration) {
+  if (!GetString(root, ::onc::encrypted::kCiphertext, &ciphertext) ||
+      !GetString(root, ::onc::encrypted::kCipher, &cipher) ||
+      !GetString(root, ::onc::encrypted::kHMAC, &hmac) ||
+      !GetString(root, ::onc::encrypted::kHMACMethod, &hmac_method) ||
+      !GetString(root, ::onc::encrypted::kIV, &initial_vector) ||
+      !GetInt(root, ::onc::encrypted::kIterations, &iterations) ||
+      !GetString(root, ::onc::encrypted::kSalt, &salt) ||
+      !GetString(root, ::onc::encrypted::kStretch, &stretch_method) ||
+      !GetString(root, ::onc::toplevel_config::kType, &onc_type) ||
+      onc_type != ::onc::toplevel_config::kEncryptedConfiguration) {
     NET_LOG(ERROR) << "Encrypted ONC malformed.";
     return nullptr;
   }
 
-  if (hmac_method != encrypted::kSHA1 || cipher != encrypted::kAES256 ||
-      stretch_method != encrypted::kPBKDF2) {
+  if (hmac_method != ::onc::encrypted::kSHA1 ||
+      cipher != ::onc::encrypted::kAES256 ||
+      stretch_method != ::onc::encrypted::kPBKDF2) {
     NET_LOG(ERROR) << "Encrypted ONC unsupported encryption scheme.";
     return nullptr;
   }
@@ -767,17 +759,17 @@
   return new_root;
 }
 
-std::string GetSourceAsString(ONCSource source) {
+std::string GetSourceAsString(::onc::ONCSource source) {
   switch (source) {
-    case ONC_SOURCE_UNKNOWN:
+    case ::onc::ONC_SOURCE_UNKNOWN:
       return "unknown";
-    case ONC_SOURCE_NONE:
+    case ::onc::ONC_SOURCE_NONE:
       return "none";
-    case ONC_SOURCE_DEVICE_POLICY:
+    case ::onc::ONC_SOURCE_DEVICE_POLICY:
       return "device policy";
-    case ONC_SOURCE_USER_POLICY:
+    case ::onc::ONC_SOURCE_USER_POLICY:
       return "user policy";
-    case ONC_SOURCE_USER_IMPORT:
+    case ::onc::ONC_SOURCE_USER_IMPORT:
       return "user import";
   }
   NOTREACHED() << "unknown ONC source " << source;
@@ -788,11 +780,11 @@
                               const VariableExpander& variable_expander,
                               base::DictionaryValue* onc_object) {
   if (&signature == &kEAPSignature) {
-    ExpandField(eap::kAnonymousIdentity, variable_expander, onc_object);
-    ExpandField(eap::kIdentity, variable_expander, onc_object);
+    ExpandField(::onc::eap::kAnonymousIdentity, variable_expander, onc_object);
+    ExpandField(::onc::eap::kIdentity, variable_expander, onc_object);
   } else if (&signature == &kL2TPSignature ||
              &signature == &kOpenVPNSignature) {
-    ExpandField(vpn::kUsername, variable_expander, onc_object);
+    ExpandField(::onc::vpn::kUsername, variable_expander, onc_object);
   }
 
   // Recurse into nested objects.
@@ -896,7 +888,7 @@
 }
 
 bool ParseAndValidateOncForImport(const std::string& onc_blob,
-                                  ONCSource onc_source,
+                                  ::onc::ONCSource onc_source,
                                   const std::string& passphrase,
                                   base::ListValue* network_configs,
                                   base::DictionaryValue* global_network_config,
@@ -919,8 +911,8 @@
 
   // Check and see if this is an encrypted ONC file. If so, decrypt it.
   std::string onc_type;
-  if (GetString(*toplevel_onc, toplevel_config::kType, &onc_type) &&
-      onc_type == toplevel_config::kEncryptedConfiguration) {
+  if (GetString(*toplevel_onc, ::onc::toplevel_config::kType, &onc_type) &&
+      onc_type == ::onc::toplevel_config::kEncryptedConfiguration) {
     toplevel_onc = Decrypt(passphrase, *toplevel_onc);
     if (!toplevel_onc) {
       LOG(ERROR) << "Couldn't decrypt the ONC from "
@@ -929,8 +921,8 @@
     }
   }
 
-  bool from_policy = (onc_source == ONC_SOURCE_USER_POLICY ||
-                      onc_source == ONC_SOURCE_DEVICE_POLICY);
+  bool from_policy = (onc_source == ::onc::ONC_SOURCE_USER_POLICY ||
+                      onc_source == ::onc::ONC_SOURCE_DEVICE_POLICY);
 
   // Validate the ONC dictionary. We are liberal and ignore unknown field
   // names and ignore invalid field names in kRecommended arrays.
@@ -965,7 +957,7 @@
 
   if (certificates) {
     base::Value* validated_certs = toplevel_onc->FindKeyOfType(
-        toplevel_config::kCertificates, base::Value::Type::LIST);
+        ::onc::toplevel_config::kCertificates, base::Value::Type::LIST);
     if (validated_certs)
       certificates->GetList().swap(validated_certs->GetList());
   }
@@ -975,7 +967,7 @@
   // value of the function (which is supposed to aggregate validation issues in
   // all segments of the ONC blob).
   base::Value* validated_networks = toplevel_onc->FindKeyOfType(
-      toplevel_config::kNetworkConfigurations, base::Value::Type::LIST);
+      ::onc::toplevel_config::kNetworkConfigurations, base::Value::Type::LIST);
   base::ListValue* validated_networks_list;
   if (validated_networks &&
       validated_networks->GetAsList(&validated_networks_list)) {
@@ -997,7 +989,7 @@
 
   if (global_network_config) {
     base::Value* validated_global_config = toplevel_onc->FindKeyOfType(
-        toplevel_config::kGlobalNetworkConfiguration,
+        ::onc::toplevel_config::kGlobalNetworkConfiguration,
         base::Value::Type::DICTIONARY);
     if (validated_global_config) {
       base::DictionaryValue* validated_global_config_dict = nullptr;
diff --git a/chromeos/network/onc/onc_validator.cc b/chromeos/network/onc/onc_validator.cc
index 682aefe..5ccc595 100644
--- a/chromeos/network/onc/onc_validator.cc
+++ b/chromeos/network/onc/onc_validator.cc
@@ -30,11 +30,6 @@
 // According to the IEEE 802.11 standard the SSID is a series of 0 to 32 octets.
 const int kMaximumSSIDLengthInBytes = 32;
 
-template <typename T, size_t N>
-std::vector<T> toVector(T const (&array)[N]) {
-  return std::vector<T>(array, array + N);
-}
-
 void AddKeyToList(const char* key, base::Value::ListStorage& list) {
   base::Value key_value(key);
   if (!base::Contains(list, key_value))
@@ -332,23 +327,28 @@
 
 bool Validator::ValidateClientCertFields(bool allow_cert_type_none,
                                          base::DictionaryValue* result) {
-  using namespace ::onc::client_cert;
-  const char* const kValidCertTypes[] = {kRef, kPattern, kPKCS11Id};
-  std::vector<const char*> valid_cert_types(toVector(kValidCertTypes));
+  std::vector<const char*> valid_cert_types = {::onc::client_cert::kRef,
+                                               ::onc::client_cert::kPattern,
+                                               ::onc::client_cert::kPKCS11Id};
   if (allow_cert_type_none)
-    valid_cert_types.push_back(kClientCertTypeNone);
-  if (FieldExistsAndHasNoValidValue(*result, kClientCertType, valid_cert_types))
+    valid_cert_types.push_back(::onc::client_cert::kClientCertTypeNone);
+  if (FieldExistsAndHasNoValidValue(
+          *result, ::onc::client_cert::kClientCertType, valid_cert_types))
     return false;
 
-  std::string cert_type = GetStringFromDict(*result, kClientCertType);
+  std::string cert_type =
+      GetStringFromDict(*result, ::onc::client_cert::kClientCertType);
   bool all_required_exist = true;
 
-  if (cert_type == kPattern)
-    all_required_exist &= RequireField(*result, kClientCertPattern);
-  else if (cert_type == kRef)
-    all_required_exist &= RequireField(*result, kClientCertRef);
-  else if (cert_type == kPKCS11Id)
-    all_required_exist &= RequireField(*result, kClientCertPKCS11Id);
+  if (cert_type == ::onc::client_cert::kPattern)
+    all_required_exist &=
+        RequireField(*result, ::onc::client_cert::kClientCertPattern);
+  else if (cert_type == ::onc::client_cert::kRef)
+    all_required_exist &=
+        RequireField(*result, ::onc::client_cert::kClientCertRef);
+  else if (cert_type == ::onc::client_cert::kPKCS11Id)
+    all_required_exist &=
+        RequireField(*result, ::onc::client_cert::kClientCertPKCS11Id);
 
   return !error_on_missing_field_ || all_required_exist;
 }
@@ -609,12 +609,11 @@
 }
 
 bool Validator::ValidateToplevelConfiguration(base::DictionaryValue* result) {
-  using namespace ::onc::toplevel_config;
-
-  const char* const kValidTypes[] = {kUnencryptedConfiguration,
-                                     kEncryptedConfiguration};
-  const std::vector<const char*> valid_types(toVector(kValidTypes));
-  if (FieldExistsAndHasNoValidValue(*result, kType, valid_types))
+  const std::vector<const char*> valid_types = {
+      ::onc::toplevel_config::kUnencryptedConfiguration,
+      ::onc::toplevel_config::kEncryptedConfiguration};
+  if (FieldExistsAndHasNoValidValue(*result, ::onc::toplevel_config::kType,
+                                    valid_types))
     return false;
 
   if (IsGlobalNetworkConfigInUserImport(*result))
@@ -624,48 +623,49 @@
 }
 
 bool Validator::ValidateNetworkConfiguration(base::DictionaryValue* result) {
-  using namespace ::onc::network_config;
-
-  const std::string* onc_type = result->FindStringKey(kType);
+  const std::string* onc_type =
+      result->FindStringKey(::onc::network_config::kType);
   if (onc_type && *onc_type == ::onc::network_type::kWimaxDeprecated) {
     AddValidationIssue(/*is_error=*/false, "WiMax is deprecated");
     return true;
   }
 
-  const char* const kValidTypes[] = {
+  const std::vector<const char*> valid_types = {
       ::onc::network_type::kEthernet, ::onc::network_type::kVPN,
       ::onc::network_type::kWiFi,     ::onc::network_type::kCellular,
       ::onc::network_type::kTether,
   };
-  const std::vector<const char*> valid_types(toVector(kValidTypes));
-  const char* const kValidIPConfigTypes[] = {kIPConfigTypeDHCP,
-                                             kIPConfigTypeStatic};
-  const std::vector<const char*> valid_ipconfig_types(
-      toVector(kValidIPConfigTypes));
-  if (FieldExistsAndHasNoValidValue(*result, kType, valid_types) ||
-      FieldExistsAndHasNoValidValue(*result, kIPAddressConfigType,
+  const std::vector<const char*> valid_ipconfig_types = {
+      ::onc::network_config::kIPConfigTypeDHCP,
+      ::onc::network_config::kIPConfigTypeStatic};
+  if (FieldExistsAndHasNoValidValue(*result, ::onc::network_config::kType,
+                                    valid_types) ||
+      FieldExistsAndHasNoValidValue(*result,
+                                    ::onc::network_config::kIPAddressConfigType,
                                     valid_ipconfig_types) ||
-      FieldExistsAndHasNoValidValue(*result, kNameServersConfigType,
-                                    valid_ipconfig_types) ||
-      FieldExistsAndIsEmpty(*result, kGUID)) {
+      FieldExistsAndHasNoValidValue(
+          *result, ::onc::network_config::kNameServersConfigType,
+          valid_ipconfig_types) ||
+      FieldExistsAndIsEmpty(*result, ::onc::network_config::kGUID)) {
     return false;
   }
 
-  if (!CheckGuidIsUniqueAndAddToSet(*result, kGUID, &network_guids_))
+  if (!CheckGuidIsUniqueAndAddToSet(*result, ::onc::network_config::kGUID,
+                                    &network_guids_))
     return false;
 
-  bool all_required_exist = RequireField(*result, kGUID);
+  bool all_required_exist = RequireField(*result, ::onc::network_config::kGUID);
 
   bool remove = false;
   result->GetBooleanWithoutPathExpansion(::onc::kRemove, &remove);
   if (!remove) {
-    all_required_exist &=
-        RequireField(*result, kName) && RequireField(*result, kType);
+    all_required_exist &= RequireField(*result, ::onc::network_config::kName) &&
+                          RequireField(*result, ::onc::network_config::kType);
 
     if (!NetworkHasCorrectStaticIPConfig(result))
       return false;
 
-    std::string type = GetStringFromDict(*result, kType);
+    std::string type = GetStringFromDict(*result, ::onc::network_config::kType);
 
     // Prohibit anything but WiFi and Ethernet for device-level policy (which
     // corresponds to shared networks). See also http://crosbug.com/28741.
@@ -702,30 +702,26 @@
 }
 
 bool Validator::ValidateEthernet(base::DictionaryValue* result) {
-  using namespace ::onc::ethernet;
-
-  const char* const kValidAuthentications[] = {kAuthenticationNone, k8021X};
-  const std::vector<const char*> valid_authentications(
-      toVector(kValidAuthentications));
-  if (FieldExistsAndHasNoValidValue(
-          *result, kAuthentication, valid_authentications)) {
+  const std::vector<const char*> valid_authentications = {
+      ::onc::ethernet::kAuthenticationNone, ::onc::ethernet::k8021X};
+  if (FieldExistsAndHasNoValidValue(*result, ::onc::ethernet::kAuthentication,
+                                    valid_authentications)) {
     return false;
   }
 
   bool all_required_exist = true;
-  std::string auth = GetStringFromDict(*result, kAuthentication);
-  if (auth == k8021X)
-    all_required_exist &= RequireField(*result, kEAP);
+  std::string auth =
+      GetStringFromDict(*result, ::onc::ethernet::kAuthentication);
+  if (auth == ::onc::ethernet::k8021X)
+    all_required_exist &= RequireField(*result, ::onc::ethernet::kEAP);
 
   return !error_on_missing_field_ || all_required_exist;
 }
 
 bool Validator::ValidateIPConfig(base::DictionaryValue* result,
                                  bool require_fields) {
-  using namespace ::onc::ipconfig;
-
-  const char* const kValidTypes[] = {kIPv4, kIPv6};
-  const std::vector<const char*> valid_types(toVector(kValidTypes));
+  const std::vector<const char*> valid_types = {::onc::ipconfig::kIPv4,
+                                                ::onc::ipconfig::kIPv6};
   if (FieldExistsAndHasNoValidValue(
           *result, ::onc::ipconfig::kType, valid_types))
     return false;
@@ -733,22 +729,25 @@
   std::string type = GetStringFromDict(*result, ::onc::ipconfig::kType);
   int lower_bound = 1;
   // In case of missing type, choose higher upper_bound.
-  int upper_bound = (type == kIPv4) ? 32 : 128;
-  if (FieldExistsAndIsNotInRange(
-          *result, kRoutingPrefix, lower_bound, upper_bound)) {
+  int upper_bound = (type == ::onc::ipconfig::kIPv4) ? 32 : 128;
+  if (FieldExistsAndIsNotInRange(*result, ::onc::ipconfig::kRoutingPrefix,
+                                 lower_bound, upper_bound)) {
     return false;
   }
 
   bool all_required_exist = true;
   if (require_fields) {
-    all_required_exist &= RequireField(*result, kIPAddress);
-    all_required_exist &= RequireField(*result, kRoutingPrefix);
-    all_required_exist &= RequireField(*result, kGateway);
-  } else {
-    all_required_exist &= FieldShouldExistOrBeRecommended(*result, kIPAddress);
+    all_required_exist &= RequireField(*result, ::onc::ipconfig::kIPAddress);
     all_required_exist &=
-        FieldShouldExistOrBeRecommended(*result, kRoutingPrefix);
-    all_required_exist &= FieldShouldExistOrBeRecommended(*result, kGateway);
+        RequireField(*result, ::onc::ipconfig::kRoutingPrefix);
+    all_required_exist &= RequireField(*result, ::onc::ipconfig::kGateway);
+  } else {
+    all_required_exist &=
+        FieldShouldExistOrBeRecommended(*result, ::onc::ipconfig::kIPAddress);
+    all_required_exist &= FieldShouldExistOrBeRecommended(
+        *result, ::onc::ipconfig::kRoutingPrefix);
+    all_required_exist &=
+        FieldShouldExistOrBeRecommended(*result, ::onc::ipconfig::kGateway);
   }
 
   return !error_on_missing_field_ || all_required_exist;
@@ -756,101 +755,98 @@
 
 bool Validator::NetworkHasCorrectStaticIPConfig(
     base::DictionaryValue* network) {
-  using namespace ::onc::network_config;
-  using namespace ::onc::ipconfig;
-
   bool must_have_ip_config = FieldIsSetToValueOrRecommended(
-      *network, kIPAddressConfigType, base::Value(kIPConfigTypeStatic));
+      *network, ::onc::network_config::kIPAddressConfigType,
+      base::Value(::onc::network_config::kIPConfigTypeStatic));
   bool must_have_nameservers = FieldIsSetToValueOrRecommended(
-      *network, kNameServersConfigType, base::Value(kIPConfigTypeStatic));
+      *network, ::onc::network_config::kNameServersConfigType,
+      base::Value(::onc::network_config::kIPConfigTypeStatic));
 
   if (!must_have_ip_config && !must_have_nameservers)
     return true;
 
-  if (!RequireField(*network, kStaticIPConfig))
+  if (!RequireField(*network, ::onc::network_config::kStaticIPConfig))
     return false;
 
   base::DictionaryValue* static_ip_config = nullptr;
-  network->GetDictionary(kStaticIPConfig, &static_ip_config);
+  network->GetDictionary(::onc::network_config::kStaticIPConfig,
+                         &static_ip_config);
   bool valid = true;
   // StaticIPConfig should have all fields required by the corresponding
   // IPAddressConfigType and NameServersConfigType values.
   if (must_have_ip_config)
     valid &= ValidateIPConfig(static_ip_config, false /* require_fields */);
   if (must_have_nameservers)
-    valid &= FieldShouldExistOrBeRecommended(*static_ip_config, kNameServers);
+    valid &= FieldShouldExistOrBeRecommended(*static_ip_config,
+                                             ::onc::ipconfig::kNameServers);
   return valid;
 }
 
 bool Validator::ValidateWiFi(base::DictionaryValue* result) {
-  using namespace ::onc::wifi;
-
-  const char* const kValidSecurities[] = {kSecurityNone, kWEP_PSK, kWEP_8021X,
-                                          kWPA_PSK, kWPA_EAP};
-  const std::vector<const char*> valid_securities(toVector(kValidSecurities));
-  if (FieldExistsAndHasNoValidValue(*result, kSecurity, valid_securities))
+  const std::vector<const char*> valid_securities = {
+      ::onc::wifi::kSecurityNone, ::onc::wifi::kWEP_PSK,
+      ::onc::wifi::kWEP_8021X, ::onc::wifi::kWPA_PSK, ::onc::wifi::kWPA_EAP};
+  if (FieldExistsAndHasNoValidValue(*result, ::onc::wifi::kSecurity,
+                                    valid_securities))
     return false;
 
   if (!ValidateSSIDAndHexSSID(result))
     return false;
 
-  bool all_required_exist = RequireField(*result, kSecurity);
+  bool all_required_exist = RequireField(*result, ::onc::wifi::kSecurity);
 
   // One of {kSSID, kHexSSID} must be present.
-  if (!result->HasKey(kSSID))
-    all_required_exist &= RequireField(*result, kHexSSID);
-  if (!result->HasKey(kHexSSID))
-    all_required_exist &= RequireField(*result, kSSID);
+  if (!result->HasKey(::onc::wifi::kSSID))
+    all_required_exist &= RequireField(*result, ::onc::wifi::kHexSSID);
+  if (!result->HasKey(::onc::wifi::kHexSSID))
+    all_required_exist &= RequireField(*result, ::onc::wifi::kSSID);
 
-  std::string security = GetStringFromDict(*result, kSecurity);
-  if (security == kWEP_8021X || security == kWPA_EAP)
-    all_required_exist &= RequireField(*result, kEAP);
-  else if (security == kWEP_PSK || security == kWPA_PSK)
-    all_required_exist &= RequireField(*result, kPassphrase);
+  std::string security = GetStringFromDict(*result, ::onc::wifi::kSecurity);
+  if (security == ::onc::wifi::kWEP_8021X || security == ::onc::wifi::kWPA_EAP)
+    all_required_exist &= RequireField(*result, ::onc::wifi::kEAP);
+  else if (security == ::onc::wifi::kWEP_PSK ||
+           security == ::onc::wifi::kWPA_PSK)
+    all_required_exist &= RequireField(*result, ::onc::wifi::kPassphrase);
 
   return !error_on_missing_field_ || all_required_exist;
 }
 
 bool Validator::ValidateVPN(base::DictionaryValue* result) {
-  using namespace ::onc::vpn;
-
-  const char* const kValidTypes[] = {kIPsec, kTypeL2TP_IPsec, kOpenVPN,
-                                     kThirdPartyVpn, kArcVpn};
-  const std::vector<const char*> valid_types(toVector(kValidTypes));
+  const std::vector<const char*> valid_types = {
+      ::onc::vpn::kIPsec, ::onc::vpn::kTypeL2TP_IPsec, ::onc::vpn::kOpenVPN,
+      ::onc::vpn::kThirdPartyVpn, ::onc::vpn::kArcVpn};
   if (FieldExistsAndHasNoValidValue(*result, ::onc::vpn::kType, valid_types))
     return false;
 
   bool all_required_exist = RequireField(*result, ::onc::vpn::kType);
   std::string type = GetStringFromDict(*result, ::onc::vpn::kType);
-  if (type == kOpenVPN) {
-    all_required_exist &= RequireField(*result, kOpenVPN);
-  } else if (type == kIPsec) {
-    all_required_exist &= RequireField(*result, kIPsec);
-  } else if (type == kTypeL2TP_IPsec) {
-    all_required_exist &=
-        RequireField(*result, kIPsec) && RequireField(*result, kL2TP);
-  } else if (type == kThirdPartyVpn) {
-    all_required_exist &= RequireField(*result, kThirdPartyVpn);
-  } else if (type == kArcVpn) {
-    all_required_exist &= RequireField(*result, kArcVpn);
+  if (type == ::onc::vpn::kOpenVPN) {
+    all_required_exist &= RequireField(*result, ::onc::vpn::kOpenVPN);
+  } else if (type == ::onc::vpn::kIPsec) {
+    all_required_exist &= RequireField(*result, ::onc::vpn::kIPsec);
+  } else if (type == ::onc::vpn::kTypeL2TP_IPsec) {
+    all_required_exist &= RequireField(*result, ::onc::vpn::kIPsec) &&
+                          RequireField(*result, ::onc::vpn::kL2TP);
+  } else if (type == ::onc::vpn::kThirdPartyVpn) {
+    all_required_exist &= RequireField(*result, ::onc::vpn::kThirdPartyVpn);
+  } else if (type == ::onc::vpn::kArcVpn) {
+    all_required_exist &= RequireField(*result, ::onc::vpn::kArcVpn);
   }
 
   return !error_on_missing_field_ || all_required_exist;
 }
 
 bool Validator::ValidateIPsec(base::DictionaryValue* result) {
-  using namespace ::onc::ipsec;
-
-  const char* const kValidAuthentications[] = {kPSK, kCert};
-  const std::vector<const char*> valid_authentications(
-      toVector(kValidAuthentications));
-  if (FieldExistsAndHasNoValidValue(
-          *result, kAuthenticationType, valid_authentications) ||
-      FieldExistsAndIsEmpty(*result, kServerCARefs)) {
+  const std::vector<const char*> valid_authentications = {::onc::ipsec::kPSK,
+                                                          ::onc::ipsec::kCert};
+  if (FieldExistsAndHasNoValidValue(*result, ::onc::ipsec::kAuthenticationType,
+                                    valid_authentications) ||
+      FieldExistsAndIsEmpty(*result, ::onc::ipsec::kServerCARefs)) {
     return false;
   }
 
-  if (!OnlyOneFieldSet(*result, kServerCARefs, kServerCARef))
+  if (!OnlyOneFieldSet(*result, ::onc::ipsec::kServerCARefs,
+                       ::onc::ipsec::kServerCARef))
     return false;
 
   if (!ValidateClientCertFields(false,  // don't allow ClientCertType None
@@ -858,25 +854,29 @@
     return false;
   }
 
-  bool all_required_exist = RequireField(*result, kAuthenticationType) &&
-                            RequireField(*result, kIKEVersion);
-  std::string auth = GetStringFromDict(*result, kAuthenticationType);
-  bool has_server_ca_cert =
-      result->HasKey(kServerCARefs) || result->HasKey(kServerCARef);
-  if (auth == kCert) {
+  bool all_required_exist =
+      RequireField(*result, ::onc::ipsec::kAuthenticationType) &&
+      RequireField(*result, ::onc::ipsec::kIKEVersion);
+  std::string auth =
+      GetStringFromDict(*result, ::onc::ipsec::kAuthenticationType);
+  bool has_server_ca_cert = result->HasKey(::onc::ipsec::kServerCARefs) ||
+                            result->HasKey(::onc::ipsec::kServerCARef);
+  if (auth == ::onc::ipsec::kCert) {
     all_required_exist &=
         RequireField(*result, ::onc::client_cert::kClientCertType);
     if (!has_server_ca_cert) {
       all_required_exist = false;
       std::ostringstream msg;
-      msg << "The required field '" << kServerCARefs << "' is missing.";
+      msg << "The required field '" << ::onc::ipsec::kServerCARefs
+          << "' is missing.";
       AddValidationIssue(error_on_missing_field_, msg.str());
     }
   } else if (has_server_ca_cert) {
     std::ostringstream msg;
-    msg << "Field '" << kServerCARefs << "' (or '" << kServerCARef
-        << "') can only be set if '" << kAuthenticationType << "' is set to '"
-        << kCert << "'.";
+    msg << "Field '" << ::onc::ipsec::kServerCARefs << "' (or '"
+        << ::onc::ipsec::kServerCARef << "') can only be set if '"
+        << ::onc::ipsec::kAuthenticationType << "' is set to '"
+        << ::onc::ipsec::kCert << "'.";
     AddValidationIssue(true /* is_error */, msg.str());
     return false;
   }
@@ -885,31 +885,24 @@
 }
 
 bool Validator::ValidateOpenVPN(base::DictionaryValue* result) {
-  using namespace ::onc::openvpn;
-
-  const char* const kValidAuthRetryValues[] = {::onc::openvpn::kNone, kInteract,
-                                               kNoInteract};
-  const std::vector<const char*> valid_auth_retry_values(
-      toVector(kValidAuthRetryValues));
-  const char* const kValidCertTlsValues[] = {::onc::openvpn::kNone,
-                                             ::onc::openvpn::kServer};
-  const std::vector<const char*> valid_cert_tls_values(
-      toVector(kValidCertTlsValues));
-  const char* const kValidUserAuthTypes[] = {
-      ::onc::openvpn_user_auth_type::kNone,
-      ::onc::openvpn_user_auth_type::kOTP,
+  const std::vector<const char*> valid_auth_retry_values = {
+      ::onc::openvpn::kNone, ::onc::openvpn::kInteract,
+      ::onc::openvpn::kNoInteract};
+  const std::vector<const char*> valid_cert_tls_values = {
+      ::onc::openvpn::kNone, ::onc::openvpn::kServer};
+  const std::vector<const char*> valid_user_auth_types = {
+      ::onc::openvpn_user_auth_type::kNone, ::onc::openvpn_user_auth_type::kOTP,
       ::onc::openvpn_user_auth_type::kPassword,
       ::onc::openvpn_user_auth_type::kPasswordAndOTP};
-  const std::vector<const char*> valid_user_auth_types(
-      toVector(kValidUserAuthTypes));
 
-  if (FieldExistsAndHasNoValidValue(
-          *result, kAuthRetry, valid_auth_retry_values) ||
-      FieldExistsAndHasNoValidValue(
-          *result, kRemoteCertTLS, valid_cert_tls_values) ||
-      FieldExistsAndHasNoValidValue(
-          *result, kUserAuthenticationType, valid_user_auth_types) ||
-      FieldExistsAndIsEmpty(*result, kServerCARefs)) {
+  if (FieldExistsAndHasNoValidValue(*result, ::onc::openvpn::kAuthRetry,
+                                    valid_auth_retry_values) ||
+      FieldExistsAndHasNoValidValue(*result, ::onc::openvpn::kRemoteCertTLS,
+                                    valid_cert_tls_values) ||
+      FieldExistsAndHasNoValidValue(*result,
+                                    ::onc::openvpn::kUserAuthenticationType,
+                                    valid_user_auth_types) ||
+      FieldExistsAndIsEmpty(*result, ::onc::openvpn::kServerCARefs)) {
     return false;
   }
 
@@ -944,7 +937,8 @@
     }
   }
 
-  if (!OnlyOneFieldSet(*result, kServerCARefs, kServerCARef))
+  if (!OnlyOneFieldSet(*result, ::onc::openvpn::kServerCARefs,
+                       ::onc::openvpn::kServerCARef))
     return false;
 
   if (!ValidateClientCertFields(true /* allow ClientCertType None */, result))
@@ -971,30 +965,30 @@
 }
 
 bool Validator::ValidateVerifyX509(base::DictionaryValue* result) {
-  using namespace ::onc::verify_x509;
+  const std::vector<const char*> valid_types = {
+      ::onc::verify_x509::types::kName, ::onc::verify_x509::types::kNamePrefix,
+      ::onc::verify_x509::types::kSubject};
 
-  const char* const kValidTypes[] = {types::kName, types::kNamePrefix,
-                                     types::kSubject};
-  const std::vector<const char*> valid_types(toVector(kValidTypes));
-
-  if (FieldExistsAndHasNoValidValue(*result, kType, valid_types))
+  if (FieldExistsAndHasNoValidValue(*result, ::onc::verify_x509::kType,
+                                    valid_types))
     return false;
 
-  bool all_required_exist = RequireField(*result, kName);
+  bool all_required_exist = RequireField(*result, ::onc::verify_x509::kName);
 
   return !error_on_missing_field_ || all_required_exist;
 }
 
 bool Validator::ValidateCertificatePattern(base::DictionaryValue* result) {
-  using namespace ::onc::client_cert;
-
   bool all_required_exist = true;
-  if (!result->HasKey(kSubject) && !result->HasKey(kIssuer) &&
-      !result->HasKey(kIssuerCARef)) {
+  if (!result->HasKey(::onc::client_cert::kSubject) &&
+      !result->HasKey(::onc::client_cert::kIssuer) &&
+      !result->HasKey(::onc::client_cert::kIssuerCARef)) {
     all_required_exist = false;
     std::ostringstream msg;
-    msg << "None of the fields '" << kSubject << "', '" << kIssuer << "', and '"
-        << kIssuerCARef << "' is present, but at least one is required.";
+    msg << "None of the fields '" << ::onc::client_cert::kSubject << "', '"
+        << ::onc::client_cert::kIssuer << "', and '"
+        << ::onc::client_cert::kIssuerCARef
+        << "' is present, but at least one is required.";
     AddValidationIssue(error_on_missing_field_, msg.str());
   }
 
@@ -1003,102 +997,104 @@
 
 bool Validator::ValidateGlobalNetworkConfiguration(
     base::DictionaryValue* result) {
-  using namespace ::onc::global_network_config;
-  using namespace ::onc::network_config;
-
   // Validate that kDisableNetworkTypes, kAllowOnlyPolicyNetworksToConnect and
   // kBlacklistedHexSSIDs are only allowed in device policy.
-  if (!IsInDevicePolicy(result, kDisableNetworkTypes) ||
-      !IsInDevicePolicy(result, kAllowOnlyPolicyNetworksToConnect) ||
-      !IsInDevicePolicy(result, kAllowOnlyPolicyNetworksToConnectIfAvailable) ||
-      !IsInDevicePolicy(result, kBlacklistedHexSSIDs)) {
+  if (!IsInDevicePolicy(result,
+                        ::onc::global_network_config::kDisableNetworkTypes) ||
+      !IsInDevicePolicy(
+          result,
+          ::onc::global_network_config::kAllowOnlyPolicyNetworksToConnect) ||
+      !IsInDevicePolicy(result,
+                        ::onc::global_network_config::
+                            kAllowOnlyPolicyNetworksToConnectIfAvailable) ||
+      !IsInDevicePolicy(result,
+                        ::onc::global_network_config::kBlacklistedHexSSIDs)) {
     return false;
   }
 
   // Ensure the list contains only legitimate network type identifiers.
-  const char* const kValidNetworkTypeValues[] = {kCellular, kEthernet, kWiFi,
-                                                 kWimaxDeprecated, kTether};
-  const std::vector<const char*> valid_network_type_values(
-      toVector(kValidNetworkTypeValues));
-  if (!ListFieldContainsValidValues(*result, kDisableNetworkTypes,
-                                    valid_network_type_values)) {
+  const std::vector<const char*> valid_network_type_values = {
+      ::onc::network_config::kCellular, ::onc::network_config::kEthernet,
+      ::onc::network_config::kWiFi, ::onc::network_config::kWimaxDeprecated,
+      ::onc::network_config::kTether};
+  if (!ListFieldContainsValidValues(
+          *result, ::onc::global_network_config::kDisableNetworkTypes,
+          valid_network_type_values)) {
     return false;
   }
   return true;
 }
 
 bool Validator::ValidateProxySettings(base::DictionaryValue* result) {
-  using namespace ::onc::proxy;
-
-  const char* const kValidTypes[] = {kDirect, kManual, kPAC, kWPAD};
-  const std::vector<const char*> valid_types(toVector(kValidTypes));
+  const std::vector<const char*> valid_types = {
+      ::onc::proxy::kDirect, ::onc::proxy::kManual, ::onc::proxy::kPAC,
+      ::onc::proxy::kWPAD};
   if (FieldExistsAndHasNoValidValue(*result, ::onc::proxy::kType, valid_types))
     return false;
 
   bool all_required_exist = RequireField(*result, ::onc::proxy::kType);
   std::string type = GetStringFromDict(*result, ::onc::proxy::kType);
-  if (type == kManual)
-    all_required_exist &= RequireField(*result, kManual);
-  else if (type == kPAC)
-    all_required_exist &= RequireField(*result, kPAC);
+  if (type == ::onc::proxy::kManual)
+    all_required_exist &= RequireField(*result, ::onc::proxy::kManual);
+  else if (type == ::onc::proxy::kPAC)
+    all_required_exist &= RequireField(*result, ::onc::proxy::kPAC);
 
   return !error_on_missing_field_ || all_required_exist;
 }
 
 bool Validator::ValidateProxyLocation(base::DictionaryValue* result) {
-  using namespace ::onc::proxy;
-
-  bool all_required_exist =
-      RequireField(*result, kHost) && RequireField(*result, kPort);
+  bool all_required_exist = RequireField(*result, ::onc::proxy::kHost) &&
+                            RequireField(*result, ::onc::proxy::kPort);
 
   return !error_on_missing_field_ || all_required_exist;
 }
 
 bool Validator::ValidateEAP(base::DictionaryValue* result) {
-  using namespace ::onc::eap;
+  const std::vector<const char*> valid_inner_values = {
+      ::onc::eap::kAutomatic, ::onc::eap::kGTC, ::onc::eap::kMD5,
+      ::onc::eap::kMSCHAPv2, ::onc::eap::kPAP};
+  const std::vector<const char*> valid_outer_values = {
+      ::onc::eap::kPEAP,   ::onc::eap::kEAP_TLS, ::onc::eap::kEAP_TTLS,
+      ::onc::eap::kLEAP,   ::onc::eap::kEAP_SIM, ::onc::eap::kEAP_FAST,
+      ::onc::eap::kEAP_AKA};
 
-  const char* const kValidInnerValues[] = {
-      kAutomatic, kGTC, kMD5, kMSCHAPv2, kPAP};
-  const std::vector<const char*> valid_inner_values(
-      toVector(kValidInnerValues));
-  const char* const kValidOuterValues[] = {
-      kPEAP, kEAP_TLS, kEAP_TTLS, kLEAP, kEAP_SIM, kEAP_FAST, kEAP_AKA};
-  const std::vector<const char*> valid_outer_values(
-      toVector(kValidOuterValues));
-
-  if (FieldExistsAndHasNoValidValue(*result, kInner, valid_inner_values) ||
-      FieldExistsAndHasNoValidValue(*result, kOuter, valid_outer_values) ||
-      FieldExistsAndIsEmpty(*result, kServerCARefs)) {
+  if (FieldExistsAndHasNoValidValue(*result, ::onc::eap::kInner,
+                                    valid_inner_values) ||
+      FieldExistsAndHasNoValidValue(*result, ::onc::eap::kOuter,
+                                    valid_outer_values) ||
+      FieldExistsAndIsEmpty(*result, ::onc::eap::kServerCARefs)) {
     return false;
   }
 
-  if (!OnlyOneFieldSet(*result, kServerCARefs, kServerCARef))
+  if (!OnlyOneFieldSet(*result, ::onc::eap::kServerCARefs,
+                       ::onc::eap::kServerCARef))
     return false;
 
   if (!ValidateClientCertFields(true /* allow ClientCertType None */, result))
     return false;
 
-  bool all_required_exist = RequireField(*result, kOuter);
+  bool all_required_exist = RequireField(*result, ::onc::eap::kOuter);
 
   return !error_on_missing_field_ || all_required_exist;
 }
 
 bool Validator::ValidateCertificate(base::DictionaryValue* result) {
-  using namespace ::onc::certificate;
-
-  const char* const kValidTypes[] = {kClient, kServer, kAuthority};
-  const std::vector<const char*> valid_types(toVector(kValidTypes));
-  if (FieldExistsAndHasNoValidValue(*result, kType, valid_types) ||
-      FieldExistsAndIsEmpty(*result, kGUID)) {
+  const std::vector<const char*> valid_types = {::onc::certificate::kClient,
+                                                ::onc::certificate::kServer,
+                                                ::onc::certificate::kAuthority};
+  if (FieldExistsAndHasNoValidValue(*result, ::onc::certificate::kType,
+                                    valid_types) ||
+      FieldExistsAndIsEmpty(*result, ::onc::certificate::kGUID)) {
     return false;
   }
 
-  std::string type = GetStringFromDict(*result, kType);
+  std::string type = GetStringFromDict(*result, ::onc::certificate::kType);
 
-  if (!CheckGuidIsUniqueAndAddToSet(*result, kGUID, &certificate_guids_))
+  if (!CheckGuidIsUniqueAndAddToSet(*result, ::onc::certificate::kGUID,
+                                    &certificate_guids_))
     return false;
 
-  bool all_required_exist = RequireField(*result, kGUID);
+  bool all_required_exist = RequireField(*result, ::onc::certificate::kGUID);
 
   bool remove = false;
   result->GetBooleanWithoutPathExpansion(::onc::kRemove, &remove);
@@ -1111,35 +1107,35 @@
     return false;
   }
 
-  all_required_exist &= RequireField(*result, kType);
+  all_required_exist &= RequireField(*result, ::onc::certificate::kType);
 
-  if (type == kClient)
-    all_required_exist &= RequireField(*result, kPKCS12);
-  else if (type == kServer || type == kAuthority)
-    all_required_exist &= RequireField(*result, kX509);
+  if (type == ::onc::certificate::kClient)
+    all_required_exist &= RequireField(*result, ::onc::certificate::kPKCS12);
+  else if (type == ::onc::certificate::kServer ||
+           type == ::onc::certificate::kAuthority)
+    all_required_exist &= RequireField(*result, ::onc::certificate::kX509);
 
   return !error_on_missing_field_ || all_required_exist;
 }
 
 bool Validator::ValidateScope(base::DictionaryValue* result) {
-  using namespace ::onc::scope;
-
-  const char* const kValidTypes[] = {kDefault, kExtension};
-  const std::vector<const char*> valid_types(toVector(kValidTypes));
-  if (FieldExistsAndHasNoValidValue(*result, kType, valid_types) ||
-      FieldExistsAndIsEmpty(*result, kId)) {
+  const std::vector<const char*> valid_types = {::onc::scope::kDefault,
+                                                ::onc::scope::kExtension};
+  if (FieldExistsAndHasNoValidValue(*result, ::onc::scope::kType,
+                                    valid_types) ||
+      FieldExistsAndIsEmpty(*result, ::onc::scope::kId)) {
     return false;
   }
 
-  bool all_required_exist = RequireField(*result, kType);
-  const std::string* type_string = result->FindStringKey(kType);
-  if (type_string && *type_string == kExtension) {
-    all_required_exist &= RequireField(*result, kId);
+  bool all_required_exist = RequireField(*result, ::onc::scope::kType);
+  const std::string* type_string = result->FindStringKey(::onc::scope::kType);
+  if (type_string && *type_string == ::onc::scope::kExtension) {
+    all_required_exist &= RequireField(*result, ::onc::scope::kId);
     // Check Id validity for type 'Extension'.
-    const std::string* id_string = result->FindStringKey(kId);
+    const std::string* id_string = result->FindStringKey(::onc::scope::kId);
     if (id_string && !crx_file::id_util::IdIsValid(*id_string)) {
       std::ostringstream msg;
-      msg << "Field '" << kId << "' is not a valid extension id.";
+      msg << "Field '" << ::onc::scope::kId << "' is not a valid extension id.";
       AddValidationIssue(false /* is_error */, msg.str());
       return false;
     }
@@ -1149,18 +1145,20 @@
 }
 
 bool Validator::ValidateTether(base::DictionaryValue* result) {
-  using namespace ::onc::tether;
-
-  if (FieldExistsAndIsNotInRange(*result, kBatteryPercentage, 0, 100) ||
-      FieldExistsAndIsNotInRange(*result, kSignalStrength, 0, 100) ||
-      FieldExistsAndIsEmpty(*result, kCarrier)) {
+  if (FieldExistsAndIsNotInRange(*result, ::onc::tether::kBatteryPercentage, 0,
+                                 100) ||
+      FieldExistsAndIsNotInRange(*result, ::onc::tether::kSignalStrength, 0,
+                                 100) ||
+      FieldExistsAndIsEmpty(*result, ::onc::tether::kCarrier)) {
     return false;
   }
 
-  bool all_required_exist = RequireField(*result, kHasConnectedToHost);
-  all_required_exist &= RequireField(*result, kBatteryPercentage);
-  all_required_exist &= RequireField(*result, kSignalStrength);
-  all_required_exist &= RequireField(*result, kCarrier);
+  bool all_required_exist =
+      RequireField(*result, ::onc::tether::kHasConnectedToHost);
+  all_required_exist &=
+      RequireField(*result, ::onc::tether::kBatteryPercentage);
+  all_required_exist &= RequireField(*result, ::onc::tether::kSignalStrength);
+  all_required_exist &= RequireField(*result, ::onc::tether::kCarrier);
 
   return !error_on_missing_field_ || all_required_exist;
 }
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index d80a4dcd..6d2a3ce 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -1208,7 +1208,7 @@
       cellular->apn_list =
           GetManagedApnList(cellular_dict->FindKey(::onc::cellular::kAPNList));
       cellular->allow_roaming =
-          GetBoolean(properties, ::onc::cellular::kAllowRoaming);
+          GetBoolean(cellular_dict, ::onc::cellular::kAllowRoaming);
       cellular->esn = GetString(cellular_dict, ::onc::cellular::kESN);
       cellular->family = GetString(cellular_dict, ::onc::cellular::kFamily);
       cellular->firmware_revision =
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index 4cd28db83..f20c3de1 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -32,7 +32,7 @@
 
 namespace {
 // Timeout to wait for unmask details from Google Payments in milliseconds.
-constexpr int64_t kUnmaskDetailsResponseTimeout = 1000;
+constexpr int64_t kUnmaskDetailsResponseTimeoutMs = 1000;
 // Time to wait between multiple calls to GetUnmaskDetails().
 constexpr int64_t kDelayForGetUnmaskDetails = 3 * 60 * 1000;  // 3 min
 
@@ -40,7 +40,7 @@
 bool WaitForEvent(base::WaitableEvent* event) {
   event->declare_only_used_while_idle();
   return event->TimedWait(
-      base::TimeDelta::FromMilliseconds(kUnmaskDetailsResponseTimeout));
+      base::TimeDelta::FromMilliseconds(kUnmaskDetailsResponseTimeoutMs));
 }
 
 // Used with PostTaskWithDelay() to signal event after a timeout.
@@ -271,21 +271,28 @@
   }
 }
 
-void CreditCardAccessManager::FIDOAuthOptChange(bool opt_in,
-                                                base::Value creation_options) {
+void CreditCardAccessManager::FIDOAuthOptChange(bool opt_in) {
 #if defined(OS_IOS)
   return;
 #else
   if (opt_in) {
-    GetOrCreateFIDOAuthenticator()->Register(
-        /*card_authorization_token=*/std::string(),
-        std::move(creation_options));
+    GetOrCreateFIDOAuthenticator()->ShowWebauthnOfferDialog(
+        /*card_authorization_token=*/std::string());
   } else {
     GetOrCreateFIDOAuthenticator()->OptOut();
   }
 #endif
 }
 
+void CreditCardAccessManager::OnSettingsPageFIDOAuthToggled(bool opt_in) {
+#if defined(OS_IOS)
+  return;
+#else
+  // TODO(crbug/949269): Add a rate limiter to counter spam clicking.
+  FIDOAuthOptChange(opt_in);
+#endif
+}
+
 void CreditCardAccessManager::Authenticate(bool did_get_unmask_details) {
   // Reset now that we have started authentication.
   ready_to_start_authentication_.Reset();
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.h b/components/autofill/core/browser/payments/credit_card_access_manager.h
index 0307279..70d70d29 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.h
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.h
@@ -91,8 +91,11 @@
   // If |opt_in| = true, opts the user into using FIDO authentication for card
   // unmasking. Otherwise, opts the user out. If |creation_options| is set,
   // WebAuthn registration prompt will be invoked to create a new credential.
-  void FIDOAuthOptChange(bool opt_in,
-                         base::Value creation_options = base::Value());
+  void FIDOAuthOptChange(bool opt_in);
+
+  // Makes a call to FIDOAuthOptChange() with |opt_in|.
+  // TODO(crbug/949269): Add a rate limiter to counter spam clicking.
+  void OnSettingsPageFIDOAuthToggled(bool opt_in);
 
   CreditCardCVCAuthenticator* GetOrCreateCVCAuthenticator();
 
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 5a0fffef..468d1cb 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -220,6 +220,39 @@
       <message name="IDS_AUTOFILL_ENABLE_CREDIT_CARDS_TOGGLE_LABEL" desc="Label for a toggle that allows users to control whether credit cards should be saved and forms should be autofilled with it. Sentence-Cased." formatter_data="android_java">
         Save and fill payment methods
       </message>
+      <!-- WebAuthn titles -->
+      <if expr="is_macosx">
+        <message name="IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_LABEL" desc="The product name of the fingerprint reader on macOS. Label for a toggle that allows users to control whether or not to use FIDO authentication for credit card unmasking. Title-Cased.">
+          Touch ID
+        </message>
+        <message name="IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_SUBLABEL" desc="Sublabel for a toggle that allows users to control whether or not to use FIDO authentication for credit card unmasking on macOS. Sentence-Cased.">
+          Use Touch ID to verify your saved payment methods.
+        </message>
+      </if>
+      <if expr="is_win">
+        <message name="IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_LABEL" desc="The product name of the authentication system on Windows. Label for a toggle that allows users to control whether or not to use FIDO authentication for credit card unmasking. Title-Cased.">
+          Windows Hello
+        </message>
+        <message name="IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_SUBLABEL" desc="Sublabel for a toggle that allows users to control whether or not to use FIDO authentication for credit card unmasking on Windows. Sentence-Cased.">
+          Use Windows Hello to verify your saved payment methods.
+        </message>
+      </if>
+      <if expr="is_android">
+        <message name="IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_LABEL" desc="The authentication mechanism on Android. Label for a toggle that allows users to control whether or not to use FIDO authentication for credit card unmasking. Title-Cased.">
+          Screen lock
+        </message>
+        <message name="IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_SUBLABEL" desc="Sublabel for a toggle that allows users to control whether or not to use FIDO authentication for credit card unmasking for Android. Sentence-Cased.">
+          Use screen lock to verify your saved payment methods.
+        </message>
+      </if>
+      <if expr="not is_macosx and not is_win and not is_android">
+        <message name="IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_LABEL" desc="Label for a toggle that allows users to control whether or not to use FIDO authentication for credit card unmasking. Title-Cased.">
+          Biometrics
+        </message>
+        <message name="IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_SUBLABEL" desc="Sublabel for a toggle that allows users to control whether or not to use FIDO authentication for credit card unmasking. Sentence-Cased.">
+          This feature is not available on your device.
+        </message>
+      </if>
     </then>
     <else>
       <message name="IDS_AUTOFILL_ADDRESSES_SETTINGS_TITLE" desc="Title for the Autofill settings page that allows user to manage their list of saved addresses and Autofill preferences for addresses. Title-Cased.">
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index fd7d746..8b4002e 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -146,8 +146,16 @@
   syncer::RepeatingModelTypeStoreFactory model_type_store_factory =
       model_type_store_service->GetStoreFactory();
 
+  // TODO(crbug.com/1005651): Consider using a separate delegate for
+  // transport-only.
   controllers.push_back(std::make_unique<ModelTypeController>(
       syncer::DEVICE_INFO,
+      /*delegate_for_full_sync_mode=*/
+      std::make_unique<syncer::ForwardingModelTypeControllerDelegate>(
+          sync_client_->GetDeviceInfoSyncService()
+              ->GetControllerDelegate()
+              .get()),
+      /*delegate_for_transport_mode=*/
       std::make_unique<syncer::ForwardingModelTypeControllerDelegate>(
           sync_client_->GetDeviceInfoSyncService()
               ->GetControllerDelegate()
diff --git a/components/cdm/browser/cdm_message_filter_android.cc b/components/cdm/browser/cdm_message_filter_android.cc
index 18fa7ee0..1357cf68 100644
--- a/components/cdm/browser/cdm_message_filter_android.cc
+++ b/components/cdm/browser/cdm_message_filter_android.cc
@@ -49,7 +49,7 @@
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
     {media::EME_CODEC_HEVC, media::kCodecHEVC},
 #endif
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
     {media::EME_CODEC_DOLBY_VISION_AVC, media::kCodecDolbyVision},
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
     {media::EME_CODEC_DOLBY_VISION_HEVC, media::kCodecDolbyVision},
@@ -68,7 +68,7 @@
 const CodecInfo<media::AudioCodec> kMP4AudioCodecsToQuery[] = {
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
     {media::EME_CODEC_AAC, media::kCodecAAC},
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
     {media::EME_CODEC_AC3, media::kCodecAC3},
     {media::EME_CODEC_EAC3, media::kCodecEAC3},
 #endif
diff --git a/components/crash/content/tools/generate_breakpad_symbols.py b/components/crash/content/tools/generate_breakpad_symbols.py
index 41895d6..dcf26b7 100755
--- a/components/crash/content/tools/generate_breakpad_symbols.py
+++ b/components/crash/content/tools/generate_breakpad_symbols.py
@@ -74,11 +74,14 @@
   return result
 
 
-def GetSharedLibraryDependenciesAndroid(binary):
-  """Return absolute paths to all shared library dependencies of the binary.
+def _GetSharedLibraryDependenciesAndroidOrChromeOS(binary):
+  """GetSharedLibraryDependencies* suitable for Android or ChromeOS.
 
-  This implementation assumes that we're running on a Linux system, but
-  compiled for Android."""
+  Both assume that the host is Linux-based, but the binary being symbolized is
+  being run on a device with potentially different architectures. Unlike ldd,
+  readelf plays nice with mixed host/device architectures (e.g. x86-64 host,
+  arm64 device), so use that.
+  """
   readelf = subprocess.check_output(['readelf', '-d', binary])
   lib_re = re.compile('Shared library: \[(.+)\]$')
   result = []
@@ -92,6 +95,14 @@
   return result
 
 
+def GetSharedLibraryDependenciesAndroid(binary):
+  """Return absolute paths to all shared library dependencies of the binary.
+
+  This implementation assumes that we're running on a Linux system, but
+  compiled for Android."""
+  return _GetSharedLibraryDependenciesAndroidOrChromeOS(binary)
+
+
 def GetDeveloperDirMac():
   """Finds a good DEVELOPER_DIR value to run Mac dev tools.
 
@@ -192,6 +203,14 @@
   return deps
 
 
+def GetSharedLibraryDependenciesChromeOS(binary):
+  """Return absolute paths to all shared library dependencies of the binary.
+
+  This implementation assumes that we're running on a Linux system, but
+  compiled for ChromeOS."""
+  return _GetSharedLibraryDependenciesAndroidOrChromeOS(binary)
+
+
 def GetSharedLibraryDependencies(options, binary, exe_path):
   """Return absolute paths to all shared library dependencies of the binary."""
   deps = []
@@ -201,6 +220,8 @@
     deps = GetSharedLibraryDependenciesAndroid(binary)
   elif options.platform == 'darwin':
     deps = GetSharedLibraryDependenciesMac(binary, exe_path)
+  elif options.platform == 'chromeos':
+    deps = GetSharedLibraryDependenciesChromeOS(binary)
   else:
     print "Platform not supported."
     sys.exit(1)
@@ -224,7 +245,8 @@
     deps = set(GetSharedLibraryDependencies(options, binary, exe_path))
     deps.add(binary)
     return list(deps)
-  elif options.platform == 'darwin' or options.platform == 'android':
+  elif (options.platform == 'darwin' or options.platform == 'android' or
+        options.platform == 'chromeos'):
     binaries = set([binary])
     queue = [binary]
     while queue:
diff --git a/components/history/core/browser/history_types.h b/components/history/core/browser/history_types.h
index c84ce0b..63f801d 100644
--- a/components/history/core/browser/history_types.h
+++ b/components/history/core/browser/history_types.h
@@ -425,8 +425,6 @@
   MostVisitedURLWithRankList moved;
 };
 
-typedef base::RefCountedData<MostVisitedURLList> MostVisitedThreadSafe;
-
 // Map from origins to a count of matching URLs and the last visited time to any
 // URL under that origin.
 typedef std::map<GURL, std::pair<int, base::Time>> OriginCountAndLastVisitMap;
diff --git a/components/history/core/browser/top_sites_backend.cc b/components/history/core/browser/top_sites_backend.cc
index f1494701..2e50dc11 100644
--- a/components/history/core/browser/top_sites_backend.cc
+++ b/components/history/core/browser/top_sites_backend.cc
@@ -46,12 +46,10 @@
 void TopSitesBackend::GetMostVisitedSites(
     GetMostVisitedSitesCallback callback,
     base::CancelableTaskTracker* tracker) {
-  scoped_refptr<MostVisitedThreadSafe> sites = new MostVisitedThreadSafe();
-  tracker->PostTaskAndReply(
+  tracker->PostTaskAndReplyWithResult(
       db_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&TopSitesBackend::GetMostVisitedSitesOnDBThread, this,
-                     sites),
-      base::BindOnce(std::move(callback), sites));
+      base::BindOnce(&TopSitesBackend::GetMostVisitedSitesOnDBThread, this),
+      std::move(callback));
 }
 
 void TopSitesBackend::UpdateTopSites(const TopSitesDelta& delta,
@@ -85,13 +83,12 @@
   db_.reset();
 }
 
-void TopSitesBackend::GetMostVisitedSitesOnDBThread(
-    scoped_refptr<MostVisitedThreadSafe> sites) {
+MostVisitedURLList TopSitesBackend::GetMostVisitedSitesOnDBThread() {
   DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
-
-  if (db_) {
-    db_->GetSites(&(sites->data));
-  }
+  MostVisitedURLList list;
+  if (db_)
+    db_->GetSites(&list);
+  return list;
 }
 
 void TopSitesBackend::UpdateTopSitesOnDBThread(
diff --git a/components/history/core/browser/top_sites_backend.h b/components/history/core/browser/top_sites_backend.h
index 38745ab..20e4839a 100644
--- a/components/history/core/browser/top_sites_backend.h
+++ b/components/history/core/browser/top_sites_backend.h
@@ -38,7 +38,7 @@
   };
 
   using GetMostVisitedSitesCallback =
-      base::OnceCallback<void(const scoped_refptr<MostVisitedThreadSafe>&)>;
+      base::OnceCallback<void(MostVisitedURLList)>;
 
   TopSitesBackend();
 
@@ -47,7 +47,7 @@
   // Schedules the db to be shutdown.
   void Shutdown();
 
-  // Fetches MostVisitedThreadSafe.
+  // Fetches MostVisitedURLList.
   void GetMostVisitedSites(GetMostVisitedSitesCallback callback,
                            base::CancelableTaskTracker* tracker);
 
@@ -70,8 +70,7 @@
   void ShutdownDBOnDBThread();
 
   // Does the work of getting the most visited sites.
-  void GetMostVisitedSitesOnDBThread(
-      scoped_refptr<MostVisitedThreadSafe> sites);
+  MostVisitedURLList GetMostVisitedSitesOnDBThread();
 
   // Updates top sites.
   void UpdateTopSitesOnDBThread(const TopSitesDelta& delta,
diff --git a/components/history/core/browser/top_sites_impl.cc b/components/history/core/browser/top_sites_impl.cc
index 29e814e..d214954 100644
--- a/components/history/core/browser/top_sites_impl.cc
+++ b/components/history/core/browser/top_sites_impl.cc
@@ -128,13 +128,11 @@
   callback.Run(filtered_urls);
 }
 
-// Returns the index of |url| in |urls|, or -1 if not found.
-static int IndexOf(const MostVisitedURLList& urls, const GURL& url) {
-  for (size_t i = 0; i < urls.size(); i++) {
-    if (urls[i].url == url)
-      return i;
-  }
-  return -1;
+static bool Contains(const MostVisitedURLList& urls, const GURL& url) {
+  return std::find_if(urls.begin(), urls.end(),
+                      [&url](const MostVisitedURL& item) {
+                        return item.url == url;
+                      }) != urls.end();
 }
 
 void TopSitesImpl::SyncWithHistory() {
@@ -232,7 +230,7 @@
 TopSitesImpl::~TopSitesImpl() = default;
 
 void TopSitesImpl::StartQueryForMostVisited() {
-  const int kDaysOfHistory = 90;
+  constexpr int kDaysOfHistory = 90;
 
   DCHECK(loaded_);
   timer_.Stop();
@@ -261,26 +259,19 @@
   // When we find a match in the old set, we'll reset its index to our special
   // marker. This allows us to quickly identify the deleted ones in a later
   // pass.
-  const size_t kAlreadyFoundMarker = static_cast<size_t>(-1);
+  constexpr size_t kAlreadyFoundMarker = static_cast<size_t>(-1);
   int rank = -1;
-  for (size_t i = 0; i < new_list.size(); i++) {
+  for (const auto& new_url : new_list) {
     rank++;
-    auto found = all_old_urls.find(new_list[i].url);
+    auto found = all_old_urls.find(new_url.url);
     if (found == all_old_urls.end()) {
-      MostVisitedURLWithRank added;
-      added.url = new_list[i];
-      added.rank = rank;
-      delta->added.push_back(added);
+      delta->added.emplace_back(MostVisitedURLWithRank{new_url, rank});
     } else {
       DCHECK(found->second != kAlreadyFoundMarker)
           << "Same URL appears twice in the new list.";
       int old_rank = found->second;
-      if (old_rank != rank) {
-        MostVisitedURLWithRank moved;
-        moved.url = new_list[i];
-        moved.rank = rank;
-        delta->moved.push_back(moved);
-      }
+      if (old_rank != rank)
+        delta->moved.emplace_back(MostVisitedURLWithRank{new_url, rank});
       found->second = kAlreadyFoundMarker;
     }
   }
@@ -296,8 +287,9 @@
 bool TopSitesImpl::AddPrepopulatedPages(MostVisitedURLList* urls) const {
   bool added = false;
   for (const auto& prepopulated_page : prepopulated_pages_) {
-    if (urls->size() < kTopSitesNumber &&
-        IndexOf(*urls, prepopulated_page.most_visited.url) == -1) {
+    if (urls->size() >= kTopSitesNumber)
+      break;
+    if (!Contains(*urls, prepopulated_page.most_visited.url)) {
       urls->push_back(prepopulated_page.most_visited);
       added = true;
     }
@@ -305,8 +297,8 @@
   return added;
 }
 
-void TopSitesImpl::ApplyBlacklist(const MostVisitedURLList& urls,
-                                  MostVisitedURLList* out) {
+MostVisitedURLList TopSitesImpl::ApplyBlacklist(
+    const MostVisitedURLList& urls) {
   // Log the number of times ApplyBlacklist is called so we can compute the
   // average number of blacklisted items per user.
   const base::DictionaryValue* blacklist =
@@ -314,15 +306,15 @@
   UMA_HISTOGRAM_BOOLEAN("TopSites.NumberOfApplyBlacklist", true);
   UMA_HISTOGRAM_COUNTS_100("TopSites.NumberOfBlacklistedItems",
       (blacklist ? blacklist->size() : 0));
-  size_t num_urls = 0;
-  for (size_t i = 0; i < urls.size(); ++i) {
-    if (!IsBlacklisted(urls[i].url)) {
-      if (num_urls >= kTopSitesNumber)
-        break;
-      num_urls++;
-      out->push_back(urls[i]);
-    }
+  MostVisitedURLList result;
+  for (const auto& url : urls) {
+    if (IsBlacklisted(url.url))
+      continue;
+    if (result.size() >= kTopSitesNumber)
+      break;
+    result.push_back(url);
   }
+  return result;
 }
 
 // static
@@ -332,11 +324,10 @@
   return base::MD5String(url.spec());
 }
 
-void TopSitesImpl::SetTopSites(const MostVisitedURLList& new_top_sites,
+void TopSitesImpl::SetTopSites(MostVisitedURLList top_sites,
                                const CallLocation location) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  MostVisitedURLList top_sites(new_top_sites);
   AddPrepopulatedPages(&top_sites);
 
   TopSitesDelta delta;
@@ -373,7 +364,7 @@
   // We always do the following steps (setting top sites in cache, and resetting
   // thread safe cache ...) as this method is invoked during startup at which
   // point the caches haven't been updated yet.
-  top_sites_ = top_sites;
+  top_sites_ = std::move(top_sites);
 
   ResetThreadSafeCache();
 
@@ -409,8 +400,8 @@
     }
   }
 
-  for (size_t i = 0; i < pending_callbacks.size(); i++)
-    pending_callbacks[i].Run(urls);
+  for (auto& callback : pending_callbacks)
+    callback.Run(urls);
 
   if (history_service_)
     history_service_observer_.Add(history_service_);
@@ -420,9 +411,7 @@
 
 void TopSitesImpl::ResetThreadSafeCache() {
   base::AutoLock lock(lock_);
-  MostVisitedURLList filtered;
-  ApplyBlacklist(top_sites_, &filtered);
-  thread_safe_cache_ = filtered;
+  thread_safe_cache_ = ApplyBlacklist(top_sites_);
 }
 
 void TopSitesImpl::ScheduleUpdateTimer() {
@@ -433,13 +422,12 @@
                &TopSitesImpl::StartQueryForMostVisited);
 }
 
-void TopSitesImpl::OnGotMostVisitedURLs(
-    const scoped_refptr<MostVisitedThreadSafe>& sites) {
+void TopSitesImpl::OnGotMostVisitedURLs(MostVisitedURLList sites) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // Set |top_sites_| directly so that SetTopSites() diffs correctly.
-  top_sites_ = sites->data;
-  SetTopSites(sites->data, CALL_LOCATION_FROM_ON_GOT_MOST_VISITED_URLS);
+  top_sites_ = sites;
+  SetTopSites(std::move(sites), CALL_LOCATION_FROM_ON_GOT_MOST_VISITED_URLS);
 
   MoveStateToLoaded();
 
@@ -449,7 +437,7 @@
 }
 
 void TopSitesImpl::OnTopSitesAvailableFromHistory(MostVisitedURLList pages) {
-  SetTopSites(pages, CALL_LOCATION_FROM_OTHER_PLACES);
+  SetTopSites(std::move(pages), CALL_LOCATION_FROM_OTHER_PLACES);
 }
 
 void TopSitesImpl::OnURLsDeleted(HistoryService* history_service,
diff --git a/components/history/core/browser/top_sites_impl.h b/components/history/core/browser/top_sites_impl.h
index 1405246b..00da319d 100644
--- a/components/history/core/browser/top_sites_impl.h
+++ b/components/history/core/browser/top_sites_impl.h
@@ -125,7 +125,7 @@
 
   // Takes |urls|, produces it's copy in |out| after removing blacklisted URLs.
   // Also ensures we respect the maximum number TopSites URLs.
-  void ApplyBlacklist(const MostVisitedURLList& urls, MostVisitedURLList* out);
+  MostVisitedURLList ApplyBlacklist(const MostVisitedURLList& urls);
 
   // Returns an MD5 hash of the URL. Hashing is required for blacklisted URLs.
   static std::string GetURLHash(const GURL& url);
@@ -133,7 +133,7 @@
   // Updates URLs in |cache_| and the db (in the background). The URLs in
   // |new_top_sites| replace those in |cache_|. All mutations to cache_ *must*
   // go through this. Should be called from the UI thread.
-  void SetTopSites(const MostVisitedURLList& new_top_sites,
+  void SetTopSites(MostVisitedURLList new_top_sites,
                    const CallLocation location);
 
   // Returns the number of most visited results to request from history. This
@@ -153,7 +153,7 @@
 
   // Callback from TopSites with the list of top sites. Should be called from
   // the UI thread.
-  void OnGotMostVisitedURLs(const scoped_refptr<MostVisitedThreadSafe>& sites);
+  void OnGotMostVisitedURLs(MostVisitedURLList sites);
 
   // Called when history service returns a list of top URLs.
   void OnTopSitesAvailableFromHistory(MostVisitedURLList data);
@@ -188,7 +188,7 @@
   // The pending requests for the top sites list. Can only be non-empty at
   // startup. After we read the top sites from the DB, we'll always have a
   // cached list and be able to run callbacks immediately.
-  PendingCallbacks pending_callbacks_;
+  PendingCallbacks pending_callbacks_ GUARDED_BY(lock_);
 
   // URL List of prepopulated page.
   const PrepopulatedPageList prepopulated_pages_;
diff --git a/components/history/core/browser/top_sites_impl_unittest.cc b/components/history/core/browser/top_sites_impl_unittest.cc
index 66ab67a..ffd99d1 100644
--- a/components/history/core/browser/top_sites_impl_unittest.cc
+++ b/components/history/core/browser/top_sites_impl_unittest.cc
@@ -203,7 +203,7 @@
   // Wrappers that allow private TopSites functions to be called from the
   // individual tests without making them all be friends.
   void SetTopSites(const MostVisitedURLList& new_top_sites) {
-    top_sites()->SetTopSites(new_top_sites,
+    top_sites()->SetTopSites(MostVisitedURLList(new_top_sites),
                              TopSitesImpl::CALL_LOCATION_FROM_OTHER_PLACES);
   }
 
diff --git a/components/omnibox/browser/document_provider.cc b/components/omnibox/browser/document_provider.cc
index fe60814..1fe631d 100644
--- a/components/omnibox/browser/document_provider.cc
+++ b/components/omnibox/browser/document_provider.cc
@@ -695,9 +695,6 @@
   // Ensure server's suggestions are added with monotonically decreasing scores.
   int previous_score = INT_MAX;
   for (size_t i = 0; i < num_results; i++) {
-    if (matches.size() >= provider_max_matches_) {
-      break;
-    }
     const base::DictionaryValue* result = nullptr;
     if (!results_list->GetDictionary(i, &result)) {
       return matches;
@@ -716,26 +713,32 @@
     int server_score = 0;
     result->GetInteger("score", &server_score);
     int score = 0;
-    if (use_client_score && use_server_score)
-      score = std::min(client_score, server_score);
-    else
-      score = use_client_score ? client_score : server_score;
+    // Set |score| only if we haven't surpassed |provider_max_matches_| yet.
+    // Otherwise, score the remaining matches 0 to avoid displaying them except
+    // when deduped with history, shortcut, or bookmark matches.
+    if (matches.size() < provider_max_matches_) {
+      if (use_client_score && use_server_score)
+        score = std::min(client_score, server_score);
+      else
+        score = use_client_score ? client_score : server_score;
 
-    if (cap_score_per_rank) {
-      int score_cap = i < score_caps.size() ? score_caps[i] : score_caps.back();
-      score = std::min(score, score_cap);
+      if (cap_score_per_rank) {
+        int score_cap =
+            i < score_caps.size() ? score_caps[i] : score_caps.back();
+        score = std::min(score, score_cap);
+      }
+
+      if (boost_owned)
+        score = BoostOwned(score, client_->ProfileUserName(), result);
+
+      // Decrement scores if necessary to ensure suggestion order is preserved.
+      // Don't decrement client scores which don't necessarily rank suggestions
+      // the same as the server.
+      if (!use_client_score && score >= previous_score)
+        score = std::max(previous_score - 1, 0);
+      previous_score = score;
     }
 
-    if (boost_owned)
-      score = BoostOwned(score, client_->ProfileUserName(), result);
-
-    // Decrement scores if necessary to ensure suggestion order is preserved.
-    // Don't decrement client scores which don't necessarily rank suggestions
-    // the same as the server.
-    if (!use_client_score && score >= previous_score)
-      score = std::max(previous_score - 1, 0);
-    previous_score = score;
-
     AutocompleteMatch match(this, score, false,
                             AutocompleteMatchType::DOCUMENT_SUGGESTION);
     // Use full URL for displayed text and navigation. Use "originalUrl" for
@@ -776,6 +779,8 @@
     match.transition = ui::PAGE_TRANSITION_GENERATED;
     match.RecordAdditionalInfo("client score", client_score);
     match.RecordAdditionalInfo("server score", server_score);
+    if (matches.size() >= provider_max_matches_)
+      match.RecordAdditionalInfo("for deduping only", "true");
     const std::string* snippet = result->FindStringPath("snippet.snippet");
     if (snippet)
       match.RecordAdditionalInfo("snippet", *snippet);
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 547b5a2..c324294 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -226,35 +226,27 @@
 // static
 std::vector<std::string> OmniboxFieldTrial::GetZeroSuggestVariants(
     OmniboxEventProto::PageClassification page_classification) {
-  std::function<std::vector<std::string>(const std::string&)> split =
-      [](const std::string& value) {
-        return base::SplitString(value, ",", base::TRIM_WHITESPACE,
-                                 base::SPLIT_WANT_ALL);
-      };
-
-  // Note: These checks are necessary because it is not possible to enable
-  // multiple features using Finch Forcing groups (omnibox::kOnFocusSuggestions
-  // as well as another feature). Therefore, in order to specify the
-  // ZeroSuggestVariant parameter in those groups we allow it to be associated
-  // with the feature that is being force enabled.
-  if (base::FeatureList::IsEnabled(omnibox::kZeroSuggestionsOnNTP)) {
-    auto result = internal::GetValueForRuleInContextByFeature(
-        omnibox::kZeroSuggestionsOnNTP, kZeroSuggestVariantRule,
-        page_classification);
-    if (!result.empty())
-      return split(result);
-  }
-  if (base::FeatureList::IsEnabled(omnibox::kZeroSuggestionsOnNTPRealbox)) {
-    auto result = internal::GetValueForRuleInContextByFeature(
-        omnibox::kZeroSuggestionsOnNTPRealbox, kZeroSuggestVariantRule,
-        page_classification);
-    if (!result.empty())
-      return split(result);
+  // We check all these features for ZeroSuggestVariant because it's not
+  // possible to enable multiple features using Finch Forcing groups
+  // (omnibox::kOnFocusSuggestions as well as another feature). Therefore, in
+  // order to specify the ZeroSuggestVariant parameter in those groups we allow
+  // it to be associated with the feature that is being force enabled.
+  const base::Feature* features_to_check[] = {
+      &omnibox::kZeroSuggestionsOnNTP,
+      &omnibox::kZeroSuggestionsOnNTPRealbox,
+      &omnibox::kZeroSuggestionsOnSERP,
+      &omnibox::kOnFocusSuggestions,
+  };
+  for (const base::Feature* feature : features_to_check) {
+    auto parameter_value = internal::GetValueForRuleInContextByFeature(
+        *feature, kZeroSuggestVariantRule, page_classification);
+    if (!parameter_value.empty()) {
+      return base::SplitString(parameter_value, ",", base::TRIM_WHITESPACE,
+                               base::SPLIT_WANT_NONEMPTY);
+    }
   }
 
-  return split(internal::GetValueForRuleInContextByFeature(
-      omnibox::kOnFocusSuggestions, kZeroSuggestVariantRule,
-      page_classification));
+  return {};
 }
 
 bool OmniboxFieldTrial::ShortcutsScoringMaxRelevance(
diff --git a/components/omnibox/browser/url_index_private_data.cc b/components/omnibox/browser/url_index_private_data.cc
index 7d6f58f8..43ff57c 100644
--- a/components/omnibox/browser/url_index_private_data.cc
+++ b/components/omnibox/browser/url_index_private_data.cc
@@ -291,7 +291,7 @@
         row_to_update.set_title(row.title());
         RowWordStarts word_starts;
         AddRowWordsToIndex(row_to_update, &word_starts);
-        word_starts_map_[row_id] = word_starts;
+        word_starts_map_[row_id] = std::move(word_starts);
       }
       row_was_updated = true;
     }
@@ -801,12 +801,13 @@
   new_row.set_typed_count(row.typed_count());
   new_row.set_last_visit(row.last_visit());
   new_row.set_title(row.title());
-  history_info_map_[history_id].url_row = new_row;
 
   // Index the words contained in the URL and title of the row.
   RowWordStarts word_starts;
   AddRowWordsToIndex(new_row, &word_starts);
-  word_starts_map_[history_id] = word_starts;
+  word_starts_map_[history_id] = std::move(word_starts);
+
+  history_info_map_[history_id].url_row = std::move(new_row);
 
   // Update the recent visits information or schedule the update
   // as appropriate.
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 6c3ff55..696ccbd 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -297,6 +297,10 @@
 const base::Feature kZeroSuggestionsOnNTPRealbox{
     "OmniboxZeroSuggestionsOnNTPRealbox", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Allow on-focus query refinements to be shown on the default SERP.
+const base::Feature kZeroSuggestionsOnSERP{"OmniboxZeroSuggestionsOnSERP",
+                                           base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Feature to provide non personalized head search suggestion from a compact
 // on device model.
 const base::Feature kOnDeviceHeadProvider{"OmniboxOnDeviceHeadProvider",
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 9d84320..4910578 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -56,6 +56,7 @@
 extern const base::Feature kOnFocusSuggestions;
 extern const base::Feature kZeroSuggestionsOnNTP;
 extern const base::Feature kZeroSuggestionsOnNTPRealbox;
+extern const base::Feature kZeroSuggestionsOnSERP;
 
 }  // namespace omnibox
 
diff --git a/components/optimization_guide/proto/BUILD.gn b/components/optimization_guide/proto/BUILD.gn
index 5340080..7e2c19f 100644
--- a/components/optimization_guide/proto/BUILD.gn
+++ b/components/optimization_guide/proto/BUILD.gn
@@ -9,6 +9,7 @@
     "common_types.proto",
     "hint_cache.proto",
     "hints.proto",
+    "models.proto",
     "previews_metadata.proto",
   ]
 }
diff --git a/components/optimization_guide/proto/common_types.proto b/components/optimization_guide/proto/common_types.proto
index 0f83460..5eb7516 100644
--- a/components/optimization_guide/proto/common_types.proto
+++ b/components/optimization_guide/proto/common_types.proto
@@ -36,3 +36,14 @@
   // 4G connection.
   EFFECTIVE_CONNECTION_TYPE_4G = 5;
 }
+
+// Context in which the items are requested.
+enum RequestContext {
+  reserved 1;
+  // Context not specified.
+  CONTEXT_UNSPECIFIED = 0;
+  // Requesting items on page navigation.
+  CONTEXT_PAGE_NAVIGATION = 2;
+  // Requesting items as part of a batch update.
+  CONTEXT_BATCH_UPDATE = 3;
+}
diff --git a/components/optimization_guide/proto/hints.proto b/components/optimization_guide/proto/hints.proto
index 8175f42..01f0d8c 100644
--- a/components/optimization_guide/proto/hints.proto
+++ b/components/optimization_guide/proto/hints.proto
@@ -96,16 +96,6 @@
   optional int32 nanos = 2;
 }
 
-// Context in which the hints are requested.
-enum RequestContext {
-  reserved 1;
-  // Context not specified.
-  CONTEXT_UNSPECIFIED = 0;
-  // Requesting hints on page navigation.
-  CONTEXT_PAGE_NAVIGATION = 2;
-  // Requesting hints as part of a batch update.
-  CONTEXT_BATCH_UPDATE = 3;
-}
 
 enum OptimizationType {
   TYPE_UNSPECIFIED = 0;
diff --git a/components/optimization_guide/proto/models.proto b/components/optimization_guide/proto/models.proto
new file mode 100644
index 0000000..5588fb5
--- /dev/null
+++ b/components/optimization_guide/proto/models.proto
@@ -0,0 +1,246 @@
+// 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.
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package optimization_guide.proto;
+
+import "common_types.proto";
+
+// A generic handle for any type of model.
+message Model {
+  reserved 3;
+
+  oneof model {
+    DecisionTree decision_tree = 1;
+    Ensemble ensemble = 2;
+  }
+}
+
+// An ensemble prediction model consisting of an ordered sequence of models.
+// This message can be used to express bagged or boosted models.
+message Ensemble {
+  reserved 2, 3, 4;
+
+  message Member { optional Model submodel = 1; }
+  repeated Member members = 100;
+}
+
+// A decision tree model with its weight for use if included in an ensemble.
+message DecisionTree {
+  reserved 2;
+
+  repeated TreeNode nodes = 1;
+  optional float weight = 3;
+}
+
+// A node of a decision tree that is a binary deicison or a leaf.
+message TreeNode {
+  reserved 6, 7;
+
+  // Following fields are provided for convenience and better readability.
+  // Filling them in is not required.
+  optional Int32Value node_id = 1;
+  optional Int32Value depth = 2;
+  optional Int32Value subtree_size = 3;
+
+  oneof node_type {
+    BinaryNode binary_node = 4;
+    Leaf leaf = 5;
+  }
+}
+
+// A tree node that contains an inequality test that during evaluation
+// determines whether to continue the left or right child.
+message BinaryNode {
+  reserved 3, 5;
+
+  optional Int32Value left_child_id = 1;
+  optional Int32Value right_child_id = 2;
+  enum Direction {
+    LEFT = 0;
+    RIGHT = 1;
+  }
+  // When a datapoint satisfies the test, it should be propagated to the left
+  // child.
+  optional InequalityTest inequality_left_child_test = 4;
+}
+
+// Vector of values for use within Models.
+message Vector {
+  repeated Value value = 1;
+}
+
+// A leaf node of a decision tree.
+message Leaf {
+  reserved 2, 3;
+
+  optional Vector vector = 1;
+}
+
+// The ID for the features used during evaluation of a Model.
+message FeatureId {
+  reserved 2;
+
+  optional StringValue id = 1;
+}
+
+// The set of inequality operations supported by binary nodes for
+// decision tree models.
+message InequalityTest {
+  reserved 4;
+
+  // When the feature is missing, the test's outcome is undefined.
+  optional FeatureId feature_id = 1;
+  enum Type {
+    LESS_OR_EQUAL = 0;
+    LESS_THAN = 1;
+    GREATER_OR_EQUAL = 2;
+    GREATER_THAN = 3;
+  };
+  optional Type type = 2;
+  optional Value threshold = 3;
+}
+
+// Represents a single value of any type, e.g. 5 or "abc".
+message Value {
+  reserved 5;
+
+  oneof value {
+    float float_value = 1;
+    double double_value = 2;
+    int32 int32_value = 3;
+    int64 int64_value = 4;
+  }
+}
+
+// Wrapper message for `int32`.
+//
+// The JSON representation for `Int32Value` is JSON number.
+message Int32Value {
+  // The int32 value.
+  optional int32 value = 1;
+}
+
+// Wrapper message for `string`.
+//
+// The JSON representation for `StringValue` is JSON string.
+message StringValue {
+  // The string value.
+  optional string value = 1;
+}
+
+// Requests prediction models to be used for a set of optimization targets.
+message GetModelsRequest {
+  // Information about the requested models.
+  repeated ModelInfo requested_models = 1;
+  // The set of hosts to get additional metadata for, if applicable.
+  repeated string hosts = 2;
+  // Context in which this request is made.
+  //
+  // If the context matches one that requires more urgency (i.e.
+  // CONTEXT_PAGE_NAVIGATION), then no model updates will be returned for the
+  // requested models.
+  optional RequestContext request_context = 3;
+}
+
+// Response to the GetModels request.
+message GetModelsResponse {
+  // The models to be used during prediction for the requested optimization
+  // targets.
+  repeated PredictionModel models = 1;
+  // A set of model features and their values for the hosts contained in the
+  // request to be expected to be consulted with during prediction.
+  //
+  // It is not guaranteed that this set will contain an entry for every
+  // requested host.
+  repeated HostModelFeatures host_model_features = 2;
+}
+
+// Holds the prediction model for a particular optimization target.
+message PredictionModel {
+  // Information about the model.
+  optional ModelInfo model_info = 1;
+  // The model to evaluate for the attached model information.
+  //
+  // This will only be set if the model that the client claims it has is stale.
+  // It is also guaranteed that the value populated as part of this field is one
+  // that the client claims to support based on the request's client model
+  // capabilities.
+  optional Model model = 2;
+}
+
+//  Metadata for a prediction model for a specific optimization target.
+message ModelInfo {
+  // The optimization target for which the model predicts.
+  optional OptimizationTarget optimization_target = 1;
+  // The version of the model, which is specific to the optimization target.
+  optional int64 version = 2;
+  // The set of model features that are supported by the model.
+  //
+  // If in the request, this represents the set of features that the client
+  // understands how to evaluate. If in the response, this represents the set
+  // of features referenced by the model.
+  repeated ClientModelFeature supported_model_features = 3;
+  // The set of model types the requesting client can use to make predictions.
+  repeated ModelType supported_model_types = 4;
+}
+
+// The scenarios for which the optimization guide has models for.
+enum OptimizationTarget {
+  OPTIMIZATION_TARGET_UNKNOWN = 0;
+  // Should only be applied when the page load is predicted to be painful.
+  OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD = 1;
+}
+
+// The features that only the client can compute during prediction and also
+// knows how to evaluate.
+enum ClientModelFeature {
+  CLIENT_MODEL_FEATURE_UNKNOWN = 0;
+  // The effective connection type for the page load.
+  CLIENT_MODEL_FEATURE_EFFECTIVE_CONNECTION_TYPE = 1;
+  // How the current page load transitioned from the previous one.
+  CLIENT_MODEL_FEATURE_PAGE_TRANSITION = 2;
+  // The site engagement score of the main frame host for the page load.
+  CLIENT_MODEL_FEATURE_SITE_ENGAGEMENT_SCORE = 3;
+  // Whether the origin for the page load matches the origin of the previous
+  // page load.
+  CLIENT_MODEL_FEATURE_SAME_ORIGIN_NAVIGATION = 4;
+  // The mean of the duration from navigation to first contentful paint for the
+  // session.
+  CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_SESSION_MEAN = 5;
+  // The standard deviation of the duration from navigation to first
+  // contentful paint for the session.
+  CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_SESSION_STANDARD_DEVIATION = 6;
+  // The duration from navigation to first contentful paint for the previous
+  // page load, if applicable.
+  CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_PREVIOUS_PAGE_LOAD = 7;
+}
+
+// The types of models that can be evaluated.
+enum ModelType {
+  MODEL_TYPE_UNKNOWN = 0;
+  // A decision tree.
+  MODEL_TYPE_DECISION_TREE = 1;
+}
+
+// A set of model features and the host that it applies to.
+message HostModelFeatures {
+  // The host that the features should be applied for.
+  optional string host = 1;
+  // The set of features and their values that apply to the host.
+  repeated ModelFeature model_features = 2;
+}
+
+// Information about a feature that is potentially referenced in a model.
+message ModelFeature {
+  // The name of the feature to match if encountered in a model.
+  optional string feature_name = 1;
+  // The value of the feature to be used during prediction.
+  oneof feature_value {
+    double double_value = 2;
+    int64 int64_value = 3;
+  }
+}
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 74ad069..aeddc8ac 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -10,8 +10,9 @@
   import("//build/config/android/config.gni")
 }
 
-# TODO(crbug.com/706392): Fix password reuse detection for Android.
-password_reuse_detection_support = !is_android && !is_ios
+# TODO(crbug.com/1006430): Fix password reuse detection not fully functional on
+# Android.
+password_reuse_detection_support = !is_ios
 password_reuse_warning_support = !is_android && !is_ios
 password_on_focus_ping_support = !is_ios
 
diff --git a/components/password_manager/core/browser/mock_password_store.h b/components/password_manager/core/browser/mock_password_store.h
index e7d690cc..d4d65844 100644
--- a/components/password_manager/core/browser/mock_password_store.h
+++ b/components/password_manager/core/browser/mock_password_store.h
@@ -72,7 +72,7 @@
   MOCK_METHOD1(AddSiteStatsImpl, void(const InteractionsStats&));
   MOCK_METHOD1(RemoveSiteStatsImpl, void(const GURL&));
   MOCK_CONST_METHOD0(IsAbleToSavePasswords, bool());
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
+
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   MOCK_METHOD3(CheckReuse,
                void(const base::string16&,
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 9fcaeb8a..2ebfa80 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -65,7 +65,6 @@
 
 }  // namespace
 
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 PasswordStore::CheckReuseRequest::CheckReuseRequest(
     PasswordReuseDetectorConsumer* consumer)
@@ -397,7 +396,6 @@
           base::Unretained(this)));
 }
 
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 void PasswordStore::CheckReuse(const base::string16& input,
                                const std::string& domain,
@@ -537,7 +535,7 @@
     syncable_service_.reset(new PasswordSyncableService(this));
     syncable_service_->InjectStartSyncFlare(flare);
   }
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
+
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   reuse_detector_ = new PasswordReuseDetector;
 
@@ -593,7 +591,7 @@
       syncable_service_->ActOnPasswordStoreChanges(changes);
     if (sync_bridge_)
       sync_bridge_->ActOnPasswordStoreChanges(changes);
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
+
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
     if (reuse_detector_)
       reuse_detector_->OnLoginsChanged(changes);
@@ -601,7 +599,6 @@
   }
 }
 
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 void PasswordStore::CheckReuseImpl(std::unique_ptr<CheckReuseRequest> request,
                                    const base::string16& input,
@@ -1060,7 +1057,7 @@
   DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
   syncable_service_.reset();
   sync_bridge_.reset();
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
+
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   delete reuse_detector_;
   reuse_detector_ = nullptr;
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index 4925923..b5e4014 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -23,7 +23,6 @@
 #include "components/password_manager/core/browser/password_store_sync.h"
 #include "components/sync/model/syncable_service.h"
 
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 #include "components/password_manager/core/browser/hash_password_manager.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
@@ -264,7 +263,6 @@
   std::unique_ptr<syncer::ProxyModelTypeControllerDelegate>
   CreateSyncControllerDelegate();
 
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   // Immediately called after |Init()| to retrieve password hash data for
   // reuse detection.
@@ -330,7 +328,6 @@
  protected:
   friend class base::RefCountedThreadSafe<PasswordStore>;
 
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   // Represents a single CheckReuse() request. Implements functionality to
   // listen to reuse events and propagate them to |consumer| on the sequence on
@@ -453,7 +450,6 @@
   // been changed.
   void NotifyLoginsChanged(const PasswordStoreChangeList& changes) override;
 
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   // Saves |username| and a hash of |password| for password reuse checking.
   // |is_gaia_password| indicates if it is a Gaia account. |event| is used for
@@ -675,7 +671,7 @@
   std::unique_ptr<PasswordSyncBridge> sync_bridge_;
 
   std::unique_ptr<AffiliatedMatchHelper> affiliated_match_helper_;
-// TODO(crbug.com/706392): Fix password reuse detection for Android.
+
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   PrefService* prefs_ = nullptr;
   // PasswordReuseDetector can be only destroyed on the background sequence. It
diff --git a/components/policy/core/common/cloud/cloud_policy_manager.h b/components/policy/core/common/cloud/cloud_policy_manager.h
index 285ff64..9626b1b8 100644
--- a/components/policy/core/common/cloud/cloud_policy_manager.h
+++ b/components/policy/core/common/cloud/cloud_policy_manager.h
@@ -108,8 +108,6 @@
   // policy update notifications are deferred until after it completes.
   bool waiting_for_policy_refresh_;
 
-  scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
-
   DISALLOW_COPY_AND_ASSIGN(CloudPolicyManager);
 };
 
diff --git a/components/previews/core/previews_features.cc b/components/previews/core/previews_features.cc
index 2578c64..d839a4e 100644
--- a/components/previews/core/previews_features.cc
+++ b/components/previews/core/previews_features.cc
@@ -58,11 +58,6 @@
 const base::Feature kSlowPageTriggering{"PreviewsSlowPageTriggering",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Allows HTTPS previews to be served via a URLLoader when network service is
-// enabled.
-const base::Feature kHTTPSServerPreviewsUsingURLLoader{
-    "HTTPSServerPreviewsUsingURLLoader", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Enables the use of a pref to only trigger Offline Previews when there is a
 // high chance that there is one to serve.
 const base::Feature kOfflinePreviewsFalsePositivePrevention{
diff --git a/components/previews/core/previews_features.h b/components/previews/core/previews_features.h
index c9ca1e7a0..44de706 100644
--- a/components/previews/core/previews_features.h
+++ b/components/previews/core/previews_features.h
@@ -17,7 +17,6 @@
 extern const base::Feature kResourceLoadingHints;
 extern const base::Feature kLitePageServerPreviews;
 extern const base::Feature kSlowPageTriggering;
-extern const base::Feature kHTTPSServerPreviewsUsingURLLoader;
 extern const base::Feature kOfflinePreviewsFalsePositivePrevention;
 extern const base::Feature kCoinFlipHoldback;
 extern const base::Feature kExcludedMediaSuffixes;
diff --git a/components/safe_browsing/browser/threat_details.cc b/components/safe_browsing/browser/threat_details.cc
index 05bcd3f..5149e91 100644
--- a/components/safe_browsing/browser/threat_details.cc
+++ b/components/safe_browsing/browser/threat_details.cc
@@ -8,7 +8,6 @@
 
 #include <stddef.h>
 #include <stdint.h>
-#include <string_view>
 #include <unordered_set>
 #include <utility>
 #include <vector>
@@ -18,6 +17,7 @@
 #include "base/lazy_instance.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/task/post_task.h"
 #include "components/history/core/browser/history_service.h"
@@ -312,7 +312,7 @@
 }
 
 void DisableBackForwardCache(content::RenderFrameHost* rfh,
-                             std::string_view reason) {
+                             base::StringPiece reason) {
   content::WebContents::FromRenderFrameHost(rfh)
       ->GetController()
       .GetBackForwardCache()
diff --git a/components/safe_browsing/features.cc b/components/safe_browsing/features.cc
index 7ffc867d..5311a11 100644
--- a/components/safe_browsing/features.cc
+++ b/components/safe_browsing/features.cc
@@ -65,6 +65,16 @@
 };
 #endif
 
+const base::Feature kSendPasswordReusePing {
+  "SafeBrowsingSendPasswordReusePing",
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+      base::FEATURE_ENABLED_BY_DEFAULT
+};
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+};
+#endif
+
 const base::Feature kSendSampledPingsForAllowlistDomains{
     "SafeBrowsingSendSampledPingsForAllowlistDomain",
     base::FEATURE_DISABLED_BY_DEFAULT};
@@ -108,6 +118,7 @@
     {&kRealTimeUrlLookupEnabled, true},
     {&kRealTimeUrlLookupFetchAllowlist, true},
     {&kSendOnFocusPing, true},
+    {&kSendPasswordReusePing, true},
     {&kSendSampledPingsForAllowlistDomains, false},
     {&kSuspiciousSiteTriggerQuotaFeature, true},
     {&kThreatDomDetailsTagAndAttributeFeature, false},
diff --git a/components/safe_browsing/features.h b/components/safe_browsing/features.h
index 98cef139..7ce0b455 100644
--- a/components/safe_browsing/features.h
+++ b/components/safe_browsing/features.h
@@ -57,6 +57,9 @@
 // Controls whether Chrome sends on focus ping.
 extern const base::Feature kSendOnFocusPing;
 
+// Controls whether Chrome sends password reuse ping.
+extern const base::Feature kSendPasswordReusePing;
+
 // Controls the daily quota for the suspicious site trigger.
 extern const base::Feature kSuspiciousSiteTriggerQuotaFeature;
 
diff --git a/components/safe_browsing/password_protection/password_protection_service.cc b/components/safe_browsing/password_protection/password_protection_service.cc
index b870186..3a54056 100644
--- a/components/safe_browsing/password_protection/password_protection_service.cc
+++ b/components/safe_browsing/password_protection/password_protection_service.cc
@@ -117,6 +117,9 @@
     const std::vector<std::string>& matching_domains,
     bool password_field_exists) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!base::FeatureList::IsEnabled(safe_browsing::kSendPasswordReusePing)) {
+    return;
+  }
   ReusedPasswordAccountType reused_password_account_type =
       GetPasswordProtectionReusedPasswordAccountType(password_type, username);
   RequestOutcome reason;
diff --git a/components/sessions/content/content_serialized_navigation_builder.cc b/components/sessions/content/content_serialized_navigation_builder.cc
index 4bd035d..1903311 100644
--- a/components/sessions/content/content_serialized_navigation_builder.cc
+++ b/components/sessions/content/content_serialized_navigation_builder.cc
@@ -4,6 +4,8 @@
 
 #include "components/sessions/content/content_serialized_navigation_builder.h"
 
+#include <string>
+
 #include "base/logging.h"
 #include "components/sessions/content/content_record_password_state.h"
 #include "components/sessions/content/content_serialized_navigation_driver.h"
@@ -88,19 +90,18 @@
 ContentSerializedNavigationBuilder::ToNavigationEntry(
     const SerializedNavigationEntry* navigation,
     content::BrowserContext* browser_context) {
-  // TODO(lukasza): https://crbug.com/976055: |initiator_origin| should be
-  // persisted across session restore.
-  base::Optional<url::Origin> initiator_origin = base::nullopt;
+  // The initial values of the NavigationEntry are only temporary - they
+  // will get cloberred by one of the SetPageState calls below.
+  //
+  // This means that things like |navigation->referrer_url| are ignored
+  // in favor of using the data stored in |navigation->encoded_page_state|.
+  GURL temporary_url;
+  content::Referrer temporary_referrer;
+  base::Optional<url::Origin> temporary_initiator_origin;
 
-  network::mojom::ReferrerPolicy policy =
-      static_cast<network::mojom::ReferrerPolicy>(navigation->referrer_policy_);
   std::unique_ptr<content::NavigationEntry> entry(
       content::NavigationController::CreateNavigationEntry(
-          navigation->virtual_url_,
-          content::Referrer::SanitizeForRequest(
-              navigation->virtual_url_,
-              content::Referrer(navigation->referrer_url_, policy)),
-          initiator_origin,
+          temporary_url, temporary_referrer, temporary_initiator_origin,
           // Use a transition type of reload so that we don't incorrectly
           // increase the typed count.
           ui::PAGE_TRANSITION_RELOAD, false,
@@ -108,9 +109,46 @@
           std::string(), browser_context,
           nullptr /* blob_url_loader_factory */));
 
+  // In some cases the |encoded_page_state| might be empty - we
+  // need to gracefully handle such data when it is deserialized.
+  //
+  // One case is tests for "foreign" session restore entries, such as
+  // SessionRestoreTest.RestoreForeignTab.  We hypothesise that old session
+  // restore entries might also contain an empty |encoded_page_state|.
+  if (navigation->encoded_page_state_.empty()) {
+    // Ensure that the deserialized/restored content::NavigationEntry (and
+    // the content::FrameNavigationEntry underneath) has a valid PageState.
+    entry->SetPageState(
+        content::PageState::CreateFromURL(navigation->virtual_url_));
+
+    // The |navigation|-based referrer set below might be inconsistent with the
+    // referrer embedded inside the PageState set above.  Nevertheless, to
+    // minimize changes to behavior of old session restore entries, we restore
+    // the deserialized referrer here.
+    //
+    // TODO(lukasza): Consider including the |deserialized_referrer| in the
+    // PageState set above + drop the SetReferrer call below.  This will
+    // slightly change the legacy behavior, but will make PageState and
+    // Referrer consistent.
+    content::Referrer referrer(navigation->referrer_url(),
+                               static_cast<network::mojom::ReferrerPolicy>(
+                                   navigation->referrer_policy()));
+    entry->SetReferrer(referrer);
+  } else {
+    // Note that PageState covers some of the values inside |navigation| (e.g.
+    // URL, Referrer).  Calling SetPageState will clobber these values in
+    // content::NavigationEntry (and FrameNavigationEntry(s) below).
+    entry->SetPageState(content::PageState::CreateFromEncodedData(
+        navigation->encoded_page_state_));
+
+    // |navigation|-level referrer information is redundant wrt PageState, but
+    // they should be consistent / in-sync.
+    DCHECK_EQ(navigation->referrer_url(), entry->GetReferrer().url);
+    DCHECK_EQ(navigation->referrer_policy(),
+              static_cast<int>(entry->GetReferrer().policy));
+  }
+
   entry->SetTitle(navigation->title_);
-  entry->SetPageState(content::PageState::CreateFromEncodedData(
-      navigation->encoded_page_state_));
   entry->SetHasPostData(navigation->has_post_data_);
   entry->SetPostID(navigation->post_id_);
   entry->SetOriginalRequestURL(navigation->original_request_url_);
@@ -118,6 +156,7 @@
   entry->SetTimestamp(navigation->timestamp_);
   entry->SetHttpStatusCode(navigation->http_status_code_);
   entry->SetRedirectChain(navigation->redirect_chain_);
+  entry->SetVirtualURL(navigation->virtual_url_);
   sessions::NavigationTaskId* navigation_task_id =
       sessions::NavigationTaskId::Get(entry.get());
   navigation_task_id->set_id(navigation->task_id());
diff --git a/components/sessions/content/content_serialized_navigation_builder_unittest.cc b/components/sessions/content/content_serialized_navigation_builder_unittest.cc
index d7832d1..d293f061 100644
--- a/components/sessions/content/content_serialized_navigation_builder_unittest.cc
+++ b/components/sessions/content/content_serialized_navigation_builder_unittest.cc
@@ -4,6 +4,10 @@
 
 #include "components/sessions/content/content_serialized_navigation_builder.h"
 
+#include <memory>
+#include <string>
+#include <vector>
+
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/sessions/content/content_record_password_state.h"
@@ -64,10 +68,9 @@
   navigation_entry->SetReferrer(content::Referrer(
       test_data::kReferrerURL,
       static_cast<network::mojom::ReferrerPolicy>(test_data::kReferrerPolicy)));
+  navigation_entry->SetURL(test_data::kURL);
   navigation_entry->SetVirtualURL(test_data::kVirtualURL);
   navigation_entry->SetTitle(test_data::kTitle);
-  navigation_entry->SetPageState(
-      content::PageState::CreateFromEncodedData(test_data::kEncodedPageState));
   navigation_entry->SetTransitionType(test_data::kTransitionType);
   navigation_entry->SetHasPostData(test_data::kHasPostData);
   navigation_entry->SetPostID(test_data::kPostID);
@@ -147,7 +150,8 @@
   EXPECT_EQ(test_data::kReferrerPolicy, navigation.referrer_policy());
   EXPECT_EQ(test_data::kVirtualURL, navigation.virtual_url());
   EXPECT_EQ(test_data::kTitle, navigation.title());
-  EXPECT_EQ(test_data::kEncodedPageState, navigation.encoded_page_state());
+  EXPECT_EQ(navigation_entry->GetPageState().ToEncodedData(),
+            navigation.encoded_page_state());
   EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
       navigation.transition_type(), test_data::kTransitionType));
   EXPECT_EQ(test_data::kHasPostData, navigation.has_post_data());
@@ -188,7 +192,7 @@
       ContentSerializedNavigationBuilder::FromNavigationEntry(
           test_data::kIndex, navigation_entry.get(),
           ContentSerializedNavigationBuilder::DEFAULT);
-  EXPECT_EQ(test_data::kEncodedPageState,
+  EXPECT_EQ(navigation_entry->GetPageState().ToEncodedData(),
             default_navigation.encoded_page_state());
 
   const SerializedNavigationEntry& excluded_page_state_navigation =
@@ -218,9 +222,10 @@
   EXPECT_EQ(test_data::kReferrerURL, new_navigation_entry->GetReferrer().url);
   EXPECT_EQ(test_data::kReferrerPolicy,
             static_cast<int>(new_navigation_entry->GetReferrer().policy));
+  EXPECT_EQ(test_data::kURL, new_navigation_entry->GetURL());
   EXPECT_EQ(test_data::kVirtualURL, new_navigation_entry->GetVirtualURL());
   EXPECT_EQ(test_data::kTitle, new_navigation_entry->GetTitle());
-  EXPECT_EQ(test_data::kEncodedPageState,
+  EXPECT_EQ(old_navigation_entry->GetPageState().ToEncodedData(),
             new_navigation_entry->GetPageState().ToEncodedData());
   EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
       new_navigation_entry->GetTransitionType(), ui::PAGE_TRANSITION_RELOAD));
diff --git a/components/sessions/core/serialized_navigation_entry_test_helper.cc b/components/sessions/core/serialized_navigation_entry_test_helper.cc
index f5f08bc..8bb74fde 100644
--- a/components/sessions/core/serialized_navigation_entry_test_helper.cc
+++ b/components/sessions/core/serialized_navigation_entry_test_helper.cc
@@ -18,7 +18,8 @@
 const int kUniqueID = 50;
 const GURL kReferrerURL = GURL("http://www.referrer.com");
 const int kReferrerPolicy = 0;
-const GURL kVirtualURL= GURL("http://www.virtual-url.com");
+const GURL kURL = GURL("http://www.url.com");
+const GURL kVirtualURL = GURL("http://www.virtual-url.com");
 const base::string16 kTitle = base::ASCIIToUTF16("title");
 const std::string kEncodedPageState = "page state";
 const ui::PageTransition kTransitionType =
diff --git a/components/sessions/core/serialized_navigation_entry_test_helper.h b/components/sessions/core/serialized_navigation_entry_test_helper.h
index 606b235..9e8af07 100644
--- a/components/sessions/core/serialized_navigation_entry_test_helper.h
+++ b/components/sessions/core/serialized_navigation_entry_test_helper.h
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_SESSIONS_SESSION_TYPES_TEST_HELPER_H_
-#define COMPONENTS_SESSIONS_SESSION_TYPES_TEST_HELPER_H_
+#ifndef COMPONENTS_SESSIONS_CORE_SERIALIZED_NAVIGATION_ENTRY_TEST_HELPER_H_
+#define COMPONENTS_SESSIONS_CORE_SERIALIZED_NAVIGATION_ENTRY_TEST_HELPER_H_
 
 #include <stdint.h>
 
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/strings/string16.h"
@@ -28,6 +29,7 @@
 extern const int kUniqueID;
 extern const GURL kReferrerURL;
 extern const int kReferrerPolicy;
+extern const GURL kURL;
 extern const GURL kVirtualURL;
 extern const base::string16 kTitle;
 extern const std::string kEncodedPageState;
@@ -63,6 +65,11 @@
                                      const SerializedNavigationEntry& actual);
 
   // Creates a SerializedNavigationEntry using the |test_data| constants above.
+  //
+  // Note that the returned SerializedNavigationEntry will have a bogus
+  // PageState and therefore can only be used in limited unit tests (e.g. it
+  // will most likely hit DCHECKs/NOTREACHEDs when passed to the //content
+  // layer).
   static SerializedNavigationEntry CreateNavigationForTest();
 
   static void SetReferrerPolicy(int policy,
@@ -97,6 +104,6 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(SerializedNavigationEntryTestHelper);
 };
 
-}  // sessions
+}  // namespace sessions
 
-#endif  // COMPONENTS_SESSIONS_SESSION_TYPES_TEST_HELPER_H_
+#endif  // COMPONENTS_SESSIONS_CORE_SERIALIZED_NAVIGATION_ENTRY_TEST_HELPER_H_
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index 61b71b8..31c290a 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -1282,6 +1282,11 @@
       }
     }
 
+    if (base::FeatureList::IsEnabled(
+            switches::kSyncDeviceInfoInTransportMode)) {
+      allowed_types.Put(DEVICE_INFO);
+    }
+
     types = Intersection(types, allowed_types);
     configure_context.sync_mode = SyncMode::kTransportOnly;
   }
diff --git a/components/sync/driver/sync_driver_switches.cc b/components/sync/driver/sync_driver_switches.cc
index 54e5a1a..fa7c865d 100644
--- a/components/sync/driver/sync_driver_switches.cc
+++ b/components/sync/driver/sync_driver_switches.cc
@@ -77,4 +77,7 @@
 const base::Feature kMergeBookmarksUsingGUIDs{
     "MergeBookmarksUsingGUIDs", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kSyncDeviceInfoInTransportMode{
+    "SyncDeviceInfoInTransportMode", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace switches
diff --git a/components/sync/driver/sync_driver_switches.h b/components/sync/driver/sync_driver_switches.h
index 2aabef6..8707b6f 100644
--- a/components/sync/driver/sync_driver_switches.h
+++ b/components/sync/driver/sync_driver_switches.h
@@ -35,6 +35,7 @@
 extern const base::Feature kSyncWifiConfigurations;
 extern const base::Feature kUpdateBookmarkGUIDWithNodeReplacement;
 extern const base::Feature kMergeBookmarksUsingGUIDs;
+extern const base::Feature kSyncDeviceInfoInTransportMode;
 
 }  // namespace switches
 
diff --git a/components/viz/service/display_embedder/skia_output_device.cc b/components/viz/service/display_embedder/skia_output_device.cc
index a33e9f1..2541bb0 100644
--- a/components/viz/service/display_embedder/skia_output_device.cc
+++ b/components/viz/service/display_embedder/skia_output_device.cc
@@ -34,6 +34,10 @@
 
 void SkiaOutputDevice::SetDrawRectangle(const gfx::Rect& draw_rectangle) {}
 
+void SkiaOutputDevice::SetGpuVSyncEnabled(bool enabled) {
+  NOTIMPLEMENTED();
+}
+
 void SkiaOutputDevice::SetEnableDCLayers(bool enable) {
   NOTIMPLEMENTED();
 }
diff --git a/components/viz/service/display_embedder/skia_output_device.h b/components/viz/service/display_embedder/skia_output_device.h
index 014432c5..9d95eea2 100644
--- a/components/viz/service/display_embedder/skia_output_device.h
+++ b/components/viz/service/display_embedder/skia_output_device.h
@@ -91,7 +91,8 @@
   // Set the rectangle that will be drawn into on the surface.
   virtual void SetDrawRectangle(const gfx::Rect& draw_rectangle);
 
-  virtual void SetEnableDCLayers(bool enable);
+  virtual void SetGpuVSyncEnabled(bool enabled);
+  virtual void SetEnableDCLayers(bool enabled);
   virtual void ScheduleDCLayers(std::vector<DCLayerOverlay> dc_layers);
 
   const OutputSurface::Capabilities& capabilities() const {
diff --git a/components/viz/service/display_embedder/skia_output_device_gl.cc b/components/viz/service/display_embedder/skia_output_device_gl.cc
index a39b7ee..98327f5 100644
--- a/components/viz/service/display_embedder/skia_output_device_gl.cc
+++ b/components/viz/service/display_embedder/skia_output_device_gl.cc
@@ -43,6 +43,7 @@
           .disable_post_sub_buffers_for_onscreen_surfaces)
     capabilities_.supports_post_sub_buffer = false;
   capabilities_.max_frames_pending = gl_surface_->GetBufferCount() - 1;
+  capabilities_.supports_gpu_vsync = gl_surface_->SupportsGpuVSync();
   capabilities_.supports_dc_layers = gl_surface_->SupportsDCLayers();
 }
 
@@ -162,6 +163,10 @@
   gl_surface_->SetDrawRectangle(draw_rectangle);
 }
 
+void SkiaOutputDeviceGL::SetGpuVSyncEnabled(bool enabled) {
+  gl_surface_->SetGpuVSyncEnabled(enabled);
+}
+
 void SkiaOutputDeviceGL::SetEnableDCLayers(bool enable) {
   gl_surface_->SetEnableDCLayers(enable);
 }
diff --git a/components/viz/service/display_embedder/skia_output_device_gl.h b/components/viz/service/display_embedder/skia_output_device_gl.h
index 2088166b..798234e 100644
--- a/components/viz/service/display_embedder/skia_output_device_gl.h
+++ b/components/viz/service/display_embedder/skia_output_device_gl.h
@@ -64,6 +64,7 @@
                      BufferPresentedCallback feedback,
                      std::vector<ui::LatencyInfo> latency_info) override;
   void SetDrawRectangle(const gfx::Rect& draw_rectangle) override;
+  void SetGpuVSyncEnabled(bool enabled) override;
   void SetEnableDCLayers(bool enable) override;
   void ScheduleDCLayers(std::vector<DCLayerOverlay> dc_layers) override;
   void EnsureBackbuffer() override;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index fd202cf8..cb773de 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -218,6 +218,16 @@
   update_vsync_parameters_callback_ = std::move(callback);
 }
 
+void SkiaOutputSurfaceImpl::SetGpuVSyncEnabled(bool enabled) {
+  auto task = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::SetGpuVSyncEnabled,
+                             base::Unretained(impl_on_gpu_.get()), enabled);
+  task_sequence_->ScheduleOrRetainTask(std::move(task), {});
+}
+
+void SkiaOutputSurfaceImpl::SetGpuVSyncCallback(GpuVSyncCallback callback) {
+  gpu_vsync_callback_ = std::move(callback);
+}
+
 void SkiaOutputSurfaceImpl::SetDisplayTransformHint(
     gfx::OverlayTransform transform) {
   if (capabilities_.supports_pre_transform)
@@ -604,22 +614,25 @@
         base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
   }
 
-  auto did_swap_buffer_complete_callback = base::BindRepeating(
-      &SkiaOutputSurfaceImpl::DidSwapBuffersComplete, weak_ptr_);
-  did_swap_buffer_complete_callback =
-      CreateSafeCallback(dependency_.get(), did_swap_buffer_complete_callback);
-  auto buffer_presented_callback =
-      base::BindRepeating(&SkiaOutputSurfaceImpl::BufferPresented, weak_ptr_);
-  buffer_presented_callback =
-      CreateSafeCallback(dependency_.get(), buffer_presented_callback);
-  auto context_lost_callback =
-      base::BindRepeating(&SkiaOutputSurfaceImpl::ContextLost, weak_ptr_);
-  context_lost_callback =
-      CreateSafeCallback(dependency_.get(), context_lost_callback);
+  auto did_swap_buffer_complete_callback = CreateSafeCallback(
+      dependency_.get(),
+      base::BindRepeating(&SkiaOutputSurfaceImpl::DidSwapBuffersComplete,
+                          weak_ptr_));
+  auto buffer_presented_callback = CreateSafeCallback(
+      dependency_.get(),
+      base::BindRepeating(&SkiaOutputSurfaceImpl::BufferPresented, weak_ptr_));
+  auto context_lost_callback = CreateSafeCallback(
+      dependency_.get(),
+      base::BindRepeating(&SkiaOutputSurfaceImpl::ContextLost, weak_ptr_));
+  auto gpu_vsync_callback = CreateSafeCallback(
+      dependency_.get(),
+      base::BindRepeating(&SkiaOutputSurfaceImpl::OnGpuVSync, weak_ptr_));
+
   impl_on_gpu_ = SkiaOutputSurfaceImplOnGpu::Create(
       dependency_.get(), renderer_settings_, task_sequence_->GetSequenceId(),
-      did_swap_buffer_complete_callback, buffer_presented_callback,
-      context_lost_callback);
+      std::move(did_swap_buffer_complete_callback),
+      std::move(buffer_presented_callback), std::move(context_lost_callback),
+      std::move(gpu_vsync_callback));
   if (!impl_on_gpu_) {
     *result = false;
   } else {
@@ -685,6 +698,13 @@
   }
 }
 
+void SkiaOutputSurfaceImpl::OnGpuVSync(base::TimeTicks timebase,
+                                       base::TimeDelta interval) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (gpu_vsync_callback_)
+    gpu_vsync_callback_.Run(timebase, interval);
+}
+
 void SkiaOutputSurfaceImpl::ScheduleGpuTaskForTesting(
     base::OnceClosure callback,
     std::vector<gpu::SyncToken> sync_tokens) {
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index e9fa29f..d8fc0f0b 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -70,6 +70,8 @@
                bool use_stencil) override;
   void SetUpdateVSyncParametersCallback(
       UpdateVSyncParametersCallback callback) override;
+  void SetGpuVSyncEnabled(bool enabled) override;
+  void SetGpuVSyncCallback(GpuVSyncCallback callback) override;
   void SetDisplayTransformHint(gfx::OverlayTransform transform) override;
   gfx::OverlayTransform GetDisplayTransform() override;
   void SwapBuffers(OutputSurfaceFrame frame) override;
@@ -149,6 +151,10 @@
   void DidSwapBuffersComplete(gpu::SwapBuffersCompleteParams params,
                               const gfx::Size& pixel_size);
   void BufferPresented(const gfx::PresentationFeedback& feedback);
+
+  // Provided as a callback for the GPU thread.
+  void OnGpuVSync(base::TimeTicks timebase, base::TimeDelta interval);
+
   void ScheduleGpuTask(base::OnceClosure callback,
                        std::vector<gpu::SyncToken> sync_tokens);
   GrBackendFormat GetGrBackendFormatForTexture(
@@ -177,6 +183,7 @@
   std::unique_ptr<SkiaOutputSurfaceDependency> dependency_;
   const bool is_using_vulkan_;
   UpdateVSyncParametersCallback update_vsync_parameters_callback_;
+  GpuVSyncCallback gpu_vsync_callback_;
   bool is_displayed_as_overlay_ = false;
 
   std::unique_ptr<base::WaitableEvent> initialize_waitable_event_;
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 566dd3f..5af4454 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
@@ -586,14 +586,16 @@
     SkiaOutputSurfaceDependency* deps,
     const RendererSettings& renderer_settings,
     const gpu::SequenceId sequence_id,
-    const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback,
-    const BufferPresentedCallback& buffer_presented_callback,
-    const ContextLostCallback& context_lost_callback) {
+    DidSwapBufferCompleteCallback did_swap_buffer_complete_callback,
+    BufferPresentedCallback buffer_presented_callback,
+    ContextLostCallback context_lost_callback,
+    GpuVSyncCallback gpu_vsync_callback) {
   TRACE_EVENT0("viz", "SkiaOutputSurfaceImplOnGpu::Create");
   auto impl_on_gpu = std::make_unique<SkiaOutputSurfaceImplOnGpu>(
       util::PassKey<SkiaOutputSurfaceImplOnGpu>(), deps, renderer_settings,
-      sequence_id, did_swap_buffer_complete_callback, buffer_presented_callback,
-      context_lost_callback);
+      sequence_id, std::move(did_swap_buffer_complete_callback),
+      std::move(buffer_presented_callback), std::move(context_lost_callback),
+      std::move(gpu_vsync_callback));
   if (!impl_on_gpu->Initialize())
     impl_on_gpu = nullptr;
   return impl_on_gpu;
@@ -604,9 +606,10 @@
     SkiaOutputSurfaceDependency* deps,
     const RendererSettings& renderer_settings,
     const gpu::SequenceId sequence_id,
-    const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback,
-    const BufferPresentedCallback& buffer_presented_callback,
-    const ContextLostCallback& context_lost_callback)
+    DidSwapBufferCompleteCallback did_swap_buffer_complete_callback,
+    BufferPresentedCallback buffer_presented_callback,
+    ContextLostCallback context_lost_callback,
+    GpuVSyncCallback gpu_vsync_callback)
     : dependency_(std::move(deps)),
       feature_info_(dependency_->GetSharedContextState()->feature_info()),
       sync_point_client_state_(
@@ -616,9 +619,11 @@
       vulkan_context_provider_(dependency_->GetVulkanContextProvider()),
       renderer_settings_(renderer_settings),
       sequence_id_(sequence_id),
-      did_swap_buffer_complete_callback_(did_swap_buffer_complete_callback),
-      buffer_presented_callback_(buffer_presented_callback),
-      context_lost_callback_(context_lost_callback),
+      did_swap_buffer_complete_callback_(
+          std::move(did_swap_buffer_complete_callback)),
+      buffer_presented_callback_(std::move(buffer_presented_callback)),
+      context_lost_callback_(std::move(context_lost_callback)),
+      gpu_vsync_callback_(std::move(gpu_vsync_callback)),
       gpu_preferences_(dependency_->GetGpuPreferences()) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
@@ -1199,6 +1204,10 @@
   output_device_->ScheduleDCLayers(std::move(dc_layers));
 }
 
+void SkiaOutputSurfaceImplOnGpu::SetGpuVSyncEnabled(bool enabled) {
+  output_device_->SetGpuVSyncEnabled(enabled);
+}
+
 void SkiaOutputSurfaceImplOnGpu::SetCapabilitiesForTesting(
     const OutputSurface::Capabilities& capabilities) {
   MakeCurrent(false /* need_fbo0 */);
@@ -1390,7 +1399,7 @@
 }
 
 GpuVSyncCallback SkiaOutputSurfaceImplOnGpu::GetGpuVSyncCallback() {
-  return base::DoNothing::Repeatedly<base::TimeTicks, base::TimeDelta>();
+  return gpu_vsync_callback_;
 }
 
 void SkiaOutputSurfaceImplOnGpu::DidSwapBuffersComplete(
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index fcf28021..ac48dab8 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -84,18 +84,20 @@
       SkiaOutputSurfaceDependency* deps,
       const RendererSettings& renderer_settings,
       const gpu::SequenceId sequence_id,
-      const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback,
-      const BufferPresentedCallback& buffer_presented_callback,
-      const ContextLostCallback& context_lost_callback);
+      DidSwapBufferCompleteCallback did_swap_buffer_complete_callback,
+      BufferPresentedCallback buffer_presented_callback,
+      ContextLostCallback context_lost_callback,
+      GpuVSyncCallback gpu_vsync_callback);
 
   SkiaOutputSurfaceImplOnGpu(
       util::PassKey<SkiaOutputSurfaceImplOnGpu> pass_key,
       SkiaOutputSurfaceDependency* deps,
       const RendererSettings& renderer_settings,
       const gpu::SequenceId sequence_id,
-      const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback,
-      const BufferPresentedCallback& buffer_presented_callback,
-      const ContextLostCallback& context_lost_callback);
+      DidSwapBufferCompleteCallback did_swap_buffer_complete_callback,
+      BufferPresentedCallback buffer_presented_callback,
+      ContextLostCallback context_lost_callback,
+      GpuVSyncCallback gpu_vsync_callback);
   ~SkiaOutputSurfaceImplOnGpu() override;
 
   gpu::CommandBufferId command_buffer_id() const {
@@ -157,6 +159,7 @@
           image_contexts);
   void SetEnableDCLayers(bool enable);
   void ScheduleDCLayers(std::vector<DCLayerOverlay> dc_layers);
+  void SetGpuVSyncEnabled(bool enabled);
 
   bool was_context_lost() { return context_state_->context_lost(); }
 
@@ -220,6 +223,7 @@
   const DidSwapBufferCompleteCallback did_swap_buffer_complete_callback_;
   const BufferPresentedCallback buffer_presented_callback_;
   const ContextLostCallback context_lost_callback_;
+  const GpuVSyncCallback gpu_vsync_callback_;
 
 #if defined(USE_OZONE)
   // This should outlive gl_surface_ and vulkan_surface_.
diff --git a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc b/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
index 807203b9..bc640286 100644
--- a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
@@ -222,12 +222,15 @@
           base::NumberToString(g_value_get_double(&property_values->new_value));
     } else if (g_strcmp0(property_values->property_name, "accessible-name") ==
                0) {
+      const char* new_name = g_value_get_string(&property_values->new_value);
       log += "NAME-CHANGED:";
-      log += g_value_get_string(&property_values->new_value);
+      log += (new_name) ? new_name : "(null)";
     } else if (g_strcmp0(property_values->property_name,
                          "accessible-description") == 0) {
+      const char* new_description =
+          g_value_get_string(&property_values->new_value);
       log += "DESCRIPTION-CHANGED:";
-      log += g_value_get_string(&property_values->new_value);
+      log += (new_description) ? new_description : "(null)";
     } else {
       return;
     }
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc
index 1a10aa9..3f515b8 100644
--- a/content/browser/accessibility/browser_accessibility_manager_win.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -242,9 +242,17 @@
       if (node->IsIgnored()) {
         FireWinAccessibilityEvent(EVENT_OBJECT_HIDE, node);
         FireUiaStructureChangedEvent(StructureChangeType_ChildRemoved, node);
+        if (node->GetRole() == ax::mojom::Role::kMenu) {
+          FireWinAccessibilityEvent(EVENT_SYSTEM_MENUPOPUPEND, node);
+          FireUiaAccessibilityEvent(UIA_MenuClosedEventId, node);
+        }
       } else {
         FireWinAccessibilityEvent(EVENT_OBJECT_SHOW, node);
         FireUiaStructureChangedEvent(StructureChangeType_ChildAdded, node);
+        if (node->GetRole() == ax::mojom::Role::kMenu) {
+          FireWinAccessibilityEvent(EVENT_SYSTEM_MENUPOPUPSTART, node);
+          FireUiaAccessibilityEvent(UIA_MenuOpenedEventId, node);
+        }
       }
       aria_properties_events_.insert(node);
       break;
@@ -411,11 +419,15 @@
     return;
   if (!ShouldFireEventForNode(node))
     return;
-  // Suppress events when |IGNORED_CHANGED| except for related SHOW / HIDE
+  // Suppress events when |IGNORED_CHANGED| except for related SHOW / HIDE.
+  // Also include MENUPOPUPSTART / MENUPOPUPEND since a change in the ignored
+  // state may show / hide a popup by exposing it to the tree or not.
   if (base::Contains(ignored_changed_nodes_, node)) {
     switch (win_event_type) {
       case EVENT_OBJECT_HIDE:
       case EVENT_OBJECT_SHOW:
+      case EVENT_SYSTEM_MENUPOPUPEND:
+      case EVENT_SYSTEM_MENUPOPUPSTART:
         break;
       default:
         return;
@@ -443,9 +455,20 @@
     return;
   if (!ShouldFireEventForNode(node))
     return;
-  // Suppress events when |IGNORED_CHANGED|
-  if (node->IsIgnored() || base::Contains(ignored_changed_nodes_, node))
+  // Suppress events when |IGNORED_CHANGED| except for MenuClosed / MenuOpen
+  // since a change in the ignored state may show / hide a popup by exposing
+  // it to the tree or not.
+  if (base::Contains(ignored_changed_nodes_, node)) {
+    switch (uia_event) {
+      case UIA_MenuClosedEventId:
+      case UIA_MenuOpenedEventId:
+        break;
+      default:
+        return;
+    }
+  } else if (node->IsIgnored()) {
     return;
+  }
 
   ::UiaRaiseAutomationEvent(ToBrowserAccessibilityWin(node)->GetCOM(),
                             uia_event);
diff --git a/content/browser/frame_host/back_forward_cache_impl.cc b/content/browser/frame_host/back_forward_cache_impl.cc
index a7c0694..3c72b7e 100644
--- a/content/browser/frame_host/back_forward_cache_impl.cc
+++ b/content/browser/frame_host/back_forward_cache_impl.cc
@@ -360,7 +360,7 @@
 }
 
 void BackForwardCacheImpl::DisableForRenderFrameHost(GlobalFrameRoutingId id,
-                                                     std::string_view reason) {
+                                                     base::StringPiece reason) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto* rfh = RenderFrameHostImpl::FromID(id);
   if (rfh)
diff --git a/content/browser/frame_host/back_forward_cache_impl.h b/content/browser/frame_host/back_forward_cache_impl.h
index 5b8e8a2..959a35fd45 100644
--- a/content/browser/frame_host/back_forward_cache_impl.h
+++ b/content/browser/frame_host/back_forward_cache_impl.h
@@ -125,7 +125,7 @@
 
   // BackForwardCache:
   void DisableForRenderFrameHost(GlobalFrameRoutingId id,
-                                 std::string_view reason) override;
+                                 base::StringPiece reason) override;
   void DisableForTesting(DisableForTestingReason reason) override;
 
  private:
diff --git a/content/browser/frame_host/navigation_controller_android.cc b/content/browser/frame_host/navigation_controller_android.cc
index 06f8fed..a279330 100644
--- a/content/browser/frame_host/navigation_controller_android.cc
+++ b/content/browser/frame_host/navigation_controller_android.cc
@@ -425,6 +425,12 @@
   return navigation_controller_->RemoveEntryAtIndex(index);
 }
 
+void NavigationControllerAndroid::PruneForwardEntries(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj) {
+  return navigation_controller_->PruneForwardEntries();
+}
+
 ScopedJavaLocalRef<jstring> NavigationControllerAndroid::GetEntryExtraData(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
diff --git a/content/browser/frame_host/navigation_controller_android.h b/content/browser/frame_host/navigation_controller_android.h
index 7f01993..261353c 100644
--- a/content/browser/frame_host/navigation_controller_android.h
+++ b/content/browser/frame_host/navigation_controller_android.h
@@ -119,6 +119,8 @@
   jboolean RemoveEntryAtIndex(JNIEnv* env,
                               const base::android::JavaParamRef<jobject>& obj,
                               jint index);
+  void PruneForwardEntries(JNIEnv* env,
+                           const base::android::JavaParamRef<jobject>& obj);
   base::android::ScopedJavaLocalRef<jstring> GetEntryExtraData(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 80caf27..d758c75 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -102,18 +102,6 @@
   nav_controller->delegate()->NotifyNavigationListPruned(details);
 }
 
-// Ensure the given NavigationEntry has a valid state, so that WebKit does not
-// get confused if we navigate back to it.
-//
-// An empty state is treated as a new navigation by WebKit, which would mean
-// losing the navigation entries and generating a new navigation entry after
-// this one. We don't want that. To avoid this we create a valid state which
-// WebKit will not treat as a new navigation.
-void SetPageStateIfEmpty(NavigationEntryImpl* entry) {
-  if (!entry->GetPageState().IsValid())
-    entry->SetPageState(PageState::CreateFromURL(entry->GetURL()));
-}
-
 // Configure all the NavigationEntries in entries for restore. This resets
 // the transition type to reload and makes sure the content state isn't empty.
 void ConfigureEntriesForRestore(
@@ -124,8 +112,6 @@
     // the typed count.
     (*entries)[i]->SetTransitionType(ui::PAGE_TRANSITION_RELOAD);
     (*entries)[i]->set_restore_type(type);
-    // NOTE(darin): This code is only needed for backwards compat.
-    SetPageStateIfEmpty((*entries)[i].get());
   }
 }
 
@@ -901,6 +887,17 @@
   return true;
 }
 
+void NavigationControllerImpl::PruneForwardEntries() {
+  DiscardNonCommittedEntries();
+  int remove_start_index = last_committed_entry_index_ + 1;
+  int num_removed = int(entries_.size()) - remove_start_index;
+  if (num_removed <= 0)
+    return;
+  entries_.erase(entries_.begin() + remove_start_index, entries_.end());
+  NotifyPrunedEntries(this, remove_start_index /* start index */,
+                      num_removed /* count */);
+}
+
 void NavigationControllerImpl::UpdateVirtualURLToURL(
     NavigationEntryImpl* entry, const GURL& new_url) {
   GURL new_virtual_url(new_url);
@@ -2445,10 +2442,8 @@
 
   DiscardNonCommittedEntries();
 
-  int current_size = static_cast<int>(entries_.size());
-
   // When replacing, don't prune the forward history.
-  if (replace && current_size > 0) {
+  if (replace && entries_.size() > 0) {
     CopyReplacedNavigationEntryDataIfPreviouslyEmpty(
         entries_[last_committed_entry_index_].get(), entry.get());
     entries_[last_committed_entry_index_] = std::move(entry);
@@ -2458,20 +2453,7 @@
   // We shouldn't see replace == true when there's no committed entries.
   DCHECK(!replace);
 
-  if (current_size > 0) {
-    // Prune any entries which are in front of the current entry.
-    int num_pruned = 0;
-    while (last_committed_entry_index_ < (current_size - 1)) {
-      num_pruned++;
-      entries_.pop_back();
-      current_size--;
-    }
-    if (num_pruned > 0) {  // Only notify if we did prune something.
-      NotifyPrunedEntries(this,
-                          last_committed_entry_index_ + 1 /* start index */,
-                          num_pruned /* count */);
-    }
-  }
+  PruneForwardEntries();
 
   PruneOldestSkippableEntryIfFull();
 
@@ -2617,10 +2599,7 @@
             sandboxed_source_frame_tree_node_id, same_document_loads) &&
         DoesSandboxNavigationStayWithinSubtree(
             sandboxed_source_frame_tree_node_id, different_document_loads);
-    UMA_HISTOGRAM_BOOLEAN(
-        "Navigation.SandboxFrameBackForwardStaysWithinSubtree",
-        navigates_inside_tree);
-    // Also count the navigations as web use counters so we can determine
+    // Count the navigations as web use counters so we can determine
     // the number of pages that trigger this.
     FrameTreeNode* sandbox_source_frame_tree_node =
         FrameTreeNode::GloballyFindByID(sandboxed_source_frame_tree_node_id);
@@ -3381,6 +3360,9 @@
 }
 
 void NavigationControllerImpl::DiscardNonCommittedEntries() {
+  // Avoid sending a notification if there is nothing to discard.
+  if (!pending_entry_ && transient_entry_index_ == -1)
+    return;
   DiscardPendingEntry(false);
   DiscardTransientEntry();
   if (delegate_)
diff --git a/content/browser/frame_host/navigation_controller_impl.h b/content/browser/frame_host/navigation_controller_impl.h
index 52661811..586d6c0 100644
--- a/content/browser/frame_host/navigation_controller_impl.h
+++ b/content/browser/frame_host/navigation_controller_impl.h
@@ -80,6 +80,7 @@
   void GoToIndex(int index) override;
   void GoToOffset(int offset) override;
   bool RemoveEntryAtIndex(int index) override;
+  void PruneForwardEntries() override;
   const SessionStorageNamespaceMap& GetSessionStorageNamespaceMap() override;
   SessionStorageNamespace* GetDefaultSessionStorageNamespace() override;
   bool NeedsReload() override;
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index c46ceb9f..8d77399 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -9735,20 +9735,14 @@
     // *main(simple2, sandbox#test(simple2))]
   }
 
-  static constexpr const char* kWithinSubtreeHistogram =
-      "Navigation.SandboxFrameBackForwardStaysWithinSubtree";
-
  private:
   base::test::ScopedFeatureList feature_list_;
 };
 
-// Tests navigations which occur from a sandboxed frame are tracked
-// accordingly in histograms.
+// Tests navigations which occur from a sandboxed frame are prevented.
 IN_PROC_BROWSER_TEST_F(SandboxedNavigationControllerBrowserTest,
                        TopLevelNavigationFromSandboxSource) {
-  base::HistogramTester histogram;
   SetupNavigation();
-  histogram.ExpectTotalCount(kWithinSubtreeHistogram, 0);
 
   FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
                             ->GetFrameTree()
@@ -9762,29 +9756,21 @@
   // Navigate sandbox frame back same-document.
   EXPECT_TRUE(ExecJs(root->child_at(1), back_script));
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, true, 1);
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, false, 0);
   EXPECT_EQ(3, controller.GetCurrentEntryIndex());
 
   // Navigate innermost frame back cross-document.
   EXPECT_TRUE(ExecJs(root->child_at(1), back_script));
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, true, 2);
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, false, 0);
   EXPECT_EQ(2, controller.GetCurrentEntryIndex());
 
   // Navigate sibling frame back cross-document. It should fail.
   EXPECT_TRUE(ExecJs(root->child_at(1), back_script));
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, true, 2);
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, false, 1);
   EXPECT_EQ(2, controller.GetCurrentEntryIndex());
 
   // Try it again and it should fail.
   EXPECT_TRUE(ExecJs(root->child_at(1), back_script));
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, true, 2);
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, false, 2);
   EXPECT_EQ(2, controller.GetCurrentEntryIndex());
 
   // Do it browser initiated. Make sure histograms don't change.
@@ -9792,8 +9778,6 @@
   controller.GoBack();
   EXPECT_EQ(1, controller.GetCurrentEntryIndex());
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, true, 2);
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, false, 2);
 
   // Go forward to reset state, then a mouse back button navigation.
   // Using the mouse back button should be allowed because it is a
@@ -9816,67 +9800,6 @@
   EXPECT_EQ(1, controller.GetCurrentEntryIndex());
 }
 
-// Tests navigations that occur inside a doubly nested sandbox
-// that affect the parent sandbox are considered outside of tree navigation.
-IN_PROC_BROWSER_TEST_F(SandboxedNavigationControllerBrowserTest,
-                       DoublyNestedSandboxConsideredOutsideOfTree) {
-  base::HistogramTester histogram;
-  SetupNavigation();
-  histogram.ExpectTotalCount(kWithinSubtreeHistogram, 0);
-
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetFrameTree()
-                            ->root();
-
-  std::string back_script = "history.back();";
-
-  // Test that a navigation in the innermost frame affecting its parent
-  // in the same sandbox is considered outside the subtree.
-  EXPECT_TRUE(ExecJs(root->child_at(1)->child_at(0), back_script));
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, true, 0);
-  histogram.ExpectBucketCount(kWithinSubtreeHistogram, false, 1);
-}
-
-// Tests navigations that influence a sandboxed frame that originate
-// from outside the sandboxed frame are not tracked in histograms.
-IN_PROC_BROWSER_TEST_F(SandboxedNavigationControllerBrowserTest,
-                       TopLevelNavigationFromNonSandboxSource) {
-  base::HistogramTester histogram;
-  SetupNavigation();
-  histogram.ExpectTotalCount(kWithinSubtreeHistogram, 0);
-
-  NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
-      shell()->web_contents()->GetController());
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetFrameTree()
-                            ->root();
-
-  std::string back_script = "history.back();";
-  std::string forward_script = "history.forward();";
-
-  // Browser initiated back. Make sure histograms don't change.
-  ASSERT_TRUE(controller.CanGoBack());
-  controller.GoBack();
-  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  histogram.ExpectTotalCount(kWithinSubtreeHistogram, 0);
-
-  // Browser initiated forward. Make sure histograms don't change.
-  ASSERT_TRUE(controller.CanGoForward());
-  controller.GoForward();
-  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  histogram.ExpectTotalCount(kWithinSubtreeHistogram, 0);
-
-  // Navigate sandbox frame back same-document originated from
-  // the main frame though.
-  EXPECT_TRUE(ExecJs(root, back_script));
-  histogram.ExpectTotalCount(kWithinSubtreeHistogram, 0);
-
-  // Navigate sandbox frame forward same-document originated from
-  // the main frame though.
-  EXPECT_TRUE(ExecJs(root, forward_script));
-  histogram.ExpectTotalCount(kWithinSubtreeHistogram, 0);
-}
-
 class SandboxedNavigationControllerPopupBrowserTest
     : public NavigationControllerBrowserTest {
  protected:
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 6e563e5..886aeed5 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -1071,7 +1071,7 @@
   EXPECT_EQ(-1, controller.GetPendingEntryIndex());
   EXPECT_TRUE(controller.GetPendingEntry());
   EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
-  EXPECT_EQ(2, delegate->navigation_state_change_count());
+  EXPECT_EQ(1, delegate->navigation_state_change_count());
 
   // Certain rare cases can make a direct DidCommitProvisionalLoad call without
   // going to the browser. Renderer reload of an about:blank is such a case.
@@ -1082,7 +1082,7 @@
   EXPECT_EQ(-1, controller.GetPendingEntryIndex());
   EXPECT_FALSE(controller.GetPendingEntry());
   EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
-  EXPECT_EQ(3, delegate->navigation_state_change_count());
+  EXPECT_EQ(2, delegate->navigation_state_change_count());
 
   contents()->SetDelegate(nullptr);
 }
@@ -1109,7 +1109,7 @@
   EXPECT_EQ(-1, controller.GetPendingEntryIndex());
   EXPECT_TRUE(controller.GetPendingEntry());
   EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
-  EXPECT_EQ(2, delegate->navigation_state_change_count());
+  EXPECT_EQ(1, delegate->navigation_state_change_count());
 
   // It may abort before committing, if it's a download or due to a stop or
   // a new navigation from the user.
@@ -1121,7 +1121,7 @@
   EXPECT_EQ(-1, controller.GetPendingEntryIndex());
   EXPECT_TRUE(controller.GetPendingEntry());
   EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
-  EXPECT_EQ(2, delegate->navigation_state_change_count());
+  EXPECT_EQ(1, delegate->navigation_state_change_count());
   NavigationEntry* pending_entry = controller.GetPendingEntry();
 
   // Ensure that a reload keeps the same pending entry.
@@ -1162,9 +1162,9 @@
   EXPECT_EQ(-1, controller.GetPendingEntryIndex());
   EXPECT_TRUE(controller.GetPendingEntry());
   EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
-  // The delegate should have been notified at least twice: once for the loading
-  // state change, and once for the url change.
-  EXPECT_EQ(3, delegate->navigation_state_change_count());
+  // The delegate should have been notified twice: once for the loading state
+  // change, and once for the url change.
+  EXPECT_EQ(2, delegate->navigation_state_change_count());
 
   // The visible entry should be the last committed URL, not the pending one.
   EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
@@ -1188,7 +1188,7 @@
   EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
   // The delegate should have been notified twice: once for the loading state
   // change, and once for the url change.
-  EXPECT_EQ(5, delegate->navigation_state_change_count());
+  EXPECT_EQ(4, delegate->navigation_state_change_count());
 
   // The visible entry should be the last committed URL, not the pending one,
   // so that no spoof is possible.
@@ -2449,7 +2449,6 @@
           std::string(), browser_context(),
           nullptr /* blob_url_loader_factory */);
   entry->SetTitle(base::ASCIIToUTF16("Title"));
-  entry->SetPageState(PageState::CreateFromEncodedData("state"));
   const base::Time timestamp = base::Time::Now();
   entry->SetTimestamp(timestamp);
   entries.push_back(std::move(entry));
@@ -2518,7 +2517,6 @@
           std::string(), browser_context(),
           nullptr /* blob_url_loader_factory */);
   new_entry->SetTitle(base::ASCIIToUTF16("Title"));
-  new_entry->SetPageState(PageState::CreateFromEncodedData("state"));
   entries.push_back(std::move(new_entry));
   std::unique_ptr<WebContents> our_contents =
       WebContents::Create(WebContents::CreateParams(browser_context()));
@@ -4840,4 +4838,106 @@
   BrowserURLHandlerImpl::GetInstance()->SetFixupHandlerForTesting(nullptr);
 }
 
+// Tests that calling RemoveForwareEntries() clears all forward entries
+// including non-committed entries.
+TEST_F(NavigationControllerTest, PruneForwardEntries) {
+  NavigationControllerImpl& controller = controller_impl();
+  const GURL url_0("http://foo/0");
+  const GURL url_1("http://foo/1");
+  const GURL url_2("http://foo/2");
+  const GURL url_3("http://foo/3");
+  const GURL url_transient("http://foo/transient");
+
+  NavigateAndCommit(url_0);
+  NavigateAndCommit(url_1);
+  NavigateAndCommit(url_2);
+  NavigateAndCommit(url_3);
+
+  // Set a WebContentsDelegate to listen for state changes.
+  std::unique_ptr<TestWebContentsDelegate> delegate(
+      new TestWebContentsDelegate());
+  EXPECT_FALSE(contents()->GetDelegate());
+  contents()->SetDelegate(delegate.get());
+
+  controller.GoBack();
+
+  // Ensure that non-committed entries are removed even if there are no forward
+  // entries.
+  EXPECT_EQ(4, controller.GetEntryCount());
+  EXPECT_EQ(2, controller.GetPendingEntryIndex());
+  EXPECT_EQ(2, controller.GetCurrentEntryIndex());
+  EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
+  int state_change_count = delegate->navigation_state_change_count();
+  controller.PruneForwardEntries();
+  EXPECT_EQ(4, controller.GetEntryCount());
+  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
+  EXPECT_EQ(3, controller.GetCurrentEntryIndex());
+  EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
+  EXPECT_EQ(0U, navigation_list_pruned_counter_);
+  EXPECT_EQ(state_change_count + 1, delegate->navigation_state_change_count());
+
+  controller.GoBack();
+  contents()->CommitPendingNavigation();
+  controller.GoBack();
+  contents()->CommitPendingNavigation();
+  controller.GoBack();
+  contents()->CommitPendingNavigation();
+  controller.GoForward();
+
+  EXPECT_EQ(1, controller.GetPendingEntryIndex());
+  EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
+  // Insert a transient entry before the pending one.
+  std::unique_ptr<NavigationEntry> transient_entry(new NavigationEntryImpl);
+  transient_entry->SetURL(url_transient);
+  controller.SetTransientEntry(std::move(transient_entry));
+
+  state_change_count = delegate->navigation_state_change_count();
+  controller.PruneForwardEntries();
+
+  EXPECT_EQ(1, controller.GetEntryCount());
+  EXPECT_FALSE(controller.CanGoForward());
+  EXPECT_FALSE(controller.CanGoBack());
+  EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
+  EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
+  EXPECT_EQ(nullptr, controller.GetPendingEntry());
+  EXPECT_EQ(nullptr, controller.GetTransientEntry());
+  EXPECT_EQ(url_0, controller.GetVisibleEntry()->GetURL());
+  EXPECT_EQ(1U, navigation_list_pruned_counter_);
+  EXPECT_EQ(1, last_navigation_entry_pruned_details_.index);
+  EXPECT_EQ(3, last_navigation_entry_pruned_details_.count);
+  EXPECT_EQ(state_change_count + 1, delegate->navigation_state_change_count());
+}
+
+// Make sure that cloning a WebContentsImpl and clearing forward entries
+// before the first commit doesn't clear all entries.
+TEST_F(NavigationControllerTest, PruneForwardEntriesAfterClone) {
+  NavigationControllerImpl& controller = controller_impl();
+  const GURL url1("http://foo1");
+  const GURL url2("http://foo2");
+
+  NavigateAndCommit(url1);
+  NavigateAndCommit(url2);
+
+  std::unique_ptr<WebContents> clone(controller.GetWebContents()->Clone());
+  clone->GetController().LoadIfNecessary();
+
+  // Set a WebContentsDelegate to listen for state changes after the clone call
+  // to only count state changes from the PruneForwardEntries call.
+  std::unique_ptr<TestWebContentsDelegate> delegate(
+      new TestWebContentsDelegate());
+  EXPECT_FALSE(clone->GetDelegate());
+  clone->SetDelegate(delegate.get());
+
+  EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
+
+  clone->GetController().PruneForwardEntries();
+
+  ASSERT_EQ(2, clone->GetController().GetEntryCount());
+  EXPECT_EQ(-1, clone->GetController().GetPendingEntryIndex());
+  EXPECT_EQ(url2, clone->GetController().GetVisibleEntry()->GetURL());
+  EXPECT_EQ(0U, navigation_list_pruned_counter_);
+  EXPECT_EQ(1, delegate->navigation_state_change_count());
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index 7575f08..dce0551 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -95,6 +95,12 @@
   }
 }
 
+base::Optional<base::string16> UrlToOptionalString16(const GURL& url) {
+  if (!url.is_valid())
+    return base::nullopt;
+  return base::UTF8ToUTF16(url.spec());
+}
+
 void RecursivelyGenerateFrameState(
     NavigationEntryImpl::TreeNode* node,
     ExplodedFrameState* state,
@@ -112,6 +118,35 @@
   // Copy the FrameNavigationEntry's frame state into the destination state.
   *state = frame_state;
 
+  // Some data is stored *both* in 1) PageState/ExplodedFrameState and 2)
+  // FrameNavigationEntry.  We want to treat FrameNavigationEntry as the
+  // authoritative source of the data, so we clobber the ExplodedFrameState with
+  // the data taken from FrameNavigationEntry.
+  //
+  // The following ExplodedFrameState fields do not have an equivalent
+  // FrameNavigationEntry field:
+  // - target
+  // - state_object
+  // - document_state
+  // - scroll_restoration_type
+  // - did_save_scroll_or_scale_state
+  // - visual_viewport_scroll_offset
+  // - scroll_offset
+  // - page_scale_factor
+  // - http_body (FrameNavigationEntry::GetPostData extracts the body from
+  //   the ExplodedFrameState)
+  // - scroll_anchor_selector
+  // - scroll_anchor_offset
+  // - scroll_anchor_simhash
+  state->url_string = UrlToOptionalString16(node->frame_entry->url());
+  state->referrer = UrlToOptionalString16(node->frame_entry->referrer().url);
+  state->referrer_policy = node->frame_entry->referrer().policy;
+  state->item_sequence_number = node->frame_entry->item_sequence_number();
+  state->document_sequence_number =
+      node->frame_entry->document_sequence_number();
+  // TODO(lukasza): https://crbug.com/976055: Persist |initiator_origin| in
+  // the ExplodedFrameState.
+
   // Copy the frame's files into the PageState's |referenced_files|.
   referenced_files->reserve(referenced_files->size() +
                             exploded_page_state.referenced_files.size());
@@ -374,6 +409,8 @@
 }
 
 void NavigationEntryImpl::SetPageState(const PageState& state) {
+  DCHECK(state.IsValid());
+
   // SetPageState should only be called before the NavigationEntry has been
   // loaded, such as for restore (when there are no subframe
   // FrameNavigationEntries yet).  However, some callers expect to call this
@@ -386,12 +423,10 @@
   if (!frame_tree_->children.empty())
     frame_tree_->children.clear();
 
-  // If the PageState can't be parsed or has no children, just store it on the
-  // main frame's FrameNavigationEntry without recursively creating subframe
-  // entries.
+  // If the PageState can't be parsed, just store it on the main frame's
+  // FrameNavigationEntry without recursively creating subframe entries.
   ExplodedPageState exploded_state;
-  if (!DecodePageState(state.ToEncodedData(), &exploded_state) ||
-      exploded_state.top.children.size() == 0U) {
+  if (!DecodePageState(state.ToEncodedData(), &exploded_state)) {
     frame_tree_->frame_entry->SetPageState(state);
     return;
   }
@@ -401,14 +436,8 @@
 }
 
 PageState NavigationEntryImpl::GetPageState() {
-  // Just return the main frame's state if there are no subframe
-  // FrameNavigationEntries.
-  if (frame_tree_->children.size() == 0U)
-    return frame_tree_->frame_entry->page_state();
-
-  // When we're using subframe entries, each FrameNavigationEntry has a
-  // frame-specific PageState.  We combine these into an ExplodedPageState tree
-  // and generate a full PageState from it.
+  // Each FrameNavigationEntry has a frame-specific PageState.  We combine these
+  // into an ExplodedPageState tree and generate a full PageState from it.
   ExplodedPageState exploded_state;
   RecursivelyGenerateFrameState(frame_tree_.get(), &exploded_state.top,
                                 &exploded_state.referenced_files);
diff --git a/content/browser/frame_host/navigation_entry_impl_unittest.cc b/content/browser/frame_host/navigation_entry_impl_unittest.cc
index 9bbcf168..45cb8cd6 100644
--- a/content/browser/frame_host/navigation_entry_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_entry_impl_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/browser/site_instance_impl.h"
+#include "content/common/page_state_serialization.h"
 #include "content/public/browser/ssl_status.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_browser_context.h"
@@ -49,6 +50,13 @@
   DISALLOW_COPY_AND_ASSIGN(TestSSLStatusData);
 };
 
+PageState CreateTestPageState() {
+  ExplodedPageState exploded_state;
+  std::string encoded_data;
+  EncodePageState(exploded_state, &encoded_data);
+  return PageState::CreateFromEncodedData(encoded_data);
+}
+
 }  // namespace
 
 class NavigationEntryTest : public testing::Test {
@@ -231,12 +239,6 @@
   entry2_->SetTitle(ASCIIToUTF16("title2"));
   EXPECT_EQ(ASCIIToUTF16("title2"), entry2_->GetTitle());
 
-  // State
-  EXPECT_FALSE(entry1_->GetPageState().IsValid());
-  EXPECT_FALSE(entry2_->GetPageState().IsValid());
-  entry2_->SetPageState(PageState::CreateFromEncodedData("state"));
-  EXPECT_EQ("state", entry2_->GetPageState().ToEncodedData());
-
   // Transition type
   EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
       entry1_->GetTransitionType(), ui::PAGE_TRANSITION_LINK));
@@ -292,10 +294,21 @@
   // Initiator origin.
   EXPECT_FALSE(
       entry1_->root_node()->frame_entry->initiator_origin().has_value());
-  EXPECT_TRUE(
+  ASSERT_TRUE(
       entry2_->root_node()->frame_entry->initiator_origin().has_value());
   EXPECT_EQ(url::Origin::Create(GURL("https://initiator.example.com")),
             entry2_->root_node()->frame_entry->initiator_origin().value());
+
+  // State.
+  //
+  // Note that calling SetPageState may also set some other FNE members
+  // (referrer, initiator, etc.).  This is why it is important to test
+  // SetPageState/GetPageState last.
+  PageState test_page_state = CreateTestPageState();
+  entry2_->SetPageState(test_page_state);
+  // TODO(lukasza): https://crbug.com/976055: Once |initiator_origin| is
+  // persisted across session restore, the test here should verify that
+  // SetPageState round-trips via GetPageState.
 }
 
 // Test basic Clone behavior.
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index ea59a44..441f632 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -469,11 +469,20 @@
 
   UMA_HISTOGRAM_BOOLEAN("Navigation.RequiresDedicatedProcess",
                         new_rfh->GetSiteInstance()->RequiresDedicatedProcess());
+  if (common_params.url.SchemeIsHTTPOrHTTPS()) {
+    UMA_HISTOGRAM_BOOLEAN(
+        "Navigation.RequiresDedicatedProcess.HTTPOrHTTPS",
+        new_rfh->GetSiteInstance()->RequiresDedicatedProcess());
+  }
 
   ChildProcessSecurityPolicyImpl* policy =
       ChildProcessSecurityPolicyImpl::GetInstance();
   GURL process_lock = policy->GetOriginLock(new_rfh->GetProcess()->GetID());
   UMA_HISTOGRAM_BOOLEAN("Navigation.IsLockedProcess", !process_lock.is_empty());
+  if (common_params.url.SchemeIsHTTPOrHTTPS()) {
+    UMA_HISTOGRAM_BOOLEAN("Navigation.IsLockedProcess.HTTPOrHTTPS",
+                          !process_lock.is_empty());
+  }
 
   if (common_params.transition & ui::PAGE_TRANSITION_FORWARD_BACK) {
     UMA_HISTOGRAM_BOOLEAN("Navigation.IsSameProcess.BackForward",
diff --git a/content/browser/indexed_db/database_impl.cc b/content/browser/indexed_db/database_impl.cc
index 3f382904..e071f7db 100644
--- a/content/browser/indexed_db/database_impl.cc
+++ b/content/browser/indexed_db/database_impl.cc
@@ -105,7 +105,7 @@
     int64_t transaction_id,
     const std::vector<int64_t>& object_store_ids,
     blink::mojom::IDBTransactionMode mode,
-    bool relaxed_durability) {
+    blink::mojom::IDBTransactionDurability durability) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!connection_->IsConnected())
     return;
@@ -125,7 +125,7 @@
       transaction_id,
       std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()), mode,
       new IndexedDBBackingStore::Transaction(
-          connection_->database()->backing_store(), relaxed_durability));
+          connection_->database()->backing_store(), durability));
   connection_->database()->RegisterAndScheduleTransaction(transaction);
 
   dispatcher_host_->CreateAndBindTransactionImpl(
diff --git a/content/browser/indexed_db/database_impl.h b/content/browser/indexed_db/database_impl.h
index 92eb2c65..ce181bd 100644
--- a/content/browser/indexed_db/database_impl.h
+++ b/content/browser/indexed_db/database_impl.h
@@ -44,12 +44,13 @@
   void RenameObjectStore(int64_t transaction_id,
                          int64_t object_store_id,
                          const base::string16& new_name) override;
-  void CreateTransaction(mojo::PendingAssociatedReceiver<
-                             blink::mojom::IDBTransaction> transaction_receiver,
-                         int64_t transaction_id,
-                         const std::vector<int64_t>& object_store_ids,
-                         blink::mojom::IDBTransactionMode mode,
-                         bool relaxed_durability) override;
+  void CreateTransaction(
+      mojo::PendingAssociatedReceiver<blink::mojom::IDBTransaction>
+          transaction_receiver,
+      int64_t transaction_id,
+      const std::vector<int64_t>& object_store_ids,
+      blink::mojom::IDBTransactionMode mode,
+      blink::mojom::IDBTransactionDurability durability) override;
   void Close() override;
   void VersionChangeIgnored() override;
   void AddObserver(int64_t transaction_id,
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index 87ced3c..a57fffd 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -831,9 +831,21 @@
 }
 
 std::unique_ptr<IndexedDBBackingStore::Transaction>
-IndexedDBBackingStore::CreateTransaction(bool relaxed_durability) {
-  return std::make_unique<IndexedDBBackingStore::Transaction>(
-      this, relaxed_durability);
+IndexedDBBackingStore::CreateTransaction(
+    blink::mojom::IDBTransactionDurability durability) {
+  return std::make_unique<IndexedDBBackingStore::Transaction>(this, durability);
+}
+
+// static
+bool IndexedDBBackingStore::ShouldSyncOnCommit(
+    blink::mojom::IDBTransactionDurability durability) {
+  switch (durability) {
+    case blink::mojom::IDBTransactionDurability::Default:
+    case blink::mojom::IDBTransactionDurability::Strict:
+      return true;
+    case blink::mojom::IDBTransactionDurability::Relaxed:
+      return false;
+  }
 }
 
 leveldb::Status IndexedDBBackingStore::GetCompleteMetadata(
@@ -1361,10 +1373,10 @@
       int64_t database_id,
       base::WeakPtr<IndexedDBBackingStore> backing_store,
       WriteDescriptorVec* blobs,
-      bool relaxed_durability_,
+      blink::mojom::IDBTransactionDurability durability,
       IndexedDBBackingStore::BlobWriteCallback callback) {
     auto writer = base::WrapRefCounted(new ChainedBlobWriterImpl(
-        database_id, backing_store, relaxed_durability_, std::move(callback)));
+        database_id, backing_store, durability, std::move(callback)));
     writer->blobs_.swap(*blobs);
     writer->iter_ = writer->blobs_.begin();
     backing_store->task_runner()->PostTask(
@@ -1406,17 +1418,18 @@
   }
 
   storage::FlushPolicy GetFlushPolicy() const override {
-    return relaxed_durability_ ? storage::FlushPolicy::NO_FLUSH_ON_COMPLETION
-                               : storage::FlushPolicy::FLUSH_ON_COMPLETION;
+    return IndexedDBBackingStore::ShouldSyncOnCommit(durability_)
+               ? storage::FlushPolicy::FLUSH_ON_COMPLETION
+               : storage::FlushPolicy::NO_FLUSH_ON_COMPLETION;
   }
 
  private:
   ChainedBlobWriterImpl(int64_t database_id,
                         base::WeakPtr<IndexedDBBackingStore> backing_store,
-                        bool relaxed_durability,
+                        blink::mojom::IDBTransactionDurability durability,
                         IndexedDBBackingStore::BlobWriteCallback callback)
       : waiting_for_callback_(false),
-        relaxed_durability_(relaxed_durability),
+        durability_(durability),
         database_id_(database_id),
         backing_store_(backing_store),
         callback_(std::move(callback)),
@@ -1444,7 +1457,7 @@
   }
 
   bool waiting_for_callback_;
-  bool relaxed_durability_;
+  blink::mojom::IDBTransactionDurability durability_;
   scoped_refptr<ChainedBlobWriterImpl> self_ref_;
   WriteDescriptorVec blobs_;
   WriteDescriptorVec::const_iterator iter_;
@@ -2849,13 +2862,13 @@
 // |backing_store| can be null in unittests (see FakeTransaction).
 IndexedDBBackingStore::Transaction::Transaction(
     IndexedDBBackingStore* backing_store,
-    bool relaxed_durability)
+    blink::mojom::IDBTransactionDurability durability)
     : backing_store_(backing_store),
       leveldb_factory_(backing_store ? backing_store->leveldb_factory_
                                      : nullptr),
       database_id_(-1),
       committing_(false),
-      relaxed_durability_(relaxed_durability) {}
+      durability_(durability) {}
 
 IndexedDBBackingStore::Transaction::~Transaction() {
   DCHECK(!committing_);
@@ -3079,8 +3092,8 @@
   // Actually commit. If this succeeds, the journals will appropriately
   // reflect pending blob work - dead files that should be deleted
   // immediately, and live files to monitor.
-  bool sync_on_commit = !relaxed_durability_;
-  s = transaction_->Commit(sync_on_commit);
+  s = transaction_->Commit(
+      IndexedDBBackingStore::ShouldSyncOnCommit(durability_));
   transaction_ = nullptr;
 
   if (!s.ok()) {
@@ -3149,7 +3162,7 @@
   // can be destructed before the callback is triggered.
   chained_blob_writer_ = ChainedBlobWriterImpl::Create(
       database_id_, backing_store_->AsWeakPtr(), new_files_to_write,
-      relaxed_durability_,
+      durability_,
       base::BindOnce(
           [](base::WeakPtr<IndexedDBBackingStore::Transaction> transaction,
              void* tracing_end_ptr, BlobWriteCallback final_callback,
diff --git a/content/browser/indexed_db/indexed_db_backing_store.h b/content/browser/indexed_db/indexed_db_backing_store.h
index 58eec91..e4507e06 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.h
+++ b/content/browser/indexed_db/indexed_db_backing_store.h
@@ -137,7 +137,7 @@
   class CONTENT_EXPORT Transaction {
    public:
     explicit Transaction(IndexedDBBackingStore* backing_store,
-                         bool relaxed_durability);
+                         blink::mojom::IDBTransactionDurability durability);
     virtual ~Transaction();
 
     virtual void Begin(std::vector<ScopeLock> locks);
@@ -300,10 +300,9 @@
     // has been bumped, and journal cleaning should be deferred.
     bool committing_;
 
-    // This flag is passed to LevelDBScopes as |sync_on_commit|.
-    // If |relaxed_durability| is false, the commit flushes to disk.
-    // If true, it avoids the flush for performance at the cost of durability.
-    bool relaxed_durability_;
+    // This flag is passed to LevelDBScopes as |sync_on_commit|, converted
+    // via ShouldSyncOnCommit.
+    blink::mojom::IDBTransactionDurability durability_;
 
     base::WeakPtrFactory<Transaction> ptr_factory_{this};
 
@@ -584,12 +583,15 @@
   bool is_incognito() const { return backing_store_mode_ == Mode::kInMemory; }
 
   virtual std::unique_ptr<Transaction> CreateTransaction(
-      bool relaxed_durability);
+      blink::mojom::IDBTransactionDurability durability);
 
   base::WeakPtr<IndexedDBBackingStore> AsWeakPtr() {
     return weak_factory_.GetWeakPtr();
   }
 
+  static bool ShouldSyncOnCommit(
+      blink::mojom::IDBTransactionDurability durability);
+
  protected:
   friend class IndexedDBOriginState;
 
diff --git a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
index 9922fc6..c4c3fd5 100644
--- a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
@@ -467,8 +467,7 @@
         IndexedDBValue value = value1_;
         {
           IndexedDBBackingStore::Transaction transaction1(
-              backing_store(),
-              /*relaxed_durability=*/true);
+              backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
           transaction1.Begin(CreateDummyLock());
           IndexedDBBackingStore::RecordIdentifier record;
           leveldb::Status s = backing_store()->PutRecord(&transaction1, 1, 1,
@@ -485,8 +484,7 @@
 
         {
           IndexedDBBackingStore::Transaction transaction2(
-              backing_store(),
-              /*relaxed_durability=*/true);
+              backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
           transaction2.Begin(CreateDummyLock());
           IndexedDBValue result_value;
           EXPECT_TRUE(backing_store()
@@ -518,7 +516,7 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Initiate transaction1 - writing blobs.
         transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction1->Begin(CreateDummyLock());
         IndexedDBBackingStore::RecordIdentifier record;
         EXPECT_TRUE(
@@ -541,7 +539,7 @@
 
         // Initiate transaction2, reading blobs.
         IndexedDBBackingStore::Transaction transaction2(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction2.Begin(CreateDummyLock());
         IndexedDBValue result_value;
         EXPECT_TRUE(backing_store()
@@ -562,7 +560,7 @@
 
         // Initiate transaction3, deleting blobs.
         transaction3 = std::make_unique<IndexedDBBackingStore::Transaction>(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction3->Begin(CreateDummyLock());
         EXPECT_TRUE(backing_store()
                         ->DeleteRange(transaction3.get(), 1, 1,
@@ -643,7 +641,7 @@
 
           // Initiate transaction1 - write records.
           transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
-              backing_store(), /*relaxed_durability=*/true);
+              backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
           transaction1->Begin(CreateDummyLock());
           IndexedDBBackingStore::RecordIdentifier record;
           for (size_t i = 0; i < values.size(); ++i) {
@@ -670,7 +668,7 @@
 
           // Initiate transaction 2 - delete range.
           transaction2 = std::make_unique<IndexedDBBackingStore::Transaction>(
-              backing_store(), /*relaxed_durability=*/true);
+              backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
           transaction2->Begin(CreateDummyLock());
           IndexedDBValue result_value;
           EXPECT_TRUE(backing_store()
@@ -762,7 +760,7 @@
 
           // Initiate transaction1 - write records.
           transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
-              backing_store(), /*relaxed_durability=*/true);
+              backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
           transaction1->Begin(CreateDummyLock());
 
           IndexedDBBackingStore::RecordIdentifier record;
@@ -789,7 +787,7 @@
 
           // Initiate transaction 2 - delete range.
           transaction2 = std::make_unique<IndexedDBBackingStore::Transaction>(
-              backing_store(), /*relaxed_durability=*/true);
+              backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
           transaction2->Begin(CreateDummyLock());
           IndexedDBValue result_value;
           EXPECT_TRUE(backing_store()
@@ -832,7 +830,7 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Initiate transaction1.
         transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction1->Begin(CreateDummyLock());
         IndexedDBBackingStore::RecordIdentifier record1;
         EXPECT_TRUE(
@@ -855,7 +853,7 @@
 
         // Initiate transaction2.
         transaction2 = std::make_unique<IndexedDBBackingStore::Transaction>(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction2->Begin(CreateDummyLock());
         IndexedDBBackingStore::RecordIdentifier record2;
         EXPECT_TRUE(
@@ -900,7 +898,7 @@
   idb_context_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction1->Begin(CreateDummyLock());
         IndexedDBBackingStore::RecordIdentifier record;
         EXPECT_TRUE(
@@ -921,7 +919,7 @@
         EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
 
         IndexedDBBackingStore::Transaction transaction2(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction2.Begin(CreateDummyLock());
         EXPECT_TRUE(
             backing_store()
@@ -942,7 +940,7 @@
         }
 
         transaction3 = std::make_unique<IndexedDBBackingStore::Transaction>(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction3->Begin(CreateDummyLock());
         EXPECT_TRUE(backing_store()
                         ->DeleteRange(transaction3.get(), 1, 1,
@@ -1016,8 +1014,7 @@
         EncodeIDBKey(index_key, &index_key_raw);
         {
           IndexedDBBackingStore::Transaction transaction1(
-              backing_store(),
-              /*relaxed_durability=*/true);
+              backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
           transaction1.Begin(CreateDummyLock());
           IndexedDBBackingStore::RecordIdentifier record;
           leveldb::Status s = backing_store()->PutRecord(
@@ -1046,8 +1043,7 @@
 
         {
           IndexedDBBackingStore::Transaction transaction2(
-              backing_store(),
-              /*relaxed_durability=*/true);
+              backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
           transaction2.Begin(CreateDummyLock());
           IndexedDBValue result_value;
           leveldb::Status s = backing_store()->GetRecord(
@@ -1100,7 +1096,7 @@
         IndexedDBValue result_value;
 
         IndexedDBBackingStore::Transaction transaction1(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction1.Begin(CreateDummyLock());
 
         IndexedDBBackingStore::RecordIdentifier record;
@@ -1191,7 +1187,7 @@
           database_id = database.id;
 
           IndexedDBBackingStore::Transaction transaction(
-              backing_store(), /*relaxed_durability=*/true);
+              backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
           transaction.Begin(CreateDummyLock());
 
           IndexedDBObjectStoreMetadata object_store;
@@ -1392,7 +1388,7 @@
           database_id = database.id;
 
           IndexedDBBackingStore::Transaction transaction(
-              backing_store(), /*relaxed_durability=*/true);
+              backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
           transaction.Begin(CreateDummyLock());
 
           IndexedDBObjectStoreMetadata object_store;
@@ -1419,7 +1415,7 @@
 
         // Save a value.
         IndexedDBBackingStore::Transaction transaction1(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction1.Begin(CreateDummyLock());
         IndexedDBBackingStore::RecordIdentifier record;
         leveldb::Status s = backing_store()->PutRecord(
@@ -1452,7 +1448,7 @@
         IndexedDBValue value = value1_;
 
         IndexedDBBackingStore::Transaction transaction2(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction2.Begin(CreateDummyLock());
         IndexedDBValue result_value;
         EXPECT_TRUE(backing_store()
@@ -1519,7 +1515,7 @@
           database_id = database.id;
 
           IndexedDBBackingStore::Transaction transaction(
-              backing_store(), /*relaxed_durability=*/true);
+              backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
           transaction.Begin(CreateDummyLock());
 
           IndexedDBObjectStoreMetadata object_store;
@@ -1544,7 +1540,7 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Initiate transaction1 - writing blobs.
         transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
-            backing_store(), /*relaxed_durability=*/true);
+            backing_store(), blink::mojom::IDBTransactionDurability::Relaxed);
         transaction1->Begin(CreateDummyLock());
         IndexedDBBackingStore::RecordIdentifier record;
         EXPECT_TRUE(backing_store()
diff --git a/content/browser/indexed_db/indexed_db_connection_coordinator.cc b/content/browser/indexed_db/indexed_db_connection_coordinator.cc
index 97d8039..26d5777 100644
--- a/content/browser/indexed_db/indexed_db_connection_coordinator.cc
+++ b/content/browser/indexed_db/indexed_db_connection_coordinator.cc
@@ -288,12 +288,13 @@
     std::vector<int64_t> object_store_ids;
 
     state_ = RequestState::kPendingTransactionComplete;
-    bool relaxed_durability = false;
     IndexedDBTransaction* transaction = connection_->CreateTransaction(
         pending_->transaction_id,
         std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()),
         blink::mojom::IDBTransactionMode::VersionChange,
-        db_->backing_store()->CreateTransaction(relaxed_durability).release());
+        db_->backing_store()
+            ->CreateTransaction(blink::mojom::IDBTransactionDurability::Strict)
+            .release());
 
     // Save a WeakPtr<IndexedDBTransaction> for the CreateAndBindTransaction
     // function to use later.
diff --git a/content/browser/indexed_db/indexed_db_database_unittest.cc b/content/browser/indexed_db/indexed_db_database_unittest.cc
index 2606195..617080a 100644
--- a/content/browser/indexed_db/indexed_db_database_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_database_unittest.cc
@@ -181,12 +181,12 @@
 
   const int64_t transaction_id = 123;
   const std::vector<int64_t> scope;
-  const bool relaxed_durability = true;
   IndexedDBTransaction* transaction = request->connection()->CreateTransaction(
       transaction_id, std::set<int64_t>(scope.begin(), scope.end()),
       blink::mojom::IDBTransactionMode::ReadOnly,
-      new IndexedDBBackingStore::Transaction(backing_store_.get(),
-                                             relaxed_durability));
+      new IndexedDBBackingStore::Transaction(
+          backing_store_.get(),
+          blink::mojom::IDBTransactionDurability::Relaxed));
   db_->RegisterAndScheduleTransaction(transaction);
 
   request->connection()->CloseAndReportForceClose();
diff --git a/content/browser/indexed_db/indexed_db_fake_backing_store.cc b/content/browser/indexed_db/indexed_db_fake_backing_store.cc
index a753e49..d48f6f6 100644
--- a/content/browser/indexed_db/indexed_db_fake_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_fake_backing_store.cc
@@ -158,7 +158,10 @@
 
 IndexedDBFakeBackingStore::FakeTransaction::FakeTransaction(
     leveldb::Status result)
-    : IndexedDBBackingStore::Transaction(nullptr, true), result_(result) {}
+    : IndexedDBBackingStore::Transaction(
+          nullptr,
+          blink::mojom::IDBTransactionDurability::Relaxed),
+      result_(result) {}
 void IndexedDBFakeBackingStore::FakeTransaction::Begin(
     std::vector<ScopeLock> locks) {}
 leveldb::Status IndexedDBFakeBackingStore::FakeTransaction::CommitPhaseOne(
@@ -177,7 +180,8 @@
 }
 
 std::unique_ptr<IndexedDBBackingStore::Transaction>
-IndexedDBFakeBackingStore::CreateTransaction(bool relaxed_durability) {
+IndexedDBFakeBackingStore::CreateTransaction(
+    blink::mojom::IDBTransactionDurability durability) {
   return std::make_unique<FakeTransaction>(leveldb::Status::OK());
 }
 
diff --git a/content/browser/indexed_db/indexed_db_fake_backing_store.h b/content/browser/indexed_db/indexed_db_fake_backing_store.h
index 240fc83..46725de 100644
--- a/content/browser/indexed_db/indexed_db_fake_backing_store.h
+++ b/content/browser/indexed_db/indexed_db_fake_backing_store.h
@@ -128,7 +128,7 @@
   };
 
   std::unique_ptr<IndexedDBBackingStore::Transaction> CreateTransaction(
-      bool relaxed_durability) override;
+      blink::mojom::IDBTransactionDurability durability) override;
 
  protected:
  private:
diff --git a/content/browser/notifications/platform_notification_context_impl.cc b/content/browser/notifications/platform_notification_context_impl.cc
index 93ae69a..2a447b7 100644
--- a/content/browser/notifications/platform_notification_context_impl.cc
+++ b/content/browser/notifications/platform_notification_context_impl.cc
@@ -438,6 +438,11 @@
     return;
   }
 
+  base::Time timestamp =
+      database_data.notification_data.show_trigger_timestamp.value();
+  UMA_HISTOGRAM_LONG_TIMES("Notifications.Triggers.DisplayDelay",
+                           base::Time::Now() - timestamp);
+
   // Remove resources from DB as we don't need them anymore.
   database_->DeleteNotificationResources(write_database_data.notification_id,
                                          write_database_data.origin);
diff --git a/content/browser/notifications/platform_notification_context_trigger_unittest.cc b/content/browser/notifications/platform_notification_context_trigger_unittest.cc
index 403973d..d5699cf 100644
--- a/content/browser/notifications/platform_notification_context_trigger_unittest.cc
+++ b/content/browser/notifications/platform_notification_context_trigger_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "content/browser/notifications/notification_trigger_constants.h"
@@ -277,4 +278,22 @@
           TimeDelta::FromSeconds(kMaximumScheduledNotificationsPerOrigin + 1)));
 }
 
+TEST_F(PlatformNotificationContextTriggerTest, RecordDisplayDelay) {
+  base::HistogramTester histogram_tester;
+  base::TimeDelta trigger_delay = TimeDelta::FromSeconds(10);
+  base::TimeDelta display_delay = TimeDelta::FromSeconds(8);
+
+  WriteNotificationData("1", Time::Now() + trigger_delay);
+  ASSERT_EQ(0u, GetDisplayedNotifications().size());
+
+  // Forward time until after the expected trigger time.
+  task_environment_.FastForwardBy(trigger_delay + display_delay);
+
+  // Trigger notification |display_delay| after it should have been displayed.
+  TriggerNotifications();
+
+  histogram_tester.ExpectUniqueSample("Notifications.Triggers.DisplayDelay",
+                                      display_delay.InMilliseconds(), 1);
+}
+
 }  // namespace content
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn
index 424a4d569..8c5d431 100644
--- a/content/child/BUILD.gn
+++ b/content/child/BUILD.gn
@@ -72,6 +72,8 @@
     "thread_safe_sender.h",
     "webthemeengine_impl_android.cc",
     "webthemeengine_impl_android.h",
+    "webthemeengine_impl_conversions.cc",
+    "webthemeengine_impl_conversions.h",
     "webthemeengine_impl_default.cc",
     "webthemeengine_impl_default.h",
     "webthemeengine_impl_mac.h",
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 2b3bb2d7..eb46ac8d 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -23,6 +23,7 @@
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
+#include "ui/accessibility/accessibility_features.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/events/blink/blink_features.h"
 #include "ui/gfx/switches.h"
@@ -435,6 +436,10 @@
   }
 #endif
 
+  WebRuntimeFeatures::EnableAccessibilityExposeDisplayNone(
+      base::FeatureList::IsEnabled(
+          features::kEnableAccessibilityExposeDisplayNone));
+
   if (command_line.HasSwitch(switches::kEnableAccessibilityObjectModel))
     WebRuntimeFeatures::EnableAccessibilityObjectModel(true);
 
diff --git a/content/child/webthemeengine_impl_conversions.cc b/content/child/webthemeengine_impl_conversions.cc
new file mode 100644
index 0000000..db004c1
--- /dev/null
+++ b/content/child/webthemeengine_impl_conversions.cc
@@ -0,0 +1,120 @@
+// 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 "content/child/webthemeengine_impl_conversions.h"
+
+namespace content {
+
+// TODO(https://crbug.com/988434): The mapping functions below are duplicated
+// inside Blink and in the Android implementation of WebThemeEngine. They should
+// be implemented in one place where dependencies between Blink and
+// ui::NativeTheme make sense.
+ui::NativeTheme::Part NativeThemePart(blink::WebThemeEngine::Part part) {
+  switch (part) {
+    case blink::WebThemeEngine::kPartScrollbarDownArrow:
+      return ui::NativeTheme::kScrollbarDownArrow;
+    case blink::WebThemeEngine::kPartScrollbarLeftArrow:
+      return ui::NativeTheme::kScrollbarLeftArrow;
+    case blink::WebThemeEngine::kPartScrollbarRightArrow:
+      return ui::NativeTheme::kScrollbarRightArrow;
+    case blink::WebThemeEngine::kPartScrollbarUpArrow:
+      return ui::NativeTheme::kScrollbarUpArrow;
+    case blink::WebThemeEngine::kPartScrollbarHorizontalThumb:
+      return ui::NativeTheme::kScrollbarHorizontalThumb;
+    case blink::WebThemeEngine::kPartScrollbarVerticalThumb:
+      return ui::NativeTheme::kScrollbarVerticalThumb;
+    case blink::WebThemeEngine::kPartScrollbarHorizontalTrack:
+      return ui::NativeTheme::kScrollbarHorizontalTrack;
+    case blink::WebThemeEngine::kPartScrollbarVerticalTrack:
+      return ui::NativeTheme::kScrollbarVerticalTrack;
+    case blink::WebThemeEngine::kPartScrollbarCorner:
+      return ui::NativeTheme::kScrollbarCorner;
+    case blink::WebThemeEngine::kPartCheckbox:
+      return ui::NativeTheme::kCheckbox;
+    case blink::WebThemeEngine::kPartRadio:
+      return ui::NativeTheme::kRadio;
+    case blink::WebThemeEngine::kPartButton:
+      return ui::NativeTheme::kPushButton;
+    case blink::WebThemeEngine::kPartTextField:
+      return ui::NativeTheme::kTextField;
+    case blink::WebThemeEngine::kPartMenuList:
+      return ui::NativeTheme::kMenuList;
+    case blink::WebThemeEngine::kPartSliderTrack:
+      return ui::NativeTheme::kSliderTrack;
+    case blink::WebThemeEngine::kPartSliderThumb:
+      return ui::NativeTheme::kSliderThumb;
+    case blink::WebThemeEngine::kPartInnerSpinButton:
+      return ui::NativeTheme::kInnerSpinButton;
+    case blink::WebThemeEngine::kPartProgressBar:
+      return ui::NativeTheme::kProgressBar;
+    default:
+      return ui::NativeTheme::kScrollbarDownArrow;
+  }
+}
+
+ui::NativeTheme::ScrollbarOverlayColorTheme
+NativeThemeScrollbarOverlayColorTheme(
+    blink::WebScrollbarOverlayColorTheme theme) {
+  switch (theme) {
+    case blink::WebScrollbarOverlayColorTheme::
+        kWebScrollbarOverlayColorThemeLight:
+      return ui::NativeTheme::ScrollbarOverlayColorThemeLight;
+    case blink::WebScrollbarOverlayColorTheme::
+        kWebScrollbarOverlayColorThemeDark:
+      return ui::NativeTheme::ScrollbarOverlayColorThemeDark;
+    default:
+      return ui::NativeTheme::ScrollbarOverlayColorThemeDark;
+  }
+}
+
+ui::NativeTheme::State NativeThemeState(blink::WebThemeEngine::State state) {
+  switch (state) {
+    case blink::WebThemeEngine::kStateDisabled:
+      return ui::NativeTheme::kDisabled;
+    case blink::WebThemeEngine::kStateHover:
+      return ui::NativeTheme::kHovered;
+    case blink::WebThemeEngine::kStateNormal:
+      return ui::NativeTheme::kNormal;
+    case blink::WebThemeEngine::kStatePressed:
+      return ui::NativeTheme::kPressed;
+    default:
+      return ui::NativeTheme::kDisabled;
+  }
+}
+
+ui::NativeTheme::ColorScheme NativeColorScheme(
+    blink::WebColorScheme color_scheme) {
+  switch (color_scheme) {
+    case blink::WebColorScheme::kLight:
+      return ui::NativeTheme::ColorScheme::kLight;
+    case blink::WebColorScheme::kDark:
+      return ui::NativeTheme::ColorScheme::kDark;
+  }
+}
+
+ui::NativeTheme::SystemThemeColor NativeSystemThemeColor(
+    blink::WebThemeEngine::SystemThemeColor theme_color) {
+  switch (theme_color) {
+    case blink::WebThemeEngine::SystemThemeColor::kButtonFace:
+      return ui::NativeTheme::SystemThemeColor::kButtonFace;
+    case blink::WebThemeEngine::SystemThemeColor::kButtonText:
+      return ui::NativeTheme::SystemThemeColor::kButtonText;
+    case blink::WebThemeEngine::SystemThemeColor::kGrayText:
+      return ui::NativeTheme::SystemThemeColor::kGrayText;
+    case blink::WebThemeEngine::SystemThemeColor::kHighlight:
+      return ui::NativeTheme::SystemThemeColor::kHighlight;
+    case blink::WebThemeEngine::SystemThemeColor::kHighlightText:
+      return ui::NativeTheme::SystemThemeColor::kHighlightText;
+    case blink::WebThemeEngine::SystemThemeColor::kHotlight:
+      return ui::NativeTheme::SystemThemeColor::kHotlight;
+    case blink::WebThemeEngine::SystemThemeColor::kWindow:
+      return ui::NativeTheme::SystemThemeColor::kWindow;
+    case blink::WebThemeEngine::SystemThemeColor::kWindowText:
+      return ui::NativeTheme::SystemThemeColor::kWindowText;
+    default:
+      return ui::NativeTheme::SystemThemeColor::kNotSupported;
+  }
+}
+
+}  // namespace content
diff --git a/content/child/webthemeengine_impl_conversions.h b/content/child/webthemeengine_impl_conversions.h
new file mode 100644
index 0000000..fdd2e56
--- /dev/null
+++ b/content/child/webthemeengine_impl_conversions.h
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_CHILD_WEBTHEMEENGINE_IMPL_CONVERSIONS_H_
+#define CONTENT_CHILD_WEBTHEMEENGINE_IMPL_CONVERSIONS_H_
+
+#include "content/child/webthemeengine_impl_default.h"
+#include "content/common/content_export.h"
+#include "ui/native_theme/native_theme.h"
+
+namespace content {
+
+CONTENT_EXPORT ui::NativeTheme::Part NativeThemePart(
+    blink::WebThemeEngine::Part part);
+
+CONTENT_EXPORT ui::NativeTheme::ScrollbarOverlayColorTheme
+NativeThemeScrollbarOverlayColorTheme(
+    blink::WebScrollbarOverlayColorTheme theme);
+
+CONTENT_EXPORT ui::NativeTheme::State NativeThemeState(
+    blink::WebThemeEngine::State state);
+
+CONTENT_EXPORT ui::NativeTheme::ColorScheme NativeColorScheme(
+    blink::WebColorScheme color_scheme);
+
+CONTENT_EXPORT ui::NativeTheme::SystemThemeColor NativeSystemThemeColor(
+    blink::WebThemeEngine::SystemThemeColor theme_color);
+
+}  // namespace content
+
+#endif  // CONTENT_CHILD_WEBTHEMEENGINE_IMPL_CONVERSIONS_H_
diff --git a/content/child/webthemeengine_impl_default.cc b/content/child/webthemeengine_impl_default.cc
index 59e6012..f6bf7200 100644
--- a/content/child/webthemeengine_impl_default.cc
+++ b/content/child/webthemeengine_impl_default.cc
@@ -5,6 +5,7 @@
 #include "content/child/webthemeengine_impl_default.h"
 
 #include "build/build_config.h"
+#include "content/child/webthemeengine_impl_conversions.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_size.h"
@@ -35,92 +36,6 @@
 
 }  // namespace
 
-// TODO(https://crbug.com/988434): The mapping functions below are duplicated
-// inside Blink and in the Android implementation of WebThemeEngine. They should
-// be implemented in one place where dependencies between Blink and
-// ui::NativeTheme make sense.
-static ui::NativeTheme::Part NativeThemePart(
-    WebThemeEngine::Part part) {
-  switch (part) {
-    case WebThemeEngine::kPartScrollbarDownArrow:
-      return ui::NativeTheme::kScrollbarDownArrow;
-    case WebThemeEngine::kPartScrollbarLeftArrow:
-      return ui::NativeTheme::kScrollbarLeftArrow;
-    case WebThemeEngine::kPartScrollbarRightArrow:
-      return ui::NativeTheme::kScrollbarRightArrow;
-    case WebThemeEngine::kPartScrollbarUpArrow:
-      return ui::NativeTheme::kScrollbarUpArrow;
-    case WebThemeEngine::kPartScrollbarHorizontalThumb:
-      return ui::NativeTheme::kScrollbarHorizontalThumb;
-    case WebThemeEngine::kPartScrollbarVerticalThumb:
-      return ui::NativeTheme::kScrollbarVerticalThumb;
-    case WebThemeEngine::kPartScrollbarHorizontalTrack:
-      return ui::NativeTheme::kScrollbarHorizontalTrack;
-    case WebThemeEngine::kPartScrollbarVerticalTrack:
-      return ui::NativeTheme::kScrollbarVerticalTrack;
-    case WebThemeEngine::kPartScrollbarCorner:
-      return ui::NativeTheme::kScrollbarCorner;
-    case WebThemeEngine::kPartCheckbox:
-      return ui::NativeTheme::kCheckbox;
-    case WebThemeEngine::kPartRadio:
-      return ui::NativeTheme::kRadio;
-    case WebThemeEngine::kPartButton:
-      return ui::NativeTheme::kPushButton;
-    case WebThemeEngine::kPartTextField:
-      return ui::NativeTheme::kTextField;
-    case WebThemeEngine::kPartMenuList:
-      return ui::NativeTheme::kMenuList;
-    case WebThemeEngine::kPartSliderTrack:
-      return ui::NativeTheme::kSliderTrack;
-    case WebThemeEngine::kPartSliderThumb:
-      return ui::NativeTheme::kSliderThumb;
-    case WebThemeEngine::kPartInnerSpinButton:
-      return ui::NativeTheme::kInnerSpinButton;
-    case WebThemeEngine::kPartProgressBar:
-      return ui::NativeTheme::kProgressBar;
-    default:
-      return ui::NativeTheme::kScrollbarDownArrow;
-  }
-}
-
-static ui::NativeTheme::ScrollbarOverlayColorTheme
-NativeThemeScrollbarOverlayColorTheme(WebScrollbarOverlayColorTheme theme) {
-  switch (theme) {
-    case WebScrollbarOverlayColorTheme::kWebScrollbarOverlayColorThemeLight:
-      return ui::NativeTheme::ScrollbarOverlayColorThemeLight;
-    case WebScrollbarOverlayColorTheme::kWebScrollbarOverlayColorThemeDark:
-      return ui::NativeTheme::ScrollbarOverlayColorThemeDark;
-    default:
-      return ui::NativeTheme::ScrollbarOverlayColorThemeDark;
-  }
-}
-
-static ui::NativeTheme::State NativeThemeState(
-    WebThemeEngine::State state) {
-  switch (state) {
-    case WebThemeEngine::kStateDisabled:
-      return ui::NativeTheme::kDisabled;
-    case WebThemeEngine::kStateHover:
-      return ui::NativeTheme::kHovered;
-    case WebThemeEngine::kStateNormal:
-      return ui::NativeTheme::kNormal;
-    case WebThemeEngine::kStatePressed:
-      return ui::NativeTheme::kPressed;
-    default:
-      return ui::NativeTheme::kDisabled;
-  }
-}
-
-static ui::NativeTheme::ColorScheme NativeColorScheme(
-    WebColorScheme color_scheme) {
-  switch (color_scheme) {
-    case WebColorScheme::kLight:
-      return ui::NativeTheme::ColorScheme::kLight;
-    case WebColorScheme::kDark:
-      return ui::NativeTheme::ColorScheme::kDark;
-  }
-}
-
 static void GetNativeThemeExtraParams(
     WebThemeEngine::Part part,
     WebThemeEngine::State state,
@@ -297,6 +212,12 @@
       NativeThemePart(part));
 }
 
+base::Optional<SkColor> WebThemeEngineDefault::GetSystemColor(
+    blink::WebThemeEngine::SystemThemeColor system_theme_color) const {
+  return ui::NativeTheme::GetInstanceForWeb()->GetSystemColorFromMap(
+      NativeSystemThemeColor(system_theme_color));
+}
+
 #if defined(OS_WIN)
 // static
 void WebThemeEngineDefault::cacheScrollBarMetrics(
diff --git a/content/child/webthemeengine_impl_default.h b/content/child/webthemeengine_impl_default.h
index 180f15c..80c69f2 100644
--- a/content/child/webthemeengine_impl_default.h
+++ b/content/child/webthemeengine_impl_default.h
@@ -28,6 +28,8 @@
   bool SupportsNinePatch(Part part) const override;
   blink::WebSize NinePatchCanvasSize(Part part) const override;
   blink::WebRect NinePatchAperture(Part part) const override;
+  base::Optional<SkColor> GetSystemColor(blink::WebThemeEngine::SystemThemeColor
+                                             system_theme_color) const override;
 #if defined(OS_WIN)
   // Caches the scrollbar metrics. These are retrieved in the browser and passed
   // to the renderer in blink::mojom::RendererPreferences because the required
diff --git a/content/child/webthemeengine_impl_unittest.cc b/content/child/webthemeengine_impl_unittest.cc
new file mode 100644
index 0000000..755ac9f
--- /dev/null
+++ b/content/child/webthemeengine_impl_unittest.cc
@@ -0,0 +1,126 @@
+// 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 "content/child/webthemeengine_impl_conversions.h"
+
+#include <vector>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_theme_engine.h"
+
+namespace content {
+
+TEST(WebThemeEngineTest, NativeSystemThemeColor) {
+  std::vector<blink::WebThemeEngine::SystemThemeColor> blink_inputs = {
+      blink::WebThemeEngine::SystemThemeColor::kButtonFace,
+      blink::WebThemeEngine::SystemThemeColor::kButtonText,
+      blink::WebThemeEngine::SystemThemeColor::kGrayText,
+      blink::WebThemeEngine::SystemThemeColor::kHighlight,
+      blink::WebThemeEngine::SystemThemeColor::kHighlightText,
+      blink::WebThemeEngine::SystemThemeColor::kHotlight,
+      blink::WebThemeEngine::SystemThemeColor::kWindow,
+      blink::WebThemeEngine::SystemThemeColor::kWindowText};
+
+  std::vector<ui::NativeTheme::SystemThemeColor> native_theme_outputs = {
+      ui::NativeTheme::SystemThemeColor::kButtonFace,
+      ui::NativeTheme::SystemThemeColor::kButtonText,
+      ui::NativeTheme::SystemThemeColor::kGrayText,
+      ui::NativeTheme::SystemThemeColor::kHighlight,
+      ui::NativeTheme::SystemThemeColor::kHighlightText,
+      ui::NativeTheme::SystemThemeColor::kHotlight,
+      ui::NativeTheme::SystemThemeColor::kWindow,
+      ui::NativeTheme::SystemThemeColor::kWindowText};
+
+  for (size_t i = 0; i < blink_inputs.size(); ++i)
+    EXPECT_EQ(NativeSystemThemeColor(blink_inputs[i]), native_theme_outputs[i]);
+}
+
+TEST(WebThemeEngineTest, NativeSystemThemePart) {
+  std::vector<blink::WebThemeEngine::Part> blink_inputs = {
+      blink::WebThemeEngine::kPartScrollbarDownArrow,
+      blink::WebThemeEngine::kPartScrollbarLeftArrow,
+      blink::WebThemeEngine::kPartScrollbarRightArrow,
+      blink::WebThemeEngine::kPartScrollbarUpArrow,
+      blink::WebThemeEngine::kPartScrollbarHorizontalThumb,
+      blink::WebThemeEngine::kPartScrollbarVerticalThumb,
+      blink::WebThemeEngine::kPartScrollbarHorizontalTrack,
+      blink::WebThemeEngine::kPartScrollbarVerticalTrack,
+      blink::WebThemeEngine::kPartScrollbarCorner,
+      blink::WebThemeEngine::kPartCheckbox,
+      blink::WebThemeEngine::kPartRadio,
+      blink::WebThemeEngine::kPartButton,
+      blink::WebThemeEngine::kPartTextField,
+      blink::WebThemeEngine::kPartMenuList,
+      blink::WebThemeEngine::kPartSliderTrack,
+      blink::WebThemeEngine::kPartSliderThumb,
+      blink::WebThemeEngine::kPartInnerSpinButton,
+      blink::WebThemeEngine::kPartProgressBar};
+
+  std::vector<ui::NativeTheme::Part> native_theme_outputs = {
+      ui::NativeTheme::kScrollbarDownArrow,
+      ui::NativeTheme::kScrollbarLeftArrow,
+      ui::NativeTheme::kScrollbarRightArrow,
+      ui::NativeTheme::kScrollbarUpArrow,
+      ui::NativeTheme::kScrollbarHorizontalThumb,
+      ui::NativeTheme::kScrollbarVerticalThumb,
+      ui::NativeTheme::kScrollbarHorizontalTrack,
+      ui::NativeTheme::kScrollbarVerticalTrack,
+      ui::NativeTheme::kScrollbarCorner,
+      ui::NativeTheme::kCheckbox,
+      ui::NativeTheme::kRadio,
+      ui::NativeTheme::kPushButton,
+      ui::NativeTheme::kTextField,
+      ui::NativeTheme::kMenuList,
+      ui::NativeTheme::kSliderTrack,
+      ui::NativeTheme::kSliderThumb,
+      ui::NativeTheme::kInnerSpinButton,
+      ui::NativeTheme::kProgressBar};
+
+  for (size_t i = 0; i < blink_inputs.size(); ++i)
+    EXPECT_EQ(NativeThemePart(blink_inputs[i]), native_theme_outputs[i]);
+}
+
+TEST(WebThemeEngineTest, NativeSystemThemeState) {
+  std::vector<blink::WebThemeEngine::State> blink_inputs = {
+      blink::WebThemeEngine::kStateDisabled,
+      blink::WebThemeEngine::kStateHover,
+      blink::WebThemeEngine::kStateNormal,
+      blink::WebThemeEngine::kStatePressed,
+  };
+
+  std::vector<ui::NativeTheme::State> native_theme_outputs = {
+      ui::NativeTheme::kDisabled, ui::NativeTheme::kHovered,
+      ui::NativeTheme::kNormal, ui::NativeTheme::kPressed};
+
+  for (size_t i = 0; i < blink_inputs.size(); ++i)
+    EXPECT_EQ(NativeThemeState(blink_inputs[i]), native_theme_outputs[i]);
+}
+
+TEST(WebThemeEngineTest, NativeColorScheme) {
+  std::vector<blink::WebColorScheme> blink_inputs = {
+      blink::WebColorScheme::kLight, blink::WebColorScheme::kDark};
+
+  std::vector<ui::NativeTheme::ColorScheme> native_theme_outputs = {
+      ui::NativeTheme::ColorScheme::kLight,
+      ui::NativeTheme::ColorScheme::kDark};
+
+  for (size_t i = 0; i < blink_inputs.size(); ++i)
+    EXPECT_EQ(NativeColorScheme(blink_inputs[i]), native_theme_outputs[i]);
+}
+
+TEST(WebThemeEngineTest, NativeThemeScrollbarOverlayColorTheme) {
+  std::vector<blink::WebScrollbarOverlayColorTheme> blink_inputs = {
+      blink::WebScrollbarOverlayColorTheme::kWebScrollbarOverlayColorThemeLight,
+      blink::WebScrollbarOverlayColorTheme::kWebScrollbarOverlayColorThemeDark};
+
+  std::vector<ui::NativeTheme::ScrollbarOverlayColorTheme>
+      native_theme_outputs = {ui::NativeTheme::ScrollbarOverlayColorThemeLight,
+                              ui::NativeTheme::ScrollbarOverlayColorThemeDark};
+
+  for (size_t i = 0; i < blink_inputs.size(); ++i) {
+    EXPECT_EQ(NativeThemeScrollbarOverlayColorTheme(blink_inputs[i]),
+              native_theme_outputs[i]);
+  }
+}
+
+}  // namespace content
diff --git a/content/common/page_state_serialization.cc b/content/common/page_state_serialization.cc
index bf884e3..2e0c0c6 100644
--- a/content/common/page_state_serialization.cc
+++ b/content/common/page_state_serialization.cc
@@ -1023,6 +1023,7 @@
   obj.version = kCurrentVersion;
   WriteMojoPageState(exploded, &obj);
   *encoded = obj.GetAsString();
+  DCHECK(!encoded->empty());
 }
 
 void LegacyEncodePageStateForTesting(const ExplodedPageState& exploded,
diff --git a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
index 53a1a08..60999a25 100644
--- a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
@@ -272,6 +272,13 @@
     }
 
     @Override
+    public void pruneForwardEntries() {
+        if (mNativeNavigationControllerAndroid == 0) return;
+        NavigationControllerImplJni.get().pruneForwardEntries(
+                mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
+    }
+
+    @Override
     public String getEntryExtraData(int index, String key) {
         if (mNativeNavigationControllerAndroid == 0) return null;
         return NavigationControllerImplJni.get().getEntryExtraData(
@@ -362,6 +369,8 @@
                 long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
         boolean removeEntryAtIndex(
                 long nativeNavigationControllerAndroid, NavigationControllerImpl caller, int index);
+        void pruneForwardEntries(
+                long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
         String getEntryExtraData(long nativeNavigationControllerAndroid,
                 NavigationControllerImpl caller, int index, String key);
         void setEntryExtraData(long nativeNavigationControllerAndroid,
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/NavigationController.java b/content/public/android/java/src/org/chromium/content_public/browser/NavigationController.java
index c01dc1a..fca6a6f 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/NavigationController.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/NavigationController.java
@@ -173,6 +173,12 @@
     public boolean removeEntryAtIndex(int index);
 
     /**
+     * Discards any transient or pending entries, then discards all entries after the current entry
+     * index.
+     */
+    void pruneForwardEntries();
+
+    /**
      * Gets extra data on the {@link NavigationEntry} at {@code index}.
      * @param index The index of the navigation entry.
      * @param key The data key.
diff --git a/content/public/browser/back_forward_cache.h b/content/public/browser/back_forward_cache.h
index 14babf3..693c41e1 100644
--- a/content/public/browser/back_forward_cache.h
+++ b/content/public/browser/back_forward_cache.h
@@ -5,8 +5,7 @@
 #ifndef CONTENT_PUBLIC_BROWSER_BACK_FORWARD_CACHE_H_
 #define CONTENT_PUBLIC_BROWSER_BACK_FORWARD_CACHE_H_
 
-#include <string_view>
-
+#include "base/strings/string_piece.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/global_routing_id.h"
 
@@ -43,7 +42,7 @@
   // |id|: If no RenderFrameHost can be found for the given id nothing happens.
   // |reason|: Free form string to be used in logging and metrics.
   virtual void DisableForRenderFrameHost(GlobalFrameRoutingId id,
-                                         std::string_view reason) = 0;
+                                         base::StringPiece reason) = 0;
 
   // List of reasons the BackForwardCache was disabled for a specific test. If a
   // test needs to be disabled for a reason not covered below, please add to
diff --git a/content/public/browser/navigation_controller.h b/content/public/browser/navigation_controller.h
index 47f66af..df7edc38 100644
--- a/content/public/browser/navigation_controller.h
+++ b/content/public/browser/navigation_controller.h
@@ -414,6 +414,10 @@
   // Otherwise this call discards any transient or pending entries.
   virtual bool RemoveEntryAtIndex(int index) = 0;
 
+  // Discards any transient or pending entries, then discards all entries after
+  // the current entry index.
+  virtual void PruneForwardEntries() = 0;
+
   // Random --------------------------------------------------------------------
 
   // Session storage depends on dom_storage that depends on blink::WebString.
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockNavigationController.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockNavigationController.java
index 9329499..bfb468a 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockNavigationController.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockNavigationController.java
@@ -121,6 +121,9 @@
     }
 
     @Override
+    public void pruneForwardEntries() {}
+
+    @Override
     public String getEntryExtraData(int index, String key) {
         return null;
     }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index d683aaa..abf86cc 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1875,6 +1875,7 @@
     "../child/dwrite_font_proxy/dwrite_font_proxy_win_unittest.cc",
     "../child/dwrite_font_proxy/font_fallback_win_unittest.cc",
     "../child/font_warmup_win_unittest.cc",
+    "../child/webthemeengine_impl_unittest.cc",
     "../common/android/gin_java_bridge_value_unittest.cc",
     "../common/background_fetch/background_fetch_mojom_traits_unittest.cc",
     "../common/common_param_traits_unittest.cc",
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 8d15097..0beccb6 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -274,6 +274,7 @@
 
 # Passthrough command decoder / Linux / OpenGL / NVIDIA
 crbug.com/766918 [ opengl linux passthrough nvidia ] conformance2/textures/image_bitmap_from_video/* [ RetryOnFailure ]
+crbug.com/965648 [ opengl linux passthrough nvidia ] deqp/functional/gles3/shaderstruct.html [ RetryOnFailure ]
 
 
 ####################
diff --git a/device/vr/openxr/openxr_api_wrapper.cc b/device/vr/openxr/openxr_api_wrapper.cc
index 68e8af1..9889f19 100644
--- a/device/vr/openxr/openxr_api_wrapper.cc
+++ b/device/vr/openxr/openxr_api_wrapper.cc
@@ -33,6 +33,7 @@
 XrResult CreateInstance(XrInstance* instance) {
   XrInstanceCreateInfo instance_create_info = {XR_TYPE_INSTANCE_CREATE_INFO};
   strcpy_s(instance_create_info.applicationInfo.applicationName, "Chromium");
+  instance_create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
 
   // xrCreateInstance validates the list of extensions and returns
   // XR_ERROR_EXTENSION_NOT_PRESENT if an extension is not supported,
@@ -683,7 +684,7 @@
     return false;
 
   XrSpaceLocation location = {XR_TYPE_SPACE_LOCATION};
-  if (FAILED(xrLocateSpace(stage_space_, local_space_,
+  if (FAILED(xrLocateSpace(local_space_, stage_space_,
                            frame_state_.predictedDisplayTime, &location)) ||
       !(location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) ||
       !(location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT)) {
diff --git a/device/vr/openxr/test/fake_openxr_impl_api.cc b/device/vr/openxr/test/fake_openxr_impl_api.cc
index a0cebe2..0c26859c 100644
--- a/device/vr/openxr/test/fake_openxr_impl_api.cc
+++ b/device/vr/openxr/test/fake_openxr_impl_api.cc
@@ -121,6 +121,9 @@
                           XrInstance* instance) {
   DLOG(INFO) << __FUNCTION__;
 
+  RETURN_IF(create_info->applicationInfo.apiVersion != XR_CURRENT_API_VERSION,
+            XR_ERROR_API_VERSION_UNSUPPORTED, "apiVersion unsupported");
+
   RETURN_IF(create_info->type != XR_TYPE_INSTANCE_CREATE_INFO,
             XR_ERROR_VALIDATION_FAILURE, "XrInstanceCreateInfo type invalid");
 
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom
index 54599020..f8d5c39 100644
--- a/device/vr/public/mojom/vr_service.mojom
+++ b/device/vr/public/mojom/vr_service.mojom
@@ -294,7 +294,7 @@
 // detected in the real world by the underlying system.
 struct XRPlaneData {
   // Unique (per session) identifier of the plane.
-  int32 id;
+  uint32 id;
 
   // Plane orientation relative to gravity.
   XRPlaneOrientation orientation;
@@ -314,7 +314,7 @@
 // present in updated_planes_data.
 struct XRPlaneDetectionData {
   // Array with ids of all tracked planes.
-  array<int32> all_planes_ids;
+  array<uint32> all_planes_ids;
 
   // Array with plane data for all updated planes. Plane is considered updated
   // if its position or polygon has changed. Updated planes are always a subset
@@ -329,7 +329,7 @@
 // evolves.
 struct XRAnchorData {
   // Unique (per session) identifier of the anchor.
-  int32 id;
+  uint32 id;
 
   // Pose of the anchor.
   VRPose pose;
@@ -341,7 +341,7 @@
 // will not be present in updated_anchors_data.
 struct XRAnchorsData {
   // Array with ids of all tracked anchors.
-  array<int32> all_anchors_ids;
+  array<uint32> all_anchors_ids;
 
   // Array with anchor data for all updated anchors. Updated anchors are always
   // a subset of all anchors (i.e. for each anchor found in
@@ -478,7 +478,7 @@
   // |result| will contain status code of the request. |anchor_id| will be valid
   // only if the |result| is SUCCESS.
   CreateAnchor(VRPose anchor_pose) => (CreateAnchorResult result,
-                                       int32 anchor_id);
+                                       uint32 anchor_id);
   // TODO(https://crbug.com/657632): Switch anchor_id to nullable integer once
   // that's available. This will allow us to remove CreateAnchorResult if we're
   // not interested in obtaining detailed error information from the device.
@@ -486,14 +486,14 @@
   // Issues a request to create an anchor attached to a plane.
   // |result| will contain status code of the request. |anchor_id| will be valid
   // only if the |result| is SUCCESS.
-  CreatePlaneAnchor(VRPose anchor_pose, int32 plane_id) =>
-    (CreateAnchorResult result, int32 anchor_id);
+  CreatePlaneAnchor(VRPose anchor_pose, uint32 plane_id) =>
+    (CreateAnchorResult result, uint32 anchor_id);
   // TODO(https://crbug.com/657632): Ditto - make anchor_id a nullable integer..
 
   // Detaches an existing anchor. The |anchor_id| must be a valid id of an
   // anchor created by one of the CreateAnchor calls, otherwise the call will be
   // ignored.
-  DetachAnchor(int32 anchor_id);
+  DetachAnchor(uint32 anchor_id);
 };
 
 // Provides a mechanism for a channel to plumb up any button click events
diff --git a/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc b/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc
index bbea360..38d41100 100644
--- a/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc
+++ b/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc
@@ -271,7 +271,11 @@
   // the device of the correct values before it sends the initial info to the
   // renderer. The frame must be submitted because WMR requires frames to be
   // submitted in the order they're created.
-  GetNextFrameData();
+  UpdateWMRDataForNextFrame();
+  UpdateDisplayInfo();
+  main_thread_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(on_display_info_changed_, current_display_info_.Clone()));
   return SubmitCompositedFrame();
 }
 
diff --git a/docs/mojo_ipc_conversion.md b/docs/mojo_ipc_conversion.md
index adb6cb2..6a16d41 100644
--- a/docs/mojo_ipc_conversion.md
+++ b/docs/mojo_ipc_conversion.md
@@ -217,13 +217,13 @@
         - If the message is sent from a renderer to the browser:
             - If an existing interface is bound by `RenderFrameHostImpl` and
               acquired either via `RenderFrame::GetRemoteInterfaces` or
-              `RenderFrame::GetDocumentInterfaceBroker` and the interface seems
+              `RenderFrame::GetBrowserInterfaceBroker` and the interface seems
               to be a good fit for this message, add the equivalent Mojo message
               to that interface.
-            - If no such interface exists, consider adding one and exposing it
-              via a new getter method on `DocumentInterfaceBroker`. See the
+            - If no such interface exists, consider adding one and registering it
+              with `RenderFrameHostImpl`'s `BrowserInterfaceBroker`. See the
               [simple example](/docs/mojo_and_services.md#Example_Defining-a-New-Frame-Interface)
-              earlier in this document.
+              in the "Intro to Mojo & Services" document.
         - If the message is sent from the browser to a renderer, consider
           adding a Mojo equivalent to the `content.mojom.Frame` interface
           defined
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 9f5849b..69d71d8 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1444,6 +1444,7 @@
   AUTOTESTPRIVATE_GETARCSTARTTIME = 1381,
   AUTOTESTPRIVATE_SETOVERVIEWMODESTATE = 1382,
   AUTOTESTPRIVATE_TAKESCREENSHOTFORDISPLAY = 1383,
+  AUTOFILLPRIVATE_SETCREDITCARDFIDOAUTHENABLEDSTATE = 1384,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/common/manifest_handlers/csp_info.cc b/extensions/common/manifest_handlers/csp_info.cc
index 2479523..c3da391 100644
--- a/extensions/common/manifest_handlers/csp_info.cc
+++ b/extensions/common/manifest_handlers/csp_info.cc
@@ -125,8 +125,8 @@
 
 // static
 const std::string* CSPInfo::GetIsolatedWorldCSP(const Extension& extension) {
-  // TODO(crbug.com/914224): This should be only called for extensions which can
-  // have isolated worlds. Figure out the case of TYPE_USER_SCRIPT and add
+  // TODO(crbug.com/1005978): This should be only called for extensions which
+  // can have isolated worlds. Figure out the case of TYPE_USER_SCRIPT and add
   // DCHECK(csp_info).
   CSPInfo* csp_info = static_cast<CSPInfo*>(
       extension.GetManifestData(keys::kContentSecurityPolicy));
@@ -163,19 +163,24 @@
   // The "content_security_policy" manifest key can either be a string or a
   // dictionary of the format
   // "content_security_policy" : {
-  //     "extension_pages" : ""
+  //     "extension_pages": "",
+  //     "sandbox": "",
+  //     "isolated_world": ""
   //  }
   const base::Value* csp = GetManifestPath(extension, key);
+  const int kManifestVersion3 = 3;
 
-  // TODO(crbug.com/914224): Remove the channel check once the support for the
-  // dictionary key is launched to other channels.
+  // TODO(crbug.com/914224): Remove the channel check once support for isolated
+  // world CSP is implemenented.
   bool csp_dictionary_supported =
       extension->GetType() == Manifest::TYPE_EXTENSION &&
-      GetCurrentChannel() == version_info::Channel::UNKNOWN;
+      (extension->manifest_version() >= kManifestVersion3 ||
+       GetCurrentChannel() == version_info::Channel::UNKNOWN);
 
   if (csp_dictionary_supported) {
-    // CSP key as dictionary is mandatory for manifest v3 extensions.
-    if (extension->manifest_version() == 3) {
+    // CSP key as dictionary is mandatory for manifest v3 (and above)
+    // extensions.
+    if (extension->manifest_version() >= kManifestVersion3) {
       if (csp && !csp->is_dict()) {
         *error = GetInvalidManifestKeyError(key);
         return false;
@@ -204,14 +209,6 @@
 
 bool CSPHandler::ParseCSPDictionary(Extension* extension,
                                     base::string16* error) {
-  if (!ParseExtensionPagesCSP(
-          extension, error, keys::kContentSecurityPolicy_ExtensionPagesPath,
-          true /* secure_only */,
-          GetManifestPath(extension,
-                          keys::kContentSecurityPolicy_ExtensionPagesPath))) {
-    return false;
-  }
-
   // keys::kSandboxedPagesCSP shouldn't be used when using
   // keys::kContentSecurityPolicy as a dictionary.
   if (extension->manifest()->HasPath(keys::kSandboxedPagesCSP)) {
@@ -219,7 +216,12 @@
     return false;
   }
 
-  return ParseSandboxCSP(
+  return ParseExtensionPagesCSP(
+             extension, error, keys::kContentSecurityPolicy_ExtensionPagesPath,
+             true /* secure_only */,
+             GetManifestPath(
+                 extension, keys::kContentSecurityPolicy_ExtensionPagesPath)) &&
+         ParseSandboxCSP(
              extension, error, keys::kContentSecurityPolicy_SandboxedPagesPath,
              GetManifestPath(
                  extension, keys::kContentSecurityPolicy_SandboxedPagesPath)) &&
@@ -376,7 +378,8 @@
 }
 
 bool CSPHandler::AlwaysParseForType(Manifest::Type type) const {
-  // TODO(karandeepb): Check if TYPE_USER_SCRIPT needs to be included here.
+  // TODO(crbug.com/1005978): Check if TYPE_USER_SCRIPT needs to be included
+  // here.
   return type == Manifest::TYPE_PLATFORM_APP ||
          type == Manifest::TYPE_EXTENSION ||
          type == Manifest::TYPE_LEGACY_PACKAGED_APP;
diff --git a/extensions/common/manifest_handlers/csp_info.h b/extensions/common/manifest_handlers/csp_info.h
index ec7812c..b6220770 100644
--- a/extensions/common/manifest_handlers/csp_info.h
+++ b/extensions/common/manifest_handlers/csp_info.h
@@ -64,8 +64,8 @@
   CSPHandler();
   ~CSPHandler() override;
 
+  // ManifestHandler override:
   bool Parse(Extension* extension, base::string16* error) override;
-  bool AlwaysParseForType(Manifest::Type type) const override;
 
  private:
   // Parses the "content_security_policy" dictionary in the manifest.
@@ -103,6 +103,8 @@
   // Helper to set the sandbox content security policy manifest data.
   void SetSandboxCSP(Extension* extension, std::string sandbox_csp);
 
+  // ManifestHandler overrides:
+  bool AlwaysParseForType(Manifest::Type type) const override;
   base::span<const char* const> Keys() const override;
 
   DISALLOW_COPY_AND_ASSIGN(CSPHandler);
diff --git a/extensions/common/manifest_handlers/csp_info_unittest.cc b/extensions/common/manifest_handlers/csp_info_unittest.cc
index 94b8871..8ba33684 100644
--- a/extensions/common/manifest_handlers/csp_info_unittest.cc
+++ b/extensions/common/manifest_handlers/csp_info_unittest.cc
@@ -266,8 +266,6 @@
 // key is mandatory for manifest v3 extensions and that defaults are applied
 // correctly.
 TEST_F(CSPInfoUnitTest, CSPDictionaryMandatoryForV3) {
-  ScopedCurrentChannel channel(version_info::Channel::UNKNOWN);
-
   LoadAndExpectError("csp_invalid_type_v3.json",
                      GetInvalidManifestKeyError(keys::kContentSecurityPolicy));
 
diff --git a/ios/build/bots/scripts/test_runner.py b/ios/build/bots/scripts/test_runner.py
index 635afc22..b444ff924 100644
--- a/ios/build/bots/scripts/test_runner.py
+++ b/ios/build/bots/scripts/test_runner.py
@@ -9,6 +9,7 @@
 import sys
 
 import collections
+import distutils.version
 import glob
 import json
 import logging
@@ -147,6 +148,33 @@
       'Sharding has not been implemented!')
 
 
+def get_device_ios_version(udid):
+  """Gets device iOS version.
+
+  Args:
+    udid: (str) iOS device UDID.
+
+  Returns:
+    Device UDID.
+  """
+  return subprocess.check_output(['ideviceinfo',
+                                  '--udid', udid,
+                                  '-k', 'ProductVersion']).strip()
+
+
+def is_iOS13_or_higher_device(udid):
+  """Checks whether device with udid has iOS 13.0+.
+
+  Args:
+    udid: (str) iOS device UDID.
+
+  Returns:
+    True for iOS 13.0+ devices otherwise false.
+  """
+  return (distutils.version.LooseVersion(get_device_ios_version(udid)) >=
+          distutils.version.LooseVersion('13.0'))
+
+
 def terminate_process(proc):
   """Terminates the process.
 
@@ -334,6 +362,24 @@
   }
 
 
+def get_xctest_from_app(app):
+  """Gets xctest path for an app.
+
+  Args:
+    app: (str) A path to an app.
+
+  Returns:
+    The xctest path.
+  """
+  plugins_dir = os.path.join(app, 'PlugIns')
+  if not os.path.exists(plugins_dir):
+    return None
+  for plugin in os.listdir(plugins_dir):
+    if plugin.endswith('.xctest'):
+      return os.path.join(plugins_dir, plugin)
+  return None
+
+
 def get_test_names(app_path):
   """Gets list of tests from test app.
 
@@ -456,6 +502,9 @@
     self.test_args = test_args or []
     self.test_cases = test_cases or []
     self.xctest_path = ''
+    # TODO(crbug.com/1006881): Separate "running style" from "parser style"
+    #  for XCtests and Gtests.
+    self.xctest = xctest
 
     self.test_results = {}
     self.test_results['version'] = 3
@@ -464,7 +513,7 @@
     # This will be overwritten when the tests complete successfully.
     self.test_results['interrupted'] = True
 
-    if xctest:
+    if self.xctest:
       plugins_dir = os.path.join(self.app_path, 'PlugIns')
       if not os.path.exists(plugins_dir):
         raise PlugInsNotFoundError(plugins_dir)
@@ -583,7 +632,7 @@
       GTestResult instance.
     """
     result = gtest_utils.GTestResult(cmd)
-    if self.xctest_path:
+    if self.xctest:
       parser = xctest_utils.XCTestLogParser()
     else:
       parser = gtest_utils.GTestLogParser()
@@ -627,7 +676,7 @@
 
       returncode = proc.returncode
 
-    if self.xctest_path and parser.SystemAlertPresent():
+    if self.xctest and parser.SystemAlertPresent():
       raise SystemAlertPresentError()
 
     LOGGER.debug('Processing test results.')
@@ -670,7 +719,7 @@
 
       try:
         # XCTests cannot currently be resumed at the next test case.
-        while not self.xctest_path and result.crashed and result.crashed_test:
+        while not self.xctest and result.crashed and result.crashed_test:
           # If the app crashes during a specific test case, then resume at the
           # next test case. This is achieved by filtering out every test case
           # which has already run.
@@ -1517,7 +1566,14 @@
     self.udid = subprocess.check_output(['idevice_id', '--list']).rstrip()
     if len(self.udid.splitlines()) != 1:
       raise DeviceDetectionError(self.udid)
-    if xctest:
+
+    is_iOS13 = is_iOS13_or_higher_device(self.udid)
+
+    # GTest-based unittests are invoked via XCTest on iOS 13+ devices
+    # but produce GTest-style log output that is parsed with a GTestLogParser.
+    if xctest or is_iOS13:
+      if is_iOS13:
+        self.xctest_path = get_xctest_from_app(self.app_path)
       self.xctestrun_file = tempfile.mkstemp()[1]
       self.xctestrun_data = {
         'TestTargetName': {
@@ -1629,6 +1685,12 @@
         self.xctestrun_data['TestTargetName'].update(
           {'OnlyTestIdentifiers': test_filter})
 
+  def get_command_line_args_xctest_unittests(self, filtered_tests):
+    command_line_args = ['--enable-run-ios-unittests-with-xctest']
+    if filtered_tests:
+      command_line_args.append('--gtest_filter=%s' % filtered_tests)
+    return command_line_args
+
   def get_launch_command(self, test_filter=None, invert=False):
     """Returns the command that can be used to launch the test app.
 
@@ -1641,13 +1703,24 @@
       A list of strings forming the command to launch the test.
     """
     if self.xctest_path:
-      self.set_xctest_filters(test_filter, invert)
       if self.env_vars:
         self.xctestrun_data['TestTargetName'].update(
           {'EnvironmentVariables': self.env_vars})
-      if self.test_args:
+
+      command_line_args = self.test_args
+
+      if self.xctest:
+        self.set_xctest_filters(test_filter, invert)
+      else:
+        filtered_tests = []
+        if test_filter:
+          filtered_tests = get_gtest_filter(test_filter, invert=invert)
+        command_line_args.append(
+            self.get_command_line_args_xctest_unittests(filtered_tests))
+
+      if command_line_args:
         self.xctestrun_data['TestTargetName'].update(
-          {'CommandLineArguments': self.test_args})
+            {'CommandLineArguments': command_line_args})
       plistlib.writePlist(self.xctestrun_data, self.xctestrun_file)
       return [
         'xcodebuild',
diff --git a/ios/build/bots/scripts/test_runner_test.py b/ios/build/bots/scripts/test_runner_test.py
index f468dc9..8dfa7ca 100755
--- a/ios/build/bots/scripts/test_runner_test.py
+++ b/ios/build/bots/scripts/test_runner_test.py
@@ -11,6 +11,7 @@
 import mock
 import os
 import subprocess
+import tempfile
 import unittest
 
 import test_runner
@@ -131,6 +132,7 @@
     self.mock(os.path, 'exists', lambda _: True)
     self.mock(test_runner.TestRunner, 'set_sigterm_handler',
       lambda self, handler: 0)
+    self.mock(os, 'listdir', lambda _: [])
 
   def test_app_not_found(self):
     """Ensures AppNotFoundError is raised."""
@@ -202,6 +204,7 @@
         'xcode-version',
         'xcode-build',
         'out-dir',
+        xctest=True,
     )
     with self.assertRaises(test_runner.AppLaunchError):
       tr.launch()
@@ -229,6 +232,7 @@
       'xcode-version',
       'xcode-build',
       'out-dir',
+      xctest=True,
     )
     self.mock(test_runner, 'shard_xctest', shard_xctest)
     self.mock(test_runner.SimulatorTestRunner, 'run_tests', run_tests)
@@ -254,6 +258,7 @@
         'xcode-version',
         'xcode-build',
         'out-dir',
+        xctest=True,
       )
       tr.xctest_path = 'fake.xctest'
       cmd = ['echo', 'System alert view is present, so skipping all tests!']
@@ -532,6 +537,8 @@
               lambda _: 'fake-bundle-id')
     self.mock(os.path, 'abspath', lambda path: '/abs/path/to/%s' % path)
     self.mock(os.path, 'exists', lambda _: True)
+    self.mock(os, 'listdir', lambda _: [])
+    self.mock(tempfile, 'mkstemp', lambda: '/tmp/tmp_file')
 
     self.tr = test_runner.DeviceTestRunner(
         'fake-app',
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 36a5e887..026f93f 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -2162,9 +2162,9 @@
   BOOL showActivityIndicator = NO;
 
   if (@available(iOS 13, *)) {
-    // TODO(crbug.com/632772): Visited links don't clearing doesn't require
-    // disabling web usage with iOS 13. Stop disabling web usage once iOS 12
-    // is not supported.
+    // TODO(crbug.com/632772): Visited links clearing doesn't require disabling
+    // web usage with iOS 13. Stop disabling web usage once iOS 12 is not
+    // supported.
     showActivityIndicator = disableWebUsageDuringRemoval;
     disableWebUsageDuringRemoval = NO;
   }
diff --git a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
index 180e261..7867755 100644
--- a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
@@ -202,10 +202,14 @@
   if (!browserState || browserState->IsOffTheRecord()) {
     return NO;
   }
-  AuthenticationService* authService =
-      AuthenticationServiceFactory::GetForBrowserState(browserState);
-  return !gSignedInAccountsViewControllerIsShown &&
-         authService->IsAuthenticated() && authService->HaveAccountsChanged();
+  // Temporary fix for regression for http://crbug.com/1006744: Disable showing
+  // the signed-in account modal dialog.
+  //
+  // AuthenticationService* authService =
+  //    AuthenticationServiceFactory::GetForBrowserState(browserState);
+  // return !gSignedInAccountsViewControllerIsShown &&
+  //       authService->IsAuthenticated() && authService->HaveAccountsChanged();
+  return NO;
 }
 
 #pragma mark Initialization
diff --git a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm
index 448a1fc..a5493a1 100644
--- a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm
@@ -59,8 +59,10 @@
 
 // Tests that the signed in accounts view should be presented when the accounts
 // have changed.
+// Temporary disabled for regression for http://crbug.com/1006744: Disable
+// showing the signed-in account modal dialog.
 TEST_F(SignedInAccountsViewControllerTest,
-       ShouldBePresentedForBrowserStateNecessary) {
+       DISABLED_ShouldBePresentedForBrowserStateNecessary) {
   auth_service_->SetHaveAccountsChanged(true);
   EXPECT_TRUE([SignedInAccountsViewController
       shouldBePresentedForBrowserState:browser_state_.get()]);
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 3487087..e53fde7 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -4389,8 +4389,8 @@
                    targetDeviceName:[command targetDeviceName]];
 }
 
-- (void)showActivityOverlay:(BOOL)shown {
-  if (!shown) {
+- (void)showActivityOverlay:(BOOL)show {
+  if (!show) {
     [self.activityOverlayCoordinator stop];
     self.activityOverlayCoordinator = nil;
   } else if (!self.activityOverlayCoordinator) {
diff --git a/ios/chrome/browser/ui/commands/browser_commands.h b/ios/chrome/browser/ui/commands/browser_commands.h
index c2c3ae48..4f8e420 100644
--- a/ios/chrome/browser/ui/commands/browser_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_commands.h
@@ -112,9 +112,9 @@
 // Sends the tab to another of the user's devices using the data in |command|.
 - (void)sendTabToSelf:(SendTabToSelfCommand*)command;
 
-// Show/Hide the activity indicator overlay that appears over the view to
+// Shows/Hides the activity indicator overlay that appears over the view to
 // prevent interaction with the web page.
-- (void)showActivityOverlay:(BOOL)shown;
+- (void)showActivityOverlay:(BOOL)show;
 
 @end
 
diff --git a/ios/web/js_messaging/web_frames_manager_impl.mm b/ios/web/js_messaging/web_frames_manager_impl.mm
index a3207b11..8cfbed6 100644
--- a/ios/web/js_messaging/web_frames_manager_impl.mm
+++ b/ios/web/js_messaging/web_frames_manager_impl.mm
@@ -53,7 +53,7 @@
     RemoveAllWebFrames();
     // TODO(crbug.com/956516): ScriptMessageHandlers should all be removed
     // manually using |removeScriptMessageHandlerForName|, however this is not
-    // possible because of the cases where the webviewconfiguration is purged,
+    // possible because of the cases where the WKWebViewConfiguration is purged,
     // in these cases the message router is deleted and it will not have
     // message handlers for the web view.
     [message_router removeAllScriptMessageHandlersForWebView:old_web_view];
diff --git a/ios/web/public/web_state/web_state.h b/ios/web/public/web_state/web_state.h
new file mode 100644
index 0000000..f19a68b
--- /dev/null
+++ b/ios/web/public/web_state/web_state.h
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_PUBLIC_WEB_STATE_WEB_STATE_H_
+#define IOS_WEB_PUBLIC_WEB_STATE_WEB_STATE_H_
+
+// TODO(crbug.com/942244): Remove this header after downstream code
+// stopped including ios/web/public/web_state/web_state.h
+
+#import "ios/web/public/web_state.h"
+
+#endif  // IOS_WEB_PUBLIC_WEB_STATE_WEB_STATE_H_
diff --git a/ios/web/public/web_state/web_state_observer.h b/ios/web/public/web_state/web_state_observer.h
new file mode 100644
index 0000000..7e200daa
--- /dev/null
+++ b/ios/web/public/web_state/web_state_observer.h
@@ -0,0 +1,13 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_PUBLIC_WEB_STATE_WEB_STATE_OBSERVER_H_
+#define IOS_WEB_PUBLIC_WEB_STATE_WEB_STATE_OBSERVER_H_
+
+// TODO(crbug.com/942244): Remove this header after downstream code
+// stopped including ios/web/public/web_state/web_state.h
+
+#include "ios/web/public/web_state_observer.h"
+
+#endif  // IOS_WEB_PUBLIC_WEB_STATE_WEB_STATE_OBSERVER_H_
diff --git a/media/BUILD.gn b/media/BUILD.gn
index f1bc9679..50361c6 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -20,13 +20,13 @@
   flags = [
     "ALTERNATE_CDM_STORAGE_ID_KEY=\"$alternate_cdm_storage_id_key\"",
     "CDM_PLATFORM_SPECIFIC_PATH=\"$cdm_platform_specific_path\"",
-    "ENABLE_AC3_EAC3_AUDIO_DEMUXING=$enable_ac3_eac3_audio_demuxing",
+    "ENABLE_PLATFORM_AC3_EAC3_AUDIO=$enable_platform_ac3_eac3_audio",
     "ENABLE_CBCS_ENCRYPTION_SCHEME=$enable_cbcs_encryption_scheme",
     "ENABLE_CDM_HOST_VERIFICATION=$enable_cdm_host_verification",
     "ENABLE_CDM_STORAGE_ID=$enable_cdm_storage_id",
     "ENABLE_DAV1D_DECODER=$enable_dav1d_decoder",
     "ENABLE_AV1_DECODER=$enable_av1_decoder",
-    "ENABLE_DOLBY_VISION_DEMUXING=$enable_dolby_vision_demuxing",
+    "ENABLE_PLATFORM_DOLBY_VISION=$enable_platform_dolby_vision",
     "ENABLE_FFMPEG=$media_use_ffmpeg",
     "ENABLE_FFMPEG_VIDEO_DECODERS=$enable_ffmpeg_video_decoders",
     "ENABLE_PLATFORM_HEVC=$enable_platform_hevc",
@@ -36,7 +36,7 @@
     "ENABLE_LOGGING_OVERRIDE=$enable_logging_override",
     "ENABLE_MEDIA_REMOTING=$enable_media_remoting",
     "ENABLE_MEDIA_REMOTING_RPC=$enable_media_remoting_rpc",
-    "ENABLE_MPEG_H_AUDIO_DEMUXING=$enable_mpeg_h_audio_demuxing",
+    "ENABLE_PLATFORM_MPEG_H_AUDIO=$enable_platform_mpeg_h_audio",
     "ENABLE_MSE_MPEG2TS_STREAM_PARSER=$enable_mse_mpeg2ts_stream_parser",
     "USE_PROPRIETARY_CODECS=$proprietary_codecs",
   ]
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn
index b27ebee..de4f3ceb 100644
--- a/media/audio/BUILD.gn
+++ b/media/audio/BUILD.gn
@@ -19,6 +19,17 @@
   }
 }
 
+if (is_android) {
+  generate_stubs("aaudio_stubs") {
+    extra_header = "android/aaudio_stub_header.fragment"
+    sigs = [ "android/aaudio.sigs" ]
+    output_name = "android/aaudio_stubs"
+    deps = [
+      "//base",
+    ]
+  }
+}
+
 config("platform_config") {
   defines = []
   if (is_chromecast) {
@@ -217,6 +228,8 @@
 
   if (is_android) {
     sources += [
+      "android/aaudio_output.cc",
+      "android/aaudio_output.h",
       "android/audio_manager_android.cc",
       "android/audio_manager_android.h",
       "android/audio_track_output_stream.cc",
@@ -231,7 +244,10 @@
       "android/opensles_wrapper.cc",
     ]
 
-    deps += [ "//media/base/android:media_jni_headers" ]
+    deps += [
+      ":aaudio_stubs",
+      "//media/base/android:media_jni_headers",
+    ]
   }
 
   if (is_linux) {
diff --git a/media/audio/android/aaudio.sigs b/media/audio/android/aaudio.sigs
new file mode 100644
index 0000000..2d8e9e4
--- /dev/null
+++ b/media/audio/android/aaudio.sigs
@@ -0,0 +1,29 @@
+// 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.
+
+//------------------------------------------------
+// Functions from AAudio used in media code.
+//------------------------------------------------
+
+const char * AAudio_convertResultToText(aaudio_result_t returnCode);
+aaudio_result_t AAudio_createStreamBuilder(AAudioStreamBuilder** builder);
+void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder, int32_t deviceId);
+void AAudioStreamBuilder_setSampleRate(AAudioStreamBuilder* builder, int32_t sampleRate);
+void AAudioStreamBuilder_setChannelCount(AAudioStreamBuilder* builder, int32_t channelCount);
+void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder, int32_t samplesPerFrame);
+void AAudioStreamBuilder_setFormat(AAudioStreamBuilder* builder, aaudio_format_t format);
+void AAudioStreamBuilder_setDirection(AAudioStreamBuilder* builder, aaudio_direction_t direction);
+void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilder* builder, int32_t numFrames);
+void AAudioStreamBuilder_setPerformanceMode(AAudioStreamBuilder* builder, aaudio_performance_mode_t mode);
+void AAudioStreamBuilder_setFramesPerDataCallback(AAudioStreamBuilder* builder, int32_t numFrames);
+void AAudioStreamBuilder_setUsage(AAudioStreamBuilder* builder, aaudio_usage_t usage);
+void AAudioStreamBuilder_setDataCallback(AAudioStreamBuilder* builder, AAudioStream_dataCallback callback, void *userData);
+void AAudioStreamBuilder_setErrorCallback(AAudioStreamBuilder* builder, AAudioStream_errorCallback callback, void *userData);
+aaudio_result_t AAudioStreamBuilder_openStream(AAudioStreamBuilder* builder, AAudioStream** stream);
+aaudio_result_t AAudioStreamBuilder_delete(AAudioStreamBuilder* builder);
+aaudio_result_t AAudioStream_close(AAudioStream* stream);
+aaudio_result_t AAudioStream_requestStart(AAudioStream* stream);
+aaudio_result_t AAudioStream_requestStop(AAudioStream* stream);
+aaudio_result_t AAudioStream_getTimestamp(AAudioStream* stream, clockid_t clockid, int64_t *framePosition, int64_t *timeNanoseconds);
+int64_t AAudioStream_getFramesWritten(AAudioStream* stream);
\ No newline at end of file
diff --git a/media/audio/android/aaudio_output.cc b/media/audio/android/aaudio_output.cc
new file mode 100644
index 0000000..94d854e
--- /dev/null
+++ b/media/audio/android/aaudio_output.cc
@@ -0,0 +1,234 @@
+// 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 "media/audio/android/aaudio_output.h"
+
+#include "base/logging.h"
+#include "media/audio/android/aaudio_stubs.h"
+#include "media/audio/android/audio_manager_android.h"
+#include "media/base/audio_bus.h"
+
+namespace media {
+
+static aaudio_data_callback_result_t OnAudioDataRequestedCallback(
+    AAudioStream* stream,
+    void* user_data,
+    void* audio_data,
+    int32_t num_frames) {
+  AAudioOutputStream* output_stream =
+      reinterpret_cast<AAudioOutputStream*>(user_data);
+  return output_stream->OnAudioDataRequested(audio_data, num_frames);
+}
+
+static void OnStreamErrorCallback(AAudioStream* stream,
+                                  void* user_data,
+                                  aaudio_result_t error) {
+  AAudioOutputStream* output_stream =
+      reinterpret_cast<AAudioOutputStream*>(user_data);
+  output_stream->OnStreamError(error);
+}
+
+AAudioOutputStream::AAudioOutputStream(AudioManagerAndroid* manager,
+                                       const AudioParameters& params,
+                                       aaudio_usage_t usage)
+    : audio_manager_(manager),
+      params_(params),
+      usage_(usage),
+      performance_mode_(AAUDIO_PERFORMANCE_MODE_NONE),
+      ns_per_frame_(base::Time::kNanosecondsPerSecond /
+                    static_cast<double>(params.sample_rate())) {
+  DCHECK(manager);
+  DCHECK(params.IsValid());
+
+  if (AudioManagerAndroid::SupportsPerformanceModeForOutput()) {
+    if (params.latency_tag() == AudioLatency::LATENCY_PLAYBACK)
+      performance_mode_ = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
+    else if (params.latency_tag() == AudioLatency::LATENCY_RTC)
+      performance_mode_ = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
+  }
+}
+
+AAudioOutputStream::~AAudioOutputStream() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void AAudioOutputStream::Flush() {}
+
+bool AAudioOutputStream::Open() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  AAudioStreamBuilder* builder;
+  auto result = AAudio_createStreamBuilder(&builder);
+  if (AAUDIO_OK != result)
+    return false;
+
+  // Parameters
+  AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
+  AAudioStreamBuilder_setSampleRate(builder, params_.sample_rate());
+  AAudioStreamBuilder_setChannelCount(builder, params_.channels());
+  AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
+  AAudioStreamBuilder_setUsage(builder, usage_);
+  AAudioStreamBuilder_setPerformanceMode(builder, performance_mode_);
+  AAudioStreamBuilder_setBufferCapacityInFrames(builder,
+                                                params_.frames_per_buffer());
+  AAudioStreamBuilder_setFramesPerDataCallback(builder,
+                                               params_.frames_per_buffer());
+
+  // Callbacks
+  AAudioStreamBuilder_setDataCallback(builder, OnAudioDataRequestedCallback,
+                                      this);
+  AAudioStreamBuilder_setErrorCallback(builder, OnStreamErrorCallback, this);
+
+  result = AAudioStreamBuilder_openStream(builder, &aaudio_stream_);
+
+  AAudioStreamBuilder_delete(builder);
+
+  if (AAUDIO_OK != result)
+    return false;
+
+  audio_bus_ = AudioBus::Create(params_);
+
+  return true;
+}
+
+void AAudioOutputStream::Close() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  Stop();
+  if (aaudio_stream_) {
+    const auto result = AAudioStream_close(aaudio_stream_);
+    if (result != AAUDIO_OK) {
+      DLOG(ERROR) << "Failed to close audio stream, result: "
+                  << AAudio_convertResultToText(result);
+    }
+  }
+
+  // Note: This must be last, it will delete |this|.
+  audio_manager_->ReleaseOutputStream(this);
+}
+
+void AAudioOutputStream::Start(AudioSourceCallback* callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(aaudio_stream_);
+
+  {
+    base::AutoLock al(lock_);
+    DCHECK(!callback_);
+    callback_ = callback;
+  }
+
+  auto result = AAudioStream_requestStart(aaudio_stream_);
+  if (result != AAUDIO_OK) {
+    DLOG(ERROR) << "Failed to start audio stream, result: "
+                << AAudio_convertResultToText(result);
+
+    // Lock is required in case a previous asynchronous requestStop() still has
+    // not completed by the time we reach this point.
+    base::AutoLock al(lock_);
+    callback_->OnError();
+    callback_ = nullptr;
+  }
+}
+
+void AAudioOutputStream::Stop() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  base::AutoLock al(lock_);
+  if (!callback_ || !aaudio_stream_)
+    return;
+
+  // Note: This call is asynchronous, so we must clear |callback_| under lock
+  // below to ensure no further calls occur after Stop().
+  auto result = AAudioStream_requestStop(aaudio_stream_);
+
+  if (result != AAUDIO_OK) {
+    DLOG(ERROR) << "Failed to stop audio stream, result: "
+                << AAudio_convertResultToText(result);
+    callback_->OnError();
+  }
+
+  callback_ = nullptr;
+}
+
+base::TimeDelta AAudioOutputStream::GetDelay(base::TimeTicks delay_timestamp) {
+  // Get the time that a known audio frame was presented for playing.
+  int64_t existing_frame_index;
+  int64_t existing_frame_pts;
+  auto result =
+      AAudioStream_getTimestamp(aaudio_stream_, CLOCK_MONOTONIC,
+                                &existing_frame_index, &existing_frame_pts);
+
+  if (result != AAUDIO_OK) {
+    DLOG(ERROR) << "Failed to get audio latency, result: "
+                << AAudio_convertResultToText(result);
+    return base::TimeDelta();
+  }
+
+  // Calculate the number of frames between our known frame and the write index.
+  const int64_t frame_index_delta =
+      AAudioStream_getFramesWritten(aaudio_stream_) - existing_frame_index;
+
+  // Calculate the time which the next frame will be presented.
+  const base::TimeDelta next_frame_pts = base::TimeDelta::FromNanosecondsD(
+      existing_frame_pts + frame_index_delta * ns_per_frame_);
+
+  // Calculate the latency between write time and presentation time.
+  return next_frame_pts - (delay_timestamp - base::TimeTicks());
+}
+
+aaudio_data_callback_result_t AAudioOutputStream::OnAudioDataRequested(
+    void* audio_data,
+    int32_t num_frames) {
+  // TODO(tguilbert): This can be downgraded to a DCHECK after we've launched.
+  CHECK_EQ(num_frames, audio_bus_->frames());
+
+  base::AutoLock al(lock_);
+  if (!callback_)
+    return AAUDIO_CALLBACK_RESULT_STOP;
+
+  const base::TimeTicks delay_timestamp = base::TimeTicks::Now();
+  const base::TimeDelta delay = GetDelay(delay_timestamp);
+
+  const int frames_filled =
+      callback_->OnMoreData(delay, delay_timestamp, 0, audio_bus_.get());
+
+  audio_bus_->Scale(muted_ ? 0.0 : volume_);
+  audio_bus_->ToInterleaved<Float32SampleTypeTraits>(
+      frames_filled, reinterpret_cast<float*>(audio_data));
+  return AAUDIO_CALLBACK_RESULT_CONTINUE;
+}
+
+void AAudioOutputStream::OnStreamError(aaudio_result_t error) {
+  base::AutoLock al(lock_);
+  if (callback_)
+    callback_->OnError();
+}
+
+void AAudioOutputStream::SetVolume(double volume) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  double volume_override = 0;
+  if (audio_manager_->HasOutputVolumeOverride(&volume_override))
+    volume = volume_override;
+
+  if (volume < 0.0 || volume > 1.0)
+    return;
+
+  base::AutoLock al(lock_);
+  volume_ = volume;
+}
+
+void AAudioOutputStream::GetVolume(double* volume) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  base::AutoLock al(lock_);
+  *volume = volume_;
+}
+
+void AAudioOutputStream::SetMute(bool muted) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  base::AutoLock al(lock_);
+  muted_ = muted;
+}
+}  // namespace media
\ No newline at end of file
diff --git a/media/audio/android/aaudio_output.h b/media/audio/android/aaudio_output.h
new file mode 100644
index 0000000..c4a81208
--- /dev/null
+++ b/media/audio/android/aaudio_output.h
@@ -0,0 +1,76 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ANDROID_AAUDIO_OUTPUT_H_
+#define MEDIA_AUDIO_ANDROID_AAUDIO_OUTPUT_H_
+
+#include <aaudio/AAudio.h>
+
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/android/muteable_audio_output_stream.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AudioManagerAndroid;
+
+class AAudioOutputStream : public MuteableAudioOutputStream {
+ public:
+  AAudioOutputStream(AudioManagerAndroid* manager,
+                     const AudioParameters& params,
+                     aaudio_usage_t usage);
+  ~AAudioOutputStream() override;
+
+  // Implementation of MuteableAudioOutputStream.
+  bool Open() override;
+  void Close() override;
+  void Start(AudioSourceCallback* callback) override;
+  void Stop() override;
+  void Flush() override;
+  void SetVolume(double volume) override;
+  void GetVolume(double* volume) override;
+  void SetMute(bool muted) override;
+
+  // Public callbacks.
+  aaudio_data_callback_result_t OnAudioDataRequested(void* audio_data,
+                                                     int32_t num_frames);
+  void OnStreamError(aaudio_result_t error);
+
+ private:
+  // Returns the amount of unplayed audio relative to |delay_timestamp|. See the
+  // definition for AudioOutputStream::AudioSourceCallback::OnMoreData() for
+  // more information on these terms.
+  base::TimeDelta GetDelay(base::TimeTicks delay_timestamp);
+
+  THREAD_CHECKER(thread_checker_);
+
+  AudioManagerAndroid* const audio_manager_;
+  const AudioParameters params_;
+
+  aaudio_usage_t usage_;
+  aaudio_performance_mode_t performance_mode_;
+
+  // Constant used for calculating latency. Amount of nanoseconds per frame.
+  const double ns_per_frame_;
+
+  std::unique_ptr<AudioBus> audio_bus_;
+
+  AAudioStream* aaudio_stream_ = nullptr;
+
+  // Lock protects all members below which may be read concurrently from the
+  // audio manager thread and the OS provided audio thread.
+  base::Lock lock_;
+
+  AudioSourceCallback* callback_ GUARDED_BY(lock_) = nullptr;
+  bool muted_ GUARDED_BY(lock_) = false;
+  double volume_ GUARDED_BY(lock_) = 1.0;
+
+  DISALLOW_COPY_AND_ASSIGN(AAudioOutputStream);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ANDROID_AAUDIO_OUTPUT_H_
\ No newline at end of file
diff --git a/media/audio/android/aaudio_stub_header.fragment b/media/audio/android/aaudio_stub_header.fragment
new file mode 100644
index 0000000..01fce79
--- /dev/null
+++ b/media/audio/android/aaudio_stub_header.fragment
@@ -0,0 +1,8 @@
+// The extra include header needed in the generated stub file for defining
+// various AAudio types.
+
+extern "C" {
+
+#include <aaudio/AAudio.h>
+
+}
diff --git a/media/audio/android/audio_manager_android.cc b/media/audio/android/audio_manager_android.cc
index 5d74f7e6..f96d9f8dc7 100644
--- a/media/audio/android/audio_manager_android.cc
+++ b/media/audio/android/audio_manager_android.cc
@@ -11,12 +11,16 @@
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/bind.h"
+#include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
+#include "media/audio/android/aaudio_output.h"
+#include "media/audio/android/aaudio_stubs.h"
 #include "media/audio/android/audio_track_output_stream.h"
 #include "media/audio/android/opensles_input.h"
 #include "media/audio/android/opensles_output.h"
 #include "media/audio/audio_device_description.h"
+#include "media/audio/audio_features.h"
 #include "media/audio/audio_manager.h"
 #include "media/audio/fake_audio_input_stream.h"
 #include "media/base/android/media_jni_headers/AudioManagerAndroid_jni.h"
@@ -31,6 +35,13 @@
 using base::android::JavaRef;
 using base::android::ScopedJavaLocalRef;
 
+using media_audio_android::InitializeStubs;
+using media_audio_android::kModuleAaudio;
+using media_audio_android::StubPathMap;
+
+static const base::FilePath::CharType kAaudioLib[] =
+    FILE_PATH_LITERAL("libaaudio.so");
+
 namespace media {
 namespace {
 
@@ -47,6 +58,18 @@
 
 }  // namespace
 
+static bool InitAAudio() {
+  StubPathMap paths;
+
+  // Check if the AAudio library is available.
+  paths[kModuleAaudio].push_back(kAaudioLib);
+  if (!InitializeStubs(paths)) {
+    VLOG(1) << "Failed on loading the AAudio library and symbols";
+    return false;
+  }
+  return true;
+}
+
 std::unique_ptr<AudioManager> CreateAudioManager(
     std::unique_ptr<AudioThread> audio_thread,
     AudioLogFactory* audio_log_factory) {
@@ -225,6 +248,10 @@
     const LogCallback& log_callback) {
   DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
   DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+
+  if (UseAAudio())
+    return new AAudioOutputStream(this, params, AAUDIO_USAGE_MEDIA);
+
   return new OpenSLESOutputStream(this, params, SL_ANDROID_STREAM_MEDIA);
 }
 
@@ -235,10 +262,18 @@
   DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
   DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
 
+  if (UseAAudio()) {
+    const aaudio_usage_t usage = communication_mode_is_on_
+                                     ? AAUDIO_USAGE_VOICE_COMMUNICATION
+                                     : AAUDIO_USAGE_MEDIA;
+    return new AAudioOutputStream(this, params, usage);
+  }
+
   // Set stream type which matches the current system-wide audio mode used by
   // the Android audio manager.
-  const SLint32 stream_type = communication_mode_is_on_ ?
-      SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA;
+  const SLint32 stream_type = communication_mode_is_on_
+                                  ? SL_ANDROID_STREAM_VOICE
+                                  : SL_ANDROID_STREAM_MEDIA;
   return new OpenSLESOutputStream(this, params, stream_type);
 }
 
@@ -443,10 +478,26 @@
   output_volume_override_ = volume;
 
   DCHECK(GetTaskRunner()->BelongsToCurrentThread());
-  for (OutputStreams::iterator it = streams_.begin();
-       it != streams_.end(); ++it) {
+  for (OutputStreams::iterator it = streams_.begin(); it != streams_.end();
+       ++it) {
     (*it)->SetVolume(volume);
   }
 }
 
+bool AudioManagerAndroid::UseAAudio() {
+  if (!base::FeatureList::IsEnabled(features::kUseAAudioDriver))
+    return false;
+
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_P) {
+    // We need APIs that weren't added until API Level 28.
+    return false;
+  }
+
+  if (!is_aaudio_available_.has_value())
+    is_aaudio_available_ = InitAAudio();
+
+  return is_aaudio_available_.value();
+}
+
 }  // namespace media
diff --git a/media/audio/android/audio_manager_android.h b/media/audio/android/audio_manager_android.h
index df27f8f..cae84442 100644
--- a/media/audio/android/audio_manager_android.h
+++ b/media/audio/android/audio_manager_android.h
@@ -107,6 +107,9 @@
   void DoSetMuteOnAudioThread(bool muted);
   void DoSetVolumeOnAudioThread(double volume);
 
+  // Returns whether or not we can and should use AAudio.
+  bool UseAAudio();
+
   // Java AudioManager instance.
   base::android::ScopedJavaGlobalRef<jobject> j_audio_manager_;
 
@@ -117,6 +120,8 @@
   // input stream is destroyed. Also affects the stream type of output streams.
   bool communication_mode_is_on_;
 
+  base::Optional<bool> is_aaudio_available_;
+
   // If set, overrides volume level on output streams
   bool output_volume_override_set_;
   double output_volume_override_;
diff --git a/media/audio/audio_features.cc b/media/audio/audio_features.cc
index ad7a952f..77ccae02 100644
--- a/media/audio/audio_features.cc
+++ b/media/audio/audio_features.cc
@@ -23,6 +23,13 @@
 const base::Feature kDumpOnAudioServiceHang{"DumpOnAudioServiceHang",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+#if defined(OS_ANDROID)
+// Enables loading and using AAudio instead of OpenSLES on compatible devices,
+// for audio output streams.
+const base::Feature kUseAAudioDriver{"UseAAudioDriver",
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 #if defined(OS_CHROMEOS)
 const base::Feature kCrOSSystemAEC{"CrOSSystemAEC",
                                    base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/media/audio/audio_features.h b/media/audio/audio_features.h
index b219d86..99e29151 100644
--- a/media/audio/audio_features.h
+++ b/media/audio/audio_features.h
@@ -14,6 +14,10 @@
 MEDIA_EXPORT extern const base::Feature kAudioServiceOutOfProcessKillAtHang;
 MEDIA_EXPORT extern const base::Feature kDumpOnAudioServiceHang;
 
+#if defined(OS_ANDROID)
+MEDIA_EXPORT extern const base::Feature kUseAAudioDriver;
+#endif
+
 #if defined(OS_CHROMEOS)
 MEDIA_EXPORT extern const base::Feature kCrOSSystemAEC;
 MEDIA_EXPORT extern const base::Feature kCrOSSystemAECDeactivatedGroups;
diff --git a/media/audio/audio_output_unittest.cc b/media/audio/audio_output_unittest.cc
index dd67725..f1c544a1 100644
--- a/media/audio/audio_output_unittest.cc
+++ b/media/audio/audio_output_unittest.cc
@@ -9,12 +9,14 @@
 #include "base/memory/aligned_memory.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "media/audio/audio_device_info_accessor_for_tests.h"
+#include "media/audio/audio_features.h"
 #include "media/audio/audio_io.h"
 #include "media/audio/audio_manager.h"
 #include "media/audio/audio_unittest_util.h"
@@ -25,13 +27,18 @@
 
 namespace media {
 
-class AudioOutputTest : public ::testing::Test {
+class AudioOutputTest : public testing::TestWithParam<bool> {
  public:
   AudioOutputTest() {
     audio_manager_ =
         AudioManager::CreateForTesting(std::make_unique<TestAudioThread>());
     audio_manager_device_info_ =
         std::make_unique<AudioDeviceInfoAccessorForTests>(audio_manager_.get());
+#if defined(OS_ANDROID)
+    // The only parameter is used to enable/disable AAudio.
+    if (GetParam())
+      features_.InitAndEnableFeature(features::kUseAAudioDriver);
+#endif
     base::RunLoop().RunUntilIdle();
   }
   ~AudioOutputTest() override {
@@ -62,17 +69,20 @@
   std::unique_ptr<AudioDeviceInfoAccessorForTests> audio_manager_device_info_;
   AudioParameters stream_params_;
   AudioOutputStream* stream_ = nullptr;
+#if defined(OS_ANDROID)
+  base::test::ScopedFeatureList features_;
+#endif
 };
 
 // Test that can it be created and closed.
-TEST_F(AudioOutputTest, GetAndClose) {
+TEST_P(AudioOutputTest, GetAndClose) {
   ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
   CreateWithDefaultParameters();
   ASSERT_TRUE(stream_);
 }
 
 // Test that it can be opened and closed.
-TEST_F(AudioOutputTest, OpenAndClose) {
+TEST_P(AudioOutputTest, OpenAndClose) {
   ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
 
   CreateWithDefaultParameters();
@@ -81,7 +91,7 @@
 }
 
 // Verify that Stop() can be called before Start().
-TEST_F(AudioOutputTest, StopBeforeStart) {
+TEST_P(AudioOutputTest, StopBeforeStart) {
   ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
   CreateWithDefaultParameters();
   EXPECT_TRUE(stream_->Open());
@@ -89,7 +99,7 @@
 }
 
 // Verify that Stop() can be called more than once.
-TEST_F(AudioOutputTest, StopTwice) {
+TEST_P(AudioOutputTest, StopTwice) {
   ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
   CreateWithDefaultParameters();
   EXPECT_TRUE(stream_->Open());
@@ -101,7 +111,7 @@
 }
 
 // This test produces actual audio for .25 seconds on the default device.
-TEST_F(AudioOutputTest, Play200HzTone) {
+TEST_P(AudioOutputTest, Play200HzTone) {
   ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
 
   stream_params_ =
@@ -139,7 +149,7 @@
 }
 
 // Test that SetVolume() and GetVolume() work as expected.
-TEST_F(AudioOutputTest, VolumeControl) {
+TEST_P(AudioOutputTest, VolumeControl) {
   ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
 
   CreateWithDefaultParameters();
@@ -159,4 +169,14 @@
   stream_->Stop();
 }
 
+// The test parameter is only relevant on Android. It controls whether or not we
+// allow the use of AAudio.
+INSTANTIATE_TEST_SUITE_P(Base, AudioOutputTest, testing::Values(false));
+
+#if defined(OS_ANDROID)
+// Run tests with AAudio enabled. On Android O and below, this is the same as
+// the Base test above, as we only use AAudio on P+.
+INSTANTIATE_TEST_SUITE_P(AAudio, AudioOutputTest, testing::Values(true));
+#endif
+
 }  // namespace media
diff --git a/media/base/eme_constants.h b/media/base/eme_constants.h
index cb9a850..ba9387f 100644
--- a/media/base/eme_constants.h
+++ b/media/base/eme_constants.h
@@ -52,12 +52,12 @@
   SupportedCodecs codecs = EME_CODEC_FLAC;
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
   codecs |= EME_CODEC_AAC;
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
   codecs |= EME_CODEC_AC3 | EME_CODEC_EAC3;
-#endif  // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
-#if BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
+#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
   codecs |= EME_CODEC_MPEG_H_AUDIO;
-#endif  // BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
   return codecs;
 }
@@ -72,12 +72,12 @@
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
   codecs |= EME_CODEC_HEVC;
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
   codecs |= EME_CODEC_DOLBY_VISION_AVC;
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
   codecs |= EME_CODEC_DOLBY_VISION_HEVC;
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
-#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
   return codecs;
 }
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 5b4cf31..7ca9a46 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -485,6 +485,11 @@
 const base::Feature kUseAudioLatencyFromHAL{"UseAudioLatencyFromHAL",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable pooling of SharedImageVideo objects for use by MCVD, to save a hop to
+// the GPU main thread during VideoFrame construction.
+const base::Feature kUsePooledSharedImageVideoProvider{
+    "UsePooledSharedImageVideoProvider", base::FEATURE_DISABLED_BY_DEFAULT};
+
 #endif  // defined(OS_ANDROID)
 
 #if defined(OS_WIN)
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 965c584..8df20f29 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -166,6 +166,7 @@
 MEDIA_EXPORT extern const base::Feature kPictureInPictureAPI;
 MEDIA_EXPORT extern const base::Feature kHlsPlayer;
 MEDIA_EXPORT extern const base::Feature kUseAudioLatencyFromHAL;
+MEDIA_EXPORT extern const base::Feature kUsePooledSharedImageVideoProvider;
 #endif  // defined(OS_ANDROID)
 
 #if defined(OS_WIN)
diff --git a/media/base/mime_util_internal.cc b/media/base/mime_util_internal.cc
index c2bdd43..8331cef 100644
--- a/media/base/mime_util_internal.cc
+++ b/media/base/mime_util_internal.cc
@@ -320,23 +320,23 @@
   CodecSet avc_and_aac(aac);
   avc_and_aac.emplace(H264);
 
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
   mp4_audio_codecs.emplace(AC3);
   mp4_audio_codecs.emplace(EAC3);
-#endif  // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
 
-#if BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
   mp4_audio_codecs.emplace(MPEG_H_AUDIO);
-#endif  // BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
 
   mp4_video_codecs.emplace(H264);
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
   mp4_video_codecs.emplace(HEVC);
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
 
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
   mp4_video_codecs.emplace(DOLBY_VISION);
-#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 #if BUILDFLAG(ENABLE_AV1_DECODER)
   mp4_video_codecs.emplace(AV1);
@@ -661,7 +661,7 @@
 
     case AC3:
     case EAC3:
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
       return true;
 #else
       return false;
@@ -831,14 +831,14 @@
   }
 #endif
 
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
   if (ParseDolbyVisionCodecId(codec_id, out_profile, out_level)) {
     out_result->codec = MimeUtil::DOLBY_VISION;
     return true;
   }
 #endif
 
-#if BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
   if (base::StartsWith(codec_id, "mhm1.", base::CompareCase::SENSITIVE) ||
       base::StartsWith(codec_id, "mha1.", base::CompareCase::SENSITIVE)) {
     out_result->codec = MimeUtil::MPEG_H_AUDIO;
diff --git a/media/base/mime_util_unittest.cc b/media/base/mime_util_unittest.cc
index f13914c..3e785a11 100644
--- a/media/base/mime_util_unittest.cc
+++ b/media/base/mime_util_unittest.cc
@@ -130,7 +130,7 @@
 }
 
 static bool HasEac3Support() {
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
   return true;
 #else
   return false;
diff --git a/media/base/video_codecs.cc b/media/base/video_codecs.cc
index 581feed..4c04f29f 100644
--- a/media/base/video_codecs.cc
+++ b/media/base/video_codecs.cc
@@ -715,7 +715,7 @@
 }
 #endif
 
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 bool IsDolbyVisionAVCCodecId(const std::string& codec_id) {
   return base::StartsWith(codec_id, "dva1.", base::CompareCase::SENSITIVE) ||
          base::StartsWith(codec_id, "dvav.", base::CompareCase::SENSITIVE);
@@ -854,7 +854,7 @@
   if (ParseHEVCCodecId(codec_id, &profile, &level))
     return kCodecHEVC;
 #endif
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
   if (ParseDolbyVisionCodecId(codec_id, &profile, &level))
     return kCodecDolbyVision;
 #endif
diff --git a/media/base/video_codecs.h b/media/base/video_codecs.h
index d344a923..bf7e4bc 100644
--- a/media/base/video_codecs.h
+++ b/media/base/video_codecs.h
@@ -138,7 +138,7 @@
                                    uint8_t* level_idc);
 #endif
 
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 MEDIA_EXPORT bool ParseDolbyVisionCodecId(const std::string& codec_id,
                                           VideoCodecProfile* profile,
                                           uint8_t* level_id);
diff --git a/media/base/video_codecs_unittest.cc b/media/base/video_codecs_unittest.cc
index 162791c9..c6d9484e 100644
--- a/media/base/video_codecs_unittest.cc
+++ b/media/base/video_codecs_unittest.cc
@@ -582,7 +582,7 @@
 }
 #endif
 
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 TEST(ParseDolbyVisionCodecIdTest, InvalidDolbyVisionCodecIds) {
   VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
   uint8_t level_id = 0;
diff --git a/media/cdm/aes_decryptor.cc b/media/cdm/aes_decryptor.cc
index ff30013..176050a 100644
--- a/media/cdm/aes_decryptor.cc
+++ b/media/cdm/aes_decryptor.cc
@@ -174,6 +174,7 @@
       session_closed_cb_(session_closed_cb),
       session_keys_change_cb_(session_keys_change_cb),
       session_expiration_update_cb_(session_expiration_update_cb) {
+  DVLOG(1) << __func__;
   // AesDecryptor doesn't keep any persistent data, so no need to do anything
   // with |security_origin|.
   DCHECK(session_message_cb_);
@@ -182,6 +183,7 @@
 }
 
 AesDecryptor::~AesDecryptor() {
+  DVLOG(1) << __func__;
   key_map_.clear();
 }
 
diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc
index f4622e1..72dc400 100644
--- a/media/ffmpeg/ffmpeg_common.cc
+++ b/media/ffmpeg/ffmpeg_common.cc
@@ -80,7 +80,7 @@
   switch (codec_id) {
     case AV_CODEC_ID_AAC:
       return kCodecAAC;
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
     case AV_CODEC_ID_AC3:
       return kCodecAC3;
     case AV_CODEC_ID_EAC3:
@@ -341,7 +341,7 @@
     // not fill |sample_fmt|.
     case kCodecAC3:
     case kCodecEAC3:
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
       // The spec for AC3/EAC3 audio is ETSI TS 102 366. According to sections
       // F.3.1 and F.5.1 in that spec the sample_format for AC3/EAC3 must be 16.
       sample_format = kSampleFormatS16;
@@ -382,7 +382,7 @@
   if (channel_layout == CHANNEL_LAYOUT_DISCRETE)
     config->SetChannelsForDiscrete(codec_context->channels);
 
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
   // These are bitstream formats unknown to ffmpeg, so they don't have
   // a known sample format size.
   if (codec == kCodecAC3 || codec == kCodecEAC3)
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index 68618dd..51e98a3 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -1384,7 +1384,7 @@
 
 TEST_F(FFmpegDemuxerTest, Read_AC3_Audio) {
   CreateDemuxer("bear-ac3-only-frag.mp4");
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
   // AC3 is not supported by default media platform. Embedders who add support
   // must declare it via MediaClient.
   MockMediaClient media_client;
@@ -1414,7 +1414,7 @@
 
 TEST_F(FFmpegDemuxerTest, Read_EAC3_Audio) {
   CreateDemuxer("bear-eac3-only-frag.mp4");
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
   // EAC3 is not supported by default media platform. Embedders who add support
   // must declare it via MediaClient.
   MockMediaClient media_client;
diff --git a/media/filters/stream_parser_factory.cc b/media/filters/stream_parser_factory.cc
index ff86540..0aaca79 100644
--- a/media/filters/stream_parser_factory.cc
+++ b/media/filters/stream_parser_factory.cc
@@ -169,7 +169,7 @@
 static const CodecInfo kHEVCHVC1CodecInfo = {
     "hvc1.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_HEVC};
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 static const CodecInfo kDolbyVisionAVCCodecInfo1 = {
     "dva1.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_DOLBYVISION};
 static const CodecInfo kDolbyVisionAVCCodecInfo2 = {
@@ -180,14 +180,14 @@
 static const CodecInfo kDolbyVisionHEVCCodecInfo2 = {
     "dvhe.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_DOLBYVISION};
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
-#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 static const CodecInfo kMPEG4AACCodecInfo = {"mp4a.40.*", CodecInfo::AUDIO,
                                              &ValidateMP4ACodecID,
                                              CodecInfo::HISTOGRAM_MPEG4AAC};
 static const CodecInfo kMPEG2AACLCCodecInfo = {
     "mp4a.67", CodecInfo::AUDIO, nullptr, CodecInfo::HISTOGRAM_MPEG2AAC};
 
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
 // The 'ac-3' and 'ec-3' are mime codec ids for AC3 and EAC3 according to
 // http://www.mp4ra.org/codecs.html
 // The object types for AC3 and EAC3 in MP4 container are 0xa5 and 0xa6, so
@@ -206,9 +206,9 @@
                                           CodecInfo::HISTOGRAM_EAC3};
 static const CodecInfo kEAC3CodecInfo3 = {"mp4a.A6", CodecInfo::AUDIO, nullptr,
                                           CodecInfo::HISTOGRAM_EAC3};
-#endif  // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
 
-#if BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
 static const CodecInfo kMpegHAudioCodecInfo1 = {
     "mhm1.*", CodecInfo::AUDIO, nullptr, CodecInfo::HISTOGRAM_MPEG_H_AUDIO};
 static const CodecInfo kMpegHAudioCodecInfo2 = {
@@ -241,7 +241,7 @@
                                                    &kHEVCHEV1CodecInfo,
                                                    &kHEVCHVC1CodecInfo,
 #endif
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
                                                    &kDolbyVisionAVCCodecInfo1,
                                                    &kDolbyVisionAVCCodecInfo2,
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
@@ -251,7 +251,7 @@
 #endif
                                                    &kMPEG4AACCodecInfo,
                                                    &kMPEG2AACLCCodecInfo,
-#if BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
                                                    &kMpegHAudioCodecInfo1,
                                                    &kMpegHAudioCodecInfo2,
 #endif
@@ -266,18 +266,18 @@
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
                                                    &kMPEG4AACCodecInfo,
                                                    &kMPEG2AACLCCodecInfo,
-#if BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
                                                    &kMpegHAudioCodecInfo1,
                                                    &kMpegHAudioCodecInfo2,
 #endif
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
                                                    &kAC3CodecInfo1,
                                                    &kAC3CodecInfo2,
                                                    &kAC3CodecInfo3,
                                                    &kEAC3CodecInfo1,
                                                    &kEAC3CodecInfo2,
                                                    &kEAC3CodecInfo3,
-#endif  // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
                                                    nullptr};
 
@@ -312,7 +312,7 @@
         has_sbr = true;
         break;
       }
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
     } else if (base::MatchPattern(codec_id, kAC3CodecInfo1.pattern) ||
                base::MatchPattern(codec_id, kAC3CodecInfo2.pattern) ||
                base::MatchPattern(codec_id, kAC3CodecInfo3.pattern)) {
@@ -321,7 +321,7 @@
                base::MatchPattern(codec_id, kEAC3CodecInfo2.pattern) ||
                base::MatchPattern(codec_id, kEAC3CodecInfo3.pattern)) {
       audio_object_types.insert(mp4::kEAC3);
-#endif  // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
     }
   }
diff --git a/media/formats/BUILD.gn b/media/formats/BUILD.gn
index 9c54c30..7b2a260 100644
--- a/media/formats/BUILD.gn
+++ b/media/formats/BUILD.gn
@@ -107,7 +107,7 @@
     ]
   }
 
-  if (proprietary_codecs && enable_dolby_vision_demuxing) {
+  if (proprietary_codecs && enable_platform_dolby_vision) {
     sources += [
       "mp4/dolby_vision.cc",
       "mp4/dolby_vision.h",
@@ -255,7 +255,7 @@
       ]
     }
 
-    if (enable_dolby_vision_demuxing) {
+    if (enable_platform_dolby_vision) {
       sources += [ "mp4/dolby_vision_unittest.cc" ]
     }
 
diff --git a/media/formats/mp4/box_definitions.cc b/media/formats/mp4/box_definitions.cc
index ebef0a9..84e5c36 100644
--- a/media/formats/mp4/box_definitions.cc
+++ b/media/formats/mp4/box_definitions.cc
@@ -29,10 +29,10 @@
 #include "media/formats/mp4/avc.h"
 #include "media/video/h264_parser.h"  // nogncheck
 
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 #include "base/optional.h"
 #include "media/formats/mp4/dolby_vision.h"
-#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
 #include "media/formats/mp4/hevc.h"
@@ -47,7 +47,7 @@
 const size_t kKeyIdSize = 16;
 const size_t kFlacMetadataBlockStreaminfoSize = 34;
 
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 // Parse dvcC or dvvC box.
 base::Optional<DOVIDecoderConfigurationRecord> ParseDOVIConfig(
     BoxReader* reader) {
@@ -69,7 +69,7 @@
 
   return base::nullopt;
 }
-#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 
 }  // namespace
 
@@ -863,7 +863,7 @@
 
       frame_bitstream_converter =
           base::MakeRefCounted<AVCBitstreamConverter>(std::move(avcConfig));
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
       // It can be Dolby Vision stream if there is DVCC box.
       auto dv_config = ParseDOVIConfig(reader);
       if (dv_config.has_value()) {
@@ -871,7 +871,7 @@
         video_codec = kCodecDolbyVision;
         video_codec_profile = dv_config->codec_profile;
       }
-#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
       break;
     }
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
@@ -885,7 +885,7 @@
       video_codec_profile = hevcConfig->GetVideoProfile();
       frame_bitstream_converter =
           base::MakeRefCounted<HEVCBitstreamConverter>(std::move(hevcConfig));
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
       // It can be Dolby Vision stream if there is DVCC box.
       auto dv_config = ParseDOVIConfig(reader);
       if (dv_config.has_value()) {
@@ -893,11 +893,11 @@
         video_codec = kCodecDolbyVision;
         video_codec_profile = dv_config->codec_profile;
       }
-#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
       break;
     }
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
     case FOURCC_DVA1:
     case FOURCC_DVAV: {
       DVLOG(2) << __func__ << " reading AVCDecoderConfigurationRecord (avcC)";
@@ -931,7 +931,7 @@
       break;
     }
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
-#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
     case FOURCC_VP09: {
       DVLOG(2) << __func__ << " parsing VPCodecConfigurationRecord (vpcC)";
@@ -980,15 +980,15 @@
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
     case FOURCC_HEV1:
     case FOURCC_HVC1:
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
     case FOURCC_DVH1:
     case FOURCC_DVHE:
-#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
     case FOURCC_DVA1:
     case FOURCC_DVAV:
-#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
     case FOURCC_VP09:
       return true;
diff --git a/media/formats/mp4/fourccs.h b/media/formats/mp4/fourccs.h
index fee277e..19924d3 100644
--- a/media/formats/mp4/fourccs.h
+++ b/media/formats/mp4/fourccs.h
@@ -14,7 +14,7 @@
 
 enum FourCC {
   FOURCC_NULL = 0,
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
   FOURCC_AC3 = 0x61632d33,   // "ac-3"
   FOURCC_EAC3 = 0x65632d33,  // "ec-3"
 #endif
@@ -33,7 +33,7 @@
   FOURCC_DFLA = 0x64664c61,  // "dfLa"
   FOURCC_DINF = 0x64696e66,
   FOURCC_DOPS = 0x644f7073,  // "dOps"
-#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
   FOURCC_DVA1 = 0x64766131,
   FOURCC_DVAV = 0x64766176,
   FOURCC_DVCC = 0x64766343,
@@ -70,7 +70,7 @@
   FOURCC_META = 0x6d657461,
   FOURCC_MFHD = 0x6d666864,
   FOURCC_MFRA = 0x6d667261,
-#if BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
   FOURCC_MHM1 = 0x6d686d31,
   FOURCC_MHA1 = 0x6d686131,
 #endif
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index a16162f..915551d2 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -329,10 +329,10 @@
                                 : entry.format;
 
       if (audio_format != FOURCC_OPUS && audio_format != FOURCC_FLAC &&
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
           audio_format != FOURCC_AC3 && audio_format != FOURCC_EAC3 &&
 #endif
-#if BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
           audio_format != FOURCC_MHM1 && audio_format != FOURCC_MHA1 &&
 #endif
           audio_format != FOURCC_MP4A) {
@@ -370,7 +370,7 @@
         sample_per_second = entry.samplerate;
         extra_data = entry.dfla.stream_info;
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-#if BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
       } else if (audio_format == FOURCC_MHM1 || audio_format == FOURCC_MHA1) {
         codec = kCodecMpegHAudio;
         channel_layout = CHANNEL_LAYOUT_BITSTREAM;
@@ -379,7 +379,7 @@
 #endif
       } else {
         uint8_t audio_type = entry.esds.object_type;
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
         if (audio_type == kForbidden) {
           if (audio_format == FOURCC_AC3)
             audio_type = kAC3;
@@ -406,7 +406,7 @@
 #if defined(OS_ANDROID)
           extra_data = aac.codec_specific_data();
 #endif
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
         } else if (audio_type == kAC3) {
           codec = kCodecAC3;
           channel_layout = GuessChannelLayout(entry.channelcount);
diff --git a/media/formats/mp4/mp4_stream_parser_unittest.cc b/media/formats/mp4/mp4_stream_parser_unittest.cc
index 43919914..134d485 100644
--- a/media/formats/mp4/mp4_stream_parser_unittest.cc
+++ b/media/formats/mp4/mp4_stream_parser_unittest.cc
@@ -594,7 +594,7 @@
   audio_object_types.insert(kAC3);
   parser_.reset(new MP4StreamParser(audio_object_types, false, false));
 
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
   bool expect_success = true;
 #else
   bool expect_success = false;
@@ -618,7 +618,7 @@
   audio_object_types.insert(kEAC3);
   parser_.reset(new MP4StreamParser(audio_object_types, false, false));
 
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
   bool expect_success = true;
 #else
   bool expect_success = false;
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index c290ec2..041f6054 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -123,6 +123,8 @@
       "android/maybe_render_early_manager.h",
       "android/media_codec_video_decoder.cc",
       "android/media_codec_video_decoder.h",
+      "android/pooled_shared_image_video_provider.cc",
+      "android/pooled_shared_image_video_provider.h",
       "android/promotion_hint_aggregator.h",
       "android/promotion_hint_aggregator_impl.cc",
       "android/promotion_hint_aggregator_impl.h",
@@ -480,6 +482,9 @@
       "android/mock_device_info.h",
       "android/mock_promotion_hint_aggregator.cc",
       "android/mock_promotion_hint_aggregator.h",
+      "android/mock_shared_image_video_provider.cc",
+      "android/mock_shared_image_video_provider.h",
+      "android/pooled_shared_image_video_provider_unittest.cc",
       "android/promotion_hint_aggregator_impl_unittest.cc",
       "android/surface_chooser_helper_unittest.cc",
       "android/video_frame_factory_impl_unittest.cc",
diff --git a/media/gpu/android/codec_image.cc b/media/gpu/android/codec_image.cc
index 8c4dd49..eaf4173 100644
--- a/media/gpu/android/codec_image.cc
+++ b/media/gpu/android/codec_image.cc
@@ -44,8 +44,7 @@
 CodecImage::CodecImage() = default;
 
 CodecImage::~CodecImage() {
-  for (auto& cb : unused_cbs_)
-    std::move(cb).Run(this);
+  NotifyUnused();
 }
 
 void CodecImage::Initialize(
@@ -63,6 +62,19 @@
   unused_cbs_.push_back(std::move(unused_cb));
 }
 
+void CodecImage::NotifyUnused() {
+  // If we haven't done so yet, release the codec output buffer.  Also drop
+  // our reference to the TextureOwner (if any).  In other words, undo anything
+  // that we did in Initialize.
+  ReleaseCodecBuffer();
+  codec_buffer_wait_coordinator_.reset();
+  promotion_hint_cb_ = PromotionHintAggregator::NotifyPromotionHintCB();
+
+  for (auto& cb : unused_cbs_)
+    std::move(cb).Run(this);
+  unused_cbs_.clear();
+}
+
 gfx::Size CodecImage::GetSize() {
   // Return a nonzero size, to avoid GL errors, even if we dropped the codec
   // buffer already.  Note that if we dropped it, there's no data in the
@@ -329,6 +341,10 @@
   return codec_buffer_wait_coordinator_->texture_owner()->GetAHardwareBuffer();
 }
 
+bool CodecImage::HasMutableState() const {
+  return false;
+}
+
 CodecImageHolder::CodecImageHolder(
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     scoped_refptr<CodecImage> codec_image)
diff --git a/media/gpu/android/codec_image.h b/media/gpu/android/codec_image.h
index fb8426c..0b13cb36 100644
--- a/media/gpu/android/codec_image.h
+++ b/media/gpu/android/codec_image.h
@@ -98,6 +98,15 @@
                            int display_y,
                            int display_width,
                            int display_height) override;
+  // If we re-use one CodecImage with different output buffers, then we must
+  // not claim to have mutable state.  Otherwise, CopyTexImage is only called
+  // once.  For pooled shared images, this must return false.  For single-use
+  // images, it works either way.
+  bool HasMutableState() const override;
+
+  // Notify us that we're no longer in-use for display, and may be pointed at
+  // another output buffer via a call to Initialize.
+  void NotifyUnused();
 
   // gpu::StreamTextureSharedImageInterface implementation.
   void ReleaseResources() override;
diff --git a/media/gpu/android/codec_image_unittest.cc b/media/gpu/android/codec_image_unittest.cc
index bbf8bf60..e545235f 100644
--- a/media/gpu/android/codec_image_unittest.cc
+++ b/media/gpu/android/codec_image_unittest.cc
@@ -82,7 +82,7 @@
   enum ImageKind { kOverlay, kTextureOwner };
   scoped_refptr<CodecImage> NewImage(
       ImageKind kind,
-      CodecImage::UnusedCB now_unused_cb = base::DoNothing()) {
+      CodecImage::UnusedCB unused_cb = base::DoNothing()) {
     std::unique_ptr<CodecOutputBuffer> buffer;
     wrapper_->DequeueOutputBuffer(nullptr, nullptr, &buffer);
     scoped_refptr<CodecImage> image = new CodecImage();
@@ -92,7 +92,7 @@
         base::BindRepeating(&PromotionHintReceiver::OnPromotionHint,
                             base::Unretained(&promotion_hint_receiver_)));
 
-    image->AddUnusedCB(std::move(now_unused_cb));
+    image->AddUnusedCB(std::move(unused_cb));
     return image;
   }
 
@@ -133,6 +133,26 @@
   i = nullptr;
 }
 
+TEST_F(CodecImageTest, UnusedCBRunsOnNotifyUnused) {
+  base::MockCallback<CodecImage::UnusedCB> cb_1;
+  base::MockCallback<CodecImage::UnusedCB> cb_2;
+  auto i = NewImage(kTextureOwner);
+  ASSERT_TRUE(i->get_codec_output_buffer_for_testing());
+  ASSERT_TRUE(i->is_texture_owner_backed());
+  i->AddUnusedCB(cb_1.Get());
+  i->AddUnusedCB(cb_2.Get());
+  EXPECT_CALL(cb_1, Run(i.get()));
+  EXPECT_CALL(cb_2, Run(i.get()));
+
+  // Also verify that the output buffer and texture owner are released.
+  i->NotifyUnused();
+  EXPECT_FALSE(i->get_codec_output_buffer_for_testing());
+  EXPECT_FALSE(i->is_texture_owner_backed());
+
+  // Verify that an additional call doesn't crash.  It should do nothing.
+  i->NotifyUnused();
+}
+
 TEST_F(CodecImageTest, ImageStartsUnrendered) {
   auto i = NewImage(kTextureOwner);
   ASSERT_FALSE(i->was_rendered_to_front_buffer());
diff --git a/media/gpu/android/media_codec_video_decoder.cc b/media/gpu/android/media_codec_video_decoder.cc
index e7f795bd..8f44b19 100644
--- a/media/gpu/android/media_codec_video_decoder.cc
+++ b/media/gpu/android/media_codec_video_decoder.cc
@@ -915,7 +915,7 @@
       CreatePromotionHintCB(),
       base::BindOnce(&MediaCodecVideoDecoder::ForwardVideoFrame,
                      weak_factory_.GetWeakPtr(), reset_generation_,
-                     std::move(async_trace)));
+                     std::move(async_trace), base::TimeTicks::Now()));
   return true;
 }
 
@@ -932,10 +932,17 @@
 void MediaCodecVideoDecoder::ForwardVideoFrame(
     int reset_generation,
     std::unique_ptr<ScopedAsyncTrace> async_trace,
+    base::TimeTicks started_at,
     scoped_refptr<VideoFrame> frame) {
   DVLOG(3) << __func__ << " : "
            << (frame ? frame->AsHumanReadableString() : "null");
 
+  // Record how long this frame was pending.
+  const base::TimeDelta duration = base::TimeTicks::Now() - started_at;
+  UMA_HISTOGRAM_CUSTOM_TIMES("Media.MCVD.ForwardVideoFrameTiming", duration,
+                             base::TimeDelta::FromMilliseconds(1),
+                             base::TimeDelta::FromMilliseconds(100), 25);
+
   // No |frame| indicates an error creating it.
   if (!frame) {
     DLOG(ERROR) << __func__ << " |frame| is null";
diff --git a/media/gpu/android/media_codec_video_decoder.h b/media/gpu/android/media_codec_video_decoder.h
index c92548cd..721bee5 100644
--- a/media/gpu/android/media_codec_video_decoder.h
+++ b/media/gpu/android/media_codec_video_decoder.h
@@ -179,9 +179,11 @@
 
   // Forwards |frame| via |output_cb_| if |reset_generation| matches
   // |reset_generation_|.  |async_trace| is the (optional) scoped trace that
-  // started when we dequeued the corresponding output buffer.
+  // started when we dequeued the corresponding output buffer.  |started_at| is
+  // the wall clock time at which we dequeued the output buffer.
   void ForwardVideoFrame(int reset_generation,
                          std::unique_ptr<ScopedAsyncTrace> async_trace,
+                         base::TimeTicks started_at,
                          scoped_refptr<VideoFrame> frame);
 
   // Starts draining the codec by queuing an EOS if required. It skips the drain
diff --git a/media/gpu/android/mock_shared_image_video_provider.cc b/media/gpu/android/mock_shared_image_video_provider.cc
new file mode 100644
index 0000000..65122e3
--- /dev/null
+++ b/media/gpu/android/mock_shared_image_video_provider.cc
@@ -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.
+
+#include "media/gpu/android/mock_shared_image_video_provider.h"
+
+namespace media {
+
+MockSharedImageVideoProvider::RequestImageArgs::RequestImageArgs(
+    ImageReadyCB cb,
+    ImageSpec spec,
+    scoped_refptr<gpu::TextureOwner> texture_owner)
+    : cb_(std::move(cb)),
+      spec_(std::move(spec)),
+      texture_owner_(std::move(texture_owner)) {}
+
+MockSharedImageVideoProvider::RequestImageArgs::~RequestImageArgs() = default;
+
+MockSharedImageVideoProvider::MockSharedImageVideoProvider() = default;
+MockSharedImageVideoProvider::~MockSharedImageVideoProvider() = default;
+
+}  // namespace media
diff --git a/media/gpu/android/mock_shared_image_video_provider.h b/media/gpu/android/mock_shared_image_video_provider.h
new file mode 100644
index 0000000..7d19831
--- /dev/null
+++ b/media/gpu/android/mock_shared_image_video_provider.h
@@ -0,0 +1,77 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_ANDROID_MOCK_SHARED_IMAGE_VIDEO_PROVIDER_H_
+#define MEDIA_GPU_ANDROID_MOCK_SHARED_IMAGE_VIDEO_PROVIDER_H_
+
+#include "media/gpu/android/pooled_shared_image_video_provider.h"
+#include "mojo/public/cpp/bindings/callback_helpers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class MockSharedImageVideoProvider : public SharedImageVideoProvider {
+ public:
+  MockSharedImageVideoProvider();
+  ~MockSharedImageVideoProvider();
+
+  void Initialize(GpuInitCB gpu_init_cb) {
+    Initialize_(gpu_init_cb);
+    gpu_init_cb_ = std::move(gpu_init_cb);
+  }
+
+  MOCK_METHOD1(Initialize_, void(GpuInitCB& gpu_init_cb));
+
+  void RequestImage(ImageReadyCB cb,
+                    const ImageSpec& spec,
+                    scoped_refptr<gpu::TextureOwner> texture_owner) override {
+    requests_.emplace_back(std::move(cb), spec, std::move(texture_owner));
+
+    MockRequestImage();
+  }
+
+  MOCK_METHOD0(MockRequestImage, void());
+
+  // Provide an image for the least recent request.  If |record| is provided,
+  // then we'll use it.  Otherwise, we'll make one up.
+  void ProvideOneRequestedImage(ImageRecord* record = nullptr) {
+    ImageRecord tmp_record;
+    if (!record) {
+      record = &tmp_record;
+      record->release_cb = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+          base::BindOnce(
+              [](int* count, const gpu::SyncToken& sync_token) { (*count)++; },
+              base::Unretained(&num_release_callbacks_)),
+          gpu::SyncToken());
+    }
+
+    auto& req = requests_.front();
+    std::move(req.cb_).Run(std::move(*record));
+    requests_.pop_front();
+  }
+
+  // Most recent arguments to Initialize.
+  GpuInitCB gpu_init_cb_;
+
+  // Most recent arguments to RequestImage.
+  struct RequestImageArgs {
+    RequestImageArgs(ImageReadyCB cb,
+                     ImageSpec spec,
+                     scoped_refptr<gpu::TextureOwner> texture_owner);
+    ~RequestImageArgs();
+    ImageReadyCB cb_;
+    ImageSpec spec_;
+    scoped_refptr<gpu::TextureOwner> texture_owner_;
+  };
+
+  std::list<RequestImageArgs> requests_;
+
+  // Number of times a release callback has been called.
+  int num_release_callbacks_ = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_ANDROID_MOCK_SHARED_IMAGE_VIDEO_PROVIDER_H_
diff --git a/media/gpu/android/pooled_shared_image_video_provider.cc b/media/gpu/android/pooled_shared_image_video_provider.cc
new file mode 100644
index 0000000..0415a15
--- /dev/null
+++ b/media/gpu/android/pooled_shared_image_video_provider.cc
@@ -0,0 +1,222 @@
+// 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 "media/gpu/android/pooled_shared_image_video_provider.h"
+
+#include "gpu/command_buffer/common/sync_token.h"
+#include "media/base/bind_to_current_loop.h"
+#include "mojo/public/cpp/bindings/callback_helpers.h"
+
+namespace media {
+
+// static
+std::unique_ptr<PooledSharedImageVideoProvider>
+PooledSharedImageVideoProvider::Create(
+    scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+    GetStubCB get_stub_cb,
+    std::unique_ptr<SharedImageVideoProvider> provider) {
+  return base::WrapUnique(new PooledSharedImageVideoProvider(
+      base::SequenceBound<GpuHelperImpl>(std::move(gpu_task_runner),
+                                         std::move(get_stub_cb)),
+      std::move(provider)));
+}
+
+PooledSharedImageVideoProvider::PooledImage::PooledImage(const ImageSpec& spec,
+                                                         ImageRecord record)
+    : spec(spec), record(std::move(record)) {}
+
+PooledSharedImageVideoProvider::PooledImage::~PooledImage() = default;
+
+PooledSharedImageVideoProvider::PendingRequest::PendingRequest(
+    const ImageSpec& spec,
+    ImageReadyCB cb)
+    : spec(spec), cb(std::move(cb)) {}
+
+PooledSharedImageVideoProvider::PendingRequest::~PendingRequest() = default;
+
+PooledSharedImageVideoProvider::PooledSharedImageVideoProvider(
+    base::SequenceBound<GpuHelper> gpu_helper,
+    std::unique_ptr<SharedImageVideoProvider> provider)
+    : provider_(std::move(provider)),
+      gpu_helper_(std::move(gpu_helper)),
+      weak_factory_(this) {}
+
+// Note that this will drop everything in |pool_|, which will call all the
+// release callbacks for the underlying byffer.
+PooledSharedImageVideoProvider::~PooledSharedImageVideoProvider() = default;
+
+// SharedImageVideoProvider
+void PooledSharedImageVideoProvider::Initialize(GpuInitCB gpu_init_cb) {
+  provider_->Initialize(std::move(gpu_init_cb));
+}
+
+void PooledSharedImageVideoProvider::RequestImage(
+    ImageReadyCB cb,
+    const ImageSpec& spec,
+    scoped_refptr<gpu::TextureOwner> texture_owner) {
+  // See if the pool matches the requested spec.
+  if (pool_spec_ != spec) {
+    // Nope -- mark any outstanding images for destruction and start a new pool.
+    // Note that this calls all the release callbacks.
+    pool_.clear();
+
+    // Any images added to the pool should match |spec|.
+    pool_spec_ = spec;
+  }
+
+  // Push this onto the pending requests.
+  // IMPORTANT BUT SUBTLE NOTE: |spec| doesn't mention the TextureOwner, but it
+  // is sent to the provider so it must also match the one that was used with
+  // |spec|.  We assume that the generation id will be updated by our caller
+  // whenever the TextureOwner changes.  While this is fragile, it's also just
+  // a temporary thing.  Keeping a strong ref to |texture_owner| would probably
+  // work, but it's not good to keep refs to those around longer than needed.
+  // It might be okay to do that directly, since the request (if any) that's
+  // pending for it would have the strong ref, so maybe we could just add it
+  // here too.
+  pending_requests_.emplace_back(spec, std::move(cb));
+
+  // Are there any free images in the pool?  If so, then pop one and use it to
+  // process the request we just pushed, assuming that it's the most recent.  We
+  // could optimize this call out if |pending_requensts_| wasn't empty before,
+  // since we know it doesn't match the pool spec if the pool's not empty.  As
+  // it is, it will just pop and re-push the pooled buffer in the (rare) case
+  // that the pool doesn't match.
+  if (!pool_.empty()) {
+    auto front = std::move(pool_.front());
+    pool_.pop_front();
+    ProcessFreePooledImage(front);
+    // TODO(liberato): See if skipping the return if |pool_| is now empty is
+    // helpful, especially during start-up.  Alternatively, just request some
+    // constant number of images (~5) when the pool spec changes, then add them
+    // one at a time if needed.
+    return;
+  }
+
+  // Request a new image, since we don't have enough.  There might be some
+  // outstanding that will be returned, but we'd like to have enough not to wait
+  // on them.  This has the nice property that everything in |pending_requests_|
+  // will have an image delivered in order for it.  Note that we might not
+  // exactly match up returned (new) images to the requests; there might be
+  // intervening returns of existing images from the client that happen to match
+  // if we switch from spec A => spec B => spec A, but that's okay.  We can be
+  // sure that there are at least as many that will arrive as we need.
+  auto ready_cb =
+      base::BindOnce(&PooledSharedImageVideoProvider::OnImageCreated,
+                     weak_factory_.GetWeakPtr(), spec);
+  provider_->RequestImage(std::move(ready_cb), spec, std::move(texture_owner));
+}
+
+void PooledSharedImageVideoProvider::OnImageCreated(ImageSpec spec,
+                                                    ImageRecord record) {
+  // Wrap |record| up for the pool, and process it.
+  scoped_refptr<PooledImage> pooled_image =
+      base::MakeRefCounted<PooledImage>(std::move(spec), std::move(record));
+  ProcessFreePooledImage(pooled_image);
+}
+
+void PooledSharedImageVideoProvider::OnImageReturned(
+    scoped_refptr<PooledImage> pooled_image,
+    const gpu::SyncToken& sync_token) {
+  // An image has been returned to us.  Wait for |sync_token| and then send it
+  // to ProcessFreePooledImage to re-use / pool / delete.
+  gpu_helper_.Post(FROM_HERE, &GpuHelper::OnImageReturned, sync_token,
+                   pooled_image->record.codec_image_holder,
+                   BindToCurrentLoop(base::BindOnce(
+                       &PooledSharedImageVideoProvider::ProcessFreePooledImage,
+                       weak_factory_.GetWeakPtr(), pooled_image)));
+}
+
+void PooledSharedImageVideoProvider::ProcessFreePooledImage(
+    scoped_refptr<PooledImage> pooled_image) {
+  // Are there any requests pending?
+  if (pending_requests_.size()) {
+    // See if |record| matches the top request.  If so, fulfill it, else drop
+    // |record| since we don't need it.  Note that it's possible to have pending
+    // requests that don't match the pool spec; the pool spec is the most recent
+    // request.  There might be other ones that were made before that which we
+    // didn't fulfill yet.
+    auto& front = pending_requests_.front();
+    if (pooled_image->spec == front.spec) {
+      // Construct a record that notifies us when the image is released.
+      // TODO(liberato): Don't copy fields this way.
+      ImageRecord record;
+      record.mailbox = pooled_image->record.mailbox;
+      record.is_vulkan = pooled_image->record.is_vulkan;
+      record.codec_image_holder = pooled_image->record.codec_image_holder;
+      // The release CB notifies us instead of |provider_|.
+      record.release_cb = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+          base::BindOnce(&PooledSharedImageVideoProvider::OnImageReturned,
+                         weak_factory_.GetWeakPtr(), std::move(pooled_image)),
+          gpu::SyncToken());
+
+      // Save the callback and remove the request, in case |cb| calls us back.
+      auto cb = std::move(front.cb);
+      pending_requests_.pop_front();
+
+      std::move(cb).Run(std::move(record));
+      return;
+    }
+
+    // Can't fulfill the topmost request.  Discard |pooled_image|, even if it
+    // matches the pool.  The reason is that any pending requests will have
+    // images created for them, which we'll use when they arrive.  It would be
+    // okay to store |pooled_image| in the pool if it matches, but then we'd
+    // have more pooled images than we expect.
+    return;
+  }
+
+  // There are no outstanding image requests, or the top one doesn't match
+  // |pooled_image|.  If this image is compatible with the pool, then pool it.
+  // Otherwise, discard it.
+
+  // See if |record| matches |pool_spec_|.  If not, then drop it.  Otherwise,
+  // pool it for later.  Note that we don't explicitly call the release cb,
+  // since dropping the image is sufficient to notify |provider_|.  Note that
+  // we've already waited for any sync token at this point, so it's okay if we
+  // don't provide one to the underlying release cb.
+  if (pool_spec_ != pooled_image->spec)
+    return;
+
+  // Add it to the pool.
+  pool_.push_front(std::move(pooled_image));
+}
+
+PooledSharedImageVideoProvider::GpuHelperImpl::GpuHelperImpl(
+    GetStubCB get_stub_cb)
+    : weak_factory_(this) {
+  gpu::CommandBufferStub* stub = get_stub_cb.Run();
+  if (stub) {
+    stub->AddDestructionObserver(this);
+    command_buffer_helper_ = CommandBufferHelper::Create(stub);
+  }
+}
+
+PooledSharedImageVideoProvider::GpuHelperImpl::~GpuHelperImpl() = default;
+
+void PooledSharedImageVideoProvider::GpuHelperImpl::OnImageReturned(
+    const gpu::SyncToken& sync_token,
+    scoped_refptr<CodecImageHolder> codec_image_holder,
+    base::OnceClosure cb) {
+  auto on_sync_token_cleared_cb = base::BindOnce(
+      &GpuHelperImpl::OnSyncTokenCleared, weak_factory_.GetWeakPtr(),
+      std::move(codec_image_holder), std::move(cb));
+  command_buffer_helper_->WaitForSyncToken(sync_token,
+                                           std::move(on_sync_token_cleared_cb));
+}
+
+void PooledSharedImageVideoProvider::GpuHelperImpl::OnSyncTokenCleared(
+    scoped_refptr<CodecImageHolder> codec_image_holder,
+    base::OnceClosure cb) {
+  codec_image_holder->codec_image_raw()->NotifyUnused();
+  // Do this last, since |cb| might post to some other thread.
+  std::move(cb).Run();
+}
+
+void PooledSharedImageVideoProvider::GpuHelperImpl::OnWillDestroyStub(
+    bool have_context) {
+  command_buffer_helper_ = nullptr;
+}
+
+}  // namespace media
diff --git a/media/gpu/android/pooled_shared_image_video_provider.h b/media/gpu/android/pooled_shared_image_video_provider.h
new file mode 100644
index 0000000..c0ec44a
--- /dev/null
+++ b/media/gpu/android/pooled_shared_image_video_provider.h
@@ -0,0 +1,141 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_ANDROID_POOLED_SHARED_IMAGE_VIDEO_PROVIDER_H_
+#define MEDIA_GPU_ANDROID_POOLED_SHARED_IMAGE_VIDEO_PROVIDER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/threading/sequence_bound.h"
+#include "gpu/ipc/service/command_buffer_stub.h"
+#include "media/gpu/android/shared_image_video_provider.h"
+#include "media/gpu/command_buffer_helper.h"
+
+namespace media {
+
+class PooledSharedImageVideoProviderTest;
+
+// Provider class for shared images.
+class MEDIA_GPU_EXPORT PooledSharedImageVideoProvider
+    : public SharedImageVideoProvider {
+ public:
+  // Helper class that processes image returns on the gpu thread.
+  class GpuHelper {
+   public:
+    GpuHelper() = default;
+    virtual ~GpuHelper() = default;
+
+    // Called (on the gpu thread) to handle image return.
+    virtual void OnImageReturned(
+        const gpu::SyncToken& sync_token,
+        scoped_refptr<CodecImageHolder> codec_image_holder,
+        base::OnceClosure cb) = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(GpuHelper);
+  };
+
+  // Create a default implementation.  |provider| is the underlying provider to
+  // create shared images.
+  static std::unique_ptr<PooledSharedImageVideoProvider> Create(
+      scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+      GetStubCB get_stub_cb,
+      std::unique_ptr<SharedImageVideoProvider> provider);
+
+  ~PooledSharedImageVideoProvider() override;
+
+  // SharedImageVideoProvider
+  void Initialize(GpuInitCB gpu_init_cb) override;
+  void RequestImage(ImageReadyCB cb,
+                    const ImageSpec& spec,
+                    scoped_refptr<gpu::TextureOwner> texture_owner) override;
+
+ private:
+  friend class PooledSharedImageVideoProviderTest;
+
+  PooledSharedImageVideoProvider(
+      base::SequenceBound<GpuHelper> gpu_helper,
+      std::unique_ptr<SharedImageVideoProvider> provider);
+
+  class GpuHelperImpl : public GpuHelper,
+                        public gpu::CommandBufferStub::DestructionObserver {
+   public:
+    GpuHelperImpl(GetStubCB get_stub_cb);
+    ~GpuHelperImpl() override;
+
+    // GpuHelper
+    void OnImageReturned(const gpu::SyncToken& sync_token,
+                         scoped_refptr<CodecImageHolder> codec_image_holder,
+                         base::OnceClosure cb) override;
+
+    // gpu::CommandBufferStub::DestructionObserver
+    void OnWillDestroyStub(bool have_context) override;
+
+   private:
+    void OnSyncTokenCleared(scoped_refptr<CodecImageHolder> codec_image_holder,
+                            base::OnceClosure cb);
+
+    scoped_refptr<CommandBufferHelper> command_buffer_helper_;
+    base::WeakPtrFactory<GpuHelperImpl> weak_factory_;
+  };
+
+  // Record of on image from |provider|.
+  class PooledImage : public base::RefCounted<PooledImage> {
+   public:
+    PooledImage(const ImageSpec& spec, ImageRecord record);
+
+    ImageSpec spec;
+    // The original record, including the original reuse callback.
+    ImageRecord record;
+
+   private:
+    virtual ~PooledImage();
+
+    friend class base::RefCounted<PooledImage>;
+  };
+
+  // One request from the client that's pending an image.
+  class PendingRequest {
+   public:
+    PendingRequest(const ImageSpec& spec, ImageReadyCB cb);
+    ~PendingRequest();
+    ImageSpec spec;
+    ImageReadyCB cb;
+    std::unique_ptr<CodecOutputBuffer> output_buffer;
+    scoped_refptr<gpu::TextureOwner> texture_owner;
+    PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb;
+  };
+
+  // Called by |provider_| when a new image is created.
+  void OnImageCreated(ImageSpec spec, ImageRecord record);
+
+  // Called by our client when it runs the release cb, to notify us that the
+  // image is no longer in use.
+  void OnImageReturned(scoped_refptr<PooledImage> pooled_image,
+                       const gpu::SyncToken& sync_token);
+
+  // Given a free image |pooled_image| that is not in our pool, use it to either
+  // fulfill a pending request, add it to the pool, or discard it.
+  void ProcessFreePooledImage(scoped_refptr<PooledImage> pooled_image);
+
+  // Underlying provider that we use to construct images.
+  std::unique_ptr<SharedImageVideoProvider> provider_;
+
+  // All currently unused images.
+  std::list<scoped_refptr<PooledImage>> pool_;
+
+  // Spec for all images in |pool_|.
+  ImageSpec pool_spec_;
+
+  std::list<PendingRequest> pending_requests_;
+
+  base::SequenceBound<GpuHelper> gpu_helper_;
+
+  base::WeakPtrFactory<PooledSharedImageVideoProvider> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PooledSharedImageVideoProvider);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_ANDROID_POOLED_SHARED_IMAGE_VIDEO_PROVIDER_H_
diff --git a/media/gpu/android/pooled_shared_image_video_provider_unittest.cc b/media/gpu/android/pooled_shared_image_video_provider_unittest.cc
new file mode 100644
index 0000000..c561eed
--- /dev/null
+++ b/media/gpu/android/pooled_shared_image_video_provider_unittest.cc
@@ -0,0 +1,260 @@
+// 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 "media/gpu/android/pooled_shared_image_video_provider.h"
+
+#include <list>
+
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "gpu/command_buffer/service/abstract_texture_impl_shared_context_state.h"
+#include "gpu/ipc/common/command_buffer_id.h"
+#include "media/gpu/android/mock_shared_image_video_provider.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Mock;
+
+namespace media {
+
+class PooledSharedImageVideoProviderTest : public testing::Test {
+ public:
+  class MockGpuHelper : public PooledSharedImageVideoProvider::GpuHelper {
+   public:
+    MockGpuHelper(gpu::SyncToken* sync_token_out)
+        : sync_token_out_(sync_token_out) {}
+
+    // PooledSharedImageVideoProvider::GpuHelper
+    void OnImageReturned(const gpu::SyncToken& sync_token,
+                         scoped_refptr<CodecImageHolder> codec_image_holder,
+                         base::OnceClosure cb) override {
+      *sync_token_out_ = sync_token;
+
+      // Run the output cb.
+      std::move(cb).Run();
+    }
+
+   private:
+    gpu::SyncToken* sync_token_out_ = nullptr;
+  };
+
+  PooledSharedImageVideoProviderTest() = default;
+
+  void SetUp() override {
+    task_runner_ = base::SequencedTaskRunnerHandle::Get();
+    base::SequenceBound<MockGpuHelper> mock_gpu_helper(task_runner_,
+                                                       &sync_token_);
+
+    std::unique_ptr<MockSharedImageVideoProvider> mock_provider =
+        std::make_unique<MockSharedImageVideoProvider>();
+    mock_provider_raw_ = mock_provider.get();
+
+    provider_ = base::WrapUnique(new PooledSharedImageVideoProvider(
+        std::move(mock_gpu_helper), std::move(mock_provider)));
+  }
+
+  // Return an ImageReadyCB that saves the ImageRecord in |image_records_|.
+  SharedImageVideoProvider::ImageReadyCB SaveImageRecordCB() {
+    return base::BindOnce(
+        [](std::list<SharedImageVideoProvider::ImageRecord>* output_list,
+           SharedImageVideoProvider::ImageRecord record) {
+          output_list->push_back(std::move(record));
+        },
+        &image_records_);
+  }
+
+  // Request an image from |provier_|, which we expect will call through to
+  // |mock_provider_raw_|.  Have |mock_provider_raw_| return an image, too.
+  void RequestAndProvideImage(const SharedImageVideoProvider::ImageSpec& spec) {
+    EXPECT_CALL(*mock_provider_raw_, MockRequestImage()).Times(1);
+    provider_->RequestImage(SaveImageRecordCB(), spec, texture_owner_);
+    base::RunLoop().RunUntilIdle();
+    Mock::VerifyAndClearExpectations(mock_provider_raw_);
+    mock_provider_raw_->ProvideOneRequestedImage();
+  }
+
+  base::test::TaskEnvironment task_environment_;
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  // Provider under test.
+  std::unique_ptr<PooledSharedImageVideoProvider> provider_;
+
+  MockSharedImageVideoProvider* mock_provider_raw_ = nullptr;
+  gpu::SyncToken sync_token_;
+
+  scoped_refptr<gpu::TextureOwner> texture_owner_;
+
+  // Image records that we've received from |provider|, via SaveImageRecordCB().
+  std::list<SharedImageVideoProvider::ImageRecord> image_records_;
+};
+
+TEST_F(PooledSharedImageVideoProviderTest, InitializeForwardsGpuCallback) {
+  bool was_called = false;
+  auto gpu_init_cb = base::BindOnce(
+      [](bool* flag, scoped_refptr<gpu::SharedContextState>) { *flag = true; },
+      &was_called);
+  provider_->Initialize(std::move(gpu_init_cb));
+  std::move(mock_provider_raw_->gpu_init_cb_).Run(nullptr);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(was_called);
+}
+
+TEST_F(PooledSharedImageVideoProviderTest, RequestImageRequestsMultipleImages) {
+  // Test the RequestImage will keep requesting images from the underlying
+  // provider as long as we don't return any.
+  SharedImageVideoProvider::ImageSpec spec(gfx::Size(1, 1), 0u);
+  RequestAndProvideImage(spec);
+  RequestAndProvideImage(spec);
+  RequestAndProvideImage(spec);
+  EXPECT_EQ(image_records_.size(), 3u);
+}
+
+TEST_F(PooledSharedImageVideoProviderTest, ReleasingAnImageForwardsSyncToken) {
+  // Calling the release callback on an image should forward the sync token to
+  // our gpu helper.
+  SharedImageVideoProvider::ImageSpec spec(gfx::Size(1, 1), 0u);
+  RequestAndProvideImage(spec);
+
+  gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
+                            gpu::CommandBufferIdFromChannelAndRoute(2, 3), 4);
+  std::move(image_records_.back().release_cb).Run(sync_token);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(sync_token, sync_token_);
+}
+
+TEST_F(PooledSharedImageVideoProviderTest,
+       ReleasingAnImageDoesntRunUnderlyingReleaseCallback) {
+  // Verify that releasing an image doesn't call the underlying release callback
+  // on it.  Presumably, it should be sent back to the pool instead.
+  SharedImageVideoProvider::ImageSpec spec(gfx::Size(1, 1), 0u);
+  RequestAndProvideImage(spec);
+
+  // Release the image.
+  image_records_.pop_back();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 0);
+}
+
+TEST_F(PooledSharedImageVideoProviderTest, RequestImageReusesReturnedImages) {
+  // Test the RequestImage will return images without requesting new ones, if
+  // some have been returned to the pool.
+  SharedImageVideoProvider::ImageSpec spec(gfx::Size(1, 1), 0u);
+  // Request two images.
+  RequestAndProvideImage(spec);
+  RequestAndProvideImage(spec);
+  EXPECT_EQ(image_records_.size(), 2u);
+  // Now return one, and request another.
+  image_records_.pop_back();
+  // Let the release CB run.
+  base::RunLoop().RunUntilIdle();
+
+  // Shouldn't call MockRequestImage a third time.
+  EXPECT_CALL(*mock_provider_raw_, MockRequestImage()).Times(0);
+  provider_->RequestImage(SaveImageRecordCB(), spec, texture_owner_);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(image_records_.size(), 2u);
+}
+
+TEST_F(PooledSharedImageVideoProviderTest,
+       DeletingProviderWithOutstandingImagesDoesntCrash) {
+  // Destroying |provider_| with outstanding images shouldn't break anything.
+  SharedImageVideoProvider::ImageSpec spec(gfx::Size(1, 1), 0u);
+  provider_->RequestImage(SaveImageRecordCB(), spec, texture_owner_);
+  base::RunLoop().RunUntilIdle();
+  provider_.reset();
+  base::RunLoop().RunUntilIdle();
+  // Shouldn't crash.
+}
+
+TEST_F(PooledSharedImageVideoProviderTest,
+       ReturnedImagesAreReleasedAfterSpecChange) {
+  // When we change the ImageSpec, old images should be released on the
+  // underlying provider as they are returned.
+  SharedImageVideoProvider::ImageSpec spec_1(gfx::Size(1, 1), 0u);
+  SharedImageVideoProvider::ImageSpec spec_2(gfx::Size(1, 2), 0u);
+  RequestAndProvideImage(spec_1);
+  RequestAndProvideImage(spec_2);
+
+  EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 0);
+
+  // Return image 1, and it should run the underlying release callback since it
+  // doesn't match the pool spec.
+  image_records_.pop_front();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
+
+  // Returning image 2 should not, since it should be put into the pool.
+  image_records_.pop_front();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
+}
+
+TEST_F(PooledSharedImageVideoProviderTest, SizeChangeEmptiesPool) {
+  // Verify that a size change in the ImageSpec causes the pool to be emptied.
+  SharedImageVideoProvider::ImageSpec spec_1(gfx::Size(1, 1), 0u);
+  SharedImageVideoProvider::ImageSpec spec_2(gfx::Size(1, 2), 0u);
+
+  // Request an image with |spec_1| and release it, to send it to the pool.
+  RequestAndProvideImage(spec_1);
+  image_records_.pop_front();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 0);
+
+  // Request an image with |spec_2|, which should release the first one.
+  RequestAndProvideImage(spec_2);
+  EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
+}
+
+TEST_F(PooledSharedImageVideoProviderTest, GenerationIdChangeEmptiesPool) {
+  // Verify that a change in the generation id causes the pool to be emptied.
+  SharedImageVideoProvider::ImageSpec spec_1(gfx::Size(1, 1), 0u);
+  SharedImageVideoProvider::ImageSpec spec_2(gfx::Size(1, 1), 1u);
+  RequestAndProvideImage(spec_1);
+  image_records_.pop_front();
+  base::RunLoop().RunUntilIdle();
+  RequestAndProvideImage(spec_2);
+  EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
+}
+
+TEST_F(PooledSharedImageVideoProviderTest, InFlightSpecChangeProvidesImage) {
+  // If we change the ImageSpec between requesting and receiving an image from
+  // the provider, it should still provide the image to the requestor.
+  SharedImageVideoProvider::ImageSpec spec_1(gfx::Size(1, 1), 0u);
+  SharedImageVideoProvider::ImageSpec spec_2(gfx::Size(1, 1), 1u);
+
+  // Request both images before providing either.
+  EXPECT_CALL(*mock_provider_raw_, MockRequestImage()).Times(2);
+  provider_->RequestImage(SaveImageRecordCB(), spec_1, texture_owner_);
+  provider_->RequestImage(SaveImageRecordCB(), spec_2, texture_owner_);
+  base::RunLoop().RunUntilIdle();
+
+  // Provide the |spec_1| image.  Nothing should be released since it should
+  // fulfill the first request.
+  mock_provider_raw_->ProvideOneRequestedImage();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 0);
+  EXPECT_EQ(image_records_.size(), 1u);
+
+  // Provide the |spec_2| image, which should also be provided to us.
+  mock_provider_raw_->ProvideOneRequestedImage();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 0);
+  EXPECT_EQ(image_records_.size(), 2u);
+
+  // Drop the |spec_1| image, which should be released rather than added back to
+  // the pool.
+  image_records_.pop_front();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
+
+  // Drop the |spec_2| image, which should be pooled rather than released.
+  image_records_.pop_front();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
+}
+
+}  // namespace media
diff --git a/media/gpu/android/shared_image_video_provider.cc b/media/gpu/android/shared_image_video_provider.cc
index 445acd0..c543495f 100644
--- a/media/gpu/android/shared_image_video_provider.cc
+++ b/media/gpu/android/shared_image_video_provider.cc
@@ -6,11 +6,23 @@
 
 namespace media {
 
-SharedImageVideoProvider::ImageSpec::ImageSpec(const gfx::Size& our_size)
-    : size(our_size) {}
+SharedImageVideoProvider::ImageSpec::ImageSpec() = default;
+SharedImageVideoProvider::ImageSpec::ImageSpec(const gfx::Size& our_size,
+                                               uint64_t our_generation_id)
+    : size(our_size), generation_id(our_generation_id) {}
 SharedImageVideoProvider::ImageSpec::ImageSpec(const ImageSpec&) = default;
 SharedImageVideoProvider::ImageSpec::~ImageSpec() = default;
 
+bool SharedImageVideoProvider::ImageSpec::operator==(
+    const ImageSpec& rhs) const {
+  return size == rhs.size && generation_id == rhs.generation_id;
+}
+
+bool SharedImageVideoProvider::ImageSpec::operator!=(
+    const ImageSpec& rhs) const {
+  return !(*this == rhs);
+}
+
 SharedImageVideoProvider::ImageRecord::ImageRecord() = default;
 SharedImageVideoProvider::ImageRecord::ImageRecord(ImageRecord&&) = default;
 SharedImageVideoProvider::ImageRecord::~ImageRecord() = default;
diff --git a/media/gpu/android/shared_image_video_provider.h b/media/gpu/android/shared_image_video_provider.h
index 4e1ea50db..83332f5 100644
--- a/media/gpu/android/shared_image_video_provider.h
+++ b/media/gpu/android/shared_image_video_provider.h
@@ -30,16 +30,28 @@
 
   // Description of the underlying properties of the shared image.
   struct ImageSpec {
-    ImageSpec(const gfx::Size& size);
+    ImageSpec();
+    ImageSpec(const gfx::Size& size, uint64_t generation_id);
     ImageSpec(const ImageSpec&);
     ~ImageSpec();
 
     // Size of the underlying texture.
     gfx::Size size;
 
+    // This is a hack to allow us to discard pooled images if the TextureOwner
+    // changes.  We don't want to keep a ref to the TextureOwner here, so we
+    // just use a generation counter.  Note that this is temporary anyway; we
+    // only need it for legacy mailbox support to construct a per-video-frame
+    // texture with the TextureOwner's service id (unowned texture hack).  Once
+    // legacy mailboxes aren't needed, SharedImageVideo::BeginAccess can just
+    // ask the CodecImage for whatever TextureOwner it is using currently, which
+    // is set by the client via CodecImage::Initialize.
+    uint64_t generation_id = 0;
+
     // TODO: Include other properties, if they matter, like texture format.
 
-    bool operator==(const ImageSpec&);
+    bool operator==(const ImageSpec&) const;
+    bool operator!=(const ImageSpec&) const;
   };
 
   using ReleaseCB = base::OnceCallback<void(const gpu::SyncToken&)>;
@@ -57,7 +69,8 @@
     // considered to be unused.
     ReleaseCB release_cb;
 
-    // CodecImage that one can use for MaybeRenderEarly.
+    // CodecImage that one can use for MaybeRenderEarly, and to attach a codec
+    // output buffer.
     scoped_refptr<CodecImageHolder> codec_image_holder;
 
     // Is the underlying context Vulkan?  If so, then one must provide YCbCrInfo
diff --git a/media/gpu/android/video_frame_factory_impl.cc b/media/gpu/android/video_frame_factory_impl.cc
index cced87f..ce422d0 100644
--- a/media/gpu/android/video_frame_factory_impl.cc
+++ b/media/gpu/android/video_frame_factory_impl.cc
@@ -109,6 +109,11 @@
 void VideoFrameFactoryImpl::SetSurfaceBundle(
     scoped_refptr<CodecSurfaceBundle> surface_bundle) {
   scoped_refptr<CodecImageGroup> image_group;
+
+  // Increase the generation ID used by the shared image provider, since we're
+  // changing the TextureOwner.  This is temporary.  See ImageSpec.
+  image_spec_.generation_id++;
+
   if (!surface_bundle) {
     // Clear everything, just so we're not holding a reference.
     codec_buffer_wait_coordinator_ = nullptr;
@@ -154,7 +159,8 @@
     return;
   }
 
-  SharedImageVideoProvider::ImageSpec spec(coded_size);
+  // Update the current spec to match the size.
+  image_spec_.size = coded_size;
 
   auto image_ready_cb = base::BindOnce(
       &VideoFrameFactoryImpl::CreateVideoFrame_OnImageReady,
@@ -164,7 +170,7 @@
       enable_threaded_texture_mailboxes_, gpu_task_runner_);
 
   image_provider_->RequestImage(
-      std::move(image_ready_cb), spec,
+      std::move(image_ready_cb), image_spec_,
       codec_buffer_wait_coordinator_
           ? codec_buffer_wait_coordinator_->texture_owner()
           : nullptr);
@@ -194,6 +200,9 @@
   // on the gpu main thread here, but it's okay since CodecImage is not being
   // used at this point.  Alternatively, we could post it, or hand it off to the
   // MaybeRenderEarlyManager to save a post.
+  //
+  // When we remove the output buffer management from CodecImage, then that's
+  // what we'd have a reference to here rather than CodecImage.
   record.codec_image_holder->codec_image_raw()->Initialize(
       std::move(output_buffer), codec_buffer_wait_coordinator,
       std::move(promotion_hint_cb));
@@ -261,18 +270,6 @@
   mailbox_holders[0] = gpu::MailboxHolder(record.mailbox, gpu::SyncToken(),
                                           GL_TEXTURE_EXTERNAL_OES);
 
-  // TODO(liberato): We should set the promotion hint cb here on the image.  We
-  // should also set the output buffer params; we shouldn't send the output
-  // buffer to the gpu thread, since the codec image isn't in use anyway.  We
-  // can access it on any thread.  We'll also need to get new images when we
-  // switch texture owners.  That's left for future work.
-
-  // TODO(liberato): When we switch to a pool, we need to provide some way to
-  // call MaybeRenderEarly that doesn't depend on |release_cb|.  I suppose we
-  // could get a RepeatingCallback that's a "reuse cb", that we'd attach to the
-  // VideoFrame's release cb, since we have to wait for the sync token anyway.
-  // That would run on the gpu thread, and could MaybeRenderEarly.
-
   gfx::Rect visible_rect(coded_size);
 
   auto frame = VideoFrame::WrapNativeTextures(
@@ -334,12 +331,24 @@
 void VideoFrameFactoryImpl::RunAfterPendingVideoFrames(
     base::OnceClosure closure) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Hop through |gpu_task_runner_| to ensure it comes after pending frames.
-  // TODO(liberato): If we're using a pool for SharedImageVideo, then this
-  // doesn't really make much sense.  SharedImageVideoProvider should do this
-  // for us instead.
-  gpu_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(),
-                                     std::move(closure));
+
+  // Run |closure| after we receive an image from |image_provider_|.  We don't
+  // need the image, but it guarantees that it's ordered after all previous
+  // requests have been fulfilled.
+
+  auto image_ready_cb = base::BindOnce(
+      [](base::OnceClosure closure,
+         SharedImageVideoProvider::ImageRecord record) {
+        // Ignore |record| since we don't actually need an image.
+        std::move(closure).Run();
+      },
+      std::move(closure));
+
+  image_provider_->RequestImage(
+      std::move(image_ready_cb), image_spec_,
+      codec_buffer_wait_coordinator_
+          ? codec_buffer_wait_coordinator_->texture_owner()
+          : nullptr);
 }
 
 }  // namespace media
diff --git a/media/gpu/android/video_frame_factory_impl.h b/media/gpu/android/video_frame_factory_impl.h
index 8352166..02ccf9e 100644
--- a/media/gpu/android/video_frame_factory_impl.h
+++ b/media/gpu/android/video_frame_factory_impl.h
@@ -137,6 +137,9 @@
   // Optional helper to get the Vulkan YCbCrInfo.
   base::SequenceBound<YCbCrHelper> ycbcr_helper_;
 
+  // The current image spec that we'll use to request images.
+  SharedImageVideoProvider::ImageSpec image_spec_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   base::WeakPtrFactory<VideoFrameFactoryImpl> weak_factory_{this};
diff --git a/media/gpu/android/video_frame_factory_impl_unittest.cc b/media/gpu/android/video_frame_factory_impl_unittest.cc
index f33c6ab..0e7d94d0 100644
--- a/media/gpu/android/video_frame_factory_impl_unittest.cc
+++ b/media/gpu/android/video_frame_factory_impl_unittest.cc
@@ -18,6 +18,7 @@
 #include "media/gpu/android/codec_buffer_wait_coordinator.h"
 #include "media/gpu/android/maybe_render_early_manager.h"
 #include "media/gpu/android/mock_codec_image.h"
+#include "media/gpu/android/mock_shared_image_video_provider.h"
 #include "media/gpu/android/shared_image_video_provider.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -39,32 +40,6 @@
   MOCK_METHOD0(MaybeRenderEarly, void());
 };
 
-class MockSharedImageVideoProvider : public SharedImageVideoProvider {
- public:
-  MockSharedImageVideoProvider() : spec_(gfx::Size(0, 0)) {}
-
-  void Initialize(GpuInitCB gpu_init_cb) { Initialize_(gpu_init_cb); }
-
-  MOCK_METHOD1(Initialize_, void(GpuInitCB& gpu_init_cb));
-
-  void RequestImage(ImageReadyCB cb,
-                    const ImageSpec& spec,
-                    scoped_refptr<gpu::TextureOwner> texture_owner) override {
-    cb_ = std::move(cb);
-    spec_ = spec;
-    texture_owner_ = std::move(texture_owner);
-
-    MockRequestImage();
-  }
-
-  MOCK_METHOD0(MockRequestImage, void());
-
-  // Most recent arguments to RequestImage.
-  ImageReadyCB cb_;
-  ImageSpec spec_;
-  scoped_refptr<gpu::TextureOwner> texture_owner_;
-};
-
 class MockYCbCrHelper : public YCbCrHelper, public DestructionObservable {
  public:
   MockYCbCrHelper(MockYCbCrHelper** thiz) { *thiz = this; }
@@ -123,6 +98,9 @@
         VideoFrame::IsValidConfig(PIXEL_FORMAT_ARGB, VideoFrame::STORAGE_OPAQUE,
                                   coded_size, visible_rect, natural_size));
 
+    // Save a copy in case the test wants it.
+    output_buffer_raw_ = output_buffer.get();
+
     // We should get a call to the output callback, but no calls to the
     // provider.
     // TODO(liberato): Verify that it's sending the proper TextureOwner.
@@ -241,7 +219,7 @@
   auto record = MakeImageRecord(&release_cb_called_flag);
   scoped_refptr<CodecImage> codec_image(
       record.codec_image_holder->codec_image_raw());
-  std::move(image_provider_raw_->cb_).Run(std::move(record));
+  image_provider_raw_->ProvideOneRequestedImage(&record);
   base::RunLoop().RunUntilIdle();
   EXPECT_NE(frame, nullptr);
 
@@ -271,7 +249,7 @@
   RequestVideoFrame();
   auto image_record = MakeImageRecord();
   image_record.is_vulkan = false;
-  std::move(image_provider_raw_->cb_).Run(std::move(image_record));
+  image_provider_raw_->ProvideOneRequestedImage(&image_record);
   base::RunLoop().RunUntilIdle();
 }
 
@@ -283,7 +261,7 @@
               MockGetYCbCrInfo(image_record.codec_image_holder))
       .Times(1);
   image_record.is_vulkan = true;
-  std::move(image_provider_raw_->cb_).Run(std::move(image_record));
+  image_provider_raw_->ProvideOneRequestedImage(&image_record);
   base::RunLoop().RunUntilIdle();
 
   // Provide YCbCrInfo.  It should provide the VideoFrame too.
@@ -307,7 +285,7 @@
   if (ycbcr_helper_raw_)
     EXPECT_CALL(*ycbcr_helper_raw_, MockGetYCbCrInfo(_)).Times(0);
   EXPECT_CALL(output_cb_, Run(_)).Times(1);
-  std::move(image_provider_raw_->cb_).Run(std::move(other_image_record));
+  image_provider_raw_->ProvideOneRequestedImage(&other_image_record);
   base::RunLoop().RunUntilIdle();
 }
 
diff --git a/media/media_options.gni b/media/media_options.gni
index 375f50a3..dfcad0e 100644
--- a/media/media_options.gni
+++ b/media/media_options.gni
@@ -63,9 +63,9 @@
   # Enables AC3/EAC3 audio demuxing. This is enabled only on Chromecast, since
   # it only provides demuxing, and is only useful for AC3/EAC3 audio
   # pass-through to HDMI sink on Chromecast.
-  enable_ac3_eac3_audio_demuxing = proprietary_codecs && is_chromecast
+  enable_platform_ac3_eac3_audio = proprietary_codecs && is_chromecast
 
-  enable_mpeg_h_audio_demuxing = proprietary_codecs && is_chromecast
+  enable_platform_mpeg_h_audio = proprietary_codecs && is_chromecast
 
   enable_mse_mpeg2ts_stream_parser =
       proprietary_codecs && (is_chromecast || is_fuchsia || use_fuzzing_engine)
@@ -82,7 +82,7 @@
   # Enable Dolby Vision demuxing. Enable by default for Chromecast. Actual
   # decoding must be provided by the platform. Note some Dolby Vision profiles
   # which are encoded using HEVC require |enable_platform_hevc| to be enabled.
-  enable_dolby_vision_demuxing = proprietary_codecs && is_chromecast
+  enable_platform_dolby_vision = proprietary_codecs && is_chromecast
 
   # Enable HLS with SAMPLE-AES decryption.
   enable_hls_sample_aes = proprietary_codecs && is_chromecast
diff --git a/media/mojo/clients/mojo_renderer_unittest.cc b/media/mojo/clients/mojo_renderer_unittest.cc
index b947a13..53d55bf 100644
--- a/media/mojo/clients/mojo_renderer_unittest.cc
+++ b/media/mojo/clients/mojo_renderer_unittest.cc
@@ -61,8 +61,10 @@
 class MojoRendererTest : public ::testing::Test {
  public:
   MojoRendererTest()
-      : mojo_cdm_service_(&cdm_factory_, &mojo_cdm_service_context_),
-        cdm_binding_(&mojo_cdm_service_) {
+      : mojo_cdm_service_(
+            std::make_unique<MojoCdmService>(&cdm_factory_,
+                                             &mojo_cdm_service_context_)),
+        cdm_binding_(mojo_cdm_service_.get()) {
     std::unique_ptr<StrictMock<MockRenderer>> mock_renderer(
         new StrictMock<MockRenderer>());
     mock_renderer_ = mock_renderer.get();
@@ -215,7 +217,7 @@
   // Service side bindings (declaration order is critical).
   MojoCdmServiceContext mojo_cdm_service_context_;
   DefaultCdmFactory cdm_factory_;
-  MojoCdmService mojo_cdm_service_;
+  std::unique_ptr<MojoCdmService> mojo_cdm_service_;
   mojo::Binding<mojom::ContentDecryptionModule> cdm_binding_;
 
   // Service side mocks and helpers.
@@ -306,6 +308,30 @@
   SetCdmAndExpect(false);
 }
 
+TEST_F(MojoRendererTest, SetCdm_ReleasedCdmId) {
+  // The CdmContext set on |mock_renderer_|.
+  CdmContext* mock_renderer_cdm_context = nullptr;
+
+  Initialize();
+  CreateCdm();
+  EXPECT_CALL(*mock_renderer_, SetCdm(_, _))
+      .WillOnce(
+          DoAll(SaveArg<0>(&mock_renderer_cdm_context), RunCallback<1>(true)));
+  SetCdmAndExpect(true);
+  EXPECT_TRUE(mock_renderer_cdm_context);
+
+  // Release the CDM.
+  mojo_cdm_service_.reset();
+  base::RunLoop().RunUntilIdle();
+
+  // SetCdm() on |mock_renderer_| should not be called.
+  SetCdmAndExpect(false);
+
+  // The CDM should still be around since it's set on the |mock_renderer_|. It
+  // should have a Decryptor since we use kClearKeyKeySystem.
+  EXPECT_TRUE(mock_renderer_cdm_context->GetDecryptor());
+}
+
 TEST_F(MojoRendererTest, SetCdm_BeforeInitialize) {
   CreateCdm();
   EXPECT_CALL(*mock_renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(true));
diff --git a/media/mojo/services/gpu_mojo_media_client.cc b/media/mojo/services/gpu_mojo_media_client.cc
index 7bae02547..47e48bb 100644
--- a/media/mojo/services/gpu_mojo_media_client.cc
+++ b/media/mojo/services/gpu_mojo_media_client.cc
@@ -33,6 +33,7 @@
 #include "media/gpu/android/direct_shared_image_video_provider.h"
 #include "media/gpu/android/maybe_render_early_manager.h"
 #include "media/gpu/android/media_codec_video_decoder.h"
+#include "media/gpu/android/pooled_shared_image_video_provider.h"
 #include "media/gpu/android/video_frame_factory_impl.h"
 #include "media/mojo/mojom/media_drm_storage.mojom.h"
 #include "media/mojo/mojom/provision_fetcher.mojom.h"
@@ -208,8 +209,14 @@
       auto get_stub_cb = base::Bind(
           &GetCommandBufferStub, gpu_task_runner_, media_gpu_channel_manager_,
           command_buffer_id->channel_token, command_buffer_id->route_id);
-      auto image_provider = std::make_unique<DirectSharedImageVideoProvider>(
+      std::unique_ptr<SharedImageVideoProvider> image_provider;
+      image_provider = std::make_unique<DirectSharedImageVideoProvider>(
           gpu_task_runner_, get_stub_cb);
+      if (base::FeatureList::IsEnabled(kUsePooledSharedImageVideoProvider)) {
+        // Wrap |image_provider| in a pool.
+        image_provider = PooledSharedImageVideoProvider::Create(
+            gpu_task_runner_, get_stub_cb, std::move(image_provider));
+      }
       // TODO(liberato): Create this only if we're using Vulkan, else it's
       // ignored.  If we can tell that here, then VideoFrameFactory can use it
       // as a signal about whether it's supposed to get YCbCrInfo rather than
diff --git a/net/BUILD.gn b/net/BUILD.gn
index b4927e0..1cfbf2596 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -2787,6 +2787,8 @@
     "ssl/test_ssl_config_service.h",
     "ssl/test_ssl_private_key.cc",
     "ssl/test_ssl_private_key.h",
+    "test/cert_builder.cc",
+    "test/cert_builder.h",
     "test/cert_test_util.cc",
     "test/cert_test_util.h",
     "test/cert_test_util_nss.cc",
diff --git a/net/base/escape.cc b/net/base/escape.cc
index 3521f9e..c9f4aec 100644
--- a/net/base/escape.cc
+++ b/net/base/escape.cc
@@ -193,8 +193,6 @@
   // TODO(https://crbug.com/829873): Try to make this use icu, both to
   // protect against regressions as the Unicode standard is updated and to
   // reduce the number of long lists of characters.
-  // TODO(https://crbug.com/824715): Add default ignorable and formatting
-  // code points.
   return !(
       // Per http://tools.ietf.org/html/rfc3987#section-4.1, certain BiDi
       // control characters are not allowed to appear unescaped in URLs.
@@ -241,7 +239,62 @@
       code_point == 0x2029 ||  // PARAGRAPH SEPARATOR        (%E2%80%A9)
       code_point == 0x202F ||  // NARROW NO-BREAK SPACE      (%E2%80%AF)
       code_point == 0x205F ||  // MEDIUM MATHEMATICAL SPACE  (%E2%81%9F)
-      code_point == 0x3000);   // IDEOGRAPHIC SPACE          (%E3%80%80)
+      code_point == 0x3000 ||  // IDEOGRAPHIC SPACE          (%E3%80%80)
+
+      // Default Ignorable ([:Default_Ignorable_Code_Point=Yes:]) and Format
+      // characters ([:Cf:]) are also banned (see crbug.com/824715).
+      code_point == 0x00AD ||  // SOFT HYPHEN               (%C2%AD)
+      code_point == 0x034F ||  // COMBINING GRAPHEME JOINER (%CD%8F)
+      // Arabic number formatting
+      (code_point >= 0x0600 && code_point <= 0x0605) ||
+      // U+061C is already banned as a BiDi control character.
+      code_point == 0x06DD ||  // ARABIC END OF AYAH          (%DB%9D)
+      code_point == 0x070F ||  // SYRIAC ABBREVIATION MARK    (%DC%8F)
+      code_point == 0x08E2 ||  // ARABIC DISPUTED END OF AYAH (%E0%A3%A2)
+      code_point == 0x115F ||  // HANGUL CHOSEONG FILLER      (%E1%85%9F)
+      code_point == 0x1160 ||  // HANGUL JUNGSEONG FILLER     (%E1%85%A0)
+      code_point == 0x17B4 ||  // KHMER VOWEL INHERENT AQ     (%E1%9E%B4)
+      code_point == 0x17B5 ||  // KHMER VOWEL INHERENT AA     (%E1%9E%B5)
+      code_point == 0x180B ||  // MONGOLIAN FREE VARIATION SELECTOR ONE
+                               // (%E1%A0%8B)
+      code_point == 0x180C ||  // MONGOLIAN FREE VARIATION SELECTOR TWO
+                               // (%E1%A0%8C)
+      code_point == 0x180D ||  // MONGOLIAN FREE VARIATION SELECTOR THREE
+                               // (%E1%A0%8D)
+      code_point == 0x180E ||  // MONGOLIAN VOWEL SEPARATOR   (%E1%A0%8E)
+      code_point == 0x200B ||  // ZERO WIDTH SPACE            (%E2%80%8B)
+      code_point == 0x200C ||  // ZERO WIDTH SPACE NON-JOINER (%E2%80%8C)
+      code_point == 0x200D ||  // ZERO WIDTH JOINER           (%E2%80%8D)
+      // U+200E, U+200F, U+202A--202E, and U+2066--2069 are already banned as
+      // BiDi control characters.
+      code_point == 0x2060 ||  // WORD JOINER          (%E2%81%A0)
+      code_point == 0x2061 ||  // FUNCTION APPLICATION (%E2%81%A1)
+      code_point == 0x2062 ||  // INVISIBLE TIMES      (%E2%81%A2)
+      code_point == 0x2063 ||  // INVISIBLE SEPARATOR  (%E2%81%A3)
+      code_point == 0x2064 ||  // INVISIBLE PLUS       (%E2%81%A4)
+      code_point == 0x2065 ||  // null (%E2%81%A5)
+      // 0x2066--0x2069 are already banned as a BiDi control characters.
+      // General Punctuation - Deprecated (U+206A--206F)
+      (code_point >= 0x206A && code_point <= 0x206F) ||
+      code_point == 0x3164 ||  // HANGUL FILLER (%E3%85%A4)
+      (code_point >= 0xFFF0 && code_point <= 0xFFF8) ||  // null
+      // Variation selectors (%EF%B8%80 -- %EF%B8%8F)
+      (code_point >= 0xFE00 && code_point <= 0xFE0F) ||
+      code_point == 0xFEFF ||   // ZERO WIDTH NO-BREAK SPACE (%EF%BB%BF)
+      code_point == 0xFFA0 ||   // HALFWIDTH HANGUL FILLER (%EF%BE%A0)
+      code_point == 0xFFF9 ||   // INTERLINEAR ANNOTATION ANCHOR     (%EF%BF%B9)
+      code_point == 0xFFFA ||   // INTERLINEAR ANNOTATION SEPARATOR  (%EF%BF%BA)
+      code_point == 0xFFFB ||   // INTERLINEAR ANNOTATION TERMINATOR (%EF%BF%BB)
+      code_point == 0x110BD ||  // KAITHI NUMBER SIGN       (%F0%91%82%BD)
+      code_point == 0x110CD ||  // KAITHI NUMBER SIGN ABOVE (%F0%91%83%8D)
+      // Egyptian hieroglyph formatting (%F0%93%90%B0 -- %F0%93%90%B8)
+      (code_point >= 0x13430 && code_point <= 0x13438) ||
+      // Shorthand format controls (%F0%9B%B2%A0 -- %F0%9B%B2%A3)
+      (code_point >= 0x1BCA0 && code_point <= 0x1BCA3) ||
+      // Beams and slurs (%F0%9D%85%B3 -- %F0%9D%85%BA)
+      (code_point >= 0x1D173 && code_point <= 0x1D17A) ||
+      // Tags, Variation Selectors, nulls
+      (code_point >= 0xE0000 && code_point <= 0xE0FFF));
 }
 
 // Unescapes |escaped_text| according to |rules|, returning the resulting
diff --git a/net/base/escape_unittest.cc b/net/base/escape_unittest.cc
index 57be371..a6dd0d5 100644
--- a/net/base/escape_unittest.cc
+++ b/net/base/escape_unittest.cc
@@ -204,6 +204,48 @@
       {"(%E2%80%AF)(%E2%81%9F)(%E3%80%80)", UnescapeRule::NORMAL,
        "(%E2%80%AF)(%E2%81%9F)(%E3%80%80)"},
 
+      // Default Ignorable and Formatting characters should not be unescaped.
+      {"(%E2%81%A5)(%EF%BF%B0)(%EF%BF%B8)", UnescapeRule::NORMAL,
+       "(%E2%81%A5)(%EF%BF%B0)(%EF%BF%B8)"},
+      {"(%F3%A0%82%80)(%F3%A0%83%BF)(%F3%A0%87%B0)", UnescapeRule::NORMAL,
+       "(%F3%A0%82%80)(%F3%A0%83%BF)(%F3%A0%87%B0)"},
+      {"(%F3%A0%BF%BF)(%C2%AD)(%CD%8F)", UnescapeRule::NORMAL,
+       "(%F3%A0%BF%BF)(%C2%AD)(%CD%8F)"},
+      {"(%D8%80%20)(%D8%85)(%DB%9D)(%DC%8F)(%E0%A3%A2)", UnescapeRule::NORMAL,
+       "(%D8%80%20)(%D8%85)(%DB%9D)(%DC%8F)(%E0%A3%A2)"},
+      {"(%E1%85%9F)(%E1%85%A0)(%E1%9E%B4)(%E1%9E%B5)", UnescapeRule::NORMAL,
+       "(%E1%85%9F)(%E1%85%A0)(%E1%9E%B4)(%E1%9E%B5)"},
+      {"(%E1%A0%8B)(%E1%A0%8C)(%E1%A0%8D)(%E1%A0%8E)", UnescapeRule::NORMAL,
+       "(%E1%A0%8B)(%E1%A0%8C)(%E1%A0%8D)(%E1%A0%8E)"},
+      {"(%E2%80%8B)(%E2%80%8C)(%E2%80%8D)(%E2%81%A0)", UnescapeRule::NORMAL,
+       "(%E2%80%8B)(%E2%80%8C)(%E2%80%8D)(%E2%81%A0)"},
+      {"(%E2%81%A1)(%E2%81%A2)(%E2%81%A3)(%E2%81%A4)", UnescapeRule::NORMAL,
+       "(%E2%81%A1)(%E2%81%A2)(%E2%81%A3)(%E2%81%A4)"},
+      {"(%E3%85%A4)(%EF%BB%BF)(%EF%BE%A0)(%EF%BF%B9)", UnescapeRule::NORMAL,
+       "(%E3%85%A4)(%EF%BB%BF)(%EF%BE%A0)(%EF%BF%B9)"},
+      {"(%EF%BF%BB)(%F0%91%82%BD)(%F0%91%83%8D)", UnescapeRule::NORMAL,
+       "(%EF%BF%BB)(%F0%91%82%BD)(%F0%91%83%8D)"},
+      {"(%F0%93%90%B0)(%F0%93%90%B8)", UnescapeRule::NORMAL,
+       "(%F0%93%90%B0)(%F0%93%90%B8)"},
+      // General Punctuation - Deprecated (U+206A--206F)
+      {"(%E2%81%AA)(%E2%81%AD)(%E2%81%AF)", UnescapeRule::NORMAL,
+       "(%E2%81%AA)(%E2%81%AD)(%E2%81%AF)"},
+      // Variation selectors (U+FE00--FE0F)
+      {"(%EF%B8%80)(%EF%B8%8C)(%EF%B8%8D)", UnescapeRule::NORMAL,
+       "(%EF%B8%80)(%EF%B8%8C)(%EF%B8%8D)"},
+      // Shorthand format controls (U+1BCA0--1BCA3)
+      {"(%F0%9B%B2%A0)(%F0%9B%B2%A1)(%F0%9B%B2%A3)", UnescapeRule::NORMAL,
+       "(%F0%9B%B2%A0)(%F0%9B%B2%A1)(%F0%9B%B2%A3)"},
+      // Musical symbols beams and slurs (U+1D173--1D17A)
+      {"(%F0%9D%85%B3)(%F0%9D%85%B9)(%F0%9D%85%BA)", UnescapeRule::NORMAL,
+       "(%F0%9D%85%B3)(%F0%9D%85%B9)(%F0%9D%85%BA)"},
+      // Tags block (U+E0000--E007F), includes unassigned points
+      {"(%F3%A0%80%80)(%F3%A0%80%81)(%F3%A0%81%8F)", UnescapeRule::NORMAL,
+       "(%F3%A0%80%80)(%F3%A0%80%81)(%F3%A0%81%8F)"},
+      // Ideographic-specific variation selectors (U+E0100--E01EF)
+      {"(%F3%A0%84%80)(%F3%A0%84%90)(%F3%A0%87%AF)", UnescapeRule::NORMAL,
+       "(%F3%A0%84%80)(%F3%A0%84%90)(%F3%A0%87%AF)"},
+
       // Two spoofing characters in a row should not be unescaped.
       {"%D8%9C%D8%9C", UnescapeRule::NORMAL, "%D8%9C%D8%9C"},
       // Non-spoofing characters surrounded by spoofing characters should be
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index b8a4eec..1a3cf326 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -22,8 +22,6 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread.h"
 #include "build/build_config.h"
-#include "crypto/openssl_util.h"
-#include "crypto/rsa_private_key.h"
 #include "crypto/sha2.h"
 #include "net/base/net_errors.h"
 #include "net/cert/asn1_util.h"
@@ -41,11 +39,11 @@
 #include "net/cert/x509_certificate.h"
 #include "net/cert/x509_util.h"
 #include "net/cert_net/cert_net_fetcher_impl.h"
-#include "net/der/encode_values.h"
 #include "net/der/input.h"
 #include "net/der/parser.h"
 #include "net/proxy_resolution/proxy_config.h"
 #include "net/proxy_resolution/proxy_config_service_fixed.h"
+#include "net/test/cert_builder.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
@@ -272,16 +270,6 @@
 #endif
 }
 
-// Helper to make creating an X509Certificate chain less verbose.
-scoped_refptr<X509Certificate> CreateX509CertificateWithIntermediate(
-    bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer,
-    bssl::UniquePtr<CRYPTO_BUFFER> intermediate_buffer) {
-  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
-  intermediates.push_back(std::move(intermediate_buffer));
-  return X509Certificate::CreateFromBuffer(std::move(cert_buffer),
-                                           std::move(intermediates));
-}
-
 std::string MakeRandomHexString(size_t num_bytes) {
   std::vector<char> rand_bytes;
   rand_bytes.resize(num_bytes);
@@ -290,568 +278,6 @@
   return base::HexEncode(&rand_bytes[0], rand_bytes.size());
 }
 
-std::string Sha256WithRSAEncryption() {
-  const uint8_t kSha256WithRSAEncryption[] = {0x30, 0x0D, 0x06, 0x09, 0x2a,
-                                              0x86, 0x48, 0x86, 0xf7, 0x0d,
-                                              0x01, 0x01, 0x0b, 0x05, 0x00};
-  return std::string(std::begin(kSha256WithRSAEncryption),
-                     std::end(kSha256WithRSAEncryption));
-}
-
-std::string Sha1WithRSAEncryption() {
-  const uint8_t kSha1WithRSAEncryption[] = {0x30, 0x0D, 0x06, 0x09, 0x2a,
-                                            0x86, 0x48, 0x86, 0xf7, 0x0d,
-                                            0x01, 0x01, 0x05, 0x05, 0x00};
-  return std::string(std::begin(kSha1WithRSAEncryption),
-                     std::end(kSha1WithRSAEncryption));
-}
-
-std::string Md5WithRSAEncryption() {
-  const uint8_t kMd5WithRSAEncryption[] = {0x30, 0x0d, 0x06, 0x09, 0x2a,
-                                           0x86, 0x48, 0x86, 0xf7, 0x0d,
-                                           0x01, 0x01, 0x04, 0x05, 0x00};
-  return std::string(std::begin(kMd5WithRSAEncryption),
-                     std::end(kMd5WithRSAEncryption));
-}
-
-// Adds bytes (specified as a StringPiece) to the given CBB.
-// The argument ordering follows the boringssl CBB_* api style.
-bool CBBAddBytes(CBB* cbb, base::StringPiece bytes) {
-  return CBB_add_bytes(cbb, reinterpret_cast<const uint8_t*>(bytes.data()),
-                       bytes.size());
-}
-
-// Adds bytes (from fixed size array) to the given CBB.
-// The argument ordering follows the boringssl CBB_* api style.
-template <size_t N>
-bool CBBAddBytes(CBB* cbb, const uint8_t (&data)[N]) {
-  return CBB_add_bytes(cbb, data, N);
-}
-
-// Adds a RFC 5280 Time value to the given CBB.
-// The argument ordering follows the boringssl CBB_* api style.
-bool CBBAddTime(CBB* cbb, const base::Time& time) {
-  der::GeneralizedTime generalized_time;
-  if (!der::EncodeTimeAsGeneralizedTime(time, &generalized_time))
-    return false;
-  CBB time_cbb;
-  if (generalized_time.year < 2050) {
-    uint8_t out[der::kUTCTimeLength];
-    if (!der::EncodeUTCTime(generalized_time, out) ||
-        !CBB_add_asn1(cbb, &time_cbb, CBS_ASN1_UTCTIME) ||
-        !CBBAddBytes(&time_cbb, out) || !CBB_flush(cbb))
-      return false;
-  } else {
-    uint8_t out[der::kGeneralizedTimeLength];
-    if (!der::EncodeGeneralizedTime(generalized_time, out) ||
-        !CBB_add_asn1(cbb, &time_cbb, CBS_ASN1_GENERALIZEDTIME) ||
-        !CBBAddBytes(&time_cbb, out) || !CBB_flush(cbb))
-      return false;
-  }
-  return true;
-}
-
-// Finalizes the CBB to a std::string.
-std::string FinishCBB(CBB* cbb) {
-  size_t cbb_len;
-  uint8_t* cbb_bytes;
-
-  if (!CBB_finish(cbb, &cbb_bytes, &cbb_len)) {
-    ADD_FAILURE() << "CBB_finish() failed";
-    return std::string();
-  }
-
-  bssl::UniquePtr<uint8_t> delete_bytes(cbb_bytes);
-  return std::string(reinterpret_cast<char*>(cbb_bytes), cbb_len);
-}
-
-// CertBuilder is a helper class to dynamically create a test certificate.
-//
-// CertBuilder is initialized using an existing certificate, from which it
-// copies most properties (see InitFromCert for details).
-//
-// The subject, serial number, and key for the final certificate are chosen
-// randomly. Using a randomized subject and serial number is important to defeat
-// certificate caching done by NSS, which otherwise can make test outcomes
-// dependent on ordering.
-class CertBuilder {
- public:
-  // Initializes the CertBuilder using |orig_cert|. If |issuer| is null
-  // then the generated certificate will be self-signed. Otherwise, it
-  // will be signed using |issuer|.
-  CertBuilder(CRYPTO_BUFFER* orig_cert, CertBuilder* issuer) : issuer_(issuer) {
-    if (!issuer_)
-      issuer_ = this;
-
-    crypto::EnsureOpenSSLInit();
-    InitFromCert(der::Input(x509_util::CryptoBufferAsStringPiece(orig_cert)));
-  }
-
-  // Sets a value for the indicated X.509 (v3) extension.
-  void SetExtension(const der::Input& oid,
-                    std::string value,
-                    bool critical = false) {
-    auto& extension_value = extensions_[oid.AsString()];
-    extension_value.critical = critical;
-    extension_value.value = std::move(value);
-
-    Invalidate();
-  }
-
-  // Removes an extension (if present).
-  void EraseExtension(const der::Input& oid) {
-    extensions_.erase(oid.AsString());
-
-    Invalidate();
-  }
-
-  // Sets an AIA extension with a single caIssuers access method.
-  void SetCaIssuersUrl(const GURL& url) {
-    std::string url_spec = url.spec();
-
-    // From RFC 5280:
-    //
-    //   AuthorityInfoAccessSyntax  ::=
-    //           SEQUENCE SIZE (1..MAX) OF AccessDescription
-    //
-    //   AccessDescription  ::=  SEQUENCE {
-    //           accessMethod          OBJECT IDENTIFIER,
-    //           accessLocation        GeneralName  }
-    bssl::ScopedCBB cbb;
-    CBB aia, ca_issuer, access_method, access_location;
-    ASSERT_TRUE(CBB_init(cbb.get(), url_spec.size()));
-    ASSERT_TRUE(CBB_add_asn1(cbb.get(), &aia, CBS_ASN1_SEQUENCE));
-    ASSERT_TRUE(CBB_add_asn1(&aia, &ca_issuer, CBS_ASN1_SEQUENCE));
-    ASSERT_TRUE(CBB_add_asn1(&ca_issuer, &access_method, CBS_ASN1_OBJECT));
-    ASSERT_TRUE(CBBAddBytes(&access_method, AdCaIssuersOid().AsStringPiece()));
-    ASSERT_TRUE(CBB_add_asn1(&ca_issuer, &access_location,
-                             CBS_ASN1_CONTEXT_SPECIFIC | 6));
-    ASSERT_TRUE(CBBAddBytes(&access_location, url_spec));
-
-    SetExtension(AuthorityInfoAccessOid(), FinishCBB(cbb.get()));
-  }
-
-  void SetCrlDistributionPointUrl(const GURL& url) {
-    std::string url_spec = url.spec();
-
-    bssl::ScopedCBB cbb;
-    ASSERT_TRUE(CBB_init(cbb.get(), url_spec.size()));
-    CBB dps, dp, dp_name, dp_fullname, dp_url;
-
-    //    CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
-    ASSERT_TRUE(CBB_add_asn1(cbb.get(), &dps, CBS_ASN1_SEQUENCE));
-
-    //    DistributionPoint ::= SEQUENCE {
-    //         distributionPoint       [0]     DistributionPointName OPTIONAL,
-    //         reasons                 [1]     ReasonFlags OPTIONAL,
-    //         cRLIssuer               [2]     GeneralNames OPTIONAL }
-    ASSERT_TRUE(CBB_add_asn1(&dps, &dp, CBS_ASN1_SEQUENCE));
-    ASSERT_TRUE(CBB_add_asn1(
-        &dp, &dp_name, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
-
-    //    DistributionPointName ::= CHOICE {
-    //         fullName                [0]     GeneralNames,
-    //         nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
-    ASSERT_TRUE(
-        CBB_add_asn1(&dp_name, &dp_fullname,
-                     CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
-
-    //   GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
-    //   GeneralName ::= CHOICE {
-    // uniformResourceIdentifier       [6]     IA5String,
-    ASSERT_TRUE(
-        CBB_add_asn1(&dp_fullname, &dp_url, CBS_ASN1_CONTEXT_SPECIFIC | 6));
-    ASSERT_TRUE(CBBAddBytes(&dp_url, url_spec));
-
-    SetExtension(CrlDistributionPointsOid(), FinishCBB(cbb.get()));
-  }
-
-  void SetSubjectCommonName(const std::string common_name) {
-    // See RFC 4519.
-    static const uint8_t kCommonName[] = {0x55, 0x04, 0x03};
-
-    // See RFC 5280, section 4.1.2.4.
-    bssl::ScopedCBB cbb;
-    CBB rdns, rdn, attr, type, value;
-    ASSERT_TRUE(CBB_init(cbb.get(), 64));
-    ASSERT_TRUE(CBB_add_asn1(cbb.get(), &rdns, CBS_ASN1_SEQUENCE));
-    ASSERT_TRUE(CBB_add_asn1(&rdns, &rdn, CBS_ASN1_SET));
-    ASSERT_TRUE(CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE));
-    ASSERT_TRUE(CBB_add_asn1(&attr, &type, CBS_ASN1_OBJECT));
-    ASSERT_TRUE(CBBAddBytes(&type, kCommonName));
-    ASSERT_TRUE(CBB_add_asn1(&attr, &value, CBS_ASN1_UTF8STRING));
-    ASSERT_TRUE(CBBAddBytes(&value, common_name));
-
-    subject_tlv_ = FinishCBB(cbb.get());
-  }
-
-  // Sets the SAN for the certificate to a single dNSName.
-  void SetSubjectAltName(const std::string& dns_name) {
-    // From RFC 5280:
-    //
-    //   SubjectAltName ::= GeneralNames
-    //
-    //   GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
-    //
-    //   GeneralName ::= CHOICE {
-    //        otherName                       [0]     OtherName,
-    //        rfc822Name                      [1]     IA5String,
-    //        dNSName                         [2]     IA5String,
-    //        ... }
-    bssl::ScopedCBB cbb;
-    CBB general_names, general_name;
-    ASSERT_TRUE(CBB_init(cbb.get(), dns_name.size()));
-    ASSERT_TRUE(CBB_add_asn1(cbb.get(), &general_names, CBS_ASN1_SEQUENCE));
-    ASSERT_TRUE(CBB_add_asn1(&general_names, &general_name,
-                             CBS_ASN1_CONTEXT_SPECIFIC | 2));
-    ASSERT_TRUE(CBBAddBytes(&general_name, dns_name));
-
-    SetExtension(SubjectAltNameOid(), FinishCBB(cbb.get()));
-  }
-
-  // Sets the signature algorithm for the certificate to either
-  // sha256WithRSAEncryption or sha1WithRSAEncryption.
-  void SetSignatureAlgorithmRsaPkca1(DigestAlgorithm digest) {
-    switch (digest) {
-      case DigestAlgorithm::Sha256: {
-        SetSignatureAlgorithm(Sha256WithRSAEncryption());
-        break;
-      }
-
-      case DigestAlgorithm::Sha1: {
-        SetSignatureAlgorithm(Sha1WithRSAEncryption());
-        break;
-      }
-
-      default:
-        ASSERT_TRUE(false);
-    }
-  }
-
-  void SetSignatureAlgorithm(std::string algorithm_tlv) {
-    signature_algorithm_tlv_ = std::move(algorithm_tlv);
-    Invalidate();
-  }
-
-  void SetRandomSerialNumber() {
-    serial_number_ = base::RandUint64();
-    Invalidate();
-  }
-
-  // Returns a CRYPTO_BUFFER to the generated certificate.
-  CRYPTO_BUFFER* GetCertBuffer() {
-    if (!cert_)
-      GenerateCertificate();
-    return cert_.get();
-  }
-
-  bssl::UniquePtr<CRYPTO_BUFFER> DupCertBuffer() {
-    return bssl::UpRef(GetCertBuffer());
-  }
-
-  // Returns the subject of the generated certificate.
-  const std::string& GetSubject() {
-    if (subject_tlv_.empty())
-      GenerateSubject();
-    return subject_tlv_;
-  }
-
-  // Returns the serial number for the generated certificate.
-  uint64_t GetSerialNumber() {
-    if (!serial_number_)
-      serial_number_ = base::RandUint64();
-    return serial_number_;
-  }
-
-  // Returns the (RSA) key for the generated certificate.
-  EVP_PKEY* GetKey() {
-    if (!key_)
-      GenerateKey();
-    return key_.get();
-  }
-
-  // Returns an X509Certificate for the generated certificate.
-  scoped_refptr<X509Certificate> GetX509Certificate() {
-    return X509Certificate::CreateFromBuffer(DupCertBuffer(), {});
-  }
-
-  // Returns a copy of the certificate's DER.
-  std::string GetDER() {
-    return x509_util::CryptoBufferAsStringPiece(GetCertBuffer()).as_string();
-  }
-
- private:
-  // Marks the generated certificate DER as invalid, so it will need to
-  // be re-generated next time the DER is accessed.
-  void Invalidate() { cert_.reset(); }
-
-  // Sets the |key_| to a 2048-bit RSA key.
-  void GenerateKey() {
-    ASSERT_FALSE(key_);
-
-    auto private_key = crypto::RSAPrivateKey::Create(2048);
-    key_ = bssl::UpRef(private_key->key());
-  }
-
-  // Generates a random subject for the certificate, comprised of just a CN.
-  void GenerateSubject() {
-    ASSERT_TRUE(subject_tlv_.empty());
-
-    // Use a random common name comprised of 12 bytes in hex.
-    std::string common_name = MakeRandomHexString(12);
-
-    SetSubjectCommonName(common_name);
-  }
-
-  // Parses |cert| and copies the following properties:
-  //   * All extensions (dropping any duplicates)
-  //   * Signature algorithm (from Certificate)
-  //   * Validity (expiration)
-  void InitFromCert(const der::Input& cert) {
-    extensions_.clear();
-    Invalidate();
-
-    // From RFC 5280, section 4.1
-    //    Certificate  ::=  SEQUENCE  {
-    //      tbsCertificate       TBSCertificate,
-    //      signatureAlgorithm   AlgorithmIdentifier,
-    //      signatureValue       BIT STRING  }
-
-    // TBSCertificate  ::=  SEQUENCE  {
-    //      version         [0]  EXPLICIT Version DEFAULT v1,
-    //      serialNumber         CertificateSerialNumber,
-    //      signature            AlgorithmIdentifier,
-    //      issuer               Name,
-    //      validity             Validity,
-    //      subject              Name,
-    //      subjectPublicKeyInfo SubjectPublicKeyInfo,
-    //      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
-    //                           -- If present, version MUST be v2 or v3
-    //      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
-    //                           -- If present, version MUST be v2 or v3
-    //      extensions      [3]  EXPLICIT Extensions OPTIONAL
-    //                           -- If present, version MUST be v3
-    //      }
-    der::Parser parser(cert);
-    der::Parser certificate;
-    der::Parser tbs_certificate;
-    ASSERT_TRUE(parser.ReadSequence(&certificate));
-    ASSERT_TRUE(certificate.ReadSequence(&tbs_certificate));
-
-    // version
-    bool unused;
-    ASSERT_TRUE(tbs_certificate.SkipOptionalTag(
-        der::kTagConstructed | der::kTagContextSpecific | 0, &unused));
-    // serialNumber
-    ASSERT_TRUE(tbs_certificate.SkipTag(der::kInteger));
-
-    // signature
-    der::Input signature_algorithm_tlv;
-    ASSERT_TRUE(tbs_certificate.ReadRawTLV(&signature_algorithm_tlv));
-    signature_algorithm_tlv_ = signature_algorithm_tlv.AsString();
-
-    // issuer
-    ASSERT_TRUE(tbs_certificate.SkipTag(der::kSequence));
-
-    // validity
-    der::Input validity_tlv;
-    ASSERT_TRUE(tbs_certificate.ReadRawTLV(&validity_tlv));
-    validity_tlv_ = validity_tlv.AsString();
-
-    // subject
-    ASSERT_TRUE(tbs_certificate.SkipTag(der::kSequence));
-    // subjectPublicKeyInfo
-    ASSERT_TRUE(tbs_certificate.SkipTag(der::kSequence));
-    // issuerUniqueID
-    ASSERT_TRUE(tbs_certificate.SkipOptionalTag(
-        der::ContextSpecificPrimitive(1), &unused));
-    // subjectUniqueID
-    ASSERT_TRUE(tbs_certificate.SkipOptionalTag(
-        der::ContextSpecificPrimitive(2), &unused));
-
-    // extensions
-    bool has_extensions = false;
-    der::Input extensions_tlv;
-    ASSERT_TRUE(tbs_certificate.ReadOptionalTag(
-        der::ContextSpecificConstructed(3), &extensions_tlv, &has_extensions));
-    if (has_extensions) {
-      std::map<der::Input, ParsedExtension> parsed_extensions;
-      ASSERT_TRUE(ParseExtensions(extensions_tlv, &parsed_extensions));
-
-      for (const auto& parsed_extension : parsed_extensions) {
-        SetExtension(parsed_extension.second.oid,
-                     parsed_extension.second.value.AsString(),
-                     parsed_extension.second.critical);
-      }
-    }
-  }
-
-  // Assembles the CertBuilder into a TBSCertificate.
-  void BuildTBSCertificate(std::string* out) {
-    bssl::ScopedCBB cbb;
-    CBB tbs_cert, version, extensions_context, extensions;
-
-    ASSERT_TRUE(CBB_init(cbb.get(), 64));
-    ASSERT_TRUE(CBB_add_asn1(cbb.get(), &tbs_cert, CBS_ASN1_SEQUENCE));
-    ASSERT_TRUE(
-        CBB_add_asn1(&tbs_cert, &version,
-                     CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
-    // Always use v3 certificates.
-    ASSERT_TRUE(CBB_add_asn1_uint64(&version, 2));
-    ASSERT_TRUE(CBB_add_asn1_uint64(&tbs_cert, GetSerialNumber()));
-    ASSERT_TRUE(AddSignatureAlgorithm(&tbs_cert));
-    ASSERT_TRUE(CBBAddBytes(&tbs_cert, issuer_->GetSubject()));
-    ASSERT_TRUE(CBBAddBytes(&tbs_cert, validity_tlv_));
-    ASSERT_TRUE(CBBAddBytes(&tbs_cert, GetSubject()));
-    ASSERT_TRUE(EVP_marshal_public_key(&tbs_cert, GetKey()));
-
-    // Serialize all the extensions.
-    if (!extensions_.empty()) {
-      ASSERT_TRUE(
-          CBB_add_asn1(&tbs_cert, &extensions_context,
-                       CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 3));
-      ASSERT_TRUE(
-          CBB_add_asn1(&extensions_context, &extensions, CBS_ASN1_SEQUENCE));
-
-      //   Extension  ::=  SEQUENCE  {
-      //        extnID      OBJECT IDENTIFIER,
-      //        critical    BOOLEAN DEFAULT FALSE,
-      //        extnValue   OCTET STRING
-      //                    -- contains the DER encoding of an ASN.1 value
-      //                    -- corresponding to the extension type identified
-      //                    -- by extnID
-      //        }
-      for (const auto& extension_it : extensions_) {
-        CBB extension_seq, oid, extn_value;
-        ASSERT_TRUE(
-            CBB_add_asn1(&extensions, &extension_seq, CBS_ASN1_SEQUENCE));
-        ASSERT_TRUE(CBB_add_asn1(&extension_seq, &oid, CBS_ASN1_OBJECT));
-        ASSERT_TRUE(CBBAddBytes(&oid, extension_it.first));
-        if (extension_it.second.critical) {
-          ASSERT_TRUE(CBB_add_asn1_bool(&extension_seq, true));
-        }
-
-        ASSERT_TRUE(
-            CBB_add_asn1(&extension_seq, &extn_value, CBS_ASN1_OCTETSTRING));
-        ASSERT_TRUE(CBBAddBytes(&extn_value, extension_it.second.value));
-        ASSERT_TRUE(CBB_flush(&extensions));
-      }
-    }
-
-    *out = FinishCBB(cbb.get());
-  }
-
-  bool AddSignatureAlgorithm(CBB* cbb) {
-    return CBBAddBytes(cbb, signature_algorithm_tlv_);
-  }
-
-  void GenerateCertificate() {
-    ASSERT_FALSE(cert_);
-
-    std::string tbs_cert;
-    BuildTBSCertificate(&tbs_cert);
-    const uint8_t* tbs_cert_bytes =
-        reinterpret_cast<const uint8_t*>(tbs_cert.data());
-
-    // Determine the correct digest algorithm to use (assumes RSA PKCS#1
-    // signatures).
-    auto signature_algorithm = SignatureAlgorithm::Create(
-        der::Input(&signature_algorithm_tlv_), nullptr);
-    ASSERT_TRUE(signature_algorithm);
-    ASSERT_EQ(SignatureAlgorithmId::RsaPkcs1, signature_algorithm->algorithm());
-    const EVP_MD* md = nullptr;
-
-    switch (signature_algorithm->digest()) {
-      case DigestAlgorithm::Sha256:
-        md = EVP_sha256();
-        break;
-
-      case DigestAlgorithm::Sha1:
-        md = EVP_sha1();
-        break;
-
-      default:
-        ASSERT_TRUE(false) << "Only rsaEncryptionWithSha256 or "
-                              "rsaEnryptionWithSha1 are supported";
-        break;
-    }
-
-    // Sign the TBSCertificate and write the entire certificate.
-    bssl::ScopedCBB cbb;
-    CBB cert, signature;
-    bssl::ScopedEVP_MD_CTX ctx;
-    uint8_t* sig_out;
-    size_t sig_len;
-
-    ASSERT_TRUE(CBB_init(cbb.get(), tbs_cert.size()));
-    ASSERT_TRUE(CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE));
-    ASSERT_TRUE(CBBAddBytes(&cert, tbs_cert));
-    ASSERT_TRUE(AddSignatureAlgorithm(&cert));
-    ASSERT_TRUE(CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING));
-    ASSERT_TRUE(CBB_add_u8(&signature, 0 /* no unused bits */));
-    ASSERT_TRUE(
-        EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr, issuer_->GetKey()));
-    ASSERT_TRUE(EVP_DigestSign(ctx.get(), nullptr, &sig_len, tbs_cert_bytes,
-                               tbs_cert.size()));
-    ASSERT_TRUE(CBB_reserve(&signature, &sig_out, sig_len));
-    ASSERT_TRUE(EVP_DigestSign(ctx.get(), sig_out, &sig_len, tbs_cert_bytes,
-                               tbs_cert.size()));
-    ASSERT_TRUE(CBB_did_write(&signature, sig_len));
-
-    auto cert_der = FinishCBB(cbb.get());
-    cert_ = x509_util::CreateCryptoBuffer(
-        reinterpret_cast<const uint8_t*>(cert_der.data()), cert_der.size());
-  }
-
-  struct ExtensionValue {
-    bool critical = false;
-    std::string value;
-  };
-
-  std::string validity_tlv_;
-  std::string subject_tlv_;
-  std::string signature_algorithm_tlv_;
-  uint64_t serial_number_ = 0;
-
-  std::map<std::string, ExtensionValue> extensions_;
-
-  bssl::UniquePtr<CRYPTO_BUFFER> cert_;
-  bssl::UniquePtr<EVP_PKEY> key_;
-
-  CertBuilder* issuer_ = nullptr;
-};
-
-// Creates a simple leaf->intermediate->root chain of CertBuilders with no AIA
-// or CrlDistributionPoint extensions, and leaf having a subjectAltName of
-// www.example.com.
-void CreateSimpleCertBuilderChain(
-    std::unique_ptr<CertBuilder>* out_leaf,
-    std::unique_ptr<CertBuilder>* out_intermediate,
-    std::unique_ptr<CertBuilder>* out_root) {
-  const char kHostname[] = "www.example.com";
-  base::FilePath certs_dir =
-      GetTestNetDataDirectory()
-          .AppendASCII("verify_certificate_chain_unittest")
-          .AppendASCII("target-and-intermediate");
-
-  CertificateList orig_certs = CreateCertificateListFromFile(
-      certs_dir, "chain.pem", X509Certificate::FORMAT_AUTO);
-  ASSERT_EQ(3U, orig_certs.size());
-
-  // Build slightly modified variants of |orig_certs|.
-  *out_root =
-      std::make_unique<CertBuilder>(orig_certs[2]->cert_buffer(), nullptr);
-  *out_intermediate = std::make_unique<CertBuilder>(
-      orig_certs[1]->cert_buffer(), out_root->get());
-  (*out_intermediate)->EraseExtension(CrlDistributionPointsOid());
-  (*out_intermediate)->EraseExtension(AuthorityInfoAccessOid());
-  *out_leaf = std::make_unique<CertBuilder>(orig_certs[0]->cert_buffer(),
-                                            out_intermediate->get());
-  (*out_leaf)->SetSubjectAltName(kHostname);
-  (*out_leaf)->EraseExtension(CrlDistributionPointsOid());
-  (*out_leaf)->EraseExtension(AuthorityInfoAccessOid());
-}
-
 }  // namespace
 
 // This fixture is for tests that apply to concrete implementations of
@@ -1237,7 +663,7 @@
 
 TEST_P(CertVerifyProcInternalTest, CertWithNullInCommonNameAndNoSAN) {
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   leaf->EraseExtension(SubjectAltNameOid());
@@ -1250,8 +676,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   int flags = 0;
@@ -1267,7 +692,7 @@
 
 TEST_P(CertVerifyProcInternalTest, CertWithNullInCommonNameAndValidSAN) {
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   leaf->SetSubjectAltName("www.fake.com");
@@ -1280,8 +705,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   int flags = 0;
@@ -1296,7 +720,7 @@
 
 TEST_P(CertVerifyProcInternalTest, CertWithNullInSAN) {
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   std::string hostname;
@@ -1307,8 +731,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   int flags = 0;
@@ -3194,7 +2617,7 @@
       bssl::UniquePtr<CRYPTO_BUFFER>* out_intermediate,
       scoped_refptr<X509Certificate>* out_root) {
     std::unique_ptr<CertBuilder> leaf, intermediate, root;
-    CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+    CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
     ASSERT_TRUE(leaf && intermediate && root);
 
     // Make the leaf certificate have an AIA (CA Issuers) that points to the
@@ -3212,126 +2635,13 @@
   }
 
   // Creates a CRL issued and signed by |crl_issuer|, marking |revoked_serials|
-  // as revoked.
-  // Returns the DER-encoded CRL.
-  static std::string CreateCrl(CertBuilder* crl_issuer,
-                               const std::vector<uint64_t>& revoked_serials,
-                               DigestAlgorithm digest) {
-    std::string signature_algorithm;
-    const EVP_MD* md = nullptr;
-    switch (digest) {
-      case DigestAlgorithm::Sha256: {
-        signature_algorithm = Sha256WithRSAEncryption();
-        md = EVP_sha256();
-        break;
-      }
-
-      case DigestAlgorithm::Sha1: {
-        signature_algorithm = Sha1WithRSAEncryption();
-        md = EVP_sha1();
-        break;
-      }
-
-      case DigestAlgorithm::Md5: {
-        signature_algorithm = Md5WithRSAEncryption();
-        md = EVP_md5();
-        break;
-      }
-
-      default:
-        ADD_FAILURE();
-        return std::string();
-    }
-    //    TBSCertList  ::=  SEQUENCE  {
-    //         version                 Version OPTIONAL,
-    //                                      -- if present, MUST be v2
-    //         signature               AlgorithmIdentifier,
-    //         issuer                  Name,
-    //         thisUpdate              Time,
-    //         nextUpdate              Time OPTIONAL,
-    //         revokedCertificates     SEQUENCE OF SEQUENCE  {
-    //              userCertificate         CertificateSerialNumber,
-    //              revocationDate          Time,
-    //              crlEntryExtensions      Extensions OPTIONAL
-    //                                       -- if present, version MUST be v2
-    //                                   }  OPTIONAL,
-    //         crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
-    //                                       -- if present, version MUST be v2
-    //                                   }
-    bssl::ScopedCBB tbs_cbb;
-    CBB tbs_cert_list, revoked_serials_cbb;
-    if (!CBB_init(tbs_cbb.get(), 10) ||
-        !CBB_add_asn1(tbs_cbb.get(), &tbs_cert_list, CBS_ASN1_SEQUENCE) ||
-        !CBB_add_asn1_uint64(&tbs_cert_list, 1 /* V2 */) ||
-        !CBBAddBytes(&tbs_cert_list, signature_algorithm) ||
-        !CBBAddBytes(&tbs_cert_list, crl_issuer->GetSubject()) ||
-        !CBBAddTime(&tbs_cert_list,
-                    base::Time::Now() - base::TimeDelta::FromDays(1)) ||
-        !CBBAddTime(&tbs_cert_list,
-                    base::Time::Now() + base::TimeDelta::FromDays(6))) {
-      ADD_FAILURE();
-      return std::string();
-    }
-    if (!revoked_serials.empty()) {
-      if (!CBB_add_asn1(&tbs_cert_list, &revoked_serials_cbb,
-                        CBS_ASN1_SEQUENCE)) {
-        ADD_FAILURE();
-        return std::string();
-      }
-      for (const int64_t revoked_serial : revoked_serials) {
-        CBB revoked_serial_cbb;
-        if (!CBB_add_asn1(&revoked_serials_cbb, &revoked_serial_cbb,
-                          CBS_ASN1_SEQUENCE) ||
-            !CBB_add_asn1_uint64(&revoked_serial_cbb, revoked_serial) ||
-            !CBBAddTime(&revoked_serial_cbb,
-                        base::Time::Now() - base::TimeDelta::FromDays(1)) ||
-            !CBB_flush(&revoked_serials_cbb)) {
-          ADD_FAILURE();
-          return std::string();
-        }
-      }
-    }
-
-    std::string tbs_tlv = FinishCBB(tbs_cbb.get());
-
-    //    CertificateList  ::=  SEQUENCE  {
-    //         tbsCertList          TBSCertList,
-    //         signatureAlgorithm   AlgorithmIdentifier,
-    //         signatureValue       BIT STRING  }
-    bssl::ScopedCBB crl_cbb;
-    CBB cert_list, signature;
-    bssl::ScopedEVP_MD_CTX ctx;
-    uint8_t* sig_out;
-    size_t sig_len;
-    if (!CBB_init(crl_cbb.get(), 10) ||
-        !CBB_add_asn1(crl_cbb.get(), &cert_list, CBS_ASN1_SEQUENCE) ||
-        !CBBAddBytes(&cert_list, tbs_tlv) ||
-        !CBBAddBytes(&cert_list, signature_algorithm) ||
-        !CBB_add_asn1(&cert_list, &signature, CBS_ASN1_BITSTRING) ||
-        !CBB_add_u8(&signature, 0 /* no unused bits */) ||
-        !EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr,
-                            crl_issuer->GetKey()) ||
-        !EVP_DigestSign(ctx.get(), nullptr, &sig_len,
-                        reinterpret_cast<const uint8_t*>(tbs_tlv.data()),
-                        tbs_tlv.size()) ||
-        !CBB_reserve(&signature, &sig_out, sig_len) ||
-        !EVP_DigestSign(ctx.get(), sig_out, &sig_len,
-                        reinterpret_cast<const uint8_t*>(tbs_tlv.data()),
-                        tbs_tlv.size()) ||
-        !CBB_did_write(&signature, sig_len)) {
-      ADD_FAILURE();
-      return std::string();
-    }
-    return FinishCBB(crl_cbb.get());
-  }
-
-  // Creates a CRL issued and signed by |crl_issuer|, marking |revoked_serials|
   // as revoked, and registers it to be served by the test server.
   // Returns the full URL to retrieve the CRL from the test server.
   GURL CreateAndServeCrl(CertBuilder* crl_issuer,
                          const std::vector<uint64_t>& revoked_serials,
                          DigestAlgorithm digest = DigestAlgorithm::Sha256) {
-    std::string crl = CreateCrl(crl_issuer, revoked_serials, digest);
+    std::string crl =
+        CertBuilder::CreateCrl(crl_issuer, revoked_serials, digest);
     std::string crl_path = MakeRandomPath(".crl");
     return RegisterSimpleTestServerHandler(crl_path, HTTP_OK,
                                            "application/pkix-crl", crl);
@@ -3734,13 +3044,12 @@
   // Create certs which have no AIA or CRL distribution points.
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with hard-fail revocation checking for local anchors.
@@ -3773,7 +3082,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Serve a root-issued CRL which does not revoke intermediate.
@@ -3784,8 +3093,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with hard-fail revocation checking for local anchors.
@@ -3814,7 +3122,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Root-issued CRL revokes leaf's serial number. This is irrelevant.
@@ -3828,8 +3136,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with hard-fail revocation checking for local anchors.
@@ -3855,7 +3162,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Root-issued CRL which does not revoke intermediate.
@@ -3867,8 +3174,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with hard-fail revocation checking for local anchors.
@@ -3893,7 +3199,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Intermediate is revoked by root issued CRL.
@@ -3905,8 +3211,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with hard-fail revocation checking for local anchors.
@@ -3934,7 +3239,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Serve a root-issued CRL which does not revoke intermediate.
@@ -3946,8 +3251,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with hard-fail revocation checking for local anchors.
@@ -3980,7 +3284,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Serve a 404 for the root-issued CRL distribution point url.
@@ -3992,8 +3296,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with hard-fail revocation checking for local anchors.
@@ -4024,13 +3327,12 @@
   // Create certs which have no AIA or CRL distribution points.
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with soft-fail revocation checking.
@@ -4060,7 +3362,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Serve a root-issued CRL which does not revoke intermediate.
@@ -4071,8 +3373,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with soft-fail revocation checking.
@@ -4099,7 +3400,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Root-issued CRL revokes leaf's serial number. This is irrelevant.
@@ -4113,8 +3414,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with soft-fail revocation checking.
@@ -4138,7 +3438,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Root-issued CRL which does not revoke intermediate.
@@ -4150,8 +3450,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with soft-fail revocation checking.
@@ -4182,7 +3481,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Intermediate is revoked by root issued CRL.
@@ -4194,8 +3493,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with soft-fail revocation checking.
@@ -4226,7 +3524,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Root-issued CRL which does not revoke intermediate.
@@ -4239,8 +3537,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with soft-fail revocation checking.
@@ -4271,7 +3568,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Root-issued CRL which does not revoke intermediate.
@@ -4284,8 +3581,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with soft-fail revocation checking.
@@ -4321,7 +3617,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Serve a root-issued CRL which does not revoke intermediate.
@@ -4333,8 +3629,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with soft-fail revocation checking.
@@ -4362,7 +3657,7 @@
 
   const char kHostname[] = "www.example.com";
   std::unique_ptr<CertBuilder> leaf, intermediate, root;
-  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  CertBuilder::CreateSimpleChain(&leaf, &intermediate, &root);
   ASSERT_TRUE(leaf && intermediate && root);
 
   // Serve a 404 for the root-issued CRL distribution point url.
@@ -4374,8 +3669,7 @@
 
   // Trust the root and build a chain to verify that includes the intermediate.
   ScopedTestRoot scoped_root(root->GetX509Certificate().get());
-  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
-      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
   ASSERT_TRUE(chain.get());
 
   // Verify with soft-fail revocation checking.
diff --git a/net/http/http_stream_factory.cc b/net/http/http_stream_factory.cc
index 55df864..c8845096 100644
--- a/net/http/http_stream_factory.cc
+++ b/net/http/http_stream_factory.cc
@@ -197,89 +197,6 @@
   }
 }
 
-HttpStreamFactory::PreconnectingProxyServer::PreconnectingProxyServer(
-    ProxyServer proxy_server,
-    PrivacyMode privacy_mode)
-    : proxy_server(proxy_server), privacy_mode(privacy_mode) {}
-
-bool HttpStreamFactory::PreconnectingProxyServer::operator<(
-    const PreconnectingProxyServer& other) const {
-  return std::tie(proxy_server, privacy_mode) <
-         std::tie(other.proxy_server, other.privacy_mode);
-}
-
-bool HttpStreamFactory::PreconnectingProxyServer::operator==(
-    const PreconnectingProxyServer& other) const {
-  return proxy_server == other.proxy_server &&
-         privacy_mode == other.privacy_mode;
-}
-
-bool HttpStreamFactory::OnInitConnection(const JobController& controller,
-                                         const ProxyInfo& proxy_info,
-                                         PrivacyMode privacy_mode) {
-  if (!controller.is_preconnect()) {
-    // Connection initialization can be skipped only for the preconnect jobs.
-    return false;
-  }
-
-  if (!ProxyServerSupportsPriorities(proxy_info))
-    return false;
-
-  PreconnectingProxyServer preconnecting_proxy_server(proxy_info.proxy_server(),
-                                                      privacy_mode);
-
-  if (base::Contains(preconnecting_proxy_servers_,
-                     preconnecting_proxy_server)) {
-    UMA_HISTOGRAM_EXACT_LINEAR("Net.PreconnectSkippedToProxyServers", 1, 2);
-    // Skip preconnect to the proxy server since we are already preconnecting
-    // (probably via some other job). See https://crbug.com/682041 for details.
-    return true;
-  }
-
-  // Add the proxy server to the set of preconnecting proxy servers.
-  // The maximum size of |preconnecting_proxy_servers_|.
-  static const size_t kMaxPreconnectingServerSize = 3;
-  if (preconnecting_proxy_servers_.size() >= kMaxPreconnectingServerSize) {
-    // Erase the first entry. A better approach (at the cost of higher memory
-    // overhead) may be to erase the least recently used entry.
-    preconnecting_proxy_servers_.erase(preconnecting_proxy_servers_.begin());
-  }
-
-  preconnecting_proxy_servers_.insert(preconnecting_proxy_server);
-  DCHECK_GE(kMaxPreconnectingServerSize, preconnecting_proxy_servers_.size());
-  // The first preconnect should be allowed.
-  return false;
-}
-
-void HttpStreamFactory::OnStreamReady(const ProxyInfo& proxy_info,
-                                      PrivacyMode privacy_mode) {
-  if (proxy_info.is_empty())
-    return;
-  preconnecting_proxy_servers_.erase(
-      PreconnectingProxyServer(proxy_info.proxy_server(), privacy_mode));
-}
-
-bool HttpStreamFactory::ProxyServerSupportsPriorities(
-    const ProxyInfo& proxy_info) const {
-  if (proxy_info.is_empty() || !proxy_info.proxy_server().is_valid())
-    return false;
-
-  if (!proxy_info.proxy_server().is_https())
-    return false;
-
-  HostPortPair host_port_pair = proxy_info.proxy_server().host_port_pair();
-  DCHECK(!host_port_pair.IsEmpty());
-
-  url::SchemeHostPort scheme_host_port("https", host_port_pair.host(),
-                                       host_port_pair.port());
-
-  // TODO(https://crbug.com/993517): Figure out what NetworkIsolationKey() to
-  // use here, and what to do about this and |preconnecting_proxy_servers_|,
-  // which leaks data across NetworkIsolationKeys.
-  return session_->http_server_properties()->SupportsRequestPriority(
-      scheme_host_port, NetworkIsolationKey());
-}
-
 void HttpStreamFactory::DumpMemoryStats(
     base::trace_event::ProcessMemoryDump* pmd,
     const std::string& parent_absolute_name) const {
diff --git a/net/http/http_stream_factory.h b/net/http/http_stream_factory.h
index b7909cc..5a16275 100644
--- a/net/http/http_stream_factory.h
+++ b/net/http/http_stream_factory.h
@@ -129,20 +129,6 @@
 
   url::SchemeHostPort RewriteHost(const url::SchemeHostPort& server);
 
-  // |PreconnectingProxyServer| holds information of a connection to a single
-  // proxy server.
-  struct PreconnectingProxyServer {
-    PreconnectingProxyServer(ProxyServer proxy_server,
-                             PrivacyMode privacy_mode);
-
-    // Needed to be an element of std::set.
-    bool operator<(const PreconnectingProxyServer& other) const;
-    bool operator==(const PreconnectingProxyServer& other) const;
-
-    const ProxyServer proxy_server;
-    const PrivacyMode privacy_mode;
-  };
-
   // Values must not be changed or reused.  Keep in sync with identically named
   // enum in histograms.xml.
   enum AlternativeServiceType {
@@ -179,21 +165,6 @@
   // from |job_controller_set_|.
   void OnJobControllerComplete(JobController* controller);
 
-  // Returns true if a connection to the proxy server contained in |proxy_info|
-  // that has privacy mode |privacy_mode| can be skipped by a job controlled by
-  // |controller|.
-  bool OnInitConnection(const JobController& controller,
-                        const ProxyInfo& proxy_info,
-                        PrivacyMode privacy_mode);
-
-  // Notifies |this| that a stream to the proxy server contained in |proxy_info|
-  // with privacy mode |privacy_mode| is ready.
-  void OnStreamReady(const ProxyInfo& proxy_info, PrivacyMode privacy_mode);
-
-  // Returns true if |proxy_info| contains a proxy server that supports request
-  // priorities.
-  bool ProxyServerSupportsPriorities(const ProxyInfo& proxy_info) const;
-
   HttpNetworkSession* const session_;
 
   // All Requests/Preconnects are assigned with a JobController to manage
@@ -206,10 +177,6 @@
   // Factory used by job controllers for creating jobs.
   std::unique_ptr<JobFactory> job_factory_;
 
-  // Set of proxy servers that support request priorities to which subsequent
-  // preconnects should be skipped.
-  std::set<PreconnectingProxyServer> preconnecting_proxy_servers_;
-
   DISALLOW_COPY_AND_ASSIGN(HttpStreamFactory);
 };
 
diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc
index 8e8a1c8a..fd4c9d3 100644
--- a/net/http/http_stream_factory_job.cc
+++ b/net/http/http_stream_factory_job.cc
@@ -702,11 +702,6 @@
   DCHECK(proxy_info_.proxy_server().is_valid());
   next_state_ = STATE_INIT_CONNECTION_COMPLETE;
 
-  if (delegate_->OnInitConnection(proxy_info_)) {
-    // Return since the connection initialization can be skipped.
-    return OK;
-  }
-
   if (proxy_info_.is_https() || proxy_info_.is_quic()) {
     // Disable network fetches for HTTPS proxies, since the network requests
     // are probably going to need to go through the proxy too.
diff --git a/net/http/http_stream_factory_job.h b/net/http/http_stream_factory_job.h
index 94d1a91..c7753aee 100644
--- a/net/http/http_stream_factory_job.h
+++ b/net/http/http_stream_factory_job.h
@@ -106,10 +106,6 @@
                                   const ProxyInfo& used_proxy_info,
                                   HttpAuthController* auth_controller) = 0;
 
-    // Returns true if the connection initialization to the proxy server
-    // contained in |proxy_info| can be skipped.
-    virtual bool OnInitConnection(const ProxyInfo& proxy_info) = 0;
-
     // Invoked when the |job| finishes pre-connecting sockets.
     virtual void OnPreconnectsComplete(Job* job) = 0;
 
diff --git a/net/http/http_stream_factory_job_controller.cc b/net/http/http_stream_factory_job_controller.cc
index 028e652..bd12958e 100644
--- a/net/http/http_stream_factory_job_controller.cc
+++ b/net/http/http_stream_factory_job_controller.cc
@@ -211,8 +211,6 @@
     const SSLConfig& used_ssl_config) {
   DCHECK(job);
 
-  factory_->OnStreamReady(job->proxy_info(), request_info_.privacy_mode);
-
   if (IsJobOrphaned(job)) {
     // We have bound a job to the associated HttpStreamRequest, |job| has been
     // orphaned.
@@ -419,12 +417,6 @@
                               auth_controller);
 }
 
-bool HttpStreamFactory::JobController::OnInitConnection(
-    const ProxyInfo& proxy_info) {
-  return factory_->OnInitConnection(*this, proxy_info,
-                                    request_info_.privacy_mode);
-}
-
 void HttpStreamFactory::JobController::OnPreconnectsComplete(Job* job) {
   DCHECK_EQ(main_job_.get(), job);
   main_job_.reset();
diff --git a/net/http/http_stream_factory_job_controller.h b/net/http/http_stream_factory_job_controller.h
index d91a559c..7c37e02 100644
--- a/net/http/http_stream_factory_job_controller.h
+++ b/net/http/http_stream_factory_job_controller.h
@@ -121,8 +121,6 @@
                         const ProxyInfo& used_proxy_info,
                         HttpAuthController* auth_controller) override;
 
-  bool OnInitConnection(const ProxyInfo& proxy_info) override;
-
   // Invoked when the |job| finishes pre-connecting sockets.
   void OnPreconnectsComplete(Job* job) override;
 
diff --git a/net/http/http_stream_factory_unittest.cc b/net/http/http_stream_factory_unittest.cc
index d1256bf..fb873fe 100644
--- a/net/http/http_stream_factory_unittest.cc
+++ b/net/http/http_stream_factory_unittest.cc
@@ -1216,169 +1216,6 @@
   }
 }
 
-// Verify that only one preconnect job succeeds to a proxy server that supports
-// request priorities.
-TEST_F(HttpStreamFactoryTest, OnlyOnePreconnectToProxyServer) {
-  for (bool set_http_server_properties : {false, true}) {
-    for (int num_streams = 1; num_streams < 3; ++num_streams) {
-      base::HistogramTester histogram_tester;
-      GURL url = GURL("http://www.google.com");
-      std::unique_ptr<ProxyResolutionService> proxy_resolution_service =
-          ProxyResolutionService::CreateFixedFromPacResult(
-              "HTTPS myproxy.org:443", TRAFFIC_ANNOTATION_FOR_TESTS);
-
-      // Set up the proxy server as a server that supports request priorities.
-      HttpServerProperties http_server_properties;
-      if (set_http_server_properties) {
-        url::SchemeHostPort spdy_server("https", "myproxy.org", 443);
-        http_server_properties.SetSupportsSpdy(spdy_server,
-                                               NetworkIsolationKey(), true);
-      }
-
-      SpdySessionDependencies session_deps;
-      HttpNetworkSession::Params session_params =
-          SpdySessionDependencies::CreateSessionParams(&session_deps);
-      session_params.enable_quic = true;
-
-      HttpNetworkSession::Context session_context =
-          SpdySessionDependencies::CreateSessionContext(&session_deps);
-      session_context.proxy_resolution_service = proxy_resolution_service.get();
-      session_context.http_server_properties = &http_server_properties;
-
-      auto session =
-          std::make_unique<HttpNetworkSession>(session_params, session_context);
-
-      HttpNetworkSessionPeer peer(session.get());
-      ProxyServer proxy_server(ProxyServer::SCHEME_HTTPS,
-                               HostPortPair("myproxy.org", 443));
-
-      for (int preconnect_request = 0; preconnect_request < 2;
-           ++preconnect_request) {
-        CommonConnectJobParams common_connect_job_params =
-            session->CreateCommonConnectJobParams();
-        CapturePreconnectsTransportSocketPool* http_proxy_pool =
-            new CapturePreconnectsTransportSocketPool(
-                &common_connect_job_params);
-
-        auto mock_pool_manager =
-            std::make_unique<MockClientSocketPoolManager>();
-        mock_pool_manager->SetSocketPool(proxy_server,
-                                         base::WrapUnique(http_proxy_pool));
-        peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
-
-        HttpRequestInfo request;
-        request.method = "GET";
-        request.url = url;
-        request.load_flags = 0;
-        request.traffic_annotation =
-            MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
-
-        if (preconnect_request == 0) {
-          // First preconnect job should succeed.
-          session->http_stream_factory()->PreconnectStreams(num_streams,
-                                                            request);
-          EXPECT_EQ(num_streams, http_proxy_pool->last_num_streams());
-        } else {
-          // Second preconnect job should not succeed.
-          session->http_stream_factory()->PreconnectStreams(num_streams,
-                                                            request);
-          if (set_http_server_properties) {
-            EXPECT_EQ(-1, http_proxy_pool->last_num_streams());
-          } else {
-            EXPECT_EQ(num_streams, http_proxy_pool->last_num_streams());
-          }
-        }
-      }
-      // Preconnects should be skipped only to proxy servers that support
-      // request priorities.
-      if (set_http_server_properties) {
-        histogram_tester.ExpectUniqueSample(
-            "Net.PreconnectSkippedToProxyServers", 1, 1);
-      } else {
-        histogram_tester.ExpectTotalCount("Net.PreconnectSkippedToProxyServers",
-                                          0);
-      }
-    }
-  }
-}
-
-// Verify that preconnect to a HTTP2 proxy server with a privacy mode different
-// than that of the in-flight preconnect job succeeds.
-TEST_F(HttpStreamFactoryTest, ProxyServerPreconnectDifferentPrivacyModes) {
-  int num_streams = 1;
-  base::HistogramTester histogram_tester;
-  GURL url = GURL("http://www.google.com");
-  std::unique_ptr<ProxyResolutionService> proxy_resolution_service =
-      ProxyResolutionService::CreateFixedFromPacResult(
-          "HTTPS myproxy.org:443", TRAFFIC_ANNOTATION_FOR_TESTS);
-
-  // Set up the proxy server as a server that supports request priorities.
-  HttpServerProperties http_server_properties;
-
-  url::SchemeHostPort spdy_server("https", "myproxy.org", 443);
-  http_server_properties.SetSupportsSpdy(spdy_server, NetworkIsolationKey(),
-                                         true);
-
-  SpdySessionDependencies session_deps;
-  HttpNetworkSession::Params session_params =
-      SpdySessionDependencies::CreateSessionParams(&session_deps);
-  session_params.enable_quic = true;
-
-  HttpNetworkSession::Context session_context =
-      SpdySessionDependencies::CreateSessionContext(&session_deps);
-  session_context.proxy_resolution_service = proxy_resolution_service.get();
-  session_context.http_server_properties = &http_server_properties;
-
-  auto session =
-      std::make_unique<HttpNetworkSession>(session_params, session_context);
-
-  HttpNetworkSessionPeer peer(session.get());
-  ProxyServer proxy_server(ProxyServer::SCHEME_HTTPS,
-                           HostPortPair("myproxy.org", 443));
-
-  CommonConnectJobParams common_connect_job_params =
-      session->CreateCommonConnectJobParams();
-  CapturePreconnectsTransportSocketPool* http_proxy_pool =
-      new CapturePreconnectsTransportSocketPool(&common_connect_job_params);
-
-  auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-  mock_pool_manager->SetSocketPool(proxy_server,
-                                   base::WrapUnique(http_proxy_pool));
-  peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
-
-  HttpRequestInfo request_privacy_mode_disabled;
-  request_privacy_mode_disabled.method = "GET";
-  request_privacy_mode_disabled.url = url;
-  request_privacy_mode_disabled.load_flags = 0;
-  request_privacy_mode_disabled.traffic_annotation =
-      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
-
-  // First preconnect job should succeed.
-  session->http_stream_factory()->PreconnectStreams(
-      num_streams, request_privacy_mode_disabled);
-  EXPECT_EQ(num_streams, http_proxy_pool->last_num_streams());
-  http_proxy_pool->reset();
-
-  // Second preconnect job with same privacy mode should not succeed.
-  session->http_stream_factory()->PreconnectStreams(
-      num_streams + 1, request_privacy_mode_disabled);
-  EXPECT_EQ(-1, http_proxy_pool->last_num_streams());
-
-  // Next request with a different privacy mode should succeed.
-  HttpRequestInfo request_privacy_mode_enabled;
-  request_privacy_mode_enabled.method = "GET";
-  request_privacy_mode_enabled.url = url;
-  request_privacy_mode_enabled.load_flags = 0;
-  request_privacy_mode_enabled.privacy_mode = PRIVACY_MODE_ENABLED;
-  request_privacy_mode_enabled.traffic_annotation =
-      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
-
-  // Request with a different privacy mode should succeed.
-  session->http_stream_factory()->PreconnectStreams(
-      num_streams, request_privacy_mode_enabled);
-  EXPECT_EQ(num_streams, http_proxy_pool->last_num_streams());
-}
-
 namespace {
 
 // Return count of distinct groups in given socket pool.
@@ -1702,93 +1539,6 @@
   EXPECT_FALSE(waiter.used_proxy_info().is_direct());
 }
 
-// Verifies that once a stream has been created to a proxy server (that supports
-// request priorities) the next preconnect job can again open new sockets.
-TEST_F(HttpStreamFactoryTest, RequestHttpStreamOverProxyWithPreconnects) {
-  SpdySessionDependencies session_deps(ProxyResolutionService::CreateFixed(
-      "https://myproxy.org:443", TRAFFIC_ANNOTATION_FOR_TESTS));
-
-  // Set up the proxy server as a server that supports request priorities.
-  auto http_server_properties = std::make_unique<HttpServerProperties>();
-  url::SchemeHostPort spdy_server("https", "myproxy.org", 443);
-  http_server_properties->SetSupportsSpdy(spdy_server, NetworkIsolationKey(),
-                                          true);
-  session_deps.http_server_properties = std::move(http_server_properties);
-
-  StaticSocketDataProvider socket_data;
-  socket_data.set_connect_data(MockConnect(ASYNC, OK));
-  session_deps.socket_factory->AddSocketDataProvider(&socket_data);
-
-  SSLSocketDataProvider ssl_data(ASYNC, OK);
-  session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_data);
-
-  std::unique_ptr<HttpNetworkSession> session(
-      SpdySessionDependencies::SpdyCreateSession(&session_deps));
-
-  // Now preconnect a request. Only the first preconnect should succeed.
-  HttpRequestInfo request_info;
-  request_info.method = "GET";
-  request_info.url = GURL("http://www.google.com");
-  request_info.load_flags = 0;
-  request_info.traffic_annotation =
-      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
-
-  base::HistogramTester histogram_tester;
-  const int num_preconnects = 5;
-
-  // First preconnect would be successful. The next |num_preconnects-1| would be
-  // skipped.
-  for (int preconnect_request = 1; preconnect_request <= num_preconnects;
-       ++preconnect_request) {
-    session->http_stream_factory()->PreconnectStreams(1, request_info);
-    base::RunLoop().RunUntilIdle();
-    while (GetSocketPoolGroupCount(session->GetSocketPool(
-               HttpNetworkSession::NORMAL_SOCKET_POOL,
-               ProxyServer(ProxyServer::SCHEME_HTTPS,
-                           HostPortPair("myproxy.org", 443)))) == 0) {
-      base::RunLoop().RunUntilIdle();
-    }
-  }
-
-  histogram_tester.ExpectUniqueSample("Net.PreconnectSkippedToProxyServers", 1,
-                                      num_preconnects - 1);
-
-  // Start a request. This should cause subsequent preconnect to proxy server to
-  // succeed.
-  SSLConfig ssl_config;
-  StreamRequestWaiter waiter;
-  std::unique_ptr<HttpStreamRequest> request(
-      session->http_stream_factory()->RequestStream(
-          request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter,
-          /* enable_ip_based_pooling = */ true,
-          /* enable_alternative_services = */ true, NetLogWithSource()));
-  waiter.WaitForStream();
-  EXPECT_TRUE(waiter.stream_done());
-  ASSERT_TRUE(nullptr != waiter.stream());
-  EXPECT_TRUE(nullptr == waiter.websocket_stream());
-
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL,
-                   ProxyServer(ProxyServer::SCHEME_HTTPS,
-                               HostPortPair("myproxy.org", 443)))));
-  EXPECT_FALSE(waiter.used_proxy_info().is_direct());
-
-  for (int preconnect_request = 1; preconnect_request <= num_preconnects;
-       ++preconnect_request) {
-    session->http_stream_factory()->PreconnectStreams(1, request_info);
-    base::RunLoop().RunUntilIdle();
-    EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPool(
-                     HttpNetworkSession::NORMAL_SOCKET_POOL,
-                     ProxyServer(ProxyServer::SCHEME_HTTPS,
-                                 HostPortPair("myproxy.org", 443)))));
-  }
-
-  // First preconnect would be successful since the stream to the proxy server
-  // has suceeded. The next |num_preconnects-1| would be skipped.
-  histogram_tester.ExpectUniqueSample("Net.PreconnectSkippedToProxyServers", 1,
-                                      2 * (num_preconnects - 1));
-}
-
 TEST_F(HttpStreamFactoryTest, RequestWebSocketBasicHandshakeStream) {
   SpdySessionDependencies session_deps(ProxyResolutionService::CreateDirect());
 
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index f78217df..888f3b6 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -984,7 +984,7 @@
       if (!VersionHasStreamType(connection()->transport_version())) {
         WritePriority(update.id, update.parent_stream_id, update.weight,
                       update.exclusive);
-      } else {
+      } else if (FLAGS_quic_allow_http3_priority) {
         quic::PriorityFrame frame;
         frame.weight = update.weight;
         frame.exclusive = update.exclusive;
@@ -3118,7 +3118,7 @@
           promised_id, priority, &parent_stream_id, &weight, &exclusive);
       if (!VersionHasStreamType(connection()->transport_version())) {
         WritePriority(promised_id, parent_stream_id, weight, exclusive);
-      } else {
+      } else if (FLAGS_quic_allow_http3_priority) {
         quic::PriorityFrame frame;
         frame.weight = weight;
         frame.exclusive = exclusive;
diff --git a/net/quic/quic_chromium_packet_reader.cc b/net/quic/quic_chromium_packet_reader.cc
index c0c2bc7..468052e 100644
--- a/net/quic/quic_chromium_packet_reader.cc
+++ b/net/quic/quic_chromium_packet_reader.cc
@@ -31,7 +31,7 @@
       yield_after_duration_(yield_after_duration),
       yield_after_(quic::QuicTime::Infinite()),
       read_buffer_(base::MakeRefCounted<IOBufferWithSize>(
-          static_cast<size_t>(quic::kMaxOutgoingPacketSize))),
+          static_cast<size_t>(quic::kMaxIncomingPacketSize))),
       net_log_(net_log) {}
 
 QuicChromiumPacketReader::~QuicChromiumPacketReader() {}
@@ -74,8 +74,7 @@
 }
 
 size_t QuicChromiumPacketReader::EstimateMemoryUsage() const {
-  // Return the size of |read_buffer_|.
-  return quic::kMaxOutgoingPacketSize;
+  return read_buffer_->size();
 }
 
 bool QuicChromiumPacketReader::ProcessReadResult(int result) {
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 26b9ac2..f2a1ebd 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -391,7 +391,4 @@
           false)
 
 // If true, support HTTP/3 priority in v99.
-// TODO(renjietang): flip back to false since draft 23
-// (https://tools.ietf.org/html/draft-ietf-quic-http-23#appendix-B.1)
-// no longer support HTTP/3 priority.
-QUIC_FLAG(bool, FLAGS_quic_allow_http3_priority, true)
+QUIC_FLAG(bool, FLAGS_quic_allow_http3_priority, false)
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index c5ac6dd..69e3099e1b 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -3352,7 +3352,7 @@
                        client_maker_.MakeConnectionClosePacket(
                            13, true, quic::QUIC_TOO_MANY_RTOS,
                            "5 consecutive retransmission timeouts"));
-  } else {
+  } else if (FLAGS_quic_allow_http3_priority) {
     quic_data.AddWrite(
         SYNCHRONOUS, client_maker_.MakeRstPacket(
                          3, true, GetNthClientInitiatedBidirectionalStreamId(0),
@@ -3390,6 +3390,46 @@
                        client_maker_.MakeConnectionClosePacket(
                            14, true, quic::QUIC_TOO_MANY_RTOS,
                            "5 consecutive retransmission timeouts"));
+  } else {
+    quic_data.AddWrite(
+        SYNCHRONOUS, client_maker_.MakeRstPacket(
+                         3, true, GetNthClientInitiatedBidirectionalStreamId(0),
+                         quic::QUIC_STREAM_CANCELLED));
+    client_maker_.RemoveSavedStreamFrames(
+        GetNthClientInitiatedBidirectionalStreamId(0));
+    // When PRIORITY is disabled, packet 2 only contains request headers. And
+    // since the request stream is reset, packet 2 will not be retransmitted.
+    // TLP 1
+    quic_data.AddWrite(SYNCHRONOUS,
+                       client_maker_.MakeRetransmissionPacket(1, 4, true));
+    // TLP 2
+    quic_data.AddWrite(SYNCHRONOUS,
+                       client_maker_.MakeRetransmissionPacket(3, 5, true));
+    // RTO 1
+    quic_data.AddWrite(SYNCHRONOUS,
+                       client_maker_.MakeRetransmissionPacket(1, 6, true));
+    quic_data.AddWrite(SYNCHRONOUS,
+                       client_maker_.MakeRetransmissionPacket(3, 7, true));
+    // RTO 2
+    quic_data.AddWrite(SYNCHRONOUS,
+                       client_maker_.MakeRetransmissionPacket(1, 8, true));
+    quic_data.AddWrite(SYNCHRONOUS,
+                       client_maker_.MakeRetransmissionPacket(3, 9, true));
+    // RTO 3
+    quic_data.AddWrite(SYNCHRONOUS,
+                       client_maker_.MakeRetransmissionPacket(1, 10, true));
+    quic_data.AddWrite(SYNCHRONOUS,
+                       client_maker_.MakeRetransmissionPacket(3, 11, true));
+    // RTO 4
+    quic_data.AddWrite(SYNCHRONOUS,
+                       client_maker_.MakeRetransmissionPacket(1, 12, true));
+    quic_data.AddWrite(SYNCHRONOUS,
+                       client_maker_.MakeRetransmissionPacket(3, 13, true));
+    // RTO 5
+    quic_data.AddWrite(SYNCHRONOUS,
+                       client_maker_.MakeConnectionClosePacket(
+                           14, true, quic::QUIC_TOO_MANY_RTOS,
+                           "5 consecutive retransmission timeouts"));
   }
 
   quic_data.AddRead(ASYNC, OK);
@@ -6553,8 +6593,10 @@
           GetNthServerInitiatedUnidirectionalStreamId(0), false,
           GetRequestHeaders("GET", "https", "/pushed.jpg"), &server_maker_));
   if ((client_headers_include_h2_stream_dependency_ &&
-       version_.transport_version >= quic::QUIC_VERSION_43) ||
-      VersionHasStreamType(version_.transport_version)) {
+       version_.transport_version >= quic::QUIC_VERSION_43 &&
+       !VersionHasStreamType(version_.transport_version)) ||
+      (VersionHasStreamType(version_.transport_version) &&
+       FLAGS_quic_allow_http3_priority)) {
     mock_quic_data.AddWrite(
         SYNCHRONOUS,
         ConstructClientPriorityPacket(
@@ -6645,8 +6687,10 @@
           GetNthServerInitiatedUnidirectionalStreamId(0), false,
           GetRequestHeaders("GET", "https", "/pushed.jpg"), &server_maker_));
   if ((client_headers_include_h2_stream_dependency_ &&
-       version_.transport_version >= quic::QUIC_VERSION_43) ||
-      VersionHasStreamType(version_.transport_version)) {
+       version_.transport_version >= quic::QUIC_VERSION_43 &&
+       !VersionHasStreamType(version_.transport_version)) ||
+      (VersionHasStreamType(version_.transport_version) &&
+       FLAGS_quic_allow_http3_priority)) {
     mock_quic_data.AddWrite(
         SYNCHRONOUS,
         ConstructClientPriorityPacket(
@@ -6913,8 +6957,10 @@
   }
 
   if ((client_headers_include_h2_stream_dependency_ &&
-       version_.transport_version >= quic::QUIC_VERSION_43) ||
-      VersionHasStreamType(version_.transport_version)) {
+       version_.transport_version >= quic::QUIC_VERSION_43 &&
+       !VersionHasStreamType(version_.transport_version)) ||
+      (VersionHasStreamType(version_.transport_version) &&
+       (FLAGS_quic_allow_http3_priority))) {
     mock_quic_data.AddWrite(
         SYNCHRONOUS,
         ConstructClientPriorityPacket(
@@ -7585,8 +7631,10 @@
           GetRequestHeaders("GET", "https", "/pushed.jpg"), &server_maker_));
 
   if ((client_headers_include_h2_stream_dependency_ &&
-       version_.transport_version >= quic::QUIC_VERSION_43) ||
-      VersionHasStreamType(version_.transport_version)) {
+       version_.transport_version >= quic::QUIC_VERSION_43 &&
+       !VersionHasStreamType(version_.transport_version)) ||
+      (VersionHasStreamType(version_.transport_version) &&
+       FLAGS_quic_allow_http3_priority)) {
     mock_quic_data.AddWrite(
         SYNCHRONOUS,
         ConstructClientPriorityPacket(
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 4bdfe48c..208abb5 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -227,6 +227,14 @@
   const base::Callback<bool(const GURL&)> origin_filter_;
 };
 
+std::set<std::string> HostsFromOrigins(std::set<HostPortPair> origins) {
+  std::set<std::string> hosts;
+  for (const auto& origin : origins) {
+    hosts.insert(origin.host());
+  }
+  return hosts;
+}
+
 }  // namespace
 
 QuicParams::QuicParams()
@@ -1098,21 +1106,6 @@
   return expect_on_host_resolution_;
 }
 
-void QuicStreamRequest::SetSession(
-    std::unique_ptr<QuicChromiumClientSession::Handle> session) {
-  session_ = move(session);
-}
-
-void QuicStreamRequest::OnConnectionFailedOnDefaultNetwork() {
-  if (!failed_on_default_network_callback_.is_null())
-    std::move(failed_on_default_network_callback_).Run(OK);
-}
-
-void QuicStreamRequest::OnRequestComplete(int rv) {
-  factory_ = nullptr;
-  std::move(callback_).Run(rv);
-}
-
 void QuicStreamRequest::ExpectOnHostResolution() {
   expect_on_host_resolution_ = true;
 }
@@ -1125,6 +1118,16 @@
   }
 }
 
+void QuicStreamRequest::OnRequestComplete(int rv) {
+  factory_ = nullptr;
+  std::move(callback_).Run(rv);
+}
+
+void QuicStreamRequest::OnConnectionFailedOnDefaultNetwork() {
+  if (!failed_on_default_network_callback_.is_null())
+    std::move(failed_on_default_network_callback_).Run(OK);
+}
+
 base::TimeDelta QuicStreamRequest::GetTimeDelayForWaitingJob() const {
   if (!factory_)
     return base::TimeDelta();
@@ -1145,17 +1148,32 @@
   return std::move(session_);
 }
 
-namespace {
-
-std::set<std::string> HostsFromOrigins(std::set<HostPortPair> origins) {
-  std::set<std::string> hosts;
-  for (const auto& origin : origins) {
-    hosts.insert(origin.host());
-  }
-  return hosts;
+void QuicStreamRequest::SetSession(
+    std::unique_ptr<QuicChromiumClientSession::Handle> session) {
+  session_ = move(session);
 }
 
-}  // namespace
+QuicStreamFactory::QuicSessionAliasKey::QuicSessionAliasKey(
+    const HostPortPair& destination,
+    const QuicSessionKey& session_key)
+    : destination_(destination), session_key_(session_key) {}
+
+bool QuicStreamFactory::QuicSessionAliasKey::operator<(
+    const QuicSessionAliasKey& other) const {
+  return std::tie(destination_, session_key_) <
+         std::tie(other.destination_, other.session_key_);
+}
+
+bool QuicStreamFactory::QuicSessionAliasKey::operator==(
+    const QuicSessionAliasKey& other) const {
+  return destination_.Equals(other.destination_) &&
+         session_key_ == other.session_key_;
+}
+
+size_t QuicStreamFactory::QuicSessionAliasKey::EstimateMemoryUsage() const {
+  return base::trace_event::EstimateMemoryUsage(destination_) +
+         base::trace_event::EstimateMemoryUsage(session_key_.server_id());
+}
 
 QuicStreamFactory::QuicStreamFactory(
     NetLog* net_log,
@@ -1218,86 +1236,6 @@
   InitializeMigrationOptions();
 }
 
-void QuicStreamFactory::InitializeMigrationOptions() {
-  // The following list of options cannot be set immediately until
-  // prerequisites are met. Cache the initial setting in local variables and
-  // reset them in |params_|.
-  bool migrate_sessions_on_network_change =
-      params_.migrate_sessions_on_network_change_v2;
-  bool migrate_sessions_early = params_.migrate_sessions_early_v2;
-  bool retry_on_alternate_network_before_handshake =
-      params_.retry_on_alternate_network_before_handshake;
-  bool migrate_idle_sessions = params_.migrate_idle_sessions;
-  bool allow_port_migration = params_.allow_port_migration;
-  params_.migrate_sessions_on_network_change_v2 = false;
-  params_.migrate_sessions_early_v2 = false;
-  params_.allow_port_migration = false;
-  params_.retry_on_alternate_network_before_handshake = false;
-  params_.migrate_idle_sessions = false;
-
-  DCHECK(!(migrate_sessions_early && params_.go_away_on_path_degrading));
-  DCHECK(!(allow_port_migration && params_.go_away_on_path_degrading));
-
-  // TODO(zhongyi): deprecate |goaway_sessions_on_ip_change| if the experiment
-  // is no longer needed.
-  // goaway_sessions_on_ip_change and close_sessions_on_ip_change should never
-  // be simultaneously set to true.
-  DCHECK(!(params_.close_sessions_on_ip_change &&
-           params_.goaway_sessions_on_ip_change));
-
-  bool handle_ip_change = params_.close_sessions_on_ip_change ||
-                          params_.goaway_sessions_on_ip_change;
-  // If IP address changes are handled explicitly, connection migration should
-  // not be set.
-  DCHECK(!(handle_ip_change && migrate_sessions_on_network_change));
-
-  if (handle_ip_change)
-    NetworkChangeNotifier::AddIPAddressObserver(this);
-
-  // Port migration and early migration both act on path degrading and thus can
-  // not be simultaneously set.
-  DCHECK(!allow_port_migration || !migrate_sessions_early);
-
-  if (allow_port_migration)
-    params_.allow_port_migration = true;
-
-  if (!NetworkChangeNotifier::AreNetworkHandlesSupported())
-    return;
-
-  NetworkChangeNotifier::AddNetworkObserver(this);
-  // Perform checks on the connection migration options.
-  if (!migrate_sessions_on_network_change) {
-    DCHECK(!migrate_sessions_early);
-    return;
-  }
-
-  // Enable migration on platform notifications.
-  params_.migrate_sessions_on_network_change_v2 = true;
-
-  if (!migrate_sessions_early) {
-    DCHECK(!migrate_idle_sessions &&
-           !retry_on_alternate_network_before_handshake);
-    return;
-  }
-
-  // Enable migration on path degrading.
-  params_.migrate_sessions_early_v2 = true;
-  // Set retransmittable on wire timeout for migration on path degrading if no
-  // value is specified.
-  if (retransmittable_on_wire_timeout_.IsZero()) {
-    retransmittable_on_wire_timeout_ = quic::QuicTime::Delta::FromMicroseconds(
-        kDefaultRetransmittableOnWireTimeout.InMicroseconds());
-  }
-
-  // Enable retry on alternate network before handshake.
-  if (retry_on_alternate_network_before_handshake)
-    params_.retry_on_alternate_network_before_handshake = true;
-
-  // Enable migration for idle sessions.
-  if (migrate_idle_sessions)
-    params_.migrate_idle_sessions = true;
-}
-
 QuicStreamFactory::~QuicStreamFactory() {
   UMA_HISTOGRAM_COUNTS_1000("Net.NumQuicSessionsAtShutdown",
                             all_sessions_.size());
@@ -1323,81 +1261,6 @@
   }
 }
 
-void QuicStreamFactory::set_is_quic_known_to_work_on_current_network(
-    bool is_quic_known_to_work_on_current_network) {
-  is_quic_known_to_work_on_current_network_ =
-      is_quic_known_to_work_on_current_network;
-  if (!(local_address_ == IPEndPoint())) {
-    if (is_quic_known_to_work_on_current_network_) {
-      http_server_properties_->SetLastLocalAddressWhenQuicWorked(
-          local_address_.address());
-    } else {
-      http_server_properties_->ClearLastLocalAddressWhenQuicWorked();
-    }
-  }
-}
-
-base::TimeDelta QuicStreamFactory::GetTimeDelayForWaitingJob(
-    const quic::QuicServerId& server_id,
-    const NetworkIsolationKey& network_isolation_key) {
-  // If |is_quic_known_to_work_on_current_network_| is false, then one of the
-  // following is true:
-  // 1) This is startup and QuicStreamFactory::CreateSession() and
-  // ConfigureSocket() have yet to be called, and it is not yet known
-  // if the current network is the last one where QUIC worked.
-  // 2) Startup has been completed, and QUIC has not been used
-  // successfully since startup, or on this network before.
-  if (!is_quic_known_to_work_on_current_network_) {
-    // If |need_to_check_persisted_supports_quic_| is false, this is case 1)
-    // above. If HasLastLocalAddressWhenQuicWorked() is also true, then there's
-    // a chance the current network is the last one on which QUIC worked. So
-    // only delay the request if there's no chance that is the case.
-    if (!need_to_check_persisted_supports_quic_ ||
-        !http_server_properties_->HasLastLocalAddressWhenQuicWorked()) {
-      return base::TimeDelta();
-    }
-  }
-
-  int64_t srtt = 1.5 * GetServerNetworkStatsSmoothedRttInMicroseconds(
-                           server_id, network_isolation_key);
-  // Picked 300ms based on mean time from
-  // Net.QuicSession.HostResolution.HandshakeConfirmedTime histogram.
-  const int kDefaultRTT = 300 * quic::kNumMicrosPerMilli;
-  if (!srtt)
-    srtt = kDefaultRTT;
-  return base::TimeDelta::FromMicroseconds(srtt);
-}
-
-void QuicStreamFactory::DumpMemoryStats(
-    base::trace_event::ProcessMemoryDump* pmd,
-    const std::string& parent_absolute_name) const {
-  if (all_sessions_.empty() && active_jobs_.empty())
-    return;
-  base::trace_event::MemoryAllocatorDump* factory_dump =
-      pmd->CreateAllocatorDump(parent_absolute_name + "/quic_stream_factory");
-  size_t memory_estimate =
-      base::trace_event::EstimateMemoryUsage(all_sessions_) +
-      base::trace_event::EstimateMemoryUsage(active_sessions_) +
-      base::trace_event::EstimateMemoryUsage(session_aliases_) +
-      base::trace_event::EstimateMemoryUsage(ip_aliases_) +
-      base::trace_event::EstimateMemoryUsage(session_peer_ip_) +
-      base::trace_event::EstimateMemoryUsage(gone_away_aliases_) +
-      base::trace_event::EstimateMemoryUsage(active_jobs_) +
-      base::trace_event::EstimateMemoryUsage(active_cert_verifier_jobs_);
-  factory_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
-                          base::trace_event::MemoryAllocatorDump::kUnitsBytes,
-                          memory_estimate);
-  factory_dump->AddScalar("all_sessions",
-                          base::trace_event::MemoryAllocatorDump::kUnitsObjects,
-                          all_sessions_.size());
-  factory_dump->AddScalar("active_jobs",
-                          base::trace_event::MemoryAllocatorDump::kUnitsObjects,
-                          active_jobs_.size());
-  factory_dump->AddScalar("active_cert_jobs",
-                          base::trace_event::MemoryAllocatorDump::kUnitsObjects,
-                          active_cert_verifier_jobs_.size());
-}
-
 bool QuicStreamFactory::CanUseExistingSession(const QuicSessionKey& session_key,
                                               const HostPortPair& destination) {
   // TODO(zhongyi): delete active_sessions_.empty() checks once the
@@ -1421,13 +1284,6 @@
   return false;
 }
 
-void QuicStreamFactory::MarkAllActiveSessionsGoingAway() {
-  while (!active_sessions_.empty()) {
-    QuicChromiumClientSession* session = active_sessions_.begin()->second;
-    OnSessionGoingAway(session);
-  }
-}
-
 int QuicStreamFactory::Create(const QuicSessionKey& session_key,
                               const HostPortPair& destination,
                               quic::ParsedQuicVersion quic_version,
@@ -1547,82 +1403,6 @@
   return rv;
 }
 
-QuicStreamFactory::QuicSessionAliasKey::QuicSessionAliasKey(
-    const HostPortPair& destination,
-    const QuicSessionKey& session_key)
-    : destination_(destination), session_key_(session_key) {}
-
-bool QuicStreamFactory::QuicSessionAliasKey::operator<(
-    const QuicSessionAliasKey& other) const {
-  return std::tie(destination_, session_key_) <
-         std::tie(other.destination_, other.session_key_);
-}
-
-bool QuicStreamFactory::QuicSessionAliasKey::operator==(
-    const QuicSessionAliasKey& other) const {
-  return destination_.Equals(other.destination_) &&
-         session_key_ == other.session_key_;
-}
-
-size_t QuicStreamFactory::QuicSessionAliasKey::EstimateMemoryUsage() const {
-  return base::trace_event::EstimateMemoryUsage(destination_) +
-         base::trace_event::EstimateMemoryUsage(session_key_.server_id());
-}
-
-bool QuicStreamFactory::HasMatchingIpSession(const QuicSessionAliasKey& key,
-                                             const AddressList& address_list) {
-  const quic::QuicServerId& server_id(key.server_id());
-  DCHECK(!HasActiveSession(key.session_key()));
-  for (const IPEndPoint& address : address_list) {
-    if (!base::Contains(ip_aliases_, address))
-      continue;
-
-    const SessionSet& sessions = ip_aliases_[address];
-    for (QuicChromiumClientSession* session : sessions) {
-      if (!session->CanPool(server_id.host(), key.session_key().privacy_mode(),
-                            key.session_key().socket_tag(),
-                            key.session_key().network_isolation_key())) {
-        continue;
-      }
-      active_sessions_[key.session_key()] = session;
-      session_aliases_[session].insert(key);
-      return true;
-    }
-  }
-  return false;
-}
-
-void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
-  auto iter = active_jobs_.find(job->key().session_key());
-  DCHECK(iter != active_jobs_.end());
-  if (rv == OK) {
-    set_is_quic_known_to_work_on_current_network(true);
-
-    auto session_it = active_sessions_.find(job->key().session_key());
-    CHECK(session_it != active_sessions_.end());
-    QuicChromiumClientSession* session = session_it->second;
-    for (auto* request : iter->second->stream_requests()) {
-      // Do not notify |request| yet.
-      request->SetSession(session->CreateHandle(job->key().destination()));
-    }
-  }
-
-  for (auto* request : iter->second->stream_requests()) {
-    // Even though we're invoking callbacks here, we don't need to worry
-    // about |this| being deleted, because the factory is owned by the
-    // profile which can not be deleted via callbacks.
-    if (rv < 0) {
-      job->PopulateNetErrorDetails(request->net_error_details());
-    }
-    request->OnRequestComplete(rv);
-  }
-  active_jobs_.erase(iter);
-}
-
-void QuicStreamFactory::OnCertVerifyJobComplete(CertVerifierJob* job, int rv) {
-  active_cert_verifier_jobs_.erase(job->server_id());
-}
-
 void QuicStreamFactory::OnSessionGoingAway(QuicChromiumClientSession* session) {
   const AliasSet& aliases = session_aliases_[session];
   for (auto it = aliases.begin(); it != aliases.end(); ++it) {
@@ -1737,140 +1517,6 @@
   }
 }
 
-void QuicStreamFactory::OnIPAddressChanged() {
-  LogPlatformNotificationInHistogram(NETWORK_IP_ADDRESS_CHANGED);
-  // Do nothing if connection migration is turned on.
-  if (params_.migrate_sessions_on_network_change_v2)
-    return;
-
-  set_is_quic_known_to_work_on_current_network(false);
-  if (params_.close_sessions_on_ip_change) {
-    CloseAllSessions(ERR_NETWORK_CHANGED, quic::QUIC_IP_ADDRESS_CHANGED);
-  } else {
-    DCHECK(params_.goaway_sessions_on_ip_change);
-    MarkAllActiveSessionsGoingAway();
-  }
-}
-
-void QuicStreamFactory::OnNetworkConnected(NetworkHandle network) {
-  LogPlatformNotificationInHistogram(NETWORK_CONNECTED);
-  if (!params_.migrate_sessions_on_network_change_v2)
-    return;
-
-  ScopedConnectionMigrationEventLog scoped_event_log(net_log_,
-                                                     "OnNetworkConnected");
-  auto it = all_sessions_.begin();
-  // Sessions may be deleted while iterating through the map.
-  while (it != all_sessions_.end()) {
-    QuicChromiumClientSession* session = it->first;
-    ++it;
-    session->OnNetworkConnected(network, scoped_event_log.net_log());
-  }
-}
-
-void QuicStreamFactory::OnNetworkMadeDefault(NetworkHandle network) {
-  LogPlatformNotificationInHistogram(NETWORK_MADE_DEFAULT);
-  if (!params_.migrate_sessions_on_network_change_v2)
-    return;
-
-  // Clear alternative services that were marked as broken until default network
-  // changes.
-  if (params_.retry_on_alternate_network_before_handshake &&
-      default_network_ != NetworkChangeNotifier::kInvalidNetworkHandle &&
-      network != default_network_) {
-    http_server_properties_->OnDefaultNetworkChanged();
-  }
-
-  DCHECK_NE(NetworkChangeNotifier::kInvalidNetworkHandle, network);
-  default_network_ = network;
-  ScopedConnectionMigrationEventLog scoped_event_log(net_log_,
-                                                     "OnNetworkMadeDefault");
-
-  auto it = all_sessions_.begin();
-  // Sessions may be deleted while iterating through the map.
-  while (it != all_sessions_.end()) {
-    QuicChromiumClientSession* session = it->first;
-    ++it;
-    session->OnNetworkMadeDefault(network, scoped_event_log.net_log());
-  }
-  set_is_quic_known_to_work_on_current_network(false);
-}
-
-void QuicStreamFactory::OnNetworkDisconnected(NetworkHandle network) {
-  LogPlatformNotificationInHistogram(NETWORK_DISCONNECTED);
-  if (!params_.migrate_sessions_on_network_change_v2)
-    return;
-
-  ScopedConnectionMigrationEventLog scoped_event_log(net_log_,
-                                                     "OnNetworkDisconnected");
-  auto it = all_sessions_.begin();
-  // Sessions may be deleted while iterating through the map.
-  while (it != all_sessions_.end()) {
-    QuicChromiumClientSession* session = it->first;
-    ++it;
-    session->OnNetworkDisconnectedV2(/*disconnected_network*/ network,
-                                     scoped_event_log.net_log());
-  }
-}
-
-// This method is expected to only be called when migrating from Cellular to
-// WiFi on Android, and should always be preceded by OnNetworkMadeDefault().
-void QuicStreamFactory::OnNetworkSoonToDisconnect(NetworkHandle network) {
-  LogPlatformNotificationInHistogram(NETWORK_SOON_TO_DISCONNECT);
-}
-
-NetworkHandle QuicStreamFactory::FindAlternateNetwork(
-    NetworkHandle old_network) {
-  // Find a new network that sessions bound to |old_network| can be migrated to.
-  NetworkChangeNotifier::NetworkList network_list;
-  NetworkChangeNotifier::GetConnectedNetworks(&network_list);
-  for (NetworkHandle new_network : network_list) {
-    if (new_network != old_network)
-      return new_network;
-  }
-  return NetworkChangeNotifier::kInvalidNetworkHandle;
-}
-
-std::unique_ptr<DatagramClientSocket> QuicStreamFactory::CreateSocket(
-    NetLog* net_log,
-    const NetLogSource& source) {
-  auto socket = client_socket_factory_->CreateDatagramClientSocket(
-      DatagramSocket::DEFAULT_BIND, net_log, source);
-  if (params_.enable_socket_recv_optimization)
-    socket->EnableRecvOptimization();
-  return socket;
-}
-
-void QuicStreamFactory::OnCertDBChanged() {
-  // We should flush the sessions if we removed trust from a
-  // cert, because a previously trusted server may have become
-  // untrusted.
-  //
-  // We should not flush the sessions if we added trust to a cert.
-  //
-  // Since the OnCertDBChanged method doesn't tell us what
-  // kind of change it is, we have to flush the socket
-  // pools to be safe.
-  MarkAllActiveSessionsGoingAway();
-}
-
-bool QuicStreamFactory::HasActiveSession(
-    const QuicSessionKey& session_key) const {
-  // TODO(rtenneti): crbug.com/498823 - delete active_sessions_.empty() check.
-  if (active_sessions_.empty())
-    return false;
-  return base::Contains(active_sessions_, session_key);
-}
-
-bool QuicStreamFactory::HasActiveJob(const QuicSessionKey& session_key) const {
-  return base::Contains(active_jobs_, session_key);
-}
-
-bool QuicStreamFactory::HasActiveCertVerifierJob(
-    const quic::QuicServerId& server_id) const {
-  return base::Contains(active_cert_verifier_jobs_, server_id);
-}
-
 int QuicStreamFactory::ConfigureSocket(DatagramClientSocket* socket,
                                        IPEndPoint addr,
                                        NetworkHandle network,
@@ -1933,6 +1579,269 @@
   return OK;
 }
 
+NetworkHandle QuicStreamFactory::FindAlternateNetwork(
+    NetworkHandle old_network) {
+  // Find a new network that sessions bound to |old_network| can be migrated to.
+  NetworkChangeNotifier::NetworkList network_list;
+  NetworkChangeNotifier::GetConnectedNetworks(&network_list);
+  for (NetworkHandle new_network : network_list) {
+    if (new_network != old_network)
+      return new_network;
+  }
+  return NetworkChangeNotifier::kInvalidNetworkHandle;
+}
+
+std::unique_ptr<DatagramClientSocket> QuicStreamFactory::CreateSocket(
+    NetLog* net_log,
+    const NetLogSource& source) {
+  auto socket = client_socket_factory_->CreateDatagramClientSocket(
+      DatagramSocket::DEFAULT_BIND, net_log, source);
+  if (params_.enable_socket_recv_optimization)
+    socket->EnableRecvOptimization();
+  return socket;
+}
+
+void QuicStreamFactory::OnIPAddressChanged() {
+  LogPlatformNotificationInHistogram(NETWORK_IP_ADDRESS_CHANGED);
+  // Do nothing if connection migration is turned on.
+  if (params_.migrate_sessions_on_network_change_v2)
+    return;
+
+  set_is_quic_known_to_work_on_current_network(false);
+  if (params_.close_sessions_on_ip_change) {
+    CloseAllSessions(ERR_NETWORK_CHANGED, quic::QUIC_IP_ADDRESS_CHANGED);
+  } else {
+    DCHECK(params_.goaway_sessions_on_ip_change);
+    MarkAllActiveSessionsGoingAway();
+  }
+}
+
+void QuicStreamFactory::OnNetworkConnected(NetworkHandle network) {
+  LogPlatformNotificationInHistogram(NETWORK_CONNECTED);
+  if (!params_.migrate_sessions_on_network_change_v2)
+    return;
+
+  ScopedConnectionMigrationEventLog scoped_event_log(net_log_,
+                                                     "OnNetworkConnected");
+  auto it = all_sessions_.begin();
+  // Sessions may be deleted while iterating through the map.
+  while (it != all_sessions_.end()) {
+    QuicChromiumClientSession* session = it->first;
+    ++it;
+    session->OnNetworkConnected(network, scoped_event_log.net_log());
+  }
+}
+
+void QuicStreamFactory::OnNetworkDisconnected(NetworkHandle network) {
+  LogPlatformNotificationInHistogram(NETWORK_DISCONNECTED);
+  if (!params_.migrate_sessions_on_network_change_v2)
+    return;
+
+  ScopedConnectionMigrationEventLog scoped_event_log(net_log_,
+                                                     "OnNetworkDisconnected");
+  auto it = all_sessions_.begin();
+  // Sessions may be deleted while iterating through the map.
+  while (it != all_sessions_.end()) {
+    QuicChromiumClientSession* session = it->first;
+    ++it;
+    session->OnNetworkDisconnectedV2(/*disconnected_network*/ network,
+                                     scoped_event_log.net_log());
+  }
+}
+
+// This method is expected to only be called when migrating from Cellular to
+// WiFi on Android, and should always be preceded by OnNetworkMadeDefault().
+void QuicStreamFactory::OnNetworkSoonToDisconnect(NetworkHandle network) {
+  LogPlatformNotificationInHistogram(NETWORK_SOON_TO_DISCONNECT);
+}
+
+void QuicStreamFactory::OnNetworkMadeDefault(NetworkHandle network) {
+  LogPlatformNotificationInHistogram(NETWORK_MADE_DEFAULT);
+  if (!params_.migrate_sessions_on_network_change_v2)
+    return;
+
+  // Clear alternative services that were marked as broken until default network
+  // changes.
+  if (params_.retry_on_alternate_network_before_handshake &&
+      default_network_ != NetworkChangeNotifier::kInvalidNetworkHandle &&
+      network != default_network_) {
+    http_server_properties_->OnDefaultNetworkChanged();
+  }
+
+  DCHECK_NE(NetworkChangeNotifier::kInvalidNetworkHandle, network);
+  default_network_ = network;
+  ScopedConnectionMigrationEventLog scoped_event_log(net_log_,
+                                                     "OnNetworkMadeDefault");
+
+  auto it = all_sessions_.begin();
+  // Sessions may be deleted while iterating through the map.
+  while (it != all_sessions_.end()) {
+    QuicChromiumClientSession* session = it->first;
+    ++it;
+    session->OnNetworkMadeDefault(network, scoped_event_log.net_log());
+  }
+  set_is_quic_known_to_work_on_current_network(false);
+}
+
+void QuicStreamFactory::OnCertDBChanged() {
+  // We should flush the sessions if we removed trust from a
+  // cert, because a previously trusted server may have become
+  // untrusted.
+  //
+  // We should not flush the sessions if we added trust to a cert.
+  //
+  // Since the OnCertDBChanged method doesn't tell us what
+  // kind of change it is, we have to flush the socket
+  // pools to be safe.
+  MarkAllActiveSessionsGoingAway();
+}
+
+void QuicStreamFactory::set_is_quic_known_to_work_on_current_network(
+    bool is_quic_known_to_work_on_current_network) {
+  is_quic_known_to_work_on_current_network_ =
+      is_quic_known_to_work_on_current_network;
+  if (!(local_address_ == IPEndPoint())) {
+    if (is_quic_known_to_work_on_current_network_) {
+      http_server_properties_->SetLastLocalAddressWhenQuicWorked(
+          local_address_.address());
+    } else {
+      http_server_properties_->ClearLastLocalAddressWhenQuicWorked();
+    }
+  }
+}
+
+base::TimeDelta QuicStreamFactory::GetTimeDelayForWaitingJob(
+    const quic::QuicServerId& server_id,
+    const NetworkIsolationKey& network_isolation_key) {
+  // If |is_quic_known_to_work_on_current_network_| is false, then one of the
+  // following is true:
+  // 1) This is startup and QuicStreamFactory::CreateSession() and
+  // ConfigureSocket() have yet to be called, and it is not yet known
+  // if the current network is the last one where QUIC worked.
+  // 2) Startup has been completed, and QUIC has not been used
+  // successfully since startup, or on this network before.
+  if (!is_quic_known_to_work_on_current_network_) {
+    // If |need_to_check_persisted_supports_quic_| is false, this is case 1)
+    // above. If HasLastLocalAddressWhenQuicWorked() is also true, then there's
+    // a chance the current network is the last one on which QUIC worked. So
+    // only delay the request if there's no chance that is the case.
+    if (!need_to_check_persisted_supports_quic_ ||
+        !http_server_properties_->HasLastLocalAddressWhenQuicWorked()) {
+      return base::TimeDelta();
+    }
+  }
+
+  int64_t srtt = 1.5 * GetServerNetworkStatsSmoothedRttInMicroseconds(
+                           server_id, network_isolation_key);
+  // Picked 300ms based on mean time from
+  // Net.QuicSession.HostResolution.HandshakeConfirmedTime histogram.
+  const int kDefaultRTT = 300 * quic::kNumMicrosPerMilli;
+  if (!srtt)
+    srtt = kDefaultRTT;
+  return base::TimeDelta::FromMicroseconds(srtt);
+}
+
+void QuicStreamFactory::DumpMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd,
+    const std::string& parent_absolute_name) const {
+  if (all_sessions_.empty() && active_jobs_.empty())
+    return;
+  base::trace_event::MemoryAllocatorDump* factory_dump =
+      pmd->CreateAllocatorDump(parent_absolute_name + "/quic_stream_factory");
+  size_t memory_estimate =
+      base::trace_event::EstimateMemoryUsage(all_sessions_) +
+      base::trace_event::EstimateMemoryUsage(active_sessions_) +
+      base::trace_event::EstimateMemoryUsage(session_aliases_) +
+      base::trace_event::EstimateMemoryUsage(ip_aliases_) +
+      base::trace_event::EstimateMemoryUsage(session_peer_ip_) +
+      base::trace_event::EstimateMemoryUsage(gone_away_aliases_) +
+      base::trace_event::EstimateMemoryUsage(active_jobs_) +
+      base::trace_event::EstimateMemoryUsage(active_cert_verifier_jobs_);
+  factory_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+                          base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                          memory_estimate);
+  factory_dump->AddScalar("all_sessions",
+                          base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+                          all_sessions_.size());
+  factory_dump->AddScalar("active_jobs",
+                          base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+                          active_jobs_.size());
+  factory_dump->AddScalar("active_cert_jobs",
+                          base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+                          active_cert_verifier_jobs_.size());
+}
+
+bool QuicStreamFactory::HasMatchingIpSession(const QuicSessionAliasKey& key,
+                                             const AddressList& address_list) {
+  const quic::QuicServerId& server_id(key.server_id());
+  DCHECK(!HasActiveSession(key.session_key()));
+  for (const IPEndPoint& address : address_list) {
+    if (!base::Contains(ip_aliases_, address))
+      continue;
+
+    const SessionSet& sessions = ip_aliases_[address];
+    for (QuicChromiumClientSession* session : sessions) {
+      if (!session->CanPool(server_id.host(), key.session_key().privacy_mode(),
+                            key.session_key().socket_tag(),
+                            key.session_key().network_isolation_key())) {
+        continue;
+      }
+      active_sessions_[key.session_key()] = session;
+      session_aliases_[session].insert(key);
+      return true;
+    }
+  }
+  return false;
+}
+
+void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
+  auto iter = active_jobs_.find(job->key().session_key());
+  DCHECK(iter != active_jobs_.end());
+  if (rv == OK) {
+    set_is_quic_known_to_work_on_current_network(true);
+
+    auto session_it = active_sessions_.find(job->key().session_key());
+    CHECK(session_it != active_sessions_.end());
+    QuicChromiumClientSession* session = session_it->second;
+    for (auto* request : iter->second->stream_requests()) {
+      // Do not notify |request| yet.
+      request->SetSession(session->CreateHandle(job->key().destination()));
+    }
+  }
+
+  for (auto* request : iter->second->stream_requests()) {
+    // Even though we're invoking callbacks here, we don't need to worry
+    // about |this| being deleted, because the factory is owned by the
+    // profile which can not be deleted via callbacks.
+    if (rv < 0) {
+      job->PopulateNetErrorDetails(request->net_error_details());
+    }
+    request->OnRequestComplete(rv);
+  }
+  active_jobs_.erase(iter);
+}
+
+void QuicStreamFactory::OnCertVerifyJobComplete(CertVerifierJob* job, int rv) {
+  active_cert_verifier_jobs_.erase(job->server_id());
+}
+
+bool QuicStreamFactory::HasActiveSession(
+    const QuicSessionKey& session_key) const {
+  // TODO(rtenneti): crbug.com/498823 - delete active_sessions_.empty() check.
+  if (active_sessions_.empty())
+    return false;
+  return base::Contains(active_sessions_, session_key);
+}
+
+bool QuicStreamFactory::HasActiveJob(const QuicSessionKey& session_key) const {
+  return base::Contains(active_jobs_, session_key);
+}
+
+bool QuicStreamFactory::HasActiveCertVerifierJob(
+    const quic::QuicServerId& server_id) const {
+  return base::Contains(active_cert_verifier_jobs_, server_id);
+}
+
 int QuicStreamFactory::CreateSession(
     const QuicSessionAliasKey& key,
     quic::ParsedQuicVersion quic_version,
@@ -2087,6 +1996,13 @@
   session_peer_ip_[session] = peer_address;
 }
 
+void QuicStreamFactory::MarkAllActiveSessionsGoingAway() {
+  while (!active_sessions_.empty()) {
+    QuicChromiumClientSession* session = active_sessions_.begin()->second;
+    OnSessionGoingAway(session);
+  }
+}
+
 void QuicStreamFactory::ConfigureInitialRttEstimate(
     const quic::QuicServerId& server_id,
     const NetworkIsolationKey& network_isolation_key,
@@ -2123,6 +2039,14 @@
   SetInitialRttEstimate(base::TimeDelta(), INITIAL_RTT_DEFAULT, config);
 }
 
+int64_t QuicStreamFactory::GetServerNetworkStatsSmoothedRttInMicroseconds(
+    const quic::QuicServerId& server_id,
+    const NetworkIsolationKey& network_isolation_key) const {
+  const base::TimeDelta* srtt =
+      GetServerNetworkStatsSmoothedRtt(server_id, network_isolation_key);
+  return srtt == nullptr ? 0 : srtt->InMicroseconds();
+}
+
 const base::TimeDelta* QuicStreamFactory::GetServerNetworkStatsSmoothedRtt(
     const quic::QuicServerId& server_id,
     const NetworkIsolationKey& network_isolation_key) const {
@@ -2135,14 +2059,6 @@
   return &(stats->srtt);
 }
 
-int64_t QuicStreamFactory::GetServerNetworkStatsSmoothedRttInMicroseconds(
-    const quic::QuicServerId& server_id,
-    const NetworkIsolationKey& network_isolation_key) const {
-  const base::TimeDelta* srtt =
-      GetServerNetworkStatsSmoothedRtt(server_id, network_isolation_key);
-  return srtt == nullptr ? 0 : srtt->InMicroseconds();
-}
-
 bool QuicStreamFactory::WasQuicRecentlyBroken(
     const QuicSessionKey& session_key) const {
   const AlternativeService alternative_service(
@@ -2152,27 +2068,6 @@
       alternative_service, session_key.network_isolation_key());
 }
 
-bool QuicStreamFactory::CryptoConfigCacheIsEmpty(
-    const quic::QuicServerId& server_id,
-    const NetworkIsolationKey& network_isolation_key) {
-  quic::QuicCryptoClientConfig::CachedState* cached = nullptr;
-  NetworkIsolationKey actual_network_isolation_key =
-      use_network_isolation_key_for_crypto_configs_ ? network_isolation_key
-                                                    : NetworkIsolationKey();
-  auto map_iterator =
-      active_crypto_config_map_.find(actual_network_isolation_key);
-  if (map_iterator != active_crypto_config_map_.end()) {
-    cached = map_iterator->second->config()->LookupOrCreate(server_id);
-  } else {
-    auto mru_iterator =
-        recent_crypto_config_map_.Peek(actual_network_isolation_key);
-    if (mru_iterator != recent_crypto_config_map_.end()) {
-      cached = mru_iterator->second->config()->LookupOrCreate(server_id);
-    }
-  }
-  return !cached || cached->IsEmpty();
-}
-
 quic::QuicAsyncStatus QuicStreamFactory::StartCertVerifyJob(
     const CryptoClientConfigHandle& crypto_config_handle,
     const quic::QuicServerId& server_id,
@@ -2197,6 +2092,86 @@
   return status;
 }
 
+void QuicStreamFactory::InitializeMigrationOptions() {
+  // The following list of options cannot be set immediately until
+  // prerequisites are met. Cache the initial setting in local variables and
+  // reset them in |params_|.
+  bool migrate_sessions_on_network_change =
+      params_.migrate_sessions_on_network_change_v2;
+  bool migrate_sessions_early = params_.migrate_sessions_early_v2;
+  bool retry_on_alternate_network_before_handshake =
+      params_.retry_on_alternate_network_before_handshake;
+  bool migrate_idle_sessions = params_.migrate_idle_sessions;
+  bool allow_port_migration = params_.allow_port_migration;
+  params_.migrate_sessions_on_network_change_v2 = false;
+  params_.migrate_sessions_early_v2 = false;
+  params_.allow_port_migration = false;
+  params_.retry_on_alternate_network_before_handshake = false;
+  params_.migrate_idle_sessions = false;
+
+  DCHECK(!(migrate_sessions_early && params_.go_away_on_path_degrading));
+  DCHECK(!(allow_port_migration && params_.go_away_on_path_degrading));
+
+  // TODO(zhongyi): deprecate |goaway_sessions_on_ip_change| if the experiment
+  // is no longer needed.
+  // goaway_sessions_on_ip_change and close_sessions_on_ip_change should never
+  // be simultaneously set to true.
+  DCHECK(!(params_.close_sessions_on_ip_change &&
+           params_.goaway_sessions_on_ip_change));
+
+  bool handle_ip_change = params_.close_sessions_on_ip_change ||
+                          params_.goaway_sessions_on_ip_change;
+  // If IP address changes are handled explicitly, connection migration should
+  // not be set.
+  DCHECK(!(handle_ip_change && migrate_sessions_on_network_change));
+
+  if (handle_ip_change)
+    NetworkChangeNotifier::AddIPAddressObserver(this);
+
+  // Port migration and early migration both act on path degrading and thus can
+  // not be simultaneously set.
+  DCHECK(!allow_port_migration || !migrate_sessions_early);
+
+  if (allow_port_migration)
+    params_.allow_port_migration = true;
+
+  if (!NetworkChangeNotifier::AreNetworkHandlesSupported())
+    return;
+
+  NetworkChangeNotifier::AddNetworkObserver(this);
+  // Perform checks on the connection migration options.
+  if (!migrate_sessions_on_network_change) {
+    DCHECK(!migrate_sessions_early);
+    return;
+  }
+
+  // Enable migration on platform notifications.
+  params_.migrate_sessions_on_network_change_v2 = true;
+
+  if (!migrate_sessions_early) {
+    DCHECK(!migrate_idle_sessions &&
+           !retry_on_alternate_network_before_handshake);
+    return;
+  }
+
+  // Enable migration on path degrading.
+  params_.migrate_sessions_early_v2 = true;
+  // Set retransmittable on wire timeout for migration on path degrading if no
+  // value is specified.
+  if (retransmittable_on_wire_timeout_.IsZero()) {
+    retransmittable_on_wire_timeout_ = quic::QuicTime::Delta::FromMicroseconds(
+        kDefaultRetransmittableOnWireTimeout.InMicroseconds());
+  }
+
+  // Enable retry on alternate network before handshake.
+  if (retry_on_alternate_network_before_handshake)
+    params_.retry_on_alternate_network_before_handshake = true;
+
+  // Enable migration for idle sessions.
+  if (migrate_idle_sessions)
+    params_.migrate_idle_sessions = true;
+}
+
 void QuicStreamFactory::InitializeCachedStateInCryptoConfig(
     const CryptoClientConfigHandle& crypto_config_handle,
     const quic::QuicServerId& server_id,
@@ -2372,4 +2347,25 @@
                             net_log);
 }
 
+bool QuicStreamFactory::CryptoConfigCacheIsEmptyForTesting(
+    const quic::QuicServerId& server_id,
+    const NetworkIsolationKey& network_isolation_key) {
+  quic::QuicCryptoClientConfig::CachedState* cached = nullptr;
+  NetworkIsolationKey actual_network_isolation_key =
+      use_network_isolation_key_for_crypto_configs_ ? network_isolation_key
+                                                    : NetworkIsolationKey();
+  auto map_iterator =
+      active_crypto_config_map_.find(actual_network_isolation_key);
+  if (map_iterator != active_crypto_config_map_.end()) {
+    cached = map_iterator->second->config()->LookupOrCreate(server_id);
+  } else {
+    auto mru_iterator =
+        recent_crypto_config_map_.Peek(actual_network_isolation_key);
+    if (mru_iterator != recent_crypto_config_map_.end()) {
+      cached = mru_iterator->second->config()->LookupOrCreate(server_id);
+    }
+  }
+  return !cached || cached->IsEmpty();
+}
+
 }  // namespace net
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
index 19bab90..58425c8 100644
--- a/net/quic/quic_stream_factory.h
+++ b/net/quic/quic_stream_factory.h
@@ -566,10 +566,6 @@
   // Helper methods.
   bool WasQuicRecentlyBroken(const QuicSessionKey& session_key) const;
 
-  bool CryptoConfigCacheIsEmpty(
-      const quic::QuicServerId& server_id,
-      const NetworkIsolationKey& network_isolation_key);
-
   // Starts an asynchronous job for cert verification if
   // |params_.race_cert_verification| is enabled and if there are cached certs
   // for the given |server_id|.
@@ -630,6 +626,10 @@
       int cert_verify_flags,
       const NetLogWithSource& net_log);
 
+  bool CryptoConfigCacheIsEmptyForTesting(
+      const quic::QuicServerId& server_id,
+      const NetworkIsolationKey& network_isolation_key);
+
   // Whether QUIC is known to work on current network. This is true when QUIC is
   // expected to work in general, rather than whether QUIC was broken / recently
   // broken when used with a particular server. That information is stored in
diff --git a/net/quic/quic_stream_factory_peer.cc b/net/quic/quic_stream_factory_peer.cc
index 4c42c16..0d0eb90 100644
--- a/net/quic/quic_stream_factory_peer.cc
+++ b/net/quic/quic_stream_factory_peer.cc
@@ -152,8 +152,8 @@
     QuicStreamFactory* factory,
     const quic::QuicServerId& quic_server_id,
     const NetworkIsolationKey& network_isolation_key) {
-  return factory->CryptoConfigCacheIsEmpty(quic_server_id,
-                                           network_isolation_key);
+  return factory->CryptoConfigCacheIsEmptyForTesting(quic_server_id,
+                                                     network_isolation_key);
 }
 
 void QuicStreamFactoryPeer::CacheDummyServerConfig(
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index 96a9bc3..8ab3faa 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -356,9 +356,11 @@
     // Send SETTINGS frame(s) if they have not already been sent.
     MaybeAddHttp3SettingsFrames(&frames);
 
-    std::string priority_data = GenerateHttp3PriorityData(priority, stream_id);
-
-    frames.push_back(GenerateNextStreamFrame(2, false, priority_data));
+    if (FLAGS_quic_allow_http3_priority) {
+      std::string priority_data =
+          GenerateHttp3PriorityData(priority, stream_id);
+      frames.push_back(GenerateNextStreamFrame(2, false, priority_data));
+    }
 
     // STREAM frames for HEADERS.
     std::vector<std::string> data = QpackEncodeHeaders(
@@ -753,9 +755,11 @@
     // Send SETTINGS frame(s) if they have not already been sent.
     MaybeAddHttp3SettingsFrames(&frames);
 
-    std::string priority_data = GenerateHttp3PriorityData(priority, stream_id);
-    frames.push_back(GenerateNextStreamFrame(2, false, priority_data));
-
+    if (FLAGS_quic_allow_http3_priority) {
+      std::string priority_data =
+          GenerateHttp3PriorityData(priority, stream_id);
+      frames.push_back(GenerateNextStreamFrame(2, false, priority_data));
+    }
     // STREAM frames for HEADERS.
     std::vector<std::string> data = QpackEncodeHeaders(
         stream_id, std::move(headers), spdy_headers_frame_length);
@@ -814,9 +818,11 @@
     // Send SETTINGS frame(s) if they have not already been sent.
     MaybeAddHttp3SettingsFrames(&frames);
 
-    std::string priority_data = GenerateHttp3PriorityData(priority, stream_id);
-    frames.push_back(GenerateNextStreamFrame(2, false, priority_data));
-
+    if (FLAGS_quic_allow_http3_priority) {
+      std::string priority_data =
+          GenerateHttp3PriorityData(priority, stream_id);
+      frames.push_back(GenerateNextStreamFrame(2, false, priority_data));
+    }
     std::vector<std::string> data = QpackEncodeHeaders(
         stream_id, std::move(headers), spdy_headers_frame_length);
 
@@ -854,9 +860,11 @@
     // Send SETTINGS frame(s) if they have not already been sent.
     MaybeAddHttp3SettingsFrames(&frames);
 
-    std::string priority_data = GenerateHttp3PriorityData(priority, stream_id);
-    frames.push_back(GenerateNextStreamFrame(2, false, priority_data));
-
+    if (FLAGS_quic_allow_http3_priority) {
+      std::string priority_data =
+          GenerateHttp3PriorityData(priority, stream_id);
+      frames.push_back(GenerateNextStreamFrame(2, false, priority_data));
+    }
     std::vector<std::string> data = QpackEncodeHeaders(
         stream_id, std::move(headers), spdy_headers_frame_length);
 
diff --git a/net/test/cert_builder.cc b/net/test/cert_builder.cc
new file mode 100644
index 0000000..1f3158cf
--- /dev/null
+++ b/net/test/cert_builder.cc
@@ -0,0 +1,671 @@
+// 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 "net/test/cert_builder.h"
+
+#include "base/files/file_path.h"
+#include "base/strings/string_number_conversions.h"
+#include "crypto/openssl_util.h"
+#include "crypto/rsa_private_key.h"
+#include "net/cert/internal/parse_certificate.h"
+#include "net/cert/x509_util.h"
+#include "net/der/encode_values.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+#include "net/der/parser.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/test_data_directory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+#include "third_party/boringssl/src/include/openssl/mem.h"
+#include "url/gurl.h"
+
+namespace net {
+
+namespace {
+
+std::string MakeRandomHexString(size_t num_bytes) {
+  std::vector<char> rand_bytes;
+  rand_bytes.resize(num_bytes);
+
+  base::RandBytes(&rand_bytes[0], rand_bytes.size());
+  return base::HexEncode(&rand_bytes[0], rand_bytes.size());
+}
+
+std::string Sha256WithRSAEncryption() {
+  const uint8_t kSha256WithRSAEncryption[] = {0x30, 0x0D, 0x06, 0x09, 0x2a,
+                                              0x86, 0x48, 0x86, 0xf7, 0x0d,
+                                              0x01, 0x01, 0x0b, 0x05, 0x00};
+  return std::string(std::begin(kSha256WithRSAEncryption),
+                     std::end(kSha256WithRSAEncryption));
+}
+
+std::string Sha1WithRSAEncryption() {
+  const uint8_t kSha1WithRSAEncryption[] = {0x30, 0x0D, 0x06, 0x09, 0x2a,
+                                            0x86, 0x48, 0x86, 0xf7, 0x0d,
+                                            0x01, 0x01, 0x05, 0x05, 0x00};
+  return std::string(std::begin(kSha1WithRSAEncryption),
+                     std::end(kSha1WithRSAEncryption));
+}
+
+std::string Md5WithRSAEncryption() {
+  const uint8_t kMd5WithRSAEncryption[] = {0x30, 0x0d, 0x06, 0x09, 0x2a,
+                                           0x86, 0x48, 0x86, 0xf7, 0x0d,
+                                           0x01, 0x01, 0x04, 0x05, 0x00};
+  return std::string(std::begin(kMd5WithRSAEncryption),
+                     std::end(kMd5WithRSAEncryption));
+}
+
+// Adds bytes (specified as a StringPiece) to the given CBB.
+// The argument ordering follows the boringssl CBB_* api style.
+bool CBBAddBytes(CBB* cbb, base::StringPiece bytes) {
+  return CBB_add_bytes(cbb, reinterpret_cast<const uint8_t*>(bytes.data()),
+                       bytes.size());
+}
+
+// Adds bytes (from fixed size array) to the given CBB.
+// The argument ordering follows the boringssl CBB_* api style.
+template <size_t N>
+bool CBBAddBytes(CBB* cbb, const uint8_t (&data)[N]) {
+  return CBB_add_bytes(cbb, data, N);
+}
+
+// Adds a RFC 5280 Time value to the given CBB.
+// The argument ordering follows the boringssl CBB_* api style.
+bool CBBAddTime(CBB* cbb, const base::Time& time) {
+  der::GeneralizedTime generalized_time;
+  if (!der::EncodeTimeAsGeneralizedTime(time, &generalized_time))
+    return false;
+  CBB time_cbb;
+  if (generalized_time.year < 2050) {
+    uint8_t out[der::kUTCTimeLength];
+    if (!der::EncodeUTCTime(generalized_time, out) ||
+        !CBB_add_asn1(cbb, &time_cbb, CBS_ASN1_UTCTIME) ||
+        !CBBAddBytes(&time_cbb, out) || !CBB_flush(cbb))
+      return false;
+  } else {
+    uint8_t out[der::kGeneralizedTimeLength];
+    if (!der::EncodeGeneralizedTime(generalized_time, out) ||
+        !CBB_add_asn1(cbb, &time_cbb, CBS_ASN1_GENERALIZEDTIME) ||
+        !CBBAddBytes(&time_cbb, out) || !CBB_flush(cbb))
+      return false;
+  }
+  return true;
+}
+
+// Finalizes the CBB to a std::string.
+std::string FinishCBB(CBB* cbb) {
+  size_t cbb_len;
+  uint8_t* cbb_bytes;
+
+  if (!CBB_finish(cbb, &cbb_bytes, &cbb_len)) {
+    ADD_FAILURE() << "CBB_finish() failed";
+    return std::string();
+  }
+
+  bssl::UniquePtr<uint8_t> delete_bytes(cbb_bytes);
+  return std::string(reinterpret_cast<char*>(cbb_bytes), cbb_len);
+}
+
+}  // namespace
+
+CertBuilder::CertBuilder(CRYPTO_BUFFER* orig_cert, CertBuilder* issuer)
+    : issuer_(issuer) {
+  if (!issuer_)
+    issuer_ = this;
+
+  crypto::EnsureOpenSSLInit();
+  InitFromCert(der::Input(x509_util::CryptoBufferAsStringPiece(orig_cert)));
+}
+
+CertBuilder::~CertBuilder() = default;
+
+// static
+void CertBuilder::CreateSimpleChain(
+    std::unique_ptr<CertBuilder>* out_leaf,
+    std::unique_ptr<CertBuilder>* out_intermediate,
+    std::unique_ptr<CertBuilder>* out_root) {
+  const char kHostname[] = "www.example.com";
+  base::FilePath certs_dir =
+      GetTestNetDataDirectory()
+          .AppendASCII("verify_certificate_chain_unittest")
+          .AppendASCII("target-and-intermediate");
+
+  CertificateList orig_certs = CreateCertificateListFromFile(
+      certs_dir, "chain.pem", X509Certificate::FORMAT_AUTO);
+  ASSERT_EQ(3U, orig_certs.size());
+
+  // Build slightly modified variants of |orig_certs|.
+  *out_root =
+      std::make_unique<CertBuilder>(orig_certs[2]->cert_buffer(), nullptr);
+  *out_intermediate = std::make_unique<CertBuilder>(
+      orig_certs[1]->cert_buffer(), out_root->get());
+  (*out_intermediate)->EraseExtension(CrlDistributionPointsOid());
+  (*out_intermediate)->EraseExtension(AuthorityInfoAccessOid());
+  *out_leaf = std::make_unique<CertBuilder>(orig_certs[0]->cert_buffer(),
+                                            out_intermediate->get());
+  (*out_leaf)->SetSubjectAltName(kHostname);
+  (*out_leaf)->EraseExtension(CrlDistributionPointsOid());
+  (*out_leaf)->EraseExtension(AuthorityInfoAccessOid());
+}
+
+void CertBuilder::SetExtension(const der::Input& oid,
+                               std::string value,
+                               bool critical) {
+  auto& extension_value = extensions_[oid.AsString()];
+  extension_value.critical = critical;
+  extension_value.value = std::move(value);
+
+  Invalidate();
+}
+
+void CertBuilder::EraseExtension(const der::Input& oid) {
+  extensions_.erase(oid.AsString());
+
+  Invalidate();
+}
+
+void CertBuilder::SetCaIssuersUrl(const GURL& url) {
+  std::string url_spec = url.spec();
+
+  // From RFC 5280:
+  //
+  //   AuthorityInfoAccessSyntax  ::=
+  //           SEQUENCE SIZE (1..MAX) OF AccessDescription
+  //
+  //   AccessDescription  ::=  SEQUENCE {
+  //           accessMethod          OBJECT IDENTIFIER,
+  //           accessLocation        GeneralName  }
+  bssl::ScopedCBB cbb;
+  CBB aia, ca_issuer, access_method, access_location;
+  ASSERT_TRUE(CBB_init(cbb.get(), url_spec.size()));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &aia, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_asn1(&aia, &ca_issuer, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_asn1(&ca_issuer, &access_method, CBS_ASN1_OBJECT));
+  ASSERT_TRUE(CBBAddBytes(&access_method, AdCaIssuersOid().AsStringPiece()));
+  ASSERT_TRUE(CBB_add_asn1(&ca_issuer, &access_location,
+                           CBS_ASN1_CONTEXT_SPECIFIC | 6));
+  ASSERT_TRUE(CBBAddBytes(&access_location, url_spec));
+
+  SetExtension(AuthorityInfoAccessOid(), FinishCBB(cbb.get()));
+}
+
+void CertBuilder::SetCrlDistributionPointUrl(const GURL& url) {
+  std::string url_spec = url.spec();
+
+  bssl::ScopedCBB cbb;
+  ASSERT_TRUE(CBB_init(cbb.get(), url_spec.size()));
+  CBB dps, dp, dp_name, dp_fullname, dp_url;
+
+  //    CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &dps, CBS_ASN1_SEQUENCE));
+
+  //    DistributionPoint ::= SEQUENCE {
+  //         distributionPoint       [0]     DistributionPointName OPTIONAL,
+  //         reasons                 [1]     ReasonFlags OPTIONAL,
+  //         cRLIssuer               [2]     GeneralNames OPTIONAL }
+  ASSERT_TRUE(CBB_add_asn1(&dps, &dp, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_asn1(
+      &dp, &dp_name, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+
+  //    DistributionPointName ::= CHOICE {
+  //         fullName                [0]     GeneralNames,
+  //         nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
+  ASSERT_TRUE(
+      CBB_add_asn1(&dp_name, &dp_fullname,
+                   CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+
+  //   GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+  //   GeneralName ::= CHOICE {
+  // uniformResourceIdentifier       [6]     IA5String,
+  ASSERT_TRUE(
+      CBB_add_asn1(&dp_fullname, &dp_url, CBS_ASN1_CONTEXT_SPECIFIC | 6));
+  ASSERT_TRUE(CBBAddBytes(&dp_url, url_spec));
+
+  SetExtension(CrlDistributionPointsOid(), FinishCBB(cbb.get()));
+}
+
+void CertBuilder::SetSubjectCommonName(const std::string common_name) {
+  // See RFC 4519.
+  static const uint8_t kCommonName[] = {0x55, 0x04, 0x03};
+
+  // See RFC 5280, section 4.1.2.4.
+  bssl::ScopedCBB cbb;
+  CBB rdns, rdn, attr, type, value;
+  ASSERT_TRUE(CBB_init(cbb.get(), 64));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &rdns, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_asn1(&rdns, &rdn, CBS_ASN1_SET));
+  ASSERT_TRUE(CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_asn1(&attr, &type, CBS_ASN1_OBJECT));
+  ASSERT_TRUE(CBBAddBytes(&type, kCommonName));
+  ASSERT_TRUE(CBB_add_asn1(&attr, &value, CBS_ASN1_UTF8STRING));
+  ASSERT_TRUE(CBBAddBytes(&value, common_name));
+
+  subject_tlv_ = FinishCBB(cbb.get());
+}
+
+void CertBuilder::SetSubjectAltName(const std::string& dns_name) {
+  // From RFC 5280:
+  //
+  //   SubjectAltName ::= GeneralNames
+  //
+  //   GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+  //
+  //   GeneralName ::= CHOICE {
+  //        otherName                       [0]     OtherName,
+  //        rfc822Name                      [1]     IA5String,
+  //        dNSName                         [2]     IA5String,
+  //        ... }
+  bssl::ScopedCBB cbb;
+  CBB general_names, general_name;
+  ASSERT_TRUE(CBB_init(cbb.get(), dns_name.size()));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &general_names, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_asn1(&general_names, &general_name,
+                           CBS_ASN1_CONTEXT_SPECIFIC | 2));
+  ASSERT_TRUE(CBBAddBytes(&general_name, dns_name));
+
+  SetExtension(SubjectAltNameOid(), FinishCBB(cbb.get()));
+}
+
+void CertBuilder::SetSignatureAlgorithmRsaPkca1(DigestAlgorithm digest) {
+  switch (digest) {
+    case DigestAlgorithm::Sha256: {
+      SetSignatureAlgorithm(Sha256WithRSAEncryption());
+      break;
+    }
+
+    case DigestAlgorithm::Sha1: {
+      SetSignatureAlgorithm(Sha1WithRSAEncryption());
+      break;
+    }
+
+    default:
+      ASSERT_TRUE(false);
+  }
+}
+
+void CertBuilder::SetSignatureAlgorithm(std::string algorithm_tlv) {
+  signature_algorithm_tlv_ = std::move(algorithm_tlv);
+  Invalidate();
+}
+
+void CertBuilder::SetRandomSerialNumber() {
+  serial_number_ = base::RandUint64();
+  Invalidate();
+}
+
+CRYPTO_BUFFER* CertBuilder::GetCertBuffer() {
+  if (!cert_)
+    GenerateCertificate();
+  return cert_.get();
+}
+
+bssl::UniquePtr<CRYPTO_BUFFER> CertBuilder::DupCertBuffer() {
+  return bssl::UpRef(GetCertBuffer());
+}
+
+const std::string& CertBuilder::GetSubject() {
+  if (subject_tlv_.empty())
+    GenerateSubject();
+  return subject_tlv_;
+}
+
+uint64_t CertBuilder::GetSerialNumber() {
+  if (!serial_number_)
+    serial_number_ = base::RandUint64();
+  return serial_number_;
+}
+
+EVP_PKEY* CertBuilder::GetKey() {
+  if (!key_)
+    GenerateKey();
+  return key_.get();
+}
+
+scoped_refptr<X509Certificate> CertBuilder::GetX509Certificate() {
+  return X509Certificate::CreateFromBuffer(DupCertBuffer(), {});
+}
+
+scoped_refptr<X509Certificate> CertBuilder::GetX509CertificateChain() {
+  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
+  // Add intermediates, not including the self-signed root.
+  for (CertBuilder* cert = issuer_; cert && cert != cert->issuer_;
+       cert = cert->issuer_) {
+    intermediates.push_back(cert->DupCertBuffer());
+  }
+  return X509Certificate::CreateFromBuffer(DupCertBuffer(),
+                                           std::move(intermediates));
+}
+
+std::string CertBuilder::GetDER() {
+  return x509_util::CryptoBufferAsStringPiece(GetCertBuffer()).as_string();
+}
+
+// static
+std::string CertBuilder::CreateCrl(CertBuilder* crl_issuer,
+                                   const std::vector<uint64_t>& revoked_serials,
+                                   DigestAlgorithm digest) {
+  std::string signature_algorithm;
+  const EVP_MD* md = nullptr;
+  switch (digest) {
+    case DigestAlgorithm::Sha256: {
+      signature_algorithm = Sha256WithRSAEncryption();
+      md = EVP_sha256();
+      break;
+    }
+
+    case DigestAlgorithm::Sha1: {
+      signature_algorithm = Sha1WithRSAEncryption();
+      md = EVP_sha1();
+      break;
+    }
+
+    case DigestAlgorithm::Md5: {
+      signature_algorithm = Md5WithRSAEncryption();
+      md = EVP_md5();
+      break;
+    }
+
+    default:
+      ADD_FAILURE();
+      return std::string();
+  }
+  //    TBSCertList  ::=  SEQUENCE  {
+  //         version                 Version OPTIONAL,
+  //                                      -- if present, MUST be v2
+  //         signature               AlgorithmIdentifier,
+  //         issuer                  Name,
+  //         thisUpdate              Time,
+  //         nextUpdate              Time OPTIONAL,
+  //         revokedCertificates     SEQUENCE OF SEQUENCE  {
+  //              userCertificate         CertificateSerialNumber,
+  //              revocationDate          Time,
+  //              crlEntryExtensions      Extensions OPTIONAL
+  //                                       -- if present, version MUST be v2
+  //                                   }  OPTIONAL,
+  //         crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
+  //                                       -- if present, version MUST be v2
+  //                                   }
+  bssl::ScopedCBB tbs_cbb;
+  CBB tbs_cert_list, revoked_serials_cbb;
+  if (!CBB_init(tbs_cbb.get(), 10) ||
+      !CBB_add_asn1(tbs_cbb.get(), &tbs_cert_list, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_asn1_uint64(&tbs_cert_list, 1 /* V2 */) ||
+      !CBBAddBytes(&tbs_cert_list, signature_algorithm) ||
+      !CBBAddBytes(&tbs_cert_list, crl_issuer->GetSubject()) ||
+      !CBBAddTime(&tbs_cert_list,
+                  base::Time::Now() - base::TimeDelta::FromDays(1)) ||
+      !CBBAddTime(&tbs_cert_list,
+                  base::Time::Now() + base::TimeDelta::FromDays(6))) {
+    ADD_FAILURE();
+    return std::string();
+  }
+  if (!revoked_serials.empty()) {
+    if (!CBB_add_asn1(&tbs_cert_list, &revoked_serials_cbb,
+                      CBS_ASN1_SEQUENCE)) {
+      ADD_FAILURE();
+      return std::string();
+    }
+    for (const int64_t revoked_serial : revoked_serials) {
+      CBB revoked_serial_cbb;
+      if (!CBB_add_asn1(&revoked_serials_cbb, &revoked_serial_cbb,
+                        CBS_ASN1_SEQUENCE) ||
+          !CBB_add_asn1_uint64(&revoked_serial_cbb, revoked_serial) ||
+          !CBBAddTime(&revoked_serial_cbb,
+                      base::Time::Now() - base::TimeDelta::FromDays(1)) ||
+          !CBB_flush(&revoked_serials_cbb)) {
+        ADD_FAILURE();
+        return std::string();
+      }
+    }
+  }
+
+  std::string tbs_tlv = FinishCBB(tbs_cbb.get());
+
+  //    CertificateList  ::=  SEQUENCE  {
+  //         tbsCertList          TBSCertList,
+  //         signatureAlgorithm   AlgorithmIdentifier,
+  //         signatureValue       BIT STRING  }
+  bssl::ScopedCBB crl_cbb;
+  CBB cert_list, signature;
+  bssl::ScopedEVP_MD_CTX ctx;
+  uint8_t* sig_out;
+  size_t sig_len;
+  if (!CBB_init(crl_cbb.get(), 10) ||
+      !CBB_add_asn1(crl_cbb.get(), &cert_list, CBS_ASN1_SEQUENCE) ||
+      !CBBAddBytes(&cert_list, tbs_tlv) ||
+      !CBBAddBytes(&cert_list, signature_algorithm) ||
+      !CBB_add_asn1(&cert_list, &signature, CBS_ASN1_BITSTRING) ||
+      !CBB_add_u8(&signature, 0 /* no unused bits */) ||
+      !EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr,
+                          crl_issuer->GetKey()) ||
+      !EVP_DigestSign(ctx.get(), nullptr, &sig_len,
+                      reinterpret_cast<const uint8_t*>(tbs_tlv.data()),
+                      tbs_tlv.size()) ||
+      !CBB_reserve(&signature, &sig_out, sig_len) ||
+      !EVP_DigestSign(ctx.get(), sig_out, &sig_len,
+                      reinterpret_cast<const uint8_t*>(tbs_tlv.data()),
+                      tbs_tlv.size()) ||
+      !CBB_did_write(&signature, sig_len)) {
+    ADD_FAILURE();
+    return std::string();
+  }
+  return FinishCBB(crl_cbb.get());
+}
+
+void CertBuilder::Invalidate() {
+  cert_.reset();
+}
+
+void CertBuilder::GenerateKey() {
+  ASSERT_FALSE(key_);
+
+  auto private_key = crypto::RSAPrivateKey::Create(2048);
+  key_ = bssl::UpRef(private_key->key());
+}
+
+void CertBuilder::GenerateSubject() {
+  ASSERT_TRUE(subject_tlv_.empty());
+
+  // Use a random common name comprised of 12 bytes in hex.
+  std::string common_name = MakeRandomHexString(12);
+
+  SetSubjectCommonName(common_name);
+}
+
+void CertBuilder::InitFromCert(const der::Input& cert) {
+  extensions_.clear();
+  Invalidate();
+
+  // From RFC 5280, section 4.1
+  //    Certificate  ::=  SEQUENCE  {
+  //      tbsCertificate       TBSCertificate,
+  //      signatureAlgorithm   AlgorithmIdentifier,
+  //      signatureValue       BIT STRING  }
+
+  // TBSCertificate  ::=  SEQUENCE  {
+  //      version         [0]  EXPLICIT Version DEFAULT v1,
+  //      serialNumber         CertificateSerialNumber,
+  //      signature            AlgorithmIdentifier,
+  //      issuer               Name,
+  //      validity             Validity,
+  //      subject              Name,
+  //      subjectPublicKeyInfo SubjectPublicKeyInfo,
+  //      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
+  //                           -- If present, version MUST be v2 or v3
+  //      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
+  //                           -- If present, version MUST be v2 or v3
+  //      extensions      [3]  EXPLICIT Extensions OPTIONAL
+  //                           -- If present, version MUST be v3
+  //      }
+  der::Parser parser(cert);
+  der::Parser certificate;
+  der::Parser tbs_certificate;
+  ASSERT_TRUE(parser.ReadSequence(&certificate));
+  ASSERT_TRUE(certificate.ReadSequence(&tbs_certificate));
+
+  // version
+  bool unused;
+  ASSERT_TRUE(tbs_certificate.SkipOptionalTag(
+      der::kTagConstructed | der::kTagContextSpecific | 0, &unused));
+  // serialNumber
+  ASSERT_TRUE(tbs_certificate.SkipTag(der::kInteger));
+
+  // signature
+  der::Input signature_algorithm_tlv;
+  ASSERT_TRUE(tbs_certificate.ReadRawTLV(&signature_algorithm_tlv));
+  signature_algorithm_tlv_ = signature_algorithm_tlv.AsString();
+
+  // issuer
+  ASSERT_TRUE(tbs_certificate.SkipTag(der::kSequence));
+
+  // validity
+  der::Input validity_tlv;
+  ASSERT_TRUE(tbs_certificate.ReadRawTLV(&validity_tlv));
+  validity_tlv_ = validity_tlv.AsString();
+
+  // subject
+  ASSERT_TRUE(tbs_certificate.SkipTag(der::kSequence));
+  // subjectPublicKeyInfo
+  ASSERT_TRUE(tbs_certificate.SkipTag(der::kSequence));
+  // issuerUniqueID
+  ASSERT_TRUE(tbs_certificate.SkipOptionalTag(der::ContextSpecificPrimitive(1),
+                                              &unused));
+  // subjectUniqueID
+  ASSERT_TRUE(tbs_certificate.SkipOptionalTag(der::ContextSpecificPrimitive(2),
+                                              &unused));
+
+  // extensions
+  bool has_extensions = false;
+  der::Input extensions_tlv;
+  ASSERT_TRUE(tbs_certificate.ReadOptionalTag(
+      der::ContextSpecificConstructed(3), &extensions_tlv, &has_extensions));
+  if (has_extensions) {
+    std::map<der::Input, ParsedExtension> parsed_extensions;
+    ASSERT_TRUE(ParseExtensions(extensions_tlv, &parsed_extensions));
+
+    for (const auto& parsed_extension : parsed_extensions) {
+      SetExtension(parsed_extension.second.oid,
+                   parsed_extension.second.value.AsString(),
+                   parsed_extension.second.critical);
+    }
+  }
+}
+
+void CertBuilder::BuildTBSCertificate(std::string* out) {
+  bssl::ScopedCBB cbb;
+  CBB tbs_cert, version, extensions_context, extensions;
+
+  ASSERT_TRUE(CBB_init(cbb.get(), 64));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &tbs_cert, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(
+      CBB_add_asn1(&tbs_cert, &version,
+                   CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+  // Always use v3 certificates.
+  ASSERT_TRUE(CBB_add_asn1_uint64(&version, 2));
+  ASSERT_TRUE(CBB_add_asn1_uint64(&tbs_cert, GetSerialNumber()));
+  ASSERT_TRUE(AddSignatureAlgorithm(&tbs_cert));
+  ASSERT_TRUE(CBBAddBytes(&tbs_cert, issuer_->GetSubject()));
+  ASSERT_TRUE(CBBAddBytes(&tbs_cert, validity_tlv_));
+  ASSERT_TRUE(CBBAddBytes(&tbs_cert, GetSubject()));
+  ASSERT_TRUE(EVP_marshal_public_key(&tbs_cert, GetKey()));
+
+  // Serialize all the extensions.
+  if (!extensions_.empty()) {
+    ASSERT_TRUE(
+        CBB_add_asn1(&tbs_cert, &extensions_context,
+                     CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 3));
+    ASSERT_TRUE(
+        CBB_add_asn1(&extensions_context, &extensions, CBS_ASN1_SEQUENCE));
+
+    //   Extension  ::=  SEQUENCE  {
+    //        extnID      OBJECT IDENTIFIER,
+    //        critical    BOOLEAN DEFAULT FALSE,
+    //        extnValue   OCTET STRING
+    //                    -- contains the DER encoding of an ASN.1 value
+    //                    -- corresponding to the extension type identified
+    //                    -- by extnID
+    //        }
+    for (const auto& extension_it : extensions_) {
+      CBB extension_seq, oid, extn_value;
+      ASSERT_TRUE(CBB_add_asn1(&extensions, &extension_seq, CBS_ASN1_SEQUENCE));
+      ASSERT_TRUE(CBB_add_asn1(&extension_seq, &oid, CBS_ASN1_OBJECT));
+      ASSERT_TRUE(CBBAddBytes(&oid, extension_it.first));
+      if (extension_it.second.critical) {
+        ASSERT_TRUE(CBB_add_asn1_bool(&extension_seq, true));
+      }
+
+      ASSERT_TRUE(
+          CBB_add_asn1(&extension_seq, &extn_value, CBS_ASN1_OCTETSTRING));
+      ASSERT_TRUE(CBBAddBytes(&extn_value, extension_it.second.value));
+      ASSERT_TRUE(CBB_flush(&extensions));
+    }
+  }
+
+  *out = FinishCBB(cbb.get());
+}
+
+bool CertBuilder::AddSignatureAlgorithm(CBB* cbb) {
+  return CBBAddBytes(cbb, signature_algorithm_tlv_);
+}
+
+void CertBuilder::GenerateCertificate() {
+  ASSERT_FALSE(cert_);
+
+  std::string tbs_cert;
+  BuildTBSCertificate(&tbs_cert);
+  const uint8_t* tbs_cert_bytes =
+      reinterpret_cast<const uint8_t*>(tbs_cert.data());
+
+  // Determine the correct digest algorithm to use (assumes RSA PKCS#1
+  // signatures).
+  auto signature_algorithm = SignatureAlgorithm::Create(
+      der::Input(&signature_algorithm_tlv_), nullptr);
+  ASSERT_TRUE(signature_algorithm);
+  ASSERT_EQ(SignatureAlgorithmId::RsaPkcs1, signature_algorithm->algorithm());
+  const EVP_MD* md = nullptr;
+
+  switch (signature_algorithm->digest()) {
+    case DigestAlgorithm::Sha256:
+      md = EVP_sha256();
+      break;
+
+    case DigestAlgorithm::Sha1:
+      md = EVP_sha1();
+      break;
+
+    default:
+      ASSERT_TRUE(false) << "Only rsaEncryptionWithSha256 or "
+                            "rsaEnryptionWithSha1 are supported";
+      break;
+  }
+
+  // Sign the TBSCertificate and write the entire certificate.
+  bssl::ScopedCBB cbb;
+  CBB cert, signature;
+  bssl::ScopedEVP_MD_CTX ctx;
+  uint8_t* sig_out;
+  size_t sig_len;
+
+  ASSERT_TRUE(CBB_init(cbb.get(), tbs_cert.size()));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBBAddBytes(&cert, tbs_cert));
+  ASSERT_TRUE(AddSignatureAlgorithm(&cert));
+  ASSERT_TRUE(CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING));
+  ASSERT_TRUE(CBB_add_u8(&signature, 0 /* no unused bits */));
+  ASSERT_TRUE(
+      EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr, issuer_->GetKey()));
+  ASSERT_TRUE(EVP_DigestSign(ctx.get(), nullptr, &sig_len, tbs_cert_bytes,
+                             tbs_cert.size()));
+  ASSERT_TRUE(CBB_reserve(&signature, &sig_out, sig_len));
+  ASSERT_TRUE(EVP_DigestSign(ctx.get(), sig_out, &sig_len, tbs_cert_bytes,
+                             tbs_cert.size()));
+  ASSERT_TRUE(CBB_did_write(&signature, sig_len));
+
+  auto cert_der = FinishCBB(cbb.get());
+  cert_ = x509_util::CreateCryptoBuffer(
+      reinterpret_cast<const uint8_t*>(cert_der.data()), cert_der.size());
+}
+
+}  // namespace net
diff --git a/net/test/cert_builder.h b/net/test/cert_builder.h
new file mode 100644
index 0000000..8660e73
--- /dev/null
+++ b/net/test/cert_builder.h
@@ -0,0 +1,148 @@
+// 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 NET_TEST_CERT_BUILDER_H_
+#define NET_TEST_CERT_BUILDER_H_
+
+#include "base/rand_util.h"
+#include "net/cert/internal/signature_algorithm.h"
+#include "net/cert/x509_certificate.h"
+#include "third_party/boringssl/src/include/openssl/base.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
+
+class GURL;
+
+namespace net {
+
+namespace der {
+class Input;
+}
+
+// CertBuilder is a helper class to dynamically create a test certificate.
+//
+// CertBuilder is initialized using an existing certificate, from which it
+// copies most properties (see InitFromCert for details).
+//
+// The subject, serial number, and key for the final certificate are chosen
+// randomly. Using a randomized subject and serial number is important to defeat
+// certificate caching done by NSS, which otherwise can make test outcomes
+// dependent on ordering.
+class CertBuilder {
+ public:
+  // Initializes the CertBuilder using |orig_cert|. If |issuer| is null
+  // then the generated certificate will be self-signed. Otherwise, it
+  // will be signed using |issuer|.
+  CertBuilder(CRYPTO_BUFFER* orig_cert, CertBuilder* issuer);
+  ~CertBuilder();
+
+  // Creates a simple leaf->intermediate->root chain of CertBuilders with no AIA
+  // or CrlDistributionPoint extensions, and leaf having a subjectAltName of
+  // www.example.com.
+  static void CreateSimpleChain(std::unique_ptr<CertBuilder>* out_leaf,
+                                std::unique_ptr<CertBuilder>* out_intermediate,
+                                std::unique_ptr<CertBuilder>* out_root);
+
+  // Sets a value for the indicated X.509 (v3) extension.
+  void SetExtension(const der::Input& oid,
+                    std::string value,
+                    bool critical = false);
+
+  // Removes an extension (if present).
+  void EraseExtension(const der::Input& oid);
+
+  // Sets an AIA extension with a single caIssuers access method.
+  void SetCaIssuersUrl(const GURL& url);
+
+  void SetCrlDistributionPointUrl(const GURL& url);
+
+  void SetSubjectCommonName(const std::string common_name);
+
+  // Sets the SAN for the certificate to a single dNSName.
+  void SetSubjectAltName(const std::string& dns_name);
+
+  // Sets the signature algorithm for the certificate to either
+  // sha256WithRSAEncryption or sha1WithRSAEncryption.
+  void SetSignatureAlgorithmRsaPkca1(DigestAlgorithm digest);
+
+  void SetSignatureAlgorithm(std::string algorithm_tlv);
+
+  void SetRandomSerialNumber();
+
+  // Returns a CRYPTO_BUFFER to the generated certificate.
+  CRYPTO_BUFFER* GetCertBuffer();
+
+  bssl::UniquePtr<CRYPTO_BUFFER> DupCertBuffer();
+
+  // Returns the subject of the generated certificate.
+  const std::string& GetSubject();
+
+  // Returns the serial number for the generated certificate.
+  uint64_t GetSerialNumber();
+
+  // Returns the (RSA) key for the generated certificate.
+  EVP_PKEY* GetKey();
+
+  // Returns an X509Certificate for the generated certificate.
+  scoped_refptr<X509Certificate> GetX509Certificate();
+
+  // Returns an X509Certificate for the generated certificate, including
+  // intermediate certificates.
+  scoped_refptr<X509Certificate> GetX509CertificateChain();
+
+  // Returns a copy of the certificate's DER.
+  std::string GetDER();
+
+  // Creates a CRL issued and signed by |crl_issuer|, marking |revoked_serials|
+  // as revoked.
+  // Returns the DER-encoded CRL.
+  static std::string CreateCrl(CertBuilder* crl_issuer,
+                               const std::vector<uint64_t>& revoked_serials,
+                               DigestAlgorithm digest);
+
+ private:
+  // Marks the generated certificate DER as invalid, so it will need to
+  // be re-generated next time the DER is accessed.
+  void Invalidate();
+
+  // Sets the |key_| to a 2048-bit RSA key.
+  void GenerateKey();
+
+  // Generates a random subject for the certificate, comprised of just a CN.
+  void GenerateSubject();
+
+  // Parses |cert| and copies the following properties:
+  //   * All extensions (dropping any duplicates)
+  //   * Signature algorithm (from Certificate)
+  //   * Validity (expiration)
+  void InitFromCert(const der::Input& cert);
+
+  // Assembles the CertBuilder into a TBSCertificate.
+  void BuildTBSCertificate(std::string* out);
+
+  bool AddSignatureAlgorithm(CBB* cbb);
+
+  void GenerateCertificate();
+
+  struct ExtensionValue {
+    bool critical = false;
+    std::string value;
+  };
+
+  std::string validity_tlv_;
+  std::string subject_tlv_;
+  std::string signature_algorithm_tlv_;
+  uint64_t serial_number_ = 0;
+
+  std::map<std::string, ExtensionValue> extensions_;
+
+  bssl::UniquePtr<CRYPTO_BUFFER> cert_;
+  bssl::UniquePtr<EVP_PKEY> key_;
+
+  CertBuilder* issuer_ = nullptr;
+};
+
+}  // namespace net
+
+#endif  // NET_TEST_CERT_BUILDER_H_
diff --git a/net/tools/quic/quic_simple_server.cc b/net/tools/quic/quic_simple_server.cc
index 3ea79e4..da14e4f 100644
--- a/net/tools/quic/quic_simple_server.cc
+++ b/net/tools/quic/quic_simple_server.cc
@@ -34,7 +34,7 @@
 
 // Allocate some extra space so we can send an error if the client goes over
 // the limit.
-const int kReadBufferSize = 2 * quic::kMaxOutgoingPacketSize;
+const int kReadBufferSize = 2 * quic::kMaxIncomingPacketSize;
 
 }  // namespace
 
diff --git a/remoting/host/installer/mac/BUILD.gn b/remoting/host/installer/mac/BUILD.gn
index d600928..22d32f3c 100644
--- a/remoting/host/installer/mac/BUILD.gn
+++ b/remoting/host/installer/mac/BUILD.gn
@@ -2,9 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//remoting/build/config/remoting_build.gni")
-
 import("//build/config/zip.gni")
+import("//chrome/installer/mac/mac_signing_sources.gni")
+import("//remoting/build/config/remoting_build.gni")
 
 action("remoting_me2me_host_archive") {
   _installer_mac_files = [
@@ -19,8 +19,14 @@
     "Scripts/remoting_preflight.sh",
     "Keystone/GoogleSoftwareUpdate.pkg",
     "//chrome/installer/mac/pkg-dmg",
+    "//chrome/installer/mac/notarize_thing.py",
   ]
 
+  # Chrome's signing/notarization Python module.
+  _mac_signing_files =
+      rebase_path(mac_signing_sources, ".", "//chrome/installer/mac")
+  _installer_mac_files += _mac_signing_files
+
   inputs = _installer_mac_files
 
   zip_path = "$root_build_dir/remoting-me2me-host-mac.zip"
diff --git a/remoting/host/installer/mac/do_signing.sh b/remoting/host/installer/mac/do_signing.sh
index c6876d8..5079f00 100755
--- a/remoting/host/installer/mac/do_signing.sh
+++ b/remoting/host/installer/mac/do_signing.sh
@@ -8,11 +8,8 @@
 # installer and then packages it into a .dmg.  It requires that Packages be
 # installed (for 'packagesbuild').
 # Packages: http://s.sudre.free.fr/Software/Packages/about.html
-#
-# usage: do_signing.sh output_dir input_dir [codesign_keychain codesign_id
-#            [productsign_id]]
-#
-# The final disk image (dmg) is placed in |output_dir|.
+# Optionally, it will submit the .dmg to Apple for notarization.
+# Run with "-h" to see usage information.
 
 set -e -u
 
@@ -204,6 +201,19 @@
   fi
 }
 
+notarize() {
+  local input_dir="${1}"
+  local dmg="${2}"
+  local user="${3}"
+
+  echo "Notarizing and stapling .dmg..."
+  "${input_dir}/notarize_thing.py" \
+      --user "${user}" \
+      --password @env:NOTARIZATION_PASSWORD \
+      --bundle-id "${HOST_BUNDLE_NAME}" \
+      "${dmg}"
+}
+
 cleanup() {
   if [[ "${#g_cleanup_dirs[@]}" > 0 ]]; then
     rm -rf "${g_cleanup_dirs[@]}"
@@ -212,7 +222,8 @@
 
 usage() {
   echo "Usage: ${ME} -o output_dir -i input_dir "\
-      "[-c codesign_id] [-p productsign_id] [-k keychain]" >&2
+       "[-c codesign_id] [-p productsign_id] [-k keychain] "\
+       "[-n notarization_user]" >&2
   echo >&2
   echo "  Sign the binaries using the specified <codesign_id>, build" >&2
   echo "  the installer, and then sign the installer using the given" >&2
@@ -221,6 +232,9 @@
   echo "  installer is built without signing any binaries." >&2
   echo "  If <keychain> is specified, it must contain all the signing ids." >&2
   echo "  If not specified, then the default keychains will be used." >&2
+  echo "  If <notarization_user> is specified, the final DMG will be" >&2
+  echo "  notarized by Apple and stapled, using the given user and the" >&2
+  echo "  password from \$NOTARIZATION_PASSWORD variable." >&2
 }
 
 main() {
@@ -230,9 +244,10 @@
   local codesign_id=""
   local productsign_id=""
   local keychain=""
+  local notarization_user=""
 
   local OPTNAME OPTIND OPTARG
-  while getopts ":o:i:c:p:k:h" OPTNAME; do
+  while getopts ":o:i:c:p:k:n:h" OPTNAME; do
     case ${OPTNAME} in
       o )
         output_dir="$(shell_safe_path "${OPTARG}")"
@@ -249,6 +264,9 @@
       k )
         keychain="$(shell_safe_path "${OPTARG}")"
         ;;
+      n )
+        notarization_user="${OPTARG}"
+        ;;
       h )
         usage
         exit 0
@@ -298,7 +316,13 @@
   fi
   build_dmg "${input_dir}" "${output_dir}"
   if [[ "${do_sign_binaries}" == 1 ]]; then
-      sign "${output_dir}/${DMG_FILE_NAME}" "${keychain}" "${codesign_id}"
+    sign "${output_dir}/${DMG_FILE_NAME}" "${keychain}" "${codesign_id}"
+  fi
+
+  if [[ -n "${notarization_user}" ]]; then
+    notarize "${input_dir}" \
+             "${output_dir}/${DMG_FILE_NAME}" \
+             "${notarization_user}"
   fi
 
   cleanup
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.h b/sandbox/linux/bpf_dsl/bpf_dsl.h
index 6f0dd4e..555d820e 100644
--- a/sandbox/linux/bpf_dsl/bpf_dsl.h
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.h
@@ -27,34 +27,32 @@
 // An idiomatic and demonstrative (albeit silly) example of this API
 // would be:
 //
-//      #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+//   #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
 //
-//      using namespace sandbox::bpf_dsl;
+//   namespace dsl = sandbox::bpf_dsl;
 //
-//      class SillyPolicy : public Policy {
-//       public:
-//        SillyPolicy() {}
-//        ~SillyPolicy() override {}
-//        ResultExpr EvaluateSyscall(int sysno) const override {
-//          if (sysno == __NR_fcntl) {
-//            Arg<int> fd(0), cmd(1);
-//            Arg<unsigned long> flags(2);
-//            const uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK;
-//            return If(AllOf(fd == 0,
-//                            cmd == F_SETFL,
-//                            (flags & ~kGoodFlags) == 0),
-//                      Allow())
-//                .ElseIf(AnyOf(cmd == F_DUPFD, cmd == F_DUPFD_CLOEXEC),
-//                        Error(EMFILE))
-//                .Else(Trap(SetFlagHandler, NULL));
-//          } else {
-//            return Allow();
-//          }
-//        }
+//   class SillyPolicy : public dsl::Policy {
+//    public:
+//     SillyPolicy() = default;
+//     SillyPolicy(const SillyPolicy&) = delete;
+//     SillyPolicy& operator=(const SillyPolicy&) = delete;
+//     ~SillyPolicy() override = default;
 //
-//       private:
-//        DISALLOW_COPY_AND_ASSIGN(SillyPolicy);
-//      };
+//     dsl::ResultExpr EvaluateSyscall(int sysno) const override {
+//       if (sysno != __NR_fcntl)
+//         return dsl::Allow();
+//       dsl::Arg<int> fd(0), cmd(1);
+//       dsl::Arg<unsigned long> flags(2);
+//       constexpr uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK;
+//       return dsl::If(dsl::AllOf(fd == 0,
+//                                 cmd == F_SETFL,
+//                                 (flags & ~kGoodFlags) == 0),
+//                      dsl::Allow())
+//           .dsl::ElseIf(dsl::AnyOf(cmd == F_DUPFD, cmd == F_DUPFD_CLOEXEC),
+//                        dsl::Error(EMFILE))
+//           .dsl::Else(dsl::Trap(SetFlagHandler, nullptr));
+//     }
+//   };
 //
 // More generally, the DSL currently supports the following grammar:
 //
diff --git a/sandbox/win/fuzzer/sandbox_ipc_fuzzer.cc b/sandbox/win/fuzzer/sandbox_ipc_fuzzer.cc
index 1c2ea8c..074d673 100644
--- a/sandbox/win/fuzzer/sandbox_ipc_fuzzer.cc
+++ b/sandbox/win/fuzzer/sandbox_ipc_fuzzer.cc
@@ -9,20 +9,19 @@
 #include "sandbox/win/src/ipc_args.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  using namespace sandbox;
-
   uint32_t output_size = 0;
-  std::unique_ptr<CrossCallParamsEx> params(CrossCallParamsEx::CreateFromBuffer(
-      const_cast<uint8_t*>(data), size, &output_size));
+  std::unique_ptr<sandbox::CrossCallParamsEx> params(
+      sandbox::CrossCallParamsEx::CreateFromBuffer(const_cast<uint8_t*>(data),
+                                                   size, &output_size));
 
   if (!params.get())
     return 0;
 
   uint32_t tag = params->GetTag();
-  IPCParams ipc_params = {0};
+  sandbox::IPCParams ipc_params = {0};
   ipc_params.ipc_tag = tag;
-  void* args[kMaxIpcParams];
-  GetArgs(params.get(), &ipc_params, args);
-  ReleaseArgs(&ipc_params, args);
+  void* args[sandbox::kMaxIpcParams];
+  sandbox::GetArgs(params.get(), &ipc_params, args);
+  sandbox::ReleaseArgs(&ipc_params, args);
   return 0;
 }
diff --git a/services/tracing/public/cpp/perfetto/android_system_producer.cc b/services/tracing/public/cpp/perfetto/android_system_producer.cc
index 92a6812c..ba7772e 100644
--- a/services/tracing/public/cpp/perfetto/android_system_producer.cc
+++ b/services/tracing/public/cpp/perfetto/android_system_producer.cc
@@ -80,6 +80,7 @@
   new_registration.set_name(data_source->name());
   new_registration.set_will_notify_on_start(true);
   new_registration.set_will_notify_on_stop(true);
+  new_registration.set_handles_incremental_state_clear(true);
   service_->RegisterDataSource(new_registration);
 }
 
diff --git a/storage/browser/quota/quota_settings.cc b/storage/browser/quota/quota_settings.cc
index 59f580f9b..1467c84 100644
--- a/storage/browser/quota/quota_settings.cc
+++ b/storage/browser/quota/quota_settings.cc
@@ -140,7 +140,7 @@
                                OptionalQuotaSettingsCallback callback) {
   base::PostTaskAndReplyWithResult(
       FROM_HERE,
-      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE,
        base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
       base::BindOnce(&CalculateNominalDynamicSettings, partition_path,
                      is_incognito, base::Unretained(disk_info_helper)),
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 733ca71de..d9a0c64 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -6680,11 +6680,11 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "device_os": "P",
+              "device_os": "PQ3A.190801.002",
+              "device_os_flavor": "google",
               "device_os_type": "userdebug",
               "device_type": "walleye",
-              "os": "Android",
-              "pool": "Chrome-GPU"
+              "os": "Android"
             }
           ],
           "idempotent": false,
@@ -6713,11 +6713,11 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "device_os": "P",
+              "device_os": "PQ3A.190801.002",
+              "device_os_flavor": "google",
               "device_os_type": "userdebug",
               "device_type": "walleye",
-              "os": "Android",
-              "pool": "Chrome-GPU"
+              "os": "Android"
             }
           ],
           "idempotent": false
@@ -6746,11 +6746,11 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "device_os": "P",
+              "device_os": "PQ3A.190801.002",
+              "device_os_flavor": "google",
               "device_os_type": "userdebug",
               "device_type": "walleye",
-              "os": "Android",
-              "pool": "Chrome-GPU"
+              "os": "Android"
             }
           ],
           "idempotent": false,
@@ -6780,11 +6780,11 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "device_os": "P",
+              "device_os": "PQ3A.190801.002",
+              "device_os_flavor": "google",
               "device_os_type": "userdebug",
               "device_type": "walleye",
-              "os": "Android",
-              "pool": "Chrome-GPU"
+              "os": "Android"
             }
           ],
           "idempotent": false,
@@ -7060,11 +7060,11 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "device_os": "P",
+              "device_os": "PQ3A.190801.002",
+              "device_os_flavor": "google",
               "device_os_type": "userdebug",
               "device_type": "walleye",
-              "os": "Android",
-              "pool": "Chrome-GPU"
+              "os": "Android"
             }
           ],
           "output_links": [
@@ -7119,11 +7119,11 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "device_os": "P",
+              "device_os": "PQ3A.190801.002",
+              "device_os_flavor": "google",
               "device_os_type": "userdebug",
               "device_type": "walleye",
-              "os": "Android",
-              "pool": "Chrome-GPU"
+              "os": "Android"
             }
           ],
           "idempotent": false,
diff --git a/testing/buildbot/filters/chromeos.browser_tests.filter b/testing/buildbot/filters/chromeos.browser_tests.filter
index 420987c4..4a879280 100644
--- a/testing/buildbot/filters/chromeos.browser_tests.filter
+++ b/testing/buildbot/filters/chromeos.browser_tests.filter
@@ -29,11 +29,6 @@
 # TODO(crbug.com/990819): Enable this.
 -StructSequence/OobeLocalizationTest.LocalizationTest/0
 
-# TODO(crbug.com/977170): Enable this.
--StructSequence/OobeLocalizationTest.LocalizationTest/3
--StructSequence/OobeLocalizationTest.LocalizationTest/4
--StructSequence/OobeLocalizationTest.LocalizationTest/8
-
 # TODO(crbug.com/978051): Enable this.
 -TabManagerTestWithTwoTabs.TabProactiveDiscardAndFocusBeforeFreezeCompletes
 
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 842f557..0ca4cd6 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -545,13 +545,6 @@
       },
     },
   },
-  'pie': {
-    'swarming': {
-      'dimensions': {
-        'device_os': 'P',
-      },
-    },
-  },
   'pie-x86-emulator': {
     '$mixin_append': {
       'args': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index f3a6d30..b75b631 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2574,8 +2574,7 @@
         'os_type': 'android',
         'skip_merge_script': True,
         'mixins': [
-          'gpu_pool',
-          'pie',
+          'pie_fleet',
           'walleye',
         ],
         'test_suites': {
@@ -2601,8 +2600,7 @@
         'browser_config': 'android-chromium',
         'skip_merge_script': True,
         'mixins': [
-          'gpu_pool',
-          'pie',
+          'pie_fleet',
           'walleye',
         ],
         'test_suites': {
diff --git a/third_party/blink/public/mojom/indexeddb/indexeddb.mojom b/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
index d173ba7..bc44681 100644
--- a/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
+++ b/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
@@ -58,6 +58,20 @@
   Min,
 };
 
+// Durability guarantees during a transaction.  This maps to whether or not
+// the writes to disk are explicitly flushed or not.
+enum IDBTransactionDurability {
+  // Unspecified by caller, and so is implementation defined.
+  // May map to either Strict or Relaxed.
+  Default,
+
+  // Always flush to disk after a write.
+  Strict,
+
+  // Do not flush to disk.  This is much faster.
+  Relaxed,
+};
+
 union IDBKeyData {
   array<IDBKey> key_array;
   array<uint8> binary;
@@ -343,7 +357,7 @@
                     int64 transaction_id,
                     array<int64> object_store_ids,
                     IDBTransactionMode mode,
-                    bool relaxed_durability);
+                    IDBTransactionDurability durability);
   Close();
   VersionChangeIgnored();
   AddObserver(int64 transaction_id,
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index e968391..7fc1ca3 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -71,6 +71,7 @@
   BLINK_PLATFORM_EXPORT static void EnableCompositorTouchAction(bool);
 
   BLINK_PLATFORM_EXPORT static void EnableAccelerated2dCanvas(bool);
+  BLINK_PLATFORM_EXPORT static void EnableAccessibilityExposeDisplayNone(bool);
   BLINK_PLATFORM_EXPORT static void EnableAccessibilityObjectModel(bool);
   BLINK_PLATFORM_EXPORT static void EnableAdTagging(bool);
   BLINK_PLATFORM_EXPORT static void EnableAllowActivationDelegationAttr(bool);
diff --git a/third_party/blink/public/platform/web_theme_engine.h b/third_party/blink/public/platform/web_theme_engine.h
index 73dbfd8..a9ce419 100644
--- a/third_party/blink/public/platform/web_theme_engine.h
+++ b/third_party/blink/public/platform/web_theme_engine.h
@@ -31,6 +31,7 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_THEME_ENGINE_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_THEME_ENGINE_H_
 
+#include "base/optional.h"
 #include "base/time/time.h"
 #include "third_party/blink/public/platform/web_color_scheme.h"
 #include "third_party/blink/public/platform/web_rect.h"
@@ -81,6 +82,21 @@
     kPartProgressBar
   };
 
+  enum class SystemThemeColor {
+    kNotSupported,
+    kButtonFace,
+    kButtonText,
+    kGrayText,
+    kHighlight,
+    kHighlightText,
+    kHotlight,
+    kMenuHighlight,
+    kScrollbar,
+    kWindow,
+    kWindowText,
+    kMaxValue = kWindowText,
+  };
+
   // Extra parameters for drawing the PartScrollbarHorizontalTrack and
   // PartScrollbarVerticalTrack.
   struct ScrollbarTrackExtraParams {
@@ -203,6 +219,11 @@
                      const WebRect&,
                      const ExtraParams*,
                      blink::WebColorScheme) {}
+
+  virtual base::Optional<SkColor> GetSystemColor(
+      SystemThemeColor system_theme) const {
+    return base::nullopt;
+  }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_paint_value.cc b/third_party/blink/renderer/core/css/css_paint_value.cc
index e01b860..d00c3af 100644
--- a/third_party/blink/renderer/core/css/css_paint_value.cc
+++ b/third_party/blink/renderer/core/css/css_paint_value.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/css/css_paint_value.h"
 
+#include "base/metrics/histogram_macros.h"
 #include "third_party/blink/renderer/core/css/css_custom_ident_value.h"
 #include "third_party/blink/renderer/core/css/css_paint_image_generator.h"
 #include "third_party/blink/renderer/core/css/css_syntax_definition.h"
@@ -24,7 +25,10 @@
     : CSSImageGeneratorValue(kPaintClass),
       name_(name),
       paint_image_generator_observer_(MakeGarbageCollected<Observer>(this)),
-      paint_off_thread_(true) {}
+      off_thread_paint_state_(
+          RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()
+              ? OffThreadPaintState::kUnknown
+              : OffThreadPaintState::kMainThread) {}
 
 CSSPaintValue::CSSPaintValue(
     CSSCustomIdentValue* name,
@@ -91,35 +95,39 @@
 
   // For Off-Thread PaintWorklet, we just collect the necessary inputs together
   // and defer the actual JavaScript call until much later (during cc Raster).
-  if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) {
-    if (paint_off_thread_) {
-      // It is not necessary for a LayoutObject to always have RareData which
-      // contains the ElementId. If this |layout_object| doesn't have an
-      // ElementId, then create one for it.
-      layout_object.GetMutableForPainting().EnsureId();
+  if (off_thread_paint_state_ != OffThreadPaintState::kMainThread) {
+    // It is not necessary for a LayoutObject to always have RareData which
+    // contains the ElementId. If this |layout_object| doesn't have an
+    // ElementId, then create one for it.
+    layout_object.GetMutableForPainting().EnsureId();
 
-      Vector<CSSPropertyID> native_properties =
-          generator_->NativeInvalidationProperties();
-      Vector<AtomicString> custom_properties =
-          generator_->CustomInvalidationProperties();
-      float zoom = layout_object.StyleRef().EffectiveZoom();
-      CompositorPaintWorkletInput::PropertyKeys input_property_keys;
-      auto style_data = PaintWorkletStylePropertyMap::BuildCrossThreadData(
-          document, layout_object.UniqueId(), style, native_properties,
-          custom_properties, input_property_keys);
-      paint_off_thread_ = style_data.has_value();
-      if (paint_off_thread_) {
-        Vector<std::unique_ptr<CrossThreadStyleValue>>
-            cross_thread_input_arguments;
-        BuildInputArgumentValues(cross_thread_input_arguments);
-        scoped_refptr<PaintWorkletInput> input =
-            base::MakeRefCounted<PaintWorkletInput>(
-                GetName(), target_size, zoom, device_scale_factor,
-                generator_->WorkletId(), std::move(style_data.value()),
-                std::move(cross_thread_input_arguments),
-                std::move(input_property_keys));
-        return PaintWorkletDeferredImage::Create(std::move(input), target_size);
-      }
+    Vector<CSSPropertyID> native_properties =
+        generator_->NativeInvalidationProperties();
+    Vector<AtomicString> custom_properties =
+        generator_->CustomInvalidationProperties();
+    float zoom = layout_object.StyleRef().EffectiveZoom();
+    CompositorPaintWorkletInput::PropertyKeys input_property_keys;
+    auto style_data = PaintWorkletStylePropertyMap::BuildCrossThreadData(
+        document, layout_object.UniqueId(), style, native_properties,
+        custom_properties, input_property_keys);
+    if (off_thread_paint_state_ == OffThreadPaintState::kUnknown) {
+      UMA_HISTOGRAM_BOOLEAN("Blink.CSSPaintValue.PaintOffThread",
+                            style_data.has_value());
+    }
+    off_thread_paint_state_ = style_data.has_value()
+                                  ? OffThreadPaintState::kOffThread
+                                  : OffThreadPaintState::kMainThread;
+    if (off_thread_paint_state_ == OffThreadPaintState::kOffThread) {
+      Vector<std::unique_ptr<CrossThreadStyleValue>>
+          cross_thread_input_arguments;
+      BuildInputArgumentValues(cross_thread_input_arguments);
+      scoped_refptr<PaintWorkletInput> input =
+          base::MakeRefCounted<PaintWorkletInput>(
+              GetName(), target_size, zoom, device_scale_factor,
+              generator_->WorkletId(), std::move(style_data.value()),
+              std::move(cross_thread_input_arguments),
+              std::move(input_property_keys));
+      return PaintWorkletDeferredImage::Create(std::move(input), target_size);
     }
   }
 
diff --git a/third_party/blink/renderer/core/css/css_paint_value.h b/third_party/blink/renderer/core/css/css_paint_value.h
index 7b384a0..8c4ce29 100644
--- a/third_party/blink/renderer/core/css/css_paint_value.h
+++ b/third_party/blink/renderer/core/css/css_paint_value.h
@@ -98,9 +98,12 @@
   Member<Observer> paint_image_generator_observer_;
   Member<CSSStyleValueVector> parsed_input_arguments_;
   Vector<scoped_refptr<CSSVariableData>> argument_variable_data_;
+  enum class OffThreadPaintState { kUnknown, kOffThread, kMainThread };
+  // Indicates whether this paint worklet is composited or not. kUnknown
+  // indicates that it has not been decided yet.
   // TODO(crbug.com/987974): Make this variable reset when there is a style
   // change.
-  bool paint_off_thread_;
+  OffThreadPaintState off_thread_paint_state_;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/css/css_paint_value_test.cc b/third_party/blink/renderer/core/css/css_paint_value_test.cc
index e1d9340..b5f3102 100644
--- a/third_party/blink/renderer/core/css/css_paint_value_test.cc
+++ b/third_party/blink/renderer/core/css/css_paint_value_test.cc
@@ -15,8 +15,10 @@
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/core/style/style_generated_image.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/platform/graphics/paint_generated_image.h"
+#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
 
@@ -78,6 +80,10 @@
   MOCK_CONST_METHOD0(IsImageGeneratorReady, bool());
   MOCK_CONST_METHOD0(WorkletId, int());
 
+  void AddNativeProperty() {
+    native_properties_.push_back(CSSPropertyID::kBorderImageSource);
+  }
+
  private:
   Vector<CSSPropertyID> native_properties_;
   Vector<AtomicString> custom_properties_;
@@ -96,6 +102,103 @@
 }
 }  // namespace
 
+TEST_P(CSSPaintValueTest, ReportingCompositedUMA) {
+  HistogramTester histogram_tester;
+  NiceMock<MockCSSPaintImageGenerator>* mock_generator =
+      MakeGarbageCollected<NiceMock<MockCSSPaintImageGenerator>>();
+  base::AutoReset<MockCSSPaintImageGenerator*> scoped_override_generator(
+      &g_override_generator, mock_generator);
+  base::AutoReset<CSSPaintImageGenerator::CSSPaintImageGeneratorCreateFunction>
+      scoped_create_function(
+          CSSPaintImageGenerator::GetCreateFunctionForTesting(),
+          ProvideOverrideGenerator);
+
+  const FloatSize target_size(100, 100);
+
+  SetBodyInnerHTML(R"HTML(<div id="target"></div>)HTML");
+  LayoutObject* target = GetLayoutObjectByElementId("target");
+  const ComputedStyle& style = *target->Style();
+
+  auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  // Mark the generator as ready - GetImage should succeed when
+  // OffMainThreadCSSPaint is enabled.
+  ON_CALL(*mock_generator, IsImageGeneratorReady()).WillByDefault(Return(true));
+  ON_CALL(*mock_generator, Paint(_, _, _, _))
+      .WillByDefault(Return(PaintGeneratedImage::Create(nullptr, target_size)));
+  ASSERT_TRUE(
+      paint_value->GetImage(*target, GetDocument(), style, target_size));
+  if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) {
+    histogram_tester.ExpectTotalCount("Blink.CSSPaintValue.PaintOffThread", 1u);
+    histogram_tester.ExpectUniqueSample("Blink.CSSPaintValue.PaintOffThread",
+                                        true, 1u);
+  } else {
+    histogram_tester.ExpectTotalCount("Blink.CSSPaintValue.PaintOffThread", 0u);
+  }
+
+  ASSERT_TRUE(
+      paint_value->GetImage(*target, GetDocument(), style, target_size));
+  if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) {
+    // Repaint, should not report histogram again.
+    histogram_tester.ExpectTotalCount("Blink.CSSPaintValue.PaintOffThread", 1u);
+    histogram_tester.ExpectUniqueSample("Blink.CSSPaintValue.PaintOffThread",
+                                        true, 1u);
+  } else {
+    histogram_tester.ExpectTotalCount("Blink.CSSPaintValue.PaintOffThread", 0u);
+  }
+}
+
+TEST_P(CSSPaintValueTest, ReportingNonCompositedUMA) {
+  HistogramTester histogram_tester;
+  NiceMock<MockCSSPaintImageGenerator>* mock_generator =
+      MakeGarbageCollected<NiceMock<MockCSSPaintImageGenerator>>();
+  mock_generator->AddNativeProperty();
+  base::AutoReset<MockCSSPaintImageGenerator*> scoped_override_generator(
+      &g_override_generator, mock_generator);
+  base::AutoReset<CSSPaintImageGenerator::CSSPaintImageGeneratorCreateFunction>
+      scoped_create_function(
+          CSSPaintImageGenerator::GetCreateFunctionForTesting(),
+          ProvideOverrideGenerator);
+
+  const FloatSize target_size(100, 100);
+
+  SetBodyInnerHTML(R"HTML(<div id="target"></div>)HTML");
+  LayoutObject* target = GetLayoutObjectByElementId("target");
+  auto style = ComputedStyle::Create();
+  auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  StyleGeneratedImage* style_image =
+      MakeGarbageCollected<StyleGeneratedImage>(*paint_value);
+  style->SetBorderImageSource(style_image);
+
+  ON_CALL(*mock_generator, IsImageGeneratorReady()).WillByDefault(Return(true));
+  EXPECT_CALL(*mock_generator, Paint(_, _, _, _))
+      .WillRepeatedly(
+          Return(PaintGeneratedImage::Create(nullptr, target_size)));
+  // The paint worklet is not composited, and falls back to the main thread
+  // paint.
+  ASSERT_TRUE(
+      paint_value->GetImage(*target, GetDocument(), *style, target_size));
+  if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) {
+    histogram_tester.ExpectTotalCount("Blink.CSSPaintValue.PaintOffThread", 1u);
+    histogram_tester.ExpectUniqueSample("Blink.CSSPaintValue.PaintOffThread",
+                                        false, 1u);
+  } else {
+    histogram_tester.ExpectTotalCount("Blink.CSSPaintValue.PaintOffThread", 0u);
+  }
+
+  // Repaint, should not report histogram again.
+  ASSERT_TRUE(
+      paint_value->GetImage(*target, GetDocument(), *style, target_size));
+  if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) {
+    histogram_tester.ExpectTotalCount("Blink.CSSPaintValue.PaintOffThread", 1u);
+    histogram_tester.ExpectUniqueSample("Blink.CSSPaintValue.PaintOffThread",
+                                        false, 1u);
+  } else {
+    histogram_tester.ExpectTotalCount("Blink.CSSPaintValue.PaintOffThread", 0u);
+  }
+}
+
 TEST_P(CSSPaintValueTest, DelayPaintUntilGeneratorReady) {
   NiceMock<MockCSSPaintImageGenerator>* mock_generator =
       MakeGarbageCollected<NiceMock<MockCSSPaintImageGenerator>>();
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index f6dc21ef..0a41da6 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -1723,7 +1723,7 @@
 }
 
 // Appends to the passed in CSSGridLineNamesValue if any, otherwise creates a
-// new one.
+// new one. Returns nullptr if an empty list is consumed.
 CSSGridLineNamesValue* ConsumeGridLineNames(
     CSSParserTokenRange& range,
     const CSSParserContext& context,
@@ -1739,6 +1739,8 @@
   if (range_copy.ConsumeIncludingWhitespace().GetType() != kRightBracketToken)
     return nullptr;
   range = range_copy;
+  if (line_names->length() == 0U)
+    return nullptr;
   return line_names;
 }
 
@@ -1939,12 +1941,11 @@
                                TrackListType track_list_type) {
   bool allow_grid_line_names = track_list_type != TrackListType::kGridAuto;
   CSSValueList* values = CSSValueList::CreateSpaceSeparated();
+  if (!allow_grid_line_names && range.Peek().GetType() == kLeftBracketToken)
+    return nullptr;
   CSSGridLineNamesValue* line_names = ConsumeGridLineNames(range, context);
-  if (line_names) {
-    if (!allow_grid_line_names)
-      return nullptr;
+  if (line_names)
     values->Append(*line_names);
-  }
 
   bool allow_repeat = track_list_type == TrackListType::kGridTemplate;
   bool seen_auto_repeat = false;
@@ -1970,12 +1971,11 @@
     }
     if (seen_auto_repeat && !all_tracks_are_fixed_sized)
       return nullptr;
+    if (!allow_grid_line_names && range.Peek().GetType() == kLeftBracketToken)
+      return nullptr;
     line_names = ConsumeGridLineNames(range, context);
-    if (line_names) {
-      if (!allow_grid_line_names)
-        return nullptr;
+    if (line_names)
       values->Append(*line_names);
-    }
   } while (!range.AtEnd() && range.Peek().GetType() != kDelimiterToken);
   return values;
 }
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
index 7e2550e4..60f9986 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -122,7 +122,8 @@
 #endif
   }
 
-  void ScheduleAnimation(const LocalFrameView*) override {
+  void ScheduleAnimation(const LocalFrameView*,
+                         base::TimeDelta = base::TimeDelta()) override {
     if (WebTestSupport::IsRunningWebTest()) {
       // In single threaded web tests, the main frame's WebWidgetClient
       // (provided by WebViewTestProxy or WebWidgetTestProxy) runs the composite
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index a8c6b6c6..1c10fa8 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -3356,9 +3356,9 @@
     resizer_areas_->erase(it);
 }
 
-void LocalFrameView::ScheduleAnimation() {
+void LocalFrameView::ScheduleAnimation(base::TimeDelta delay) {
   if (auto* client = GetChromeClient())
-    client->ScheduleAnimation(this);
+    client->ScheduleAnimation(this, delay);
 }
 
 bool LocalFrameView::FrameIsScrollableDidChange() {
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 52a76f6..319a0d6 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -474,7 +474,7 @@
     return resizer_areas_.get();
   }
 
-  void ScheduleAnimation();
+  void ScheduleAnimation(base::TimeDelta = base::TimeDelta());
 
   // FIXME: This should probably be renamed as the 'inSubtreeLayout' parameter
   // passed around the LocalFrameView layout methods can be true while this
diff --git a/third_party/blink/renderer/core/frame/local_frame_view_test.cc b/third_party/blink/renderer/core/frame/local_frame_view_test.cc
index ca2e05b..0c9c6049 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view_test.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view_test.cc
@@ -42,7 +42,8 @@
     MockSetToolTip(&frame, tooltip_text, dir);
   }
 
-  void ScheduleAnimation(const LocalFrameView*) override {
+  void ScheduleAnimation(const LocalFrameView*,
+                         base::TimeDelta = base::TimeDelta()) override {
     has_scheduled_animation_ = true;
   }
   bool has_scheduled_animation_;
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
index eee1f2f..cda9010 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -60,6 +60,10 @@
 void WebFrameWidgetBase::BindLocalRoot(WebLocalFrame& local_root) {
   local_root_ = To<WebLocalFrameImpl>(local_root);
   local_root_->SetFrameWidget(this);
+  request_animation_after_delay_timer_.reset(
+      new TaskRunnerTimer<WebFrameWidgetBase>(
+          local_root.GetTaskRunner(TaskType::kInternalDefault), this,
+          &WebFrameWidgetBase::RequestAnimationAfterDelayTimerFired));
 }
 
 void WebFrameWidgetBase::Close() {
@@ -67,6 +71,7 @@
   local_root_->SetFrameWidget(nullptr);
   local_root_ = nullptr;
   client_ = nullptr;
+  request_animation_after_delay_timer_.reset();
 }
 
 WebLocalFrame* WebFrameWidgetBase::LocalRoot() const {
@@ -433,6 +438,23 @@
   return WebLocalFrameImpl::FromFrame(FocusedLocalFrameInWidget());
 }
 
+void WebFrameWidgetBase::RequestAnimationAfterDelay(
+    const base::TimeDelta& delay) {
+  DCHECK(request_animation_after_delay_timer_.get());
+  if (request_animation_after_delay_timer_->IsActive() &&
+      request_animation_after_delay_timer_->NextFireInterval() > delay) {
+    request_animation_after_delay_timer_->Stop();
+  }
+  if (!request_animation_after_delay_timer_->IsActive()) {
+    request_animation_after_delay_timer_->StartOneShot(delay, FROM_HERE);
+  }
+}
+
+void WebFrameWidgetBase::RequestAnimationAfterDelayTimerFired(TimerBase*) {
+  if (client_)
+    client_->ScheduleAnimation();
+}
+
 base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>
 WebFrameWidgetBase::EnsureCompositorMutatorDispatcher(
     scoped_refptr<base::SingleThreadTaskRunner>* mutator_task_runner) {
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.h b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
index 456e789..37af84b 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/core/dom/user_gesture_indicator.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/timer.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace cc {
@@ -142,6 +143,8 @@
   // focused frame has a different local root.
   LocalFrame* FocusedLocalFrameInWidget() const;
 
+  void RequestAnimationAfterDelay(const base::TimeDelta&);
+
   virtual void Trace(blink::Visitor*);
 
  protected:
@@ -186,6 +189,7 @@
 
  private:
   void CancelDrag();
+  void RequestAnimationAfterDelayTimerFired(TimerBase*);
 
   WebWidgetClient* client_;
 
@@ -211,6 +215,9 @@
   base::WeakPtr<PaintWorkletPaintDispatcher> paint_dispatcher_;
   scoped_refptr<base::SingleThreadTaskRunner> paint_task_runner_;
 
+  std::unique_ptr<TaskRunnerTimer<WebFrameWidgetBase>>
+      request_animation_after_delay_timer_;
+
   friend class WebViewImpl;
 };
 
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.cc b/third_party/blink/renderer/core/input/mouse_event_manager.cc
index 4917895..005e1f7 100644
--- a/third_party/blink/renderer/core/input/mouse_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_event_manager.cc
@@ -790,7 +790,8 @@
 
   if (RuntimeEnabledFeatures::TextFragmentIdentifiersEnabled(
           frame_->GetDocument())) {
-    frame_->View()->DismissFragmentAnchor();
+    if (frame_->View())
+      frame_->View()->DismissFragmentAnchor();
   }
 
   if (frame_->GetDocument()->IsSVGDocument() &&
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
index c07392fc..5cad314 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
@@ -16,6 +16,11 @@
 
 namespace {
 
+Document& TrackingDocument(const IntersectionObservation* observation) {
+  if (observation->Observer()->RootIsImplicit())
+    return observation->Target()->GetDocument();
+  return (observation->Observer()->root()->GetDocument());
+}
 
 }  // namespace
 
@@ -97,8 +102,10 @@
   DOMHighResTimeStamp timestamp = observer_->GetTimeStamp();
   if (timestamp == -1)
     return false;
-  if (!(flags & kIgnoreDelay) &&
-      timestamp - last_run_time_ < observer_->GetEffectiveDelay()) {
+  base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
+      observer_->GetEffectiveDelay() - (timestamp - last_run_time_));
+  if (!(flags & kIgnoreDelay) && delay > base::TimeDelta()) {
+    TrackingDocument(this).View()->ScheduleAnimation(delay);
     return false;
   }
   if (target_->isConnected() && Observer()->trackVisibility()) {
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index a475ab80..ed5939cf 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -1227,7 +1227,8 @@
                                   ItemPosition position,
                                   LayoutUnit ascent,
                                   LayoutUnit max_ascent,
-                                  bool is_wrap_reverse) {
+                                  bool is_wrap_reverse,
+                                  bool is_deprecated_webkit_box) {
   switch (position) {
     case ItemPosition::kLegacy:
     case ItemPosition::kAuto:
@@ -1248,8 +1249,10 @@
       break;
     case ItemPosition::kFlexEnd:
       return available_free_space;
-    case ItemPosition::kCenter:
-      return available_free_space / 2;
+    case ItemPosition::kCenter: {
+      const LayoutUnit result = (available_free_space / 2);
+      return is_deprecated_webkit_box ? result.ClampNegativeToZero() : result;
+    }
     case ItemPosition::kBaseline:
       // FIXME: If we get here in columns, we want the use the descent, except
       // we currently can't get the ascent/descent of orthogonal children.
@@ -1299,14 +1302,18 @@
       available_space,
       FlexLayoutAlgorithm::AlignmentForChild(StyleRef(), child.StyleRef()),
       LayoutUnit(), LayoutUnit(),
-      StyleRef().FlexWrap() == EFlexWrap::kWrapReverse);
+      StyleRef().FlexWrap() == EFlexWrap::kWrapReverse,
+      StyleRef().IsDeprecatedWebkitBox());
 }
 
 LayoutUnit LayoutFlexibleBox::StaticInlinePositionForPositionedChild(
     const LayoutBox& child) {
-  return StartOffsetForContent() +
-         (IsColumnFlow() ? StaticCrossAxisPositionForPositionedChild(child)
-                         : StaticMainAxisPositionForPositionedChild(child));
+  const LayoutUnit start_offset = StartOffsetForContent();
+  if (IsColumnFlow() && StyleRef().IsDeprecatedWebkitBox())
+    return start_offset;
+  return start_offset + (IsColumnFlow()
+                             ? StaticCrossAxisPositionForPositionedChild(child)
+                             : StaticMainAxisPositionForPositionedChild(child));
 }
 
 LayoutUnit LayoutFlexibleBox::StaticBlockPositionForPositionedChild(
@@ -1623,7 +1630,8 @@
       LayoutUnit available_space = flex_item.AvailableAlignmentSpace();
       LayoutUnit offset = AlignmentOffset(
           available_space, position, flex_item.MarginBoxAscent(), max_ascent,
-          StyleRef().FlexWrap() == EFlexWrap::kWrapReverse);
+          StyleRef().FlexWrap() == EFlexWrap::kWrapReverse,
+          StyleRef().IsDeprecatedWebkitBox());
       AdjustAlignmentForChild(*flex_item.box, offset);
       if (position == ItemPosition::kBaseline &&
           StyleRef().FlexWrap() == EFlexWrap::kWrapReverse) {
diff --git a/third_party/blink/renderer/core/layout/layout_theme_win.cc b/third_party/blink/renderer/core/layout/layout_theme_win.cc
index 43fd8ef9..e333838 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_win.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_win.cc
@@ -6,6 +6,7 @@
 
 #include <windows.h>
 
+#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
@@ -25,45 +26,42 @@
     return LayoutThemeDefault::SystemColor(css_value_id, color_scheme);
   }
 
-  int system_index;
-
+  blink::WebThemeEngine::SystemThemeColor theme_color;
   switch (css_value_id) {
     case CSSValueID::kButtonface:
-      system_index = COLOR_BTNFACE;
+      theme_color = blink::WebThemeEngine::SystemThemeColor::kButtonFace;
       break;
     case CSSValueID::kButtontext:
-      system_index = COLOR_BTNTEXT;
+      theme_color = blink::WebThemeEngine::SystemThemeColor::kButtonText;
       break;
     case CSSValueID::kGraytext:
-      system_index = COLOR_GRAYTEXT;
+      theme_color = blink::WebThemeEngine::SystemThemeColor::kGrayText;
       break;
     case CSSValueID::kHighlight:
-      system_index = COLOR_HIGHLIGHT;
+      theme_color = blink::WebThemeEngine::SystemThemeColor::kHighlight;
       break;
     case CSSValueID::kHighlighttext:
-      system_index = COLOR_HIGHLIGHTTEXT;
+      theme_color = blink::WebThemeEngine::SystemThemeColor::kHighlightText;
       break;
     case CSSValueID::kLinktext:
     case CSSValueID::kVisitedtext:
-      system_index = COLOR_HOTLIGHT;
+      theme_color = blink::WebThemeEngine::SystemThemeColor::kHotlight;
       break;
     case CSSValueID::kWindow:
-      system_index = COLOR_WINDOW;
+      theme_color = blink::WebThemeEngine::SystemThemeColor::kWindow;
       break;
     case CSSValueID::kWindowtext:
-      system_index = COLOR_WINDOWTEXT;
+      theme_color = blink::WebThemeEngine::SystemThemeColor::kWindowText;
       break;
     default:
       return LayoutThemeDefault::SystemColor(css_value_id, color_scheme);
   }
 
-  return SystemColorBySystemIndex(system_index);
-}
-
-Color LayoutThemeWin::SystemColorBySystemIndex(int system_index) {
-  DWORD system_color = ::GetSysColor(system_index);
-  return Color(GetRValue(system_color), GetGValue(system_color),
-               GetBValue(system_color));
+  const base::Optional<SkColor> system_color =
+      Platform::Current()->ThemeEngine()->GetSystemColor(theme_color);
+  if (system_color == base::nullopt)
+    return LayoutThemeDefault::SystemColor(css_value_id, color_scheme);
+  return Color(system_color.value());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_theme_win.h b/third_party/blink/renderer/core/layout/layout_theme_win.h
index 842870a4..860a0a74 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_win.h
+++ b/third_party/blink/renderer/core/layout/layout_theme_win.h
@@ -15,9 +15,6 @@
 
   Color SystemColor(CSSValueID css_value_id,
                     WebColorScheme color_scheme) const override;
-
- private:
-  static Color SystemColorBySystemIndex(int system_index);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index f805de2..ce3ceb09 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -948,8 +948,7 @@
 
   LayoutUnit child_origin_line_offset =
       ConstraintSpace().BfcOffset().line_offset +
-      border_scrollbar_padding_.LineLeft(direction) +
-      child_data.margins.LineLeft(direction).ClampNegativeToZero();
+      border_scrollbar_padding_.LineLeft(direction);
 
   // If the child has a block-start margin, and the BFC block offset is still
   // unresolved, and we have preceding adjoining floats, things get complicated
@@ -1044,11 +1043,12 @@
   bool abort_if_cleared = child_data.margins.block_start != LayoutUnit() &&
                           !child_margin_got_separated &&
                           child_determined_bfc_offset;
-  NGLayoutOpportunity opportunity;
-  scoped_refptr<const NGLayoutResult> layout_result;
-  std::tie(layout_result, opportunity) = LayoutNewFormattingContext(
-      child, child_break_token, child_data,
-      {child_origin_line_offset, child_bfc_offset_estimate}, abort_if_cleared);
+  NGBfcOffset child_bfc_offset;
+  scoped_refptr<const NGLayoutResult> layout_result =
+      LayoutNewFormattingContext(
+          child, child_break_token, child_data,
+          {child_origin_line_offset, child_bfc_offset_estimate},
+          abort_if_cleared, &child_bfc_offset);
 
   if (!layout_result) {
     DCHECK(abort_if_cleared);
@@ -1085,81 +1085,20 @@
       }
     }
 
-    DCHECK_GT(opportunity.rect.start_offset.block_offset,
-              child_bfc_offset_estimate);
     child_bfc_offset_estimate = non_adjoining_bfc_offset_estimate;
     child_margin_got_separated = true;
 
     // We can re-layout the child right away. This re-layout *must* produce a
-    // fragment and opportunity which fits within the exclusion space.
-    std::tie(layout_result, opportunity) = LayoutNewFormattingContext(
+    // fragment which fits within the exclusion space.
+    layout_result = LayoutNewFormattingContext(
         child, child_break_token, child_data,
         {child_origin_line_offset, child_bfc_offset_estimate},
-        /* abort_if_cleared */ false);
+        /* abort_if_cleared */ false, &child_bfc_offset);
   }
 
   NGFragment fragment(ConstraintSpace().GetWritingMode(),
                       layout_result->PhysicalFragment());
 
-  // Auto-margins are applied within the layout opportunity which fits. We'll
-  // pretend that computed margins are 0 here, as they have already been
-  // excluded from the layout opportunity rectangle.
-  NGBoxStrut auto_margins;
-  if (child.IsListMarker()) {
-    // Deal with marker's margin. It happens only when marker needs to occupy
-    // the whole line.
-    DCHECK(child.ListMarkerOccupiesWholeLine());
-    // Because the marker is laid out as a normal block child, its inline size
-    // is extended to fill up the space. Compute the regular marker size from
-    // the first child.
-    const NGPhysicalContainerFragment& marker_fragment =
-        layout_result->PhysicalFragment();
-    LayoutUnit marker_inline_size = LayoutUnit();
-    if (!marker_fragment.Children().empty()) {
-      const NGPhysicalFragment& marker_child_fragment =
-          *marker_fragment.Children().front();
-      marker_inline_size =
-          marker_child_fragment.Size()
-              .ConvertToLogical(ConstraintSpace().GetWritingMode())
-              .inline_size;
-    }
-    auto_margins.inline_start = NGUnpositionedListMarker(To<NGBlockNode>(child))
-                                    .InlineOffset(marker_inline_size);
-    auto_margins.inline_end = opportunity.rect.InlineSize() -
-                              fragment.InlineSize() - auto_margins.inline_start;
-  } else {
-    LayoutUnit inline_size = fragment.InlineSize();
-    // Negative margins are not used to determine opportunity, but need to take
-    // them into account for positioning.
-    LayoutUnit inline_margin = child_data.margins.InlineSum();
-    if (inline_margin < 0)
-      inline_size += inline_margin;
-    ResolveInlineMargins(child_style, Style(), opportunity.rect.InlineSize(),
-                         inline_size, &auto_margins);
-  }
-
-  LayoutUnit child_bfc_line_offset = opportunity.rect.start_offset.line_offset +
-                                     auto_margins.LineLeft(direction);
-
-  // When there are negative margins present, a new formatting context can move
-  // outside its layout opportunity. This occurs when the *line-left* edge
-  // hasn't been shifted by floats.
-  //
-  // NOTE: Firefox and EdgeHTML both match this behaviour of only considering
-  // the line-left edge. WebKit also considers this line-right edge, but this
-  // is slightly more complicated to implement, and probably not needed for web
-  // compatibility.
-  bool can_move_outside_opportunity =
-      opportunity.rect.start_offset.line_offset == child_origin_line_offset;
-
-  if (can_move_outside_opportunity) {
-    child_bfc_line_offset +=
-        child_data.margins.LineLeft(direction).ClampPositiveToZero();
-  }
-
-  NGBfcOffset child_bfc_offset(child_bfc_line_offset,
-                               opportunity.rect.start_offset.block_offset);
-
   LogicalOffset logical_offset = LogicalFromBfcOffsets(
       child_bfc_offset, ContainerBfcOffset(), fragment.InlineSize(),
       container_builder_.Size().inline_size, ConstraintSpace().Direction());
@@ -1198,36 +1137,28 @@
   return true;
 }
 
-std::pair<scoped_refptr<const NGLayoutResult>, NGLayoutOpportunity>
+scoped_refptr<const NGLayoutResult>
 NGBlockLayoutAlgorithm::LayoutNewFormattingContext(
     NGLayoutInputNode child,
     const NGBreakToken* child_break_token,
     const NGInflowChildData& child_data,
     NGBfcOffset origin_offset,
-    bool abort_if_cleared) {
+    bool abort_if_cleared,
+    NGBfcOffset* out_child_bfc_offset) {
+  const ComputedStyle& child_style = child.Style();
+  const TextDirection direction = ConstraintSpace().Direction();
+  const WritingMode writing_mode = ConstraintSpace().GetWritingMode();
+
   // The origin offset is where we should start looking for layout
   // opportunities. It needs to be adjusted by the child's clearance.
   AdjustToClearance(
-      exclusion_space_.ClearanceOffset(child.Style().Clear(Style())),
+      exclusion_space_.ClearanceOffset(child_style.Clear(Style())),
       &origin_offset);
   DCHECK(container_builder_.BfcBlockOffset());
 
-  // Before we lay out, figure out how much inline space we have available at
-  // the start block offset estimate (the child is not allowed to overlap with
-  // floats, so we need to find out how much space is used by floats at this
-  // block offset). This may affect the inline size of the child, e.g. when it's
-  // specified as auto, or if it's a table (with table-layout:auto). This will
-  // not affect percentage resolution, because that's going to be resolved
-  // against the containing block, regardless of adjacent floats. When looking
-  // for space, we ignore inline margins, as they will overlap with any adjacent
-  // floats.
-  LayoutUnit inline_margin = child_data.margins.InlineSum();
-  LayoutUnit inline_size =
-      (child_available_size_.inline_size - inline_margin.ClampNegativeToZero())
-          .ClampNegativeToZero();
-
   LayoutOpportunityVector opportunities =
-      exclusion_space_.AllLayoutOpportunities(origin_offset, inline_size);
+      exclusion_space_.AllLayoutOpportunities(
+          origin_offset, child_available_size_.inline_size);
 
   // We should always have at least one opportunity.
   DCHECK_GT(opportunities.size(), 0u);
@@ -1243,32 +1174,60 @@
       // Abort if we got pushed downwards. We need to adjust
       // origin_offset.block_offset, reposition any floats affected by that, and
       // try again.
-      return std::make_pair(nullptr, opportunity);
+      return nullptr;
     }
 
-    // When the inline dimensions of layout opportunity match the available
-    // space, a new formatting context can expand outside of the opportunity if
-    // negative margins are present.
-    bool can_expand_outside_opportunity =
-        (opportunity.rect.start_offset.line_offset ==
-             origin_offset.line_offset &&
-         opportunity.rect.InlineSize() == inline_size);
+    // Find the available inline-size which should be given to the child.
+    LayoutUnit line_left_offset = opportunity.rect.start_offset.line_offset;
+    LayoutUnit line_right_offset = opportunity.rect.end_offset.line_offset;
 
-    LayoutUnit inline_negative_margin =
-        can_expand_outside_opportunity ? inline_margin.ClampPositiveToZero()
-                                       : LayoutUnit();
+    LayoutUnit line_left_margin = child_data.margins.LineLeft(direction);
+    LayoutUnit line_right_margin = child_data.margins.LineRight(direction);
+
+    // When the inline dimensions of layout opportunity match the available
+    // inline-size, a new formatting context can expand outside of the
+    // opportunity if negative margins are present.
+    bool can_expand_outside_opportunity =
+        opportunity.rect.start_offset.line_offset ==
+            origin_offset.line_offset &&
+        opportunity.rect.InlineSize() == child_available_size_.inline_size;
+
+    if (can_expand_outside_opportunity) {
+      // No floats have affected the available inline-size, adjust the
+      // available inline-size by the margins.
+      DCHECK_EQ(line_left_offset, origin_offset.line_offset);
+      DCHECK_EQ(line_right_offset,
+                origin_offset.line_offset + child_available_size_.inline_size);
+      line_left_offset += line_left_margin;
+      line_right_offset -= line_right_margin;
+    } else {
+      // Margins are applied from the content-box, not the layout opportunity
+      // area. Instead of adjusting by the size of the margins, we "shrink" the
+      // available inline-size if required.
+      line_left_offset = std::max(
+          line_left_offset,
+          origin_offset.line_offset + line_left_margin.ClampNegativeToZero());
+      line_right_offset = std::min(line_right_offset,
+                                   origin_offset.line_offset +
+                                       child_available_size_.inline_size -
+                                       line_right_margin.ClampNegativeToZero());
+    }
+    LayoutUnit opportunity_size =
+        (line_right_offset - line_left_offset).ClampNegativeToZero();
 
     // The available inline size in the child constraint space needs to include
     // inline margins, since layout algorithms (both legacy and NG) will resolve
     // auto inline size by subtracting the inline margins from available inline
     // size. We have calculated a layout opportunity without margins in mind,
     // since they overlap with adjacent floats. Now we need to add them.
-    LogicalSize child_available_size = {
-        (opportunity.rect.InlineSize() - inline_negative_margin + inline_margin)
-            .ClampNegativeToZero(),
-        child_available_size_.block_size};
+    LayoutUnit child_available_inline_size =
+        (opportunity_size + child_data.margins.InlineSum())
+            .ClampNegativeToZero();
+
     NGConstraintSpace child_space = CreateConstraintSpaceForChild(
-        child, child_data, child_available_size, /* is_new_fc */ true);
+        child, child_data,
+        {child_available_inline_size, child_available_size_.block_size},
+        /* is_new_fc */ true);
 
     // All formatting context roots (like this child) should start with an empty
     // exclusion space.
@@ -1281,18 +1240,54 @@
     // should be returned.
     DCHECK(layout_result->ExclusionSpace().IsEmpty());
 
-    NGFragment fragment(ConstraintSpace().GetWritingMode(),
-                        layout_result->PhysicalFragment());
+    NGFragment fragment(writing_mode, layout_result->PhysicalFragment());
 
-    // Now we can check if the fragment will fit in this layout opportunity.
-    if ((opportunity.rect.InlineSize() >= fragment.InlineSize() ||
-         opportunity.rect.InlineSize() == inline_size) &&
-        opportunity.rect.BlockSize() >= fragment.BlockSize())
-      return std::make_pair(std::move(layout_result), opportunity);
+    // Check if the fragment will fit in this layout opportunity, if not proceed
+    // to the next opportunity.
+    if ((fragment.InlineSize() > opportunity.rect.InlineSize() &&
+         !can_expand_outside_opportunity) ||
+        fragment.BlockSize() > opportunity.rect.BlockSize())
+      continue;
+
+    // Now find the fragment's (final) position calculating the auto margins.
+    NGBoxStrut auto_margins = child_data.margins;
+    if (child.IsListMarker()) {
+      // Deal with marker's margin. It happens only when marker needs to occupy
+      // the whole line.
+      DCHECK(child.ListMarkerOccupiesWholeLine());
+      // Because the marker is laid out as a normal block child, its inline
+      // size is extended to fill up the space. Compute the regular marker size
+      // from the first child.
+      const auto& marker_fragment = layout_result->PhysicalFragment();
+      LayoutUnit marker_inline_size;
+      if (!marker_fragment.Children().empty()) {
+        marker_inline_size =
+            NGFragment(writing_mode, *marker_fragment.Children().front())
+                .InlineSize();
+      }
+      auto_margins.inline_start =
+          NGUnpositionedListMarker(To<NGBlockNode>(child))
+              .InlineOffset(marker_inline_size);
+      auto_margins.inline_end = opportunity.rect.InlineSize() -
+                                fragment.InlineSize() -
+                                auto_margins.inline_start;
+    } else {
+      ResolveInlineMargins(child_style, Style(), child_available_inline_size,
+                           fragment.InlineSize(), &auto_margins);
+    }
+
+    // |auto_margins| are initialized as a copy of the child's initial margins.
+    // To determine the effect of the auto-margins we only apply the difference.
+    LayoutUnit auto_margin_line_left =
+        auto_margins.LineLeft(direction) - line_left_margin;
+
+    *out_child_bfc_offset = {line_left_offset + auto_margin_line_left,
+                             opportunity.rect.start_offset.block_offset};
+    return layout_result;
   }
 
   NOTREACHED();
-  return std::make_pair(nullptr, NGLayoutOpportunity());
+  return nullptr;
 }
 
 bool NGBlockLayoutAlgorithm::HandleInflow(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
index 8f98f60..edd2d5b9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
@@ -173,12 +173,13 @@
 
   // Performs the actual layout of a new formatting context. This may be called
   // multiple times from HandleNewFormattingContext.
-  std::pair<scoped_refptr<const NGLayoutResult>, NGLayoutOpportunity>
-  LayoutNewFormattingContext(NGLayoutInputNode child,
-                             const NGBreakToken* child_break_token,
-                             const NGInflowChildData&,
-                             NGBfcOffset origin_offset,
-                             bool abort_if_cleared);
+  scoped_refptr<const NGLayoutResult> LayoutNewFormattingContext(
+      NGLayoutInputNode child,
+      const NGBreakToken* child_break_token,
+      const NGInflowChildData&,
+      NGBfcOffset origin_offset,
+      bool abort_if_cleared,
+      NGBfcOffset* out_child_bfc_offset);
 
   // Handle an in-flow child.
   // Returns false if we need to abort layout, because a previously unknown BFC
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 75161db..22c752ec 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -169,7 +169,8 @@
   bool TabsToLinks() override { return false; }
 
   void InvalidateRect(const IntRect&) override {}
-  void ScheduleAnimation(const LocalFrameView*) override {}
+  void ScheduleAnimation(const LocalFrameView*,
+                         base::TimeDelta = base::TimeDelta()) override {}
 
   IntRect ViewportToScreen(const IntRect& r,
                            const LocalFrameView*) const override {
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h
index 4e051c2..51e7258 100644
--- a/third_party/blink/renderer/core/page/chrome_client.h
+++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -126,7 +126,8 @@
   virtual IntRect ViewportToScreen(const IntRect&,
                                    const LocalFrameView*) const = 0;
 
-  virtual void ScheduleAnimation(const LocalFrameView*) = 0;
+  virtual void ScheduleAnimation(const LocalFrameView*,
+                                 base::TimeDelta = base::TimeDelta()) = 0;
 
   // The specified rectangle is adjusted for the minimum window size and the
   // screen, then setWindowRect with the adjusted rectangle is called.
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index 6d90e9c..8d8deb9 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -429,7 +429,8 @@
     web_view_->InvalidateRect(update_rect);
 }
 
-void ChromeClientImpl::ScheduleAnimation(const LocalFrameView* frame_view) {
+void ChromeClientImpl::ScheduleAnimation(const LocalFrameView* frame_view,
+                                         base::TimeDelta delay) {
   LocalFrame& frame = frame_view->GetFrame();
   WebLocalFrameImpl* web_frame = WebLocalFrameImpl::FromFrame(frame);
   DCHECK(web_frame);
@@ -440,9 +441,13 @@
   // WebFrameWidget needs to be initialized before initializing the core frame?
   WebFrameWidgetBase* widget = web_frame->LocalRootFrameWidget();
   if (widget) {
-    // LocalRootFrameWidget() is a WebWidget, its client is the embedder.
-    WebWidgetClient* web_widget_client = widget->Client();
-    web_widget_client->ScheduleAnimation();
+    if (delay.is_zero()) {
+      // LocalRootFrameWidget() is a WebWidget, its client is the embedder.
+      WebWidgetClient* web_widget_client = widget->Client();
+      web_widget_client->ScheduleAnimation();
+    } else {
+      widget->RequestAnimationAfterDelay(delay);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.h b/third_party/blink/renderer/core/page/chrome_client_impl.h
index faca220..1754fe8 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.h
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.h
@@ -120,7 +120,8 @@
                                     String& result) override;
   bool TabsToLinks() override;
   void InvalidateRect(const IntRect&) override;
-  void ScheduleAnimation(const LocalFrameView*) override;
+  void ScheduleAnimation(const LocalFrameView*,
+                         base::TimeDelta = base::TimeDelta()) override;
   IntRect ViewportToScreen(const IntRect&,
                            const LocalFrameView*) const override;
   float WindowToViewportScalar(const float) const override;
diff --git a/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc b/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
index 9aac603..80e376e4 100644
--- a/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
+++ b/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
@@ -40,11 +40,12 @@
     EmptyChromeClient::Trace(visitor);
   }
 
-  void ScheduleAnimation(const LocalFrameView*) override {
+  void ScheduleAnimation(const LocalFrameView*,
+                         base::TimeDelta delay = base::TimeDelta()) override {
     // Need to pass LocalFrameView for the anchor element because the Frame for
     // this overlay doesn't have an associated WebFrameWidget, which schedules
     // animation.
-    main_chrome_client_->ScheduleAnimation(anchor_view_);
+    main_chrome_client_->ScheduleAnimation(anchor_view_, delay);
   }
 
   float WindowToViewportScalar(const float scalar_value) const override {
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index a820923..c94a0d5 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -488,7 +488,7 @@
 
   // border-image-source
   StyleImage* BorderImageSource() const { return BorderImage().GetImage(); }
-  void SetBorderImageSource(StyleImage*);
+  CORE_EXPORT void SetBorderImageSource(StyleImage*);
 
   // border-image-width
   const BorderImageLengthBox& BorderImageWidth() const {
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.cc b/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.cc
index 8389427..eba9f3f 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.cc
@@ -91,7 +91,8 @@
   image_->RestoreAnimation();
 }
 
-void SVGImageChromeClient::ScheduleAnimation(const LocalFrameView*) {
+void SVGImageChromeClient::ScheduleAnimation(const LocalFrameView*,
+                                             base::TimeDelta fire_time) {
   // Because a single SVGImage can be shared by multiple pages, we can't key
   // our svg image layout on the page's real animation frame. Therefore, we
   // run this fake animation timer to trigger layout in SVGImages. The name,
@@ -103,11 +104,11 @@
   // animations, but prefer a fixed, jittery, frame-delay if there're any
   // animations. Checking for pending/active animations could be more
   // stringent.
-  base::TimeDelta fire_time;
   if (image_->MaybeAnimated()) {
     if (IsSuspended())
       return;
-    fire_time = kAnimationFrameDelay;
+    if (fire_time.is_zero())
+      fire_time = kAnimationFrameDelay;
   }
   animation_timer_->StartOneShot(fire_time, FROM_HERE);
 }
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h b/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h
index ead7000f..6460951 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h
@@ -56,7 +56,8 @@
  private:
   void ChromeDestroyed() override;
   void InvalidateRect(const IntRect&) override;
-  void ScheduleAnimation(const LocalFrameView*) override;
+  void ScheduleAnimation(const LocalFrameView*,
+                         base::TimeDelta = base::TimeDelta()) override;
 
   void SetTimer(std::unique_ptr<TimerBase>);
   TimerBase* GetTimerForTesting() const { return animation_timer_.get(); }
diff --git a/third_party/blink/renderer/devtools/front_end/console/ConsoleView.js b/third_party/blink/renderer/devtools/front_end/console/ConsoleView.js
index f07fec7..d7ab2c1 100644
--- a/third_party/blink/renderer/devtools/front_end/console/ConsoleView.js
+++ b/third_party/blink/renderer/devtools/front_end/console/ConsoleView.js
@@ -1238,7 +1238,7 @@
     const filterKeys = Object.values(Console.ConsoleFilter.FilterType);
     this._suggestionBuilder = new UI.FilterSuggestionBuilder(filterKeys);
     this._textFilterUI = new UI.ToolbarInput(
-        Common.UIString('Filter'), 0.2, 1, Common.UIString('e.g. /event\\d/ -cdn url:a.com'),
+        Common.UIString('Filter'), '', 0.2, 1, Common.UIString('e.g. /event\\d/ -cdn url:a.com'),
         this._suggestionBuilder.completions.bind(this._suggestionBuilder));
     this._textFilterSetting = Common.settings.createSetting('console.textFilter', '');
     if (this._textFilterSetting.get())
diff --git a/third_party/blink/renderer/devtools/front_end/coverage/CoverageView.js b/third_party/blink/renderer/devtools/front_end/coverage/CoverageView.js
index 3ecb51a..ba45c2eb 100644
--- a/third_party/blink/renderer/devtools/front_end/coverage/CoverageView.js
+++ b/third_party/blink/renderer/devtools/front_end/coverage/CoverageView.js
@@ -42,7 +42,7 @@
     /** @type {?RegExp} */
     this._textFilterRegExp = null;
     toolbar.appendSeparator();
-    this._filterInput = new UI.ToolbarInput(Common.UIString('URL filter'), 0.4, 1);
+    this._filterInput = new UI.ToolbarInput(Common.UIString('URL filter'), '', 0.4, 1);
     this._filterInput.setEnabled(false);
     this._filterInput.addEventListener(UI.ToolbarInput.Event.TextChanged, this._onFilterChanged, this);
     toolbar.appendToolbarItem(this._filterInput);
diff --git a/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/HeapSnapshot.js b/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/HeapSnapshot.js
index dc0d8b3b..cfa160b 100644
--- a/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/HeapSnapshot.js
+++ b/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/HeapSnapshot.js
@@ -912,34 +912,34 @@
     this._firstDominatedNodeIndex = new Uint32Array(this.nodeCount + 1);
     this._dominatedNodes = new Uint32Array(this.nodeCount - 1);
 
-    this._progress.updateStatus('Building edge indexes\u2026');
+    this._progress.updateStatus(ls`Building edge indexes\u2026`);
     this._buildEdgeIndexes();
-    this._progress.updateStatus('Building retainers\u2026');
+    this._progress.updateStatus(ls`Building retainers\u2026`);
     this._buildRetainers();
-    this._progress.updateStatus('Calculating node flags\u2026');
+    this._progress.updateStatus(ls`Calculating node flags\u2026`);
     this.calculateFlags();
-    this._progress.updateStatus('Calculating distances\u2026');
+    this._progress.updateStatus(ls`Calculating distances\u2026`);
     this.calculateDistances();
-    this._progress.updateStatus('Building postorder index\u2026');
+    this._progress.updateStatus(ls`Building postorder index\u2026`);
     const result = this._buildPostOrderIndex();
     // Actually it is array that maps node ordinal number to dominator node ordinal number.
-    this._progress.updateStatus('Building dominator tree\u2026');
+    this._progress.updateStatus(ls`Building dominator tree\u2026`);
     this._dominatorsTree =
         this._buildDominatorTree(result.postOrderIndex2NodeOrdinal, result.nodeOrdinal2PostOrderIndex);
-    this._progress.updateStatus('Calculating retained sizes\u2026');
+    this._progress.updateStatus(ls`Calculating retained sizes\u2026`);
     this._calculateRetainedSizes(result.postOrderIndex2NodeOrdinal);
-    this._progress.updateStatus('Building dominated nodes\u2026');
+    this._progress.updateStatus(ls`Building dominated nodes\u2026`);
     this._buildDominatedNodes();
-    this._progress.updateStatus('Calculating statistics\u2026');
+    this._progress.updateStatus(ls`Calculating statistics\u2026`);
     this.calculateStatistics();
-    this._progress.updateStatus('Calculating samples\u2026');
+    this._progress.updateStatus(ls`Calculating samples\u2026`);
     this._buildSamples();
-    this._progress.updateStatus('Building locations\u2026');
+    this._progress.updateStatus(ls`Building locations\u2026`);
     this._buildLocationMap();
-    this._progress.updateStatus('Finished processing.');
+    this._progress.updateStatus(ls`Finished processing.`);
 
     if (this._profile.snapshot.trace_function_count) {
-      this._progress.updateStatus('Building allocation statistics\u2026');
+      this._progress.updateStatus(ls`Building allocation statistics\u2026`);
       const nodes = this.nodes;
       const nodesLength = nodes.length;
       const nodeFieldCount = this._nodeFieldCount;
@@ -956,7 +956,7 @@
         stats.ids.push(node.id());
       }
       this._allocationProfile = new HeapSnapshotWorker.AllocationProfile(this._profile, liveObjects);
-      this._progress.updateStatus('Done');
+      this._progress.updateStatus(ls`Done`);
     }
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/HeapSnapshotLoader.js b/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/HeapSnapshotLoader.js
index b7dd4f87..84bc429 100644
--- a/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/HeapSnapshotLoader.js
+++ b/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/HeapSnapshotLoader.js
@@ -109,7 +109,7 @@
   }
 
   _parseStringsArray() {
-    this._progress.updateStatus('Parsing strings\u2026');
+    this._progress.updateStatus(ls`Parsing strings\u2026`);
     const closingBracketIndex = this._json.lastIndexOf(']');
     if (closingBracketIndex === -1)
       throw new Error('Incomplete JSON');
diff --git a/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/heap_snapshot_worker_strings.grdp b/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/heap_snapshot_worker_strings.grdp
index bea64f4..db239fb 100644
--- a/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/heap_snapshot_worker_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/heap_snapshot_worker_strings.grdp
@@ -1,27 +1,72 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
+  <message name="IDS_DEVTOOLS_0f705e2ea0d470cf4e78f85ce73abc3c" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Calculating samples…
+  </message>
+  <message name="IDS_DEVTOOLS_15e828e7c9470740e5da2bd96f26809f" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Building locations…
+  </message>
+  <message name="IDS_DEVTOOLS_1e6dbaafd43ee6c7ce436757c5563160" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Building retainers…
+  </message>
   <message name="IDS_DEVTOOLS_239026e523e6f9780426e03637c5b4a4" desc="Text in Heap Snapshot Loader of the Memory panel when taking a heap snapshot">
     Loading strings…
   </message>
+  <message name="IDS_DEVTOOLS_42b1c19d788d56dae8f4274ae0e06388" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Building dominated nodes…
+  </message>
+  <message name="IDS_DEVTOOLS_508b42300b35ed4e3062adf330b76aad" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Building dominator tree…
+  </message>
+  <message name="IDS_DEVTOOLS_58d7770bfad35df21bae2d5820e3be52" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Building edge indexes…
+  </message>
   <message name="IDS_DEVTOOLS_58e8a5ca400f834526b09d680ddbc5d5" desc="Text in Heap Snapshot Loader of the Memory panel when taking a heap snapshot">
     Loading samples…
   </message>
+  <message name="IDS_DEVTOOLS_5e3a0ca620683b8428b4dbda33bf1f5d" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Finished processing.
+  </message>
   <message name="IDS_DEVTOOLS_6a542856aa30e3e814eaed146e67b2fd" desc="Text in Heap Snapshot Loader of the Memory panel when taking a heap snapshot">
     Loading nodes… <ph name="PH1">$1d<ex>38</ex></ph>%%
   </message>
   <message name="IDS_DEVTOOLS_7da2f27d62adf93b01c2c041f37667b9" desc="Text in Heap Snapshot Loader of the Memory panel when taking a heap snapshot">
     Processing snapshot…
   </message>
+  <message name="IDS_DEVTOOLS_82e20e6748434e5048a36bf2d4b33578" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Calculating retained sizes…
+  </message>
   <message name="IDS_DEVTOOLS_84c0ec1ee2bc769daf265e63bd793201" desc="Text in Heap Snapshot Loader of the Memory panel when taking a heap snapshot">
     Loading snapshot info…
   </message>
+  <message name="IDS_DEVTOOLS_93093f55895541ab4360826523fe066e" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Building postorder index…
+  </message>
+  <message name="IDS_DEVTOOLS_991feea6b04d59d7115962d5427a64fe" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Calculating node flags…
+  </message>
   <message name="IDS_DEVTOOLS_9bdae72f224411b7f1027a2c5c7c519b" desc="Text in Heap Snapshot Loader of the Memory panel when taking a heap snapshot">
     Loading locations…
   </message>
   <message name="IDS_DEVTOOLS_b0da59cea23d810234b75c2357748ad3" desc="Text in Heap Snapshot Loader of the Memory panel when taking a heap snapshot">
     Loading edges… <ph name="PH1">$1d<ex>30</ex></ph>%%
   </message>
+  <message name="IDS_DEVTOOLS_bfb105e5a3da1a90bd1eb98b4fa36f3f" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Calculating statistics…
+  </message>
+  <message name="IDS_DEVTOOLS_d0622d0589a212a59cf8b5859e58c670" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Calculating distances…
+  </message>
   <message name="IDS_DEVTOOLS_d67d63eca2747a30e1ffaeb04389c0bb" desc="Text in Heap Snapshot Loader of the Memory panel when taking a heap snapshot">
     Loading allocation traces… <ph name="PH1">$1d<ex>30</ex></ph>%%
   </message>
+  <message name="IDS_DEVTOOLS_d86c288a983db9a91432d2d13386170f" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Building allocation statistics…
+  </message>
+  <message name="IDS_DEVTOOLS_f92965e2c8a7afb3c1b9a5c09a263636" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Done
+  </message>
+  <message name="IDS_DEVTOOLS_fae03f5c27a9765eec716f3e6c600945" desc="Text to indicate the status of a heap snapshot in the Performance Pane">
+    Parsing strings…
+  </message>
 </grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js b/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js
index 71a180b..f8160fa1 100644
--- a/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js
@@ -73,7 +73,7 @@
     this._filterType = null;
 
     const placeholder = 'Enter regex, for example: (web)?socket';
-    this._filterTextInput = new UI.ToolbarInput(Common.UIString(placeholder), 0.4);
+    this._filterTextInput = new UI.ToolbarInput(Common.UIString(placeholder), '', 0.4);
     this._filterTextInput.addEventListener(UI.ToolbarInput.Event.TextChanged, this._updateFilterSetting, this);
     this._mainToolbar.appendToolbarItem(this._filterTextInput);
     this._filterRegex = null;
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js b/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js
index bc6d095..38f6a6e 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/ProfileView.js
@@ -34,6 +34,7 @@
     this.dataGrid.addEventListener(DataGrid.DataGrid.Events.DeselectedNode, this._nodeSelected.bind(this, false));
 
     this.viewSelectComboBox = new UI.ToolbarComboBox(this._changeView.bind(this));
+    this.viewSelectComboBox.setTitle(ls`Profile view mode`);
 
     this.focusButton = new UI.ToolbarButton(Common.UIString('Focus selected function'), 'largeicon-visibility');
     this.focusButton.setEnabled(false);
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp b/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
index 9aa023fa..574fbcda 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
@@ -181,6 +181,9 @@
   <message name="IDS_DEVTOOLS_6db0d0f1832acb4a56fc82f2317e61e6" desc="Text in Profile View of a profiler tool">
     Parsing…
   </message>
+  <message name="IDS_DEVTOOLS_6de06ad67fbc56af1a7a5b16a41d2f2c" desc="Aria-label for profiles view combobox in memory tool">
+    Profile view mode
+  </message>
   <message name="IDS_DEVTOOLS_6e9dd9cf5419fd65f433f1e4bbc5e6ad" desc="Text in Heap Profile View of a profiler tool">
     SAMPLING PROFILES
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/protocol_monitor/ProtocolMonitor.js b/third_party/blink/renderer/devtools/front_end/protocol_monitor/ProtocolMonitor.js
index 2e32fc1..fb1ad72 100644
--- a/third_party/blink/renderer/devtools/front_end/protocol_monitor/ProtocolMonitor.js
+++ b/third_party/blink/renderer/devtools/front_end/protocol_monitor/ProtocolMonitor.js
@@ -60,8 +60,8 @@
     this._filterParser = new TextUtils.FilterParser(keys);
     this._suggestionBuilder = new UI.FilterSuggestionBuilder(keys);
 
-    this._textFilterUI =
-        new UI.ToolbarInput(ls`Filter`, 1, .2, '', this._suggestionBuilder.completions.bind(this._suggestionBuilder));
+    this._textFilterUI = new UI.ToolbarInput(
+        ls`Filter`, '', 1, .2, '', this._suggestionBuilder.completions.bind(this._suggestionBuilder));
     this._textFilterUI.addEventListener(UI.ToolbarInput.Event.TextChanged, event => {
       const query = /** @type {string} */ (event.data);
       const filters = this._filterParser.parse(query);
diff --git a/third_party/blink/renderer/devtools/front_end/resources/DatabaseTableView.js b/third_party/blink/renderer/devtools/front_end/resources/DatabaseTableView.js
index 2fd959ea..b76d74c 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/DatabaseTableView.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/DatabaseTableView.js
@@ -39,7 +39,7 @@
 
     this.refreshButton = new UI.ToolbarButton(Common.UIString('Refresh'), 'largeicon-refresh');
     this.refreshButton.addEventListener(UI.ToolbarButton.Events.Click, this._refreshButtonClicked, this);
-    this._visibleColumnsInput = new UI.ToolbarInput(Common.UIString('Visible columns'), 1);
+    this._visibleColumnsInput = new UI.ToolbarInput(Common.UIString('Visible columns'), '', 1);
     this._visibleColumnsInput.addEventListener(UI.ToolbarInput.Event.TextChanged, this._onVisibleColumnsChanged, this);
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js b/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js
index d0713123..02f383c 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js
@@ -222,7 +222,7 @@
     this._pageForwardButton.addEventListener(UI.ToolbarButton.Events.Click, this._pageForwardButtonClicked, this);
     editorToolbar.appendToolbarItem(this._pageForwardButton);
 
-    this._keyInput = new UI.ToolbarInput(ls`Start from key`, 0.5);
+    this._keyInput = new UI.ToolbarInput(ls`Start from key`, '', 0.5);
     this._keyInput.addEventListener(UI.ToolbarInput.Event.TextChanged, this._updateData.bind(this, false));
     editorToolbar.appendToolbarItem(this._keyInput);
     editorToolbar.appendToolbarItem(new UI.ToolbarSeparator());
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkerCacheViews.js b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkerCacheViews.js
index 8b29e4a..89fb6e9 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkerCacheViews.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkerCacheViews.js
@@ -55,7 +55,7 @@
     this._deleteSelectedButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._deleteButtonClicked(null));
     editorToolbar.appendToolbarItem(this._deleteSelectedButton);
 
-    const entryPathFilterBox = new UI.ToolbarInput(ls`Filter by Path`, 1);
+    const entryPathFilterBox = new UI.ToolbarInput(ls`Filter by Path`, '', 1);
     editorToolbar.appendToolbarItem(entryPathFilterBox);
     const entryPathFilterThrottler = new Common.Throttler(300);
     this._entryPathFilter = '';
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
index 9dc6f28..3a96d898 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
@@ -43,7 +43,7 @@
     });
 
     const toolbar = new UI.Toolbar('service-worker-filter-toolbar', this._otherSWFilter);
-    this._filter = new UI.ToolbarInput(ls`Filter service worker`, 1);
+    this._filter = new UI.ToolbarInput(ls`Filter service worker`, '', 1);
     this._filter.addEventListener(UI.ToolbarInput.Event.TextChanged, () => this._filterChanged());
     toolbar.appendToolbarItem(this._filter);
 
diff --git a/third_party/blink/renderer/devtools/front_end/resources/StorageItemsView.js b/third_party/blink/renderer/devtools/front_end/resources/StorageItemsView.js
index 9137521..635cd24b 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/StorageItemsView.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/StorageItemsView.js
@@ -16,7 +16,7 @@
 
     this._mainToolbar = new UI.Toolbar('top-resources-toolbar', this.element);
 
-    this._filterItem = new UI.ToolbarInput(Common.UIString('Filter'), 0.4);
+    this._filterItem = new UI.ToolbarInput(Common.UIString('Filter'), '', 0.4);
     this._filterItem.addEventListener(UI.ToolbarInput.Event.TextChanged, this._filterChanged, this);
 
     const toolbarSeparator = new UI.ToolbarSeparator();
diff --git a/third_party/blink/renderer/devtools/front_end/timeline/EventsTimelineTreeView.js b/third_party/blink/renderer/devtools/front_end/timeline/EventsTimelineTreeView.js
index 48d94ae9..9a3e6ff 100644
--- a/third_party/blink/renderer/devtools/front_end/timeline/EventsTimelineTreeView.js
+++ b/third_party/blink/renderer/devtools/front_end/timeline/EventsTimelineTreeView.js
@@ -45,6 +45,14 @@
 
   /**
    * @override
+   * @return {string}
+   */
+  getToolbarInputAccessiblePlaceHolder() {
+    return ls`Filter event log`;
+  }
+
+  /**
+   * @override
    * @return {!TimelineModel.TimelineProfileTree.Node}
    */
   _buildTree() {
diff --git a/third_party/blink/renderer/devtools/front_end/timeline/TimelineTreeView.js b/third_party/blink/renderer/devtools/front_end/timeline/TimelineTreeView.js
index 7f93e22..2815c593 100644
--- a/third_party/blink/renderer/devtools/front_end/timeline/TimelineTreeView.js
+++ b/third_party/blink/renderer/devtools/front_end/timeline/TimelineTreeView.js
@@ -48,6 +48,13 @@
   }
 
   /**
+   * @return {string}
+   */
+  getToolbarInputAccessiblePlaceHolder() {
+    return '';
+  }
+
+  /**
    * @protected
    * @return {?Timeline.PerformanceModel} model
    */
@@ -156,7 +163,7 @@
    * @param {!UI.Toolbar} toolbar
    */
   populateToolbar(toolbar) {
-    this._textFilterUI = new UI.ToolbarInput(Common.UIString('Filter'));
+    this._textFilterUI = new UI.ToolbarInput(Common.UIString('Filter'), this.getToolbarInputAccessiblePlaceHolder());
     this._textFilterUI.addEventListener(UI.ToolbarInput.Event.TextChanged, textFilterChanged, this);
     toolbar.appendToolbarItem(this._textFilterUI);
 
@@ -253,6 +260,9 @@
     this._updateDetailsForSelection();
     if (this._searchableView)
       this._searchableView.refreshSearch();
+    const rootNode = this._dataGrid.rootNode();
+    if (rootNode.children.length > 0)
+      rootNode.children[0].select();
   }
 
   /**
@@ -396,10 +406,11 @@
    * @param {!DataGrid.DataGridNode} gridNode
    */
   _onContextMenu(contextMenu, gridNode) {
+    if (gridNode._linkElement && !contextMenu.containsTarget(gridNode._linkElement))
+      contextMenu.appendApplicableItems(gridNode._linkElement);
     const profileNode = gridNode._profileNode;
-    if (!profileNode)
-      return;
-    this._appendContextMenuItems(contextMenu, profileNode);
+    if (profileNode)
+      this._appendContextMenuItems(contextMenu, profileNode);
   }
 
   /**
@@ -494,6 +505,7 @@
     this._grandTotalTime = grandTotalTime;
     this._maxSelfTime = maxSelfTime;
     this._maxTotalTime = maxTotalTime;
+    this._linkElement = null;
   }
 
   /**
@@ -532,10 +544,13 @@
         container.createChild('div', 'activity-warning').title = Common.UIString('Not optimized: %s', deoptReason);
 
       name.textContent = Timeline.TimelineUIUtils.eventTitle(event);
-      const link = this._treeView._linkifyLocation(event);
-      if (link)
-        container.createChild('div', 'activity-link').appendChild(link);
-      icon.style.backgroundColor = Timeline.TimelineUIUtils.eventColor(event);
+      this._linkElement = this._treeView._linkifyLocation(event);
+      if (this._linkElement)
+        container.createChild('div', 'activity-link').appendChild(this._linkElement);
+      const eventStyle = Timeline.TimelineUIUtils.eventStyle(event);
+      const eventCategory = eventStyle.category;
+      UI.ARIAUtils.setAccessibleName(icon, eventCategory.title);
+      icon.style.backgroundColor = eventCategory.color;
     }
     return cell;
   }
@@ -665,7 +680,7 @@
     super.updateContents(selection);
     const rootNode = this._dataGrid.rootNode();
     if (rootNode.children.length)
-      rootNode.children[0].revealAndSelect();
+      rootNode.children[0].select();
   }
 
   _updateExtensionResolver() {
@@ -772,7 +787,8 @@
       {label: Common.UIString('Group by Subdomain'), value: groupBy.Subdomain},
       {label: Common.UIString('Group by URL'), value: groupBy.URL},
     ];
-    toolbar.appendToolbarItem(new UI.ToolbarSettingComboBox(options, this._groupBySetting));
+    toolbar.appendToolbarItem(
+        new UI.ToolbarSettingComboBox(options, this._groupBySetting, undefined /* optGroup */, ls`Group by`));
     toolbar.appendSpacer();
     toolbar.appendToolbarItem(this._splitWidget.createShowHideSidebarButton(Common.UIString('heaviest stack')));
   }
@@ -972,6 +988,14 @@
 
   /**
    * @override
+   * @return {string}
+   */
+  getToolbarInputAccessiblePlaceHolder() {
+    return ls`Filter call tree`;
+  }
+
+  /**
+   * @override
    * @return {!TimelineModel.TimelineProfileTree.Node}
    */
   _buildTree() {
@@ -991,6 +1015,14 @@
 
   /**
    * @override
+   * @return {string}
+   */
+  getToolbarInputAccessiblePlaceHolder() {
+    return ls`Filter bottom-up`;
+  }
+
+  /**
+   * @override
    * @return {!TimelineModel.TimelineProfileTree.Node}
    */
   _buildTree() {
diff --git a/third_party/blink/renderer/devtools/front_end/timeline/timeline_strings.grdp b/third_party/blink/renderer/devtools/front_end/timeline/timeline_strings.grdp
index a508ae8..389883b 100644
--- a/third_party/blink/renderer/devtools/front_end/timeline/timeline_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/timeline/timeline_strings.grdp
@@ -69,6 +69,9 @@
   <message name="IDS_DEVTOOLS_08db5f30f625a406882862b8a29b1bc6" desc="Text in Timeline UIUtils of the Performance panel">
     Major GC
   </message>
+  <message name="IDS_DEVTOOLS_099ca81d7a70c42447d89e172198ba5d" desc="Aria-label for filter bar in Event Log view">
+    Filter event log
+  </message>
   <message name="IDS_DEVTOOLS_09d66464ad53e6080988ad30824ea023" desc="Text in Timeline Flame Chart Data Provider of the Performance panel">
     Frame — <ph name="TRACK_URL">$1s<ex>https://example.com</ex></ph>
   </message>
@@ -424,6 +427,9 @@
   <message name="IDS_DEVTOOLS_66f1aed235ade25269a561e81cbbb43a" desc="Text in Timeline UIUtils of the Performance panel">
     Scripting
   </message>
+  <message name="IDS_DEVTOOLS_6734ebb8322bff404034f6ebbcc0d2fb" desc="Aria-label for filter bar in Call Tree view">
+    Filter call tree
+  </message>
   <message name="IDS_DEVTOOLS_67eed98f4bc75c4ab261b79a0e2c52cd" desc="Text in Timeline UIUtils of the Performance panel">
     Cache Rejected
   </message>
@@ -717,6 +723,9 @@
   <message name="IDS_DEVTOOLS_be85f6b3661854c85ddbde4c1429b2a2" desc="Text in Timeline UIUtils of the Performance panel">
     (<ph name="CLIP___">$1s<ex>2</ex></ph>, <ph name="CLIP___">$2s<ex>2</ex></ph>)
   </message>
+  <message name="IDS_DEVTOOLS_be87d8c3de2675bbc53f9442af9d8feb" desc="Aria-label for grouping combo box in Timeline Details View">
+    Group by
+  </message>
   <message name="IDS_DEVTOOLS_be97c5beb33f5316b2c221bfbb95852b" desc="Text in Timeline UIUtils of the Performance panel">
     MIME Type
   </message>
@@ -810,6 +819,9 @@
   <message name="IDS_DEVTOOLS_d5f967961bccf06a9eb7989d69d94a15" desc="Text in Timeline UIUtils of the Performance panel">
     network transfer
   </message>
+  <message name="IDS_DEVTOOLS_d6ef31f5b235ec815c3b62fc6f236804" desc="Aria-label for the filter bar in Bottom-Up view">
+    Filter bottom-up
+  </message>
   <message name="IDS_DEVTOOLS_d84fe3f1d290e4855cc0487b5ea18a4a" desc="Text in Timeline UIUtils of the Performance panel">
     Pinch Begin
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js b/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js
index 3aded96..69b9b89 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js
@@ -499,6 +499,14 @@
 
   /**
    * @param {!Object} target
+   * @return {boolean}
+   */
+  containsTarget(target) {
+    return this._pendingTargets.indexOf(target) >= 0;
+  }
+
+  /**
+   * @param {!Object} target
    */
   appendApplicableItems(target) {
     this._pendingPromises.push(self.runtime.allInstances(UI.ContextMenu.Provider, target));
diff --git a/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js b/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js
index 6c4aae4..84c71d1 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js
@@ -308,9 +308,10 @@
   /**
    * @param {string} id
    * @param {boolean=} userGesture
+   * @param {boolean=} forceFocus
    * @return {boolean}
    */
-  selectTab(id, userGesture) {
+  selectTab(id, userGesture, forceFocus) {
     if (this._currentTabLocked)
       return false;
     const focused = this._viewHasFocus();
@@ -330,7 +331,7 @@
     this._tabsHistory.splice(0, 0, tab);
 
     this._updateTabElements();
-    if (focused)
+    if (focused || forceFocus)
       this.focus();
 
     const eventData = {tabId: id, view: tab.view, isUserGesture: userGesture};
@@ -603,7 +604,7 @@
    */
   _dropDownMenuItemSelected(tab) {
     this._lastSelectedOverflowTab = tab;
-    this.selectTab(tab.id, true);
+    this.selectTab(tab.id, true, true);
   }
 
   _totalWidth() {
diff --git a/third_party/blink/renderer/devtools/front_end/ui/Toolbar.js b/third_party/blink/renderer/devtools/front_end/ui/Toolbar.js
index 8434b8b..5882bdf 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/Toolbar.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/Toolbar.js
@@ -558,12 +558,13 @@
 UI.ToolbarInput = class extends UI.ToolbarItem {
   /**
    * @param {string} placeholder
+   * @param {string=} accessiblePlaceholder
    * @param {number=} growFactor
    * @param {number=} shrinkFactor
    * @param {string=} tooltip
    * @param {(function(string, string, boolean=):!Promise<!UI.SuggestBox.Suggestions>)=} completions
    */
-  constructor(placeholder, growFactor, shrinkFactor, tooltip, completions) {
+  constructor(placeholder, accessiblePlaceholder, growFactor, shrinkFactor, tooltip, completions) {
     super(createElementWithClass('div', 'toolbar-input'));
 
     const internalPromptElement = this.element.createChild('div', 'toolbar-input-prompt');
@@ -577,7 +578,7 @@
     this._prompt.initialize(completions || (() => Promise.resolve([])), ' ');
     if (tooltip)
       this._prompt.setTitle(tooltip);
-    this._prompt.setPlaceholder(placeholder);
+    this._prompt.setPlaceholder(placeholder, accessiblePlaceholder);
     this._prompt.addEventListener(UI.TextPrompt.Events.TextChanged, this._onChangeCallback.bind(this));
 
     if (growFactor)
@@ -971,12 +972,15 @@
    * @param {!Array<!{value: string, label: string}>} options
    * @param {!Common.Setting} setting
    * @param {string=} optGroup
+   * @param {string=} accessibleName
    */
-  constructor(options, setting, optGroup) {
+  constructor(options, setting, optGroup, accessibleName) {
     super(null);
     this._setting = setting;
     this._options = options;
     this._selectElement.addEventListener('change', this._valueChanged.bind(this), false);
+    if (accessibleName)
+      UI.ARIAUtils.setAccessibleName(this._selectElement, accessibleName);
     if (optGroup) {
       const optGroupElement = this._selectElement.createChild('optgroup');
       optGroupElement.label = optGroup;
diff --git a/third_party/blink/renderer/devtools/scripts/check_localizable_resources.js b/third_party/blink/renderer/devtools/scripts/check_localizable_resources.js
index 50e1019..21e37366 100644
--- a/third_party/blink/renderer/devtools/scripts/check_localizable_resources.js
+++ b/third_party/blink/renderer/devtools/scripts/check_localizable_resources.js
@@ -33,7 +33,7 @@
     if (shouldAutoFix)
       await autofix(error);
     else
-      await getErrors(error);
+      getErrors();
   } catch (e) {
     console.log(e.stack);
     console.log(`Error: ${e.message}`);
@@ -43,11 +43,12 @@
 
 main();
 
-async function getErrors(existingError) {
-  const toAddError = await checkLocalizedStrings.getAndReportResourcesToAdd();
+function getErrors(existingError) {
+  const toAddError = checkLocalizedStrings.getAndReportResourcesToAdd();
   const toModifyError = checkLocalizedStrings.getAndReportIDSKeysToModify();
   const toRemoveError = checkLocalizedStrings.getAndReportResourcesToRemove();
-  let error = `${existingError}\n${toAddError || ''}${toModifyError || ''}${toRemoveError || ''}`;
+  let error =
+      `${existingError ? `${existingError}\n` : ''}${toAddError || ''}${toModifyError || ''}${toRemoveError || ''}`;
 
   if (error === '') {
     console.log('DevTools localizable resources checker passed.');
diff --git a/third_party/blink/renderer/devtools/scripts/localization_utils/check_localized_strings.js b/third_party/blink/renderer/devtools/scripts/localization_utils/check_localized_strings.js
index ab4d65a..90dd6b6 100644
--- a/third_party/blink/renderer/devtools/scripts/localization_utils/check_localized_strings.js
+++ b/third_party/blink/renderer/devtools/scripts/localization_utils/check_localized_strings.js
@@ -188,6 +188,12 @@
  * Parse localizable resources.
  */
 async function parseLocalizableResourceMaps() {
+  if (frontendStrings.size === 0 && IDSkeys.size === 0)
+    await parseLocalizableResourceMapsHelper();
+  return [frontendStrings, IDSkeys];
+}
+
+async function parseLocalizableResourceMapsHelper() {
   const grdpToFiles = new Map();
   const dirs = devtoolsFrontendDirs || await localizationUtils.getChildDirectoriesFromDirectory(devtoolsFrontendPath);
   const grdpToFilesPromises = dirs.map(dir => {
@@ -485,7 +491,7 @@
  * with grdp <message>s and report error of resources to add,
  * remove or modify.
  */
-async function getAndReportResourcesToAdd() {
+function getAndReportResourcesToAdd() {
   const keysToAddToGRD = getMessagesToAdd();
   if (keysToAddToGRD.size === 0)
     return;
@@ -625,8 +631,6 @@
 }
 
 module.exports = {
-  frontendStrings,
-  IDSkeys,
   parseLocalizableResourceMaps,
   getAndReportIDSKeysToModify,
   getAndReportResourcesToAdd,
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index f39a8c7..f9e7f73 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -56,6 +56,7 @@
 #include "third_party/blink/renderer/modules/accessibility/ax_range.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.h"
 #include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
@@ -1213,16 +1214,29 @@
   if (IsLineBreakingObject())
     return true;
 
-  // Allow the browser side ax tree to access aria-hidden="true", "visibility:
-  // hidden", and "visibility: collapse" nodes. This is useful for APIs that
-  // return the node referenced by aria-labeledby and aria-describedby
-  if (GetLayoutObject()) {
+  // Allow the browser side ax tree to access "visibility: [hidden|collapse]"
+  // and "display: none" nodes. This is useful for APIs that return the node
+  // referenced by aria-labeledby and aria-describedby.
+  // An element must have an id attribute or it cannot be referenced by
+  // aria-labelledby or aria-describedby.
+  if (RuntimeEnabledFeatures::AccessibilityExposeDisplayNoneEnabled()) {
+    if (Element* element = GetElement()) {
+      if (element->FastHasAttribute(kIdAttr) &&
+          IsHiddenForTextAlternativeCalculation()) {
+        return true;
+      }
+    }
+  } else if (GetLayoutObject()) {
     if (GetLayoutObject()->Style()->Visibility() != EVisibility::kVisible)
       return true;
-    if (AriaHiddenRoot())
-      return true;
   }
 
+  // Allow the browser side ax tree to access "aria-hidden" nodes.
+  // This is useful for APIs that return the node referenced by
+  // aria-labeledby and aria-describedby.
+  if (GetLayoutObject() && AriaHiddenRoot())
+    return true;
+
   return false;
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 932e25f..bdcae67 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -88,6 +88,7 @@
 #include "third_party/blink/renderer/modules/accessibility/ax_virtual_object.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_elements_helper.h"
 #include "third_party/blink/renderer/modules/permissions/permission_utils.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
@@ -168,6 +169,45 @@
   documents_.erase(document);
 }
 
+Node* AXObjectCacheImpl::FocusedElement() {
+  Node* focused_node = document_->FocusedElement();
+  if (!focused_node)
+    focused_node = document_;
+
+  // If it's an image map, get the focused link within the image map.
+  if (IsA<HTMLAreaElement>(focused_node))
+    return focused_node;
+
+  // See if there's a page popup, for example a calendar picker.
+  Element* adjusted_focused_element = document_->AdjustedFocusedElement();
+  if (auto* input = ToHTMLInputElementOrNull(adjusted_focused_element)) {
+    if (AXObject* ax_popup = input->PopupRootAXObject()) {
+      if (Element* focused_element_in_popup =
+              ax_popup->GetDocument()->FocusedElement())
+        focused_node = focused_element_in_popup;
+    }
+  }
+
+  return focused_node;
+}
+
+AXObject* AXObjectCacheImpl::GetOrCreateFocusedObjectFromNode(Node* node) {
+  // If it's an image map, get the focused link within the image map.
+  if (auto* area = DynamicTo<HTMLAreaElement>(node))
+    return FocusedImageMapUIElement(area);
+
+  AXObject* obj = GetOrCreate(node);
+  if (!obj)
+    return nullptr;
+
+  // the HTML element, for example, is focusable but has an AX object that is
+  // ignored
+  if (!obj->AccessibilityIsIncludedInTree())
+    obj = obj->ParentObjectIncludedInTree();
+
+  return obj;
+}
+
 AXObject* AXObjectCacheImpl::FocusedImageMapUIElement(
     HTMLAreaElement* area_element) {
   // Find the corresponding accessibility object for the HTMLAreaElement. This
@@ -198,34 +238,7 @@
 }
 
 AXObject* AXObjectCacheImpl::FocusedObject() {
-  Node* focused_node = document_->FocusedElement();
-  if (!focused_node)
-    focused_node = document_;
-
-  // If it's an image map, get the focused link within the image map.
-  if (auto* area = DynamicTo<HTMLAreaElement>(focused_node))
-    return FocusedImageMapUIElement(area);
-
-  // See if there's a page popup, for example a calendar picker.
-  Element* adjusted_focused_element = document_->AdjustedFocusedElement();
-  if (auto* input = ToHTMLInputElementOrNull(adjusted_focused_element)) {
-    if (AXObject* ax_popup = input->PopupRootAXObject()) {
-      if (Element* focused_element_in_popup =
-              ax_popup->GetDocument()->FocusedElement())
-        focused_node = focused_element_in_popup;
-    }
-  }
-
-  AXObject* obj = GetOrCreate(focused_node);
-  if (!obj)
-    return nullptr;
-
-  // the HTML element, for example, is focusable but has an AX object that is
-  // ignored
-  if (!obj->AccessibilityIsIncludedInTree())
-    obj = obj->ParentObjectUnignored();
-
-  return obj;
+  return GetOrCreateFocusedObjectFromNode(this->FocusedElement());
 }
 
 AXObject* AXObjectCacheImpl::Get(LayoutObject* layout_object) {
@@ -1175,6 +1188,30 @@
     PostNotification(listbox, ax::mojom::Event::kSelectedChildrenChanged);
 }
 
+void AXObjectCacheImpl::HandleNodeLostFocusWithCleanLayout(Node* node) {
+  DCHECK(node);
+  DCHECK(!node->GetDocument().NeedsLayoutTreeUpdateForNode(*node));
+  AXObject* obj = Get(node);
+  if (!obj)
+    return;
+
+  PostNotification(obj, ax::mojom::Event::kBlur);
+}
+
+void AXObjectCacheImpl::HandleNodeGainedFocusWithCleanLayout(Node* node) {
+  DCHECK(node);
+  DCHECK(!node->GetDocument().NeedsLayoutTreeUpdateForNode(*node));
+  // Something about the call chain for this method seems to leave distribution
+  // in a dirty state - update it before we call GetOrCreate so that we don't
+  // crash.
+  node->UpdateDistributionForFlatTreeTraversal();
+  AXObject* obj = GetOrCreateFocusedObjectFromNode(node);
+  if (!obj)
+    return;
+
+  PostNotification(obj, ax::mojom::Event::kFocus);
+}
+
 // This might be the new target of a relation. Handle all possible cases.
 void AXObjectCacheImpl::MaybeNewRelationTarget(Node* node, AXObject* obj) {
   // Track reverse relations
@@ -1219,12 +1256,8 @@
 
   DCHECK(!node->GetDocument().NeedsLayoutTreeUpdateForNode(*node));
 
-  AXObject* obj = Get(node);
-  if (!obj && IsHTMLSelectElement(node))
-    obj = GetOrCreate(node);
-
   // Invalidate the current object and make the parent reconsider its children.
-  if (obj) {
+  if (AXObject* obj = GetOrCreate(node)) {
     // Save parent for later use.
     AXObject* parent = obj->ParentObject();
 
@@ -1590,13 +1623,23 @@
   if (!page)
     return;
 
-  AXObject* focused_object = this->FocusedObject();
-  if (!focused_object)
-    return;
+  if (RuntimeEnabledFeatures::AccessibilityExposeDisplayNoneEnabled()) {
+    if (old_focused_element) {
+      DeferTreeUpdate(&AXObjectCacheImpl::HandleNodeLostFocusWithCleanLayout,
+                      old_focused_element);
+    }
 
-  AXObject* old_focused_object = Get(old_focused_element);
-  PostNotification(old_focused_object, ax::mojom::Event::kBlur);
-  PostNotification(focused_object, ax::mojom::Event::kFocus);
+    DeferTreeUpdate(&AXObjectCacheImpl::HandleNodeGainedFocusWithCleanLayout,
+                    this->FocusedElement());
+  } else {
+    AXObject* focused_object = this->FocusedObject();
+    if (!focused_object)
+      return;
+
+    AXObject* old_focused_object = Get(old_focused_element);
+    PostNotification(old_focused_object, ax::mojom::Event::kBlur);
+    PostNotification(focused_object, ax::mojom::Event::kFocus);
+  }
 }
 
 void AXObjectCacheImpl::HandleInitialFocus() {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
index 100038e6..2ca263cb 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -194,6 +194,8 @@
   void HandleRoleChangeIfNotEditableWithCleanLayout(Node*);
   void HandleAriaExpandedChangeWithCleanLayout(Node*);
   void HandleAriaSelectedChangedWithCleanLayout(Node*);
+  void HandleNodeLostFocusWithCleanLayout(Node*);
+  void HandleNodeGainedFocusWithCleanLayout(Node*);
 
   bool InlineTextBoxAccessibilityEnabled();
 
@@ -322,6 +324,12 @@
   // ContextLifecycleObserver overrides.
   void ContextDestroyed(ExecutionContext*) override;
 
+  // Get the currently focused Node element.
+  Node* FocusedElement();
+
+  // GetOrCreate the focusable AXObject for a specific Node.
+  AXObject* GetOrCreateFocusedObjectFromNode(Node*);
+
   AXObject* FocusedImageMapUIElement(HTMLAreaElement*);
 
   AXID GetOrCreateAXID(AXObject*);
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
index 63e740a0..08cb59a 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
@@ -71,8 +71,11 @@
   registration_ = registration;
   registration_service_.Bind(std::move(registration_service));
 
-  auto task_runner =
-      GetExecutionContext()->GetTaskRunner(TaskType::kBackgroundFetch);
+  ExecutionContext* context = GetExecutionContext();
+  if (!context || context->IsContextDestroyed())
+    return;
+
+  auto task_runner = context->GetTaskRunner(TaskType::kBackgroundFetch);
   registration_service_->AddRegistrationObserver(
       observer_receiver_.BindNewPipeAndPassRemote(task_runner));
 }
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.cc b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
index 7f0794e..bcb343ac 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
@@ -421,13 +421,19 @@
           ->GetTaskRunner(TaskType::kDatabaseAccess),
       transaction_id);
 
-  bool relaxed_durability = false;
-  if (RuntimeEnabledFeatures::IDBRelaxedDurabilityEnabled() && options)
-    relaxed_durability = options->relaxedDurability();
+  mojom::IDBTransactionDurability durability =
+      mojom::IDBTransactionDurability::Default;
+  if (RuntimeEnabledFeatures::IDBRelaxedDurabilityEnabled() && options) {
+    if (options->durability() == "relaxed") {
+      durability = mojom::IDBTransactionDurability::Relaxed;
+    } else if (options->durability() == "strict") {
+      durability = mojom::IDBTransactionDurability::Strict;
+    }
+  }
 
   backend_->CreateTransaction(transaction_backend->CreateReceiver(),
                               transaction_id, object_store_ids, mode,
-                              relaxed_durability);
+                              durability);
 
   return IDBTransaction::CreateNonVersionChange(
       script_state, std::move(transaction_backend), transaction_id, scope, mode,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
index 84de726..4ec6a3ad 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
@@ -90,7 +90,7 @@
                          int64_t transaction_id,
                          const WTF::Vector<int64_t>& object_store_ids,
                          mojom::blink::IDBTransactionMode mode,
-                         bool relaxed_durability) override {}
+                         mojom::blink::IDBTransactionDurability) override {}
   MOCK_METHOD0(Close, void());
   void VersionChangeIgnored() override {}
   void AddObserver(int64_t transaction_id,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_transaction_options.idl b/third_party/blink/renderer/modules/indexeddb/idb_transaction_options.idl
index 6da6153..64109b9 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_transaction_options.idl
+++ b/third_party/blink/renderer/modules/indexeddb/idb_transaction_options.idl
@@ -2,8 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+enum IDBTransactionDurability { "default", "strict", "relaxed" };
+
 dictionary IDBTransactionOptions {
-    // If relaxedDurability is true, then the transaction will not be flushed
-    // to disk.  This is more performant at the cost of durability.
-    boolean relaxedDurability = false;
+    // If durability is "relaxed", then the transaction will not be flushed
+    // to disk.  This is more performant at the cost of durability.  If it is
+    // "strict", then it will always be flushed to disk.  "default" leaves the
+    // decision up to the implementation.
+    IDBTransactionDurability durability = "default";
 };
diff --git a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
index 337cbc7..56ff8283 100644
--- a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
+++ b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
@@ -326,7 +326,7 @@
   StringOrStringSequence scope;
   scope.SetString(object_store_name);
   IDBTransactionOptions options;
-  options.setRelaxedDurability(true);
+  options.setDurability("relaxed");
   IDBTransaction* idb_transaction = idb_database->transaction(
       script_state, scope, mode, &options, exception_state);
   if (exception_state.HadException())
diff --git a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_database.h b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_database.h
index 6f91bde..9dce5f2 100644
--- a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_database.h
+++ b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_database.h
@@ -29,7 +29,7 @@
                     int64_t id,
                     const Vector<int64_t>& scope,
                     mojom::IDBTransactionMode,
-                    bool relaxed_durability));
+                    mojom::IDBTransactionDurability));
   MOCK_METHOD0(Close, void());
   MOCK_METHOD0(VersionChangeIgnored, void());
   MOCK_METHOD1(Abort, void(int64_t transaction_id));
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_database.h b/third_party/blink/renderer/modules/indexeddb/web_idb_database.h
index 04ec3a37..34ed7bc4 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_database.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database.h
@@ -53,7 +53,7 @@
       int64_t id,
       const Vector<int64_t>& scope,
       mojom::IDBTransactionMode,
-      bool relaxed_durability) = 0;
+      mojom::IDBTransactionDurability) = 0;
   virtual void Close() = 0;
   virtual void VersionChangeIgnored() = 0;
 
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc
index 482b56de..ce509c8 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc
@@ -37,9 +37,9 @@
     int64_t transaction_id,
     const Vector<int64_t>& object_store_ids,
     mojom::IDBTransactionMode mode,
-    bool relaxed_durability) {
+    mojom::IDBTransactionDurability durability) {
   database_->CreateTransaction(std::move(transaction_receiver), transaction_id,
-                               object_store_ids, mode, relaxed_durability);
+                               object_store_ids, mode, durability);
 }
 
 void WebIDBDatabaseImpl::Close() {
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h
index 63309de..84f75aa5 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h
@@ -36,7 +36,7 @@
                          int64_t transaction_id,
                          const Vector<int64_t>& scope,
                          mojom::IDBTransactionMode mode,
-                         bool relaxed_durability) override;
+                         mojom::IDBTransactionDurability durability) override;
 
   void Close() override;
   void VersionChangeIgnored() override;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_bundle_encoder.idl b/third_party/blink/renderer/modules/webgpu/gpu_render_bundle_encoder.idl
index 6d3a898..9689b025 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_render_bundle_encoder.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_render_bundle_encoder.idl
@@ -17,7 +17,7 @@
 
     // GPURenderEncoderBase methods
     void setPipeline(GPURenderPipeline pipeline);
-    void setIndexBuffer(GPUBuffer buffer, unsigned long long offset);
+    void setIndexBuffer(GPUBuffer buffer, optional unsigned long long offset = 0);
     [RaisesException] void setVertexBuffers(unsigned long startSlot,
                                             sequence<GPUBuffer> buffers,
                                             sequence<unsigned long long> offsets);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.idl b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.idl
index b3db800..40d0203 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.idl
@@ -24,7 +24,7 @@
                      float minDepth, float maxDepth);
     void setScissorRect(unsigned long x, unsigned long y,
                         unsigned long width, unsigned long height);
-    void setIndexBuffer(GPUBuffer buffer, unsigned long long offset);
+    void setIndexBuffer(GPUBuffer buffer, optional unsigned long long offset = 0);
     [RaisesException] void setVertexBuffers(unsigned long startSlot,
                                             sequence<GPUBuffer> buffers,
                                             sequence<unsigned long long> offsets);
diff --git a/third_party/blink/renderer/modules/xr/xr_anchor.cc b/third_party/blink/renderer/modules/xr/xr_anchor.cc
index fee23ea2..d3b6f7ab 100644
--- a/third_party/blink/renderer/modules/xr/xr_anchor.cc
+++ b/third_party/blink/renderer/modules/xr/xr_anchor.cc
@@ -10,10 +10,10 @@
 
 namespace blink {
 
-XRAnchor::XRAnchor(int32_t id, XRSession* session)
+XRAnchor::XRAnchor(uint32_t id, XRSession* session)
     : id_(id), session_(session), anchor_data_(base::nullopt) {}
 
-XRAnchor::XRAnchor(int32_t id,
+XRAnchor::XRAnchor(uint32_t id,
                    XRSession* session,
                    const device::mojom::blink::XRAnchorDataPtr& anchor_data,
                    double timestamp)
diff --git a/third_party/blink/renderer/modules/xr/xr_anchor.h b/third_party/blink/renderer/modules/xr/xr_anchor.h
index fdec8bb..1fcb6373 100644
--- a/third_party/blink/renderer/modules/xr/xr_anchor.h
+++ b/third_party/blink/renderer/modules/xr/xr_anchor.h
@@ -21,9 +21,9 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  XRAnchor(int32_t id, XRSession* session);
+  XRAnchor(uint32_t id, XRSession* session);
 
-  XRAnchor(int32_t id,
+  XRAnchor(uint32_t id,
            XRSession* session,
            const device::mojom::blink::XRAnchorDataPtr& anchor_data,
            double timestamp);
@@ -54,7 +54,7 @@
                double timestamp);
   };
 
-  const int32_t id_;
+  const uint32_t id_;
 
   Member<XRSession> session_;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.cc b/third_party/blink/renderer/modules/xr/xr_plane.cc
index 14496fb..e7cb010 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.cc
+++ b/third_party/blink/renderer/modules/xr/xr_plane.cc
@@ -13,7 +13,7 @@
 
 namespace blink {
 
-XRPlane::XRPlane(int32_t id,
+XRPlane::XRPlane(uint32_t id,
                  XRSession* session,
                  const device::mojom::blink::XRPlaneDataPtr& plane_data,
                  double timestamp)
@@ -26,7 +26,7 @@
                   plane_data->polygon),
               timestamp) {}
 
-XRPlane::XRPlane(int32_t id,
+XRPlane::XRPlane(uint32_t id,
                  XRSession* session,
                  const base::Optional<Orientation>& orientation,
                  const TransformationMatrix& pose_matrix,
@@ -41,7 +41,7 @@
   DVLOG(3) << __func__;
 }
 
-int32_t XRPlane::id() const {
+uint32_t XRPlane::id() const {
   return id_;
 }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.h b/third_party/blink/renderer/modules/xr/xr_plane.h
index e7cf645c..516b004 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.h
+++ b/third_party/blink/renderer/modules/xr/xr_plane.h
@@ -27,18 +27,18 @@
  public:
   enum Orientation { kHorizontal, kVertical };
 
-  XRPlane(int32_t id,
+  XRPlane(uint32_t id,
           XRSession* session,
           const device::mojom::blink::XRPlaneDataPtr& plane_data,
           double timestamp);
-  XRPlane(int32_t id,
+  XRPlane(uint32_t id,
           XRSession* session,
           const base::Optional<Orientation>& orientation,
           const TransformationMatrix& pose_matrix,
           const HeapVector<Member<DOMPointReadOnly>>& polygon,
           double timestamp);
 
-  int32_t id() const;
+  uint32_t id() const;
 
   XRSpace* planeSpace() const;
 
@@ -62,7 +62,7 @@
   void Trace(blink::Visitor* visitor) override;
 
  private:
-  const int32_t id_;
+  const uint32_t id_;
   HeapVector<Member<DOMPointReadOnly>> polygon_;
   base::Optional<Orientation> orientation_;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index f3e17ab..59b4df8 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -253,36 +253,20 @@
     return;
   }
 
-  if (init->hasBaseLayer() && init->baseLayer()) {
-    // Validate that any baseLayer provided was created with this session.
-    if (init->baseLayer()->session() != this) {
-      exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                        kIncompatibleLayer);
-      return;
-    }
-
-    // If the baseLayer was previously null and there are outstanding rAF
-    // callbacks, kick off a new frame request to flush them out.
-    if (!render_state_->baseLayer() && !pending_frame_ &&
-        !callback_collection_->IsEmpty()) {
-      // Kick off a request for a new XR frame.
-      xr_->frameProvider()->RequestFrame(this);
-      pending_frame_ = true;
-    }
-
-    if (!immersive() && init->baseLayer()->output_canvas()) {
-      // If the output canvas was previously null and there are outstanding rAF
-      // callbacks, kick off a new frame request to flush them out.
-      if (!render_state_->output_canvas() && !pending_frame_ &&
-          !callback_collection_->IsEmpty()) {
-        // Kick off a request for a new XR frame.
-        xr_->frameProvider()->RequestFrame(this);
-        pending_frame_ = true;
-      }
-    }
+  // Validate that any baseLayer provided was created with this session.
+  if (init->hasBaseLayer() && init->baseLayer() &&
+      init->baseLayer()->session() != this) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      kIncompatibleLayer);
+    return;
   }
 
   pending_render_state_.push_back(init);
+
+  // Updating our render state may have caused us to be in a state where we
+  // should be requesting frames again. Kick off a new frame request in case
+  // there are any pending callbacks to flush them out.
+  MaybeRequestFrame();
 }
 
 void XRSession::updateWorldTrackingState(
@@ -518,11 +502,7 @@
     return 0;
 
   int id = callback_collection_->RegisterCallback(callback);
-  if (!pending_frame_) {
-    // Kick off a request for a new XR frame.
-    xr_->frameProvider()->RequestFrame(this);
-    pending_frame_ = true;
-  }
+  MaybeRequestFrame();
   return id;
 }
 
@@ -615,7 +595,7 @@
 
 void XRSession::OnCreateAnchorResult(ScriptPromiseResolver* resolver,
                                      device::mojom::CreateAnchorResult result,
-                                     int32_t id) {
+                                     uint32_t id) {
   DCHECK(create_anchor_promises_.Contains(resolver));
   create_anchor_promises_.erase(resolver);
 
@@ -680,7 +660,7 @@
 
   is_tracked_anchors_null_ = false;
 
-  HeapHashMap<int32_t, Member<XRAnchor>> updated_anchors;
+  HeapHashMap<uint32_t, Member<XRAnchor>> updated_anchors;
 
   // First, process all planes that had their information updated (new planes
   // are also processed here).
@@ -846,20 +826,35 @@
   if (visibility_state_ != state) {
     visibility_state_ = state;
 
-    // If the visibility state was changed to something other than hidden and
-    // there are rAF callbacks still waiting to resolve kickstart the rAF loop
-    // again.
-    if (visibility_state_ != XRVisibilityState::HIDDEN && !pending_frame_ &&
-        !callback_collection_->IsEmpty()) {
-      xr_->frameProvider()->RequestFrame(this);
-      pending_frame_ = true;
-    }
+    // If the visibility state was changed to something other than hidden, we
+    // may be able to restart the frame loop.
+    MaybeRequestFrame();
 
     DispatchEvent(
         *XRSessionEvent::Create(event_type_names::kVisibilitychange, this));
   }
 }
 
+void XRSession::MaybeRequestFrame() {
+  bool will_have_base_layer = !!render_state_->baseLayer();
+  for (const auto& init : pending_render_state_) {
+    if (init->hasBaseLayer()) {
+      will_have_base_layer = !!init->baseLayer();
+    }
+  }
+
+  // We can request a frame if we're not hidden, we don't already have a pending
+  // frame, we have pending callbacks, and we will have a base layer when it
+  // resolves.
+  bool can_request_frame =
+      visibility_state_ != XRVisibilityState::HIDDEN && !pending_frame_ &&
+      !callback_collection_->IsEmpty() && will_have_base_layer;
+  if (can_request_frame) {
+    xr_->frameProvider()->RequestFrame(this);
+    pending_frame_ = true;
+  }
+}
+
 void XRSession::DetachOutputCanvas(HTMLCanvasElement* canvas) {
   if (!canvas)
     return;
@@ -876,8 +871,9 @@
 }
 
 void XRSession::ApplyPendingRenderState() {
+  DCHECK(!prev_base_layer_);
   if (pending_render_state_.size() > 0) {
-    XRWebGLLayer* prev_base_layer = render_state_->baseLayer();
+    prev_base_layer_ = render_state_->baseLayer();
     HTMLCanvasElement* prev_ouput_canvas = render_state_->output_canvas();
     update_views_next_frame_ = true;
 
@@ -890,7 +886,7 @@
     // If this is an inline session and the base layer has changed, give it an
     // opportunity to update it's drawing buffer size.
     if (!immersive() && render_state_->baseLayer() &&
-        render_state_->baseLayer() != prev_base_layer) {
+        render_state_->baseLayer() != prev_base_layer_) {
       render_state_->baseLayer()->OnResize();
     }
 
@@ -961,6 +957,7 @@
     return;
 
   // If there are pending render state changes, apply them now.
+  prev_base_layer_ = nullptr;
   ApplyPendingRenderState();
 
   if (pending_frame_) {
@@ -969,8 +966,17 @@
     // Don't allow frames to be processed if there's no layers attached to the
     // session. That would allow tracking with no associated visuals.
     XRWebGLLayer* frame_base_layer = render_state_->baseLayer();
-    if (!frame_base_layer)
+    if (!frame_base_layer) {
+      // If we previously had a frame base layer, we need to still attempt to
+      // submit a frame back to the runtime, as all "GetFrameData" calls need a
+      // matching submit.
+      if (prev_base_layer_) {
+        prev_base_layer_->OnFrameStart(output_mailbox_holder);
+        prev_base_layer_->OnFrameEnd();
+        prev_base_layer_ = nullptr;
+      }
       return;
+    }
 
     // Don't allow frames to be processed if an inline session doesn't have an
     // output canvas.
@@ -1312,6 +1318,7 @@
   visitor->Trace(create_anchor_promises_);
   visitor->Trace(reference_spaces_);
   visitor->Trace(anchor_ids_to_anchors_);
+  visitor->Trace(prev_base_layer_);
   EventTargetWithInlineData::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index 9a344bfe..5eb4147 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -45,6 +45,7 @@
 class XRRigidTransform;
 class XRSpace;
 class XRViewData;
+class XRWebGLLayer;
 class XRWorldInformation;
 class XRWorldTrackingState;
 class XRWorldTrackingStateInit;
@@ -243,6 +244,8 @@
   void UpdateCanvasDimensions(Element*);
   void ApplyPendingRenderState();
 
+  void MaybeRequestFrame();
+
   void OnInputStateChangeInternal(
       int16_t frame_id,
       base::span<const device::mojom::blink::XRInputSourceStatePtr>
@@ -264,7 +267,7 @@
 
   void OnCreateAnchorResult(ScriptPromiseResolver* resolver,
                             device::mojom::CreateAnchorResult result,
-                            int32_t id);
+                            uint32_t id);
 
   void EnsureEnvironmentErrorHandler();
   void OnEnvironmentProviderError();
@@ -288,11 +291,12 @@
   XRSessionFeatureSet enabled_features_;
 
   bool is_tracked_anchors_null_ = true;
-  HeapHashMap<int32_t, Member<XRAnchor>> anchor_ids_to_anchors_;
+  HeapHashMap<uint32_t, Member<XRAnchor>> anchor_ids_to_anchors_;
 
   WTF::Vector<XRViewData> views_;
 
   Member<XRInputSourceArray> input_sources_;
+  Member<XRWebGLLayer> prev_base_layer_;
   Member<ResizeObserver> resize_observer_;
   Member<XRCanvasInputProvider> canvas_input_provider_;
   bool environment_error_handler_subscribed_ = false;
diff --git a/third_party/blink/renderer/modules/xr/xr_world_information.cc b/third_party/blink/renderer/modules/xr/xr_world_information.cc
index 0feed185..b74d9fd 100644
--- a/third_party/blink/renderer/modules/xr/xr_world_information.cc
+++ b/third_party/blink/renderer/modules/xr/xr_world_information.cc
@@ -58,7 +58,7 @@
 
   is_detected_planes_null_ = false;
 
-  HeapHashMap<int32_t, Member<XRPlane>> updated_planes;
+  HeapHashMap<uint32_t, Member<XRPlane>> updated_planes;
 
   // First, process all planes that had their information updated (new planes
   // are also processed here).
diff --git a/third_party/blink/renderer/modules/xr/xr_world_information.h b/third_party/blink/renderer/modules/xr/xr_world_information.h
index 6f6e816..cbe0246 100644
--- a/third_party/blink/renderer/modules/xr/xr_world_information.h
+++ b/third_party/blink/renderer/modules/xr/xr_world_information.h
@@ -37,7 +37,7 @@
   // This is the case if we have a freshly constructed instance, or if our
   // last `ProcessPlaneInformation()` was called with base::nullopt.
   bool is_detected_planes_null_ = true;
-  HeapHashMap<int32_t, Member<XRPlane>> plane_ids_to_planes_;
+  HeapHashMap<uint32_t, Member<XRPlane>> plane_ids_to_planes_;
 
   Member<XRSession> session_;
 };
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index dff5f2e..bd433b1 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -88,6 +88,10 @@
   RuntimeEnabledFeatures::SetAccelerated2dCanvasEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableAccessibilityExposeDisplayNone(bool enable) {
+  RuntimeEnabledFeatures::SetAccessibilityExposeDisplayNoneEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnableAccessibilityObjectModel(bool enable) {
   RuntimeEnabledFeatures::SetAccessibilityObjectModelEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index ce12da2c..88c9d2b 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -99,6 +99,9 @@
       status: "stable",
     },
     {
+      name: "AccessibilityExposeDisplayNone",
+    },
+    {
       name: "AccessibilityObjectModel",
       status: "experimental",
     },
diff --git a/third_party/blink/tools/blinkpy/web_tests/controllers/single_test_runner.py b/third_party/blink/tools/blinkpy/web_tests/controllers/single_test_runner.py
index 76e5ff60..875d91bb 100644
--- a/third_party/blink/tools/blinkpy/web_tests/controllers/single_test_runner.py
+++ b/third_party/blink/tools/blinkpy/web_tests/controllers/single_test_runner.py
@@ -512,11 +512,11 @@
         test_result = None
         putAllMismatchBeforeMatch = sorted
         reference_test_names = []
+        if self._port.lookup_virtual_test_base(self._test_name):
+            args = self._port.lookup_virtual_reference_args(self._test_name)
+        else:
+            args = self._port.lookup_physical_reference_args(self._test_name)
         for expectation, reference_filename in putAllMismatchBeforeMatch(self._reference_files):
-            if self._port.lookup_virtual_test_base(self._test_name):
-                args = self._port.lookup_virtual_reference_args(self._test_name)
-            else:
-                args = self._port.lookup_physical_reference_args(self._test_name)
             reference_test_name = self._port.relative_test_filename(reference_filename)
             reference_test_names.append(reference_test_name)
             driver_input = DriverInput(reference_test_name, self._timeout_ms,
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
index c8b8d291e..04c5a4a5 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
@@ -10,6 +10,7 @@
 crbug.com/591099 external/wpt/css/CSS2/floats/float-nowrap-3.html [ Failure ]
 crbug.com/591099 external/wpt/css/CSS2/floats/floats-line-wrap-shifted-001.html [ Failure ]
 crbug.com/591099 external/wpt/css/CSS2/floats/overhanging-float-paint-order.html [ Failure ]
+crbug.com/591099 external/wpt/css/CSS2/floats/zero-width-floats-positioning.tentative.html [ Failure ]
 
 ### external/wpt/css/CSS2/floats-clear/
 crbug.com/591099 external/wpt/css/CSS2/floats-clear/no-clearance-adjoining-opposite-float.html [ Failure ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index 715d091..00a6bff 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -1518,6 +1518,67 @@
 virtual/threaded/external/wpt/css/css-animations/animation-timing-function-011-manual.html [ WontFix ]
 virtual/threaded/external/wpt/css/css-animations/animation-timing-function-012-manual.html [ WontFix ]
 virtual/threaded/external/wpt/css/css-animations/animationstart-and-animationend-events-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-delay-000-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-delay-002-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-delay-003-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-duration-002-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-duration-003-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-duration-004-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-003-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-004-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-005-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-006-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-007-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-008-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-009-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-010-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-011-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-012-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-013-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-014-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-015-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-016-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-017-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-018-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-019-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-020-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-021-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-022-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-023-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-024-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-025-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-026-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-027-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-028-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-029-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-030-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-031-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-032-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-033-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-034-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-035-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-036-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-037-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-038-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-039-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-040-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-041-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-042-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-043-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-044-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-property-045-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-002-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-003-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-004-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-005-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-006-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-007-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-008-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-009-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-010-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-011-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-012-manual.html [ WontFix ]
+external/wpt/css/css-transitions/transition-timing-function-013-manual.html [ WontFix ]
 external/wpt/css/css-ui/cursor-pointer-links-001-manual.html [ WontFix ]
 external/wpt/css/css-ui/cursor-pointer-links-002-manual.html [ WontFix ]
 external/wpt/css/css-ui/cursor-pointer-links-003-manual.html [ WontFix ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 8efeecb6..81f4bf4 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3898,67 +3898,6 @@
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002b.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001b.html [ Failure ]
 crbug.com/626703 [ Win7 ] external/wpt/css/css-fonts/variations/font-opentype-collections.html [ Timeout ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-017.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-030.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-015.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-021.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-039.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-008.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-002.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-012.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-014.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-016.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-007.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-009.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-029.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-023.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-027.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-010.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-003.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-011.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-008.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-011.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-020.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-013.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-012.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-031.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-delay-000.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-041.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-026.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-duration-003.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-delay-002.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-005.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-004.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-006.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-018.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-022.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-duration-002.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-024.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-009.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-037.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-007.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-005.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-042.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-036.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-038.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-delay-003.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-010.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-028.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-004.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-043.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-040.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-019.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-034.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-045.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-035.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-033.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-003.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-044.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-timing-function-006.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-duration-004.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-032.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-025.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-transitions/transition-property-013.html [ Skip ]
 crbug.com/626703 external/wpt/css/css-animations/animation-delay-010.html [ Failure ]
 crbug.com/626703 virtual/threaded/external/wpt/css/css-animations/animation-delay-010.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/system-numeric.html [ Failure ]
@@ -5912,9 +5851,6 @@
 crbug.com/963141 [ Linux ] media/video-object-fit.html [ Pass Failure ]
 crbug.com/963141 [ Linux ] virtual/audio-service/media/video-object-fit.html [ Pass Failure ]
 
-# Sheriff 2019-05-17
-crbug.com/964239 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-margin.html [ Pass Failure ]
-
 # Workaround for bug; should be removed soon.
 crbug.com/959693 external/wpt/trusted-types/WorkerGlobalScope-importScripts.https.html [ Timeout ]
 
@@ -5923,7 +5859,6 @@
 crbug.com/972476 std-switch/switch-appearance-customization.html [ Failure ]
 
 # Sheriff 2019-05-20
-crbug.com/964239 external/wpt/css/css-scroll-snap/scroll-margin.html [ Pass Failure ]
 crbug.com/965389 [ Mac ] media/track/track-cue-rendering-position-auto.html [ Pass Failure ]
 crbug.com/965389 [ Mac ] virtual/audio-service/media/track/track-cue-rendering-position-auto.html [ Pass Failure ]
 
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/transaction-relaxed-durability.tentative.any.js b/third_party/blink/web_tests/external/wpt/IndexedDB/transaction-relaxed-durability.tentative.any.js
index c6a6f37..2ba96ec 100644
--- a/third_party/blink/web_tests/external/wpt/IndexedDB/transaction-relaxed-durability.tentative.any.js
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/transaction-relaxed-durability.tentative.any.js
@@ -12,8 +12,9 @@
 let cases = [
   undefined,
   {},
-  {relaxedDurability: false},
-  {relaxedDurability: true},
+  {durability: "default"},
+  {durability: "relaxed"},
+  {durability: "strict"},
 ];
 
 for (let i = 0; i < cases.length; ++i) {
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/floats/new-fc-beside-float-with-margin-rtl.html b/third_party/blink/web_tests/external/wpt/css/CSS2/floats/new-fc-beside-float-with-margin-rtl.html
new file mode 100644
index 0000000..5a564f78
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/floats/new-fc-beside-float-with-margin-rtl.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="help" href="https://www.w3.org/TR/CSS22/visuren.html#bfc-next-to-float" title="9.5 Floats">
+<meta name="assert" content="The new formatting context's margin-right does not push it down to the next area.">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="width: 100px; height: 100px; background: red; direction: rtl;">
+  <div style="float: left; width: 50px; height: 100px; background: green;"></div>
+  <div style="overflow: hidden; height: 100px; margin-left: -20px; background: green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/floats/new-fc-beside-float-with-margin.html b/third_party/blink/web_tests/external/wpt/css/CSS2/floats/new-fc-beside-float-with-margin.html
new file mode 100644
index 0000000..ead8e54
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/floats/new-fc-beside-float-with-margin.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="help" href="https://www.w3.org/TR/CSS22/visuren.html#bfc-next-to-float" title="9.5 Floats">
+<meta name="assert" content="The new formatting context's margin-right does not push it down to the next area.">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="width: 100px; height: 100px; background: red;">
+  <div style="float:left; width:50px; height:100px; background:green;"></div>
+  <div style="overflow: hidden; margin-right: 1px; width:50px; height:100px; background:green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/floats/zero-width-floats-positioning.html b/third_party/blink/web_tests/external/wpt/css/CSS2/floats/zero-width-floats-positioning.tentative.html
similarity index 92%
rename from third_party/blink/web_tests/external/wpt/css/CSS2/floats/zero-width-floats-positioning.html
rename to third_party/blink/web_tests/external/wpt/css/CSS2/floats/zero-width-floats-positioning.tentative.html
index 7f8e34d..18f8f6e 100644
--- a/third_party/blink/web_tests/external/wpt/css/CSS2/floats/zero-width-floats-positioning.html
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/floats/zero-width-floats-positioning.tentative.html
@@ -4,7 +4,7 @@
 <link rel="help" href="https://www.w3.org/TR/CSS22/visudet.html#float-width" title="10.3.5 Floating, non-replaced elements">
 <link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
 <p>Test passes if there is a filled green square.</p>
-<div style="margin-left: 50px; width: 125px;">
+<div style="width: 125px;">
   <div style="float: left; width: 0px; height: 50px;"></div>
   <div style="float: right; clear: left; width: 25px; height: 50px;"></div>
   <div style="overflow: hidden; margin-left: -50px; height: 100px; background: green;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-content/inheritance-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-content/inheritance-expected.txt
new file mode 100644
index 0000000..a75fca40
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-content/inheritance-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+FAIL Property quotes has initial value auto assert_equals: expected "auto" but got ""
+PASS Property quotes inherits
+FAIL Property bookmark-level has initial value none assert_true: bookmark-level doesn't seem to be supported in the computed style expected true got false
+FAIL Property bookmark-level does not inherit assert_true: expected true got false
+FAIL Property bookmark-state has initial value open assert_true: bookmark-state doesn't seem to be supported in the computed style expected true got false
+FAIL Property bookmark-state does not inherit assert_true: expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-content/inheritance.html b/third_party/blink/web_tests/external/wpt/css/css-content/inheritance.html
new file mode 100644
index 0000000..a324255
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-content/inheritance.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Inheritance of CSS Contents properties</title>
+<link rel="help" href="https://drafts.csswg.org/css-content/#propdef-quotes">
+<link rel="help" href="https://drafts.csswg.org/css-content/#property-index">
+<meta name="assert" content="Properties inherit or not according to the spec.">
+<meta name="assert" content="Properties have initial values according to the spec.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/inheritance-testcommon.js"></script>
+</head>
+<body>
+<div id="container">
+  <div id="target"></div>
+</div>
+<script>
+assert_inherited('quotes', 'auto', 'none');
+assert_not_inherited('bookmark-level', 'none', '1');
+assert_not_inherited('bookmark-state', 'open', 'closed');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-area-shorthand.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-area-shorthand.html
new file mode 100644
index 0000000..9a26550
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-area-shorthand.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: grid-area sets longhands</title>
+<link rel="help" href="https://drafts.csswg.org/css-grid-1/#propdef-grid-area">
+<meta name="assert" content="grid-area supports the full grammar '<grid-line> [ / <grid-line> ]{0,3}'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/shorthand-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_shorthand_value('grid-area', 'auto', {
+  'grid-row-start': 'auto',
+  'grid-column-start': 'auto',
+  'grid-row-end': 'auto',
+  'grid-column-end': 'auto'
+});
+
+// <custom-ident>
+test_shorthand_value('grid-area', '--a', {
+  'grid-row-start': '--a',
+  'grid-column-start': '--a',
+  'grid-row-end': '--a',
+  'grid-column-end': '--a'
+});
+
+test_shorthand_value('grid-area', 'a / b', {
+  'grid-row-start': 'a',
+  'grid-column-start': 'b',
+  'grid-row-end': 'a',
+  'grid-column-end': 'b'
+});
+
+test_shorthand_value('grid-area', 'a / b / c', {
+  'grid-row-start': 'a',
+  'grid-column-start': 'b',
+  'grid-row-end': 'c',
+  'grid-column-end': 'b'
+});
+
+test_shorthand_value('grid-area', 'a / b / c / d', {
+  'grid-row-start': 'a',
+  'grid-column-start': 'b',
+  'grid-row-end': 'c',
+  'grid-column-end': 'd'
+});
+
+// <integer> && <custom-ident>?
+// span && [ <integer> || <custom-ident> ]
+test_shorthand_value('grid-area', '+90 -a- / 2 i span', {
+  'grid-row-start': '90 -a-',
+  'grid-column-start': 'span 2 i',
+  'grid-row-end': 'auto',
+  'grid-column-end': 'auto'
+});
+
+test_shorthand_value('grid-area', '1 / 2 / 3 / 4', {
+  'grid-row-start': '1',
+  'grid-column-start': '2',
+  'grid-row-end': '3',
+  'grid-column-end': '4'
+});
+
+
+test_shorthand_value('grid-row', 'auto', {
+  'grid-row-start': 'auto',
+  'grid-row-end': 'auto'
+});
+
+test_shorthand_value('grid-row', 'one / 2', {
+  'grid-row-start': 'one',
+  'grid-row-end': '2'
+});
+
+test_shorthand_value('grid-row', '1 two / four 3', {
+  'grid-row-start': '1 two',
+  'grid-row-end': '3 four'
+});
+
+
+test_shorthand_value('grid-column', '5 span', {
+  'grid-column-start': 'span 5',
+  'grid-column-end': 'auto'
+});
+
+test_shorthand_value('grid-column', '1 / two', {
+  'grid-column-start': '1',
+  'grid-column-end': 'two'
+});
+
+test_shorthand_value('grid-column', 'span 1 two / four 3 span', {
+  'grid-column-start': 'span 1 two',
+  'grid-column-end': 'span 3 four'
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-auto-columns-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-auto-columns-invalid.html
index 40b6059..04e0fadf 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-auto-columns-invalid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-auto-columns-invalid.html
@@ -32,7 +32,8 @@
 // <track-size>+
 test_invalid_value("grid-auto-columns", "2em / 3em");
 test_invalid_value("grid-auto-columns", "auto, 10%");
-test_invalid_value("grid-auto-rows", "1px [a] 1px");
+test_invalid_value("grid-auto-columns", "1px [a] 1px");
+test_invalid_value("grid-auto-columns", "[] 1px []");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-auto-rows-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-auto-rows-invalid.html
index 4111e25..1b61479 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-auto-rows-invalid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-auto-rows-invalid.html
@@ -32,6 +32,7 @@
 test_invalid_value("grid-auto-rows", "2em / 3em");
 test_invalid_value("grid-auto-rows", "auto, 10%");
 test_invalid_value("grid-auto-rows", "1px [a] 1px");
+test_invalid_value("grid-auto-rows", "[] 1px []");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-valid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-valid.html
index 85ac76a..6bf2e7bc 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-valid.html
@@ -47,7 +47,7 @@
 // 'repeat(1, 10px)' in Firefox
 // '[] 10px' in Safari
 // '10px' in Edge 18
-test_valid_value("grid-template-columns", 'repeat(1, [] 10px)', ['repeat(1, 10px)', 'repeat(1, [] 10px)']);
+test_valid_value("grid-template-columns", 'repeat(1, [] 10px [])', 'repeat(1, 10px)');
 
 // 'repeat(1, [one two] 20%)' in Blink, Firefox
 // '[one two] 20%' in Safari, Edge 18
@@ -61,6 +61,7 @@
 
 // <track-list> = [ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?
 test_valid_value("grid-template-columns", 'min-content repeat(5, minmax(10px, auto))');
+test_valid_value("grid-template-columns", '[] 150px [] 1fr []', '150px 1fr');
 
 // <auto-repeat> = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
 test_valid_value("grid-template-columns", 'repeat(auto-fill, 10px)');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-valid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-valid.html
index ec8d64f..2d3a1ed 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-valid.html
@@ -43,11 +43,7 @@
 
 // <track-repeat> = repeat( [ <positive-integer> ] , [ <line-names>? <track-size> ]+ <line-names>? )
 
-// 'repeat(1, [] 10px)' in Blink
-// 'repeat(1, 10px)' in Firefox
-// '[] 10px' in Safari
-// '10px' in Edge 18
-test_valid_value("grid-template-rows", 'repeat(1, [] 10px)', ['repeat(1, 10px)', 'repeat(1, [] 10px)']);
+test_valid_value("grid-template-rows", 'repeat(1, [] 10px [])', 'repeat(1, 10px)');
 
 // 'repeat(1, [one two] 20%)' in Blink, Firefox
 // '[one two] 20%' in Safari, Edge 18
@@ -61,6 +57,7 @@
 
 // <track-list> = [ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?
 test_valid_value("grid-template-rows", 'min-content repeat(5, minmax(10px, auto))');
+test_valid_value("grid-template-rows", '[] 150px [] 1fr []', '150px 1fr');
 
 // <auto-repeat> = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
 test_valid_value("grid-template-rows", 'repeat(auto-fill, 10px)');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-shorthand.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-shorthand.html
new file mode 100644
index 0000000..b916535
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-shorthand.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: grid-template sets longhands</title>
+<link rel="help" href="https://drafts.csswg.org/css-grid-1/#propdef-grid-template">
+<meta name="assert" content="grid-template supports the full grammar 'none | [ <grid-template-rows> / <grid-template-columns> ] | [ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <explicit-track-list> ]?'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/shorthand-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_shorthand_value('grid-template', 'none', {
+  'grid-template-rows': 'none',
+  'grid-template-columns': 'none',
+  'grid-template-areas': 'none'
+});
+
+// <grid-template-rows> / <grid-template-columns>
+test_shorthand_value('grid-template', '10px / 20%', {
+  'grid-template-rows': '10px',
+  'grid-template-columns': '20%',
+  'grid-template-areas': 'none'
+});
+
+test_shorthand_value('grid-template', 'fit-content(calc(-0.5em + 10px)) / fit-content(calc(0.5em + 10px))', {
+  'grid-template-rows': 'fit-content(calc(-0.5em + 10px))',
+  'grid-template-columns': 'fit-content(calc(0.5em + 10px))',
+  'grid-template-areas': 'none'
+});
+
+// [ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <explicit-track-list> ]?
+test_shorthand_value('grid-template',
+                     '[header-top] "a a a"     [header-bottom]' +
+                     '  [main-top] "b b b" 1fr [main-bottom]' +
+                     '           / auto 1fr auto', {
+  'grid-template-rows': '[header-top] auto [header-bottom main-top] 1fr [main-bottom]',
+  'grid-template-columns': 'auto 1fr auto',
+  'grid-template-areas': '"a a a" "b b b"'
+});
+
+test_shorthand_value('grid-template',
+                     '  "a a a"' +
+                     '  "b b b" 1fr' +
+                     '/ auto 1fr auto', {
+  'grid-template-rows': 'auto 1fr',
+  'grid-template-columns': 'auto 1fr auto',
+  'grid-template-areas': '"a a a" "b b b"'
+});
+
+test_shorthand_value('grid-template',
+                     ' [] "a a a"     []' +
+                     ' [] "b b b" 1fr []' +
+                     '  / [] auto 1fr [] auto []', {
+  'grid-template-rows': 'auto 1fr',
+  'grid-template-columns': 'auto 1fr auto',
+  'grid-template-areas': '"a a a" "b b b"'
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/scroll-margin.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/scroll-margin.html
index c85232e..e6ce4ac4 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/scroll-margin.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/scroll-margin.html
@@ -58,14 +58,31 @@
 test(() => {
   target.style.left = "0px";
   target.style.top = "0px";
-
   target.style.scrollSnapAlign = "start";
-  target.style.scrollMargin = "100px";
 
-  // Scroll to the position between #target and #another-target elements but
-  // if the scroll-margin 100px contributed to the snap start-aligned snap
-  // position it will be farther than #another-target.
-  scroller.scrollTo(200, 200);
+  // Since the target is at (0px, 0px) in the scroll port, the added margin
+  // should not be considered, and the snap points for this snap area should be
+  // the closest points in the scroll port (i.e x=0 or y=0).
+  target.style.scrollMargin = "200px";
+
+  // Distance from target without margin:
+  // `scroll position (150px, 150px)` - `target position (0px, 0px)`
+  // = (150px, 150px)
+  //
+  // Distance from target with margin:
+  // `scroll position (150px, 150px)` - [`target position (0px, 0px)` -
+  // `target margin (200px, 200px)`]
+  // = (350px, 350px)
+  //
+  // Distance from other target:
+  // `other target position (400px, 400px)` - `scroll position (150px, 150px)`
+  // = (250px, 250px)
+  //
+  // Therefore if the "out-of-scrollport" scroll-margin contributes to the
+  // calculation, then the other target would be snapped to. However if the
+  // scroll-margin is not considered, then the (0px, 0px) target should be
+  // snapped to.
+  scroller.scrollTo(150, 150);
   assert_equals(scroller.scrollLeft, 0);
   assert_equals(scroller.scrollTop, 0);
 }, "scroll-margin doesn't contribute to the snap position of the element " +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-computed.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-computed.html
index 9834dfd..e57856b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-computed.html
@@ -13,6 +13,8 @@
 <div id="target"></div>
 <script>
 test_computed_value("transition-timing-function", "linear");
+test_computed_value("transition-timing-function", "", "ease");
+test_computed_value("transition-timing-function", "initial", "ease");
 
 test_computed_value("transition-timing-function", "ease");
 test_computed_value("transition-timing-function", "ease-in");
@@ -22,7 +24,9 @@
 test_computed_value("transition-timing-function", "cubic-bezier(0, -2, 1, 3)");
 test_computed_value("transition-timing-function", "cubic-bezier(0, 0.7, 1, 1.3)");
 
-
+test_computed_value("transition-timing-function", "step-start", "steps(1, start)");
+test_computed_value("transition-timing-function", "step-end", "steps(1)");
+test_computed_value("transition-timing-function", "steps(4)");
 test_computed_value("transition-timing-function", "steps(4, start)");
 test_computed_value("transition-timing-function", "steps(2, end)", "steps(2)");
 test_computed_value("transition-timing-function", "steps(2, jump-start)");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-invalid.html
index 00bd213..d736ef0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-invalid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-invalid.html
@@ -14,11 +14,23 @@
 <script>
 test_invalid_value("transition-timing-function", "auto");
 test_invalid_value("transition-timing-function", "ease-in ease-out");
+
+test_invalid_value("transition-timing-function", "cubic-bezier(foobar)");
 test_invalid_value("transition-timing-function", "cubic-bezier(1, 2, 3)");
 test_invalid_value("transition-timing-function", "cubic-bezier(1, 2, 3, infinite)");
 test_invalid_value("transition-timing-function", "cubic-bezier(1, 2, 3, 4, 5)");
 test_invalid_value("transition-timing-function", "cubic-bezier(-0.1, 0.1, 0.5, 0.9)");
 test_invalid_value("transition-timing-function", "cubic-bezier(0.5, 0.1, 1.1, 0.9)");
+
+test_invalid_value("transition-timing-function", "steps(foobar)");
+test_invalid_value("transition-timing-function", "steps(3.3, end)");
+test_invalid_value("transition-timing-function", "steps(3, top)");
+test_invalid_value("transition-timing-function", "steps(-3, top)");
+test_invalid_value("transition-timing-function", "steps(0, jump-start)");
+test_invalid_value("transition-timing-function", "steps(0, jump-end)");
+test_invalid_value("transition-timing-function", "steps(0, jump-both)");
+test_invalid_value("transition-timing-function", "steps(1, jump-none)");
+
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-valid-expected.txt
new file mode 100644
index 0000000..0d8cfd0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-valid-expected.txt
@@ -0,0 +1,21 @@
+This is a testharness.js-based test.
+PASS e.style['transition-timing-function'] = "linear" should set the property value
+PASS e.style['transition-timing-function'] = "ease" should set the property value
+PASS e.style['transition-timing-function'] = "ease-in" should set the property value
+PASS e.style['transition-timing-function'] = "ease-out" should set the property value
+PASS e.style['transition-timing-function'] = "ease-in-out" should set the property value
+PASS e.style['transition-timing-function'] = "cubic-bezier(0.1, 0.2, 0.8, 0.9)" should set the property value
+PASS e.style['transition-timing-function'] = "cubic-bezier(0, -2, 1, 3)" should set the property value
+PASS e.style['transition-timing-function'] = "cubic-bezier(0, 0.7, 1, 1.3)" should set the property value
+FAIL e.style['transition-timing-function'] = "step-start" should set the property value assert_equals: serialization should be canonical expected "steps(1, start)" but got "step-start"
+FAIL e.style['transition-timing-function'] = "step-end" should set the property value assert_equals: serialization should be canonical expected "steps(1)" but got "step-end"
+PASS e.style['transition-timing-function'] = "steps(4)" should set the property value
+PASS e.style['transition-timing-function'] = "steps(4, start)" should set the property value
+PASS e.style['transition-timing-function'] = "steps(2, end)" should set the property value
+PASS e.style['transition-timing-function'] = "steps(2, jump-start)" should set the property value
+PASS e.style['transition-timing-function'] = "steps(2, jump-end)" should set the property value
+PASS e.style['transition-timing-function'] = "steps(2, jump-both)" should set the property value
+PASS e.style['transition-timing-function'] = "steps(2, jump-none)" should set the property value
+PASS e.style['transition-timing-function'] = "linear, ease, linear" should set the property value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-valid.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-valid.html
index 2e2c182..5402fda 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-timing-function-valid.html
@@ -22,6 +22,9 @@
 test_valid_value("transition-timing-function", "cubic-bezier(0, -2, 1, 3)");
 test_valid_value("transition-timing-function", "cubic-bezier(0, 0.7, 1, 1.3)");
 
+test_valid_value("transition-timing-function", "step-start", "steps(1, start)");
+test_valid_value("transition-timing-function", "step-end", "steps(1)");
+test_valid_value("transition-timing-function", "steps(4)");
 test_valid_value("transition-timing-function", "steps(4, start)");
 test_valid_value("transition-timing-function", "steps(2, end)", "steps(2)");
 test_valid_value("transition-timing-function", "steps(2, jump-start)");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-000.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-000-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-000.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-000-manual.html
index 2b4cedb..b2050859 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-000.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-000-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.4. The 'transition-delay' Property" href="http://www.w3.org/TR/css3-transitions/#transition-delay-property">
-<meta name="flags" content="interact">
 <meta name="assert" content="The 'transition-delay' property set positive number to delay the execution of transition">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-002.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-002-manual.html
similarity index 93%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-002.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-002-manual.html
index 70e952b..168f94b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-002-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.4. The 'transition-delay' Property" href="http://www.w3.org/TR/css3-transitions/#transition-delay-property">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'transition-delay' property set 0 will not delay the execution of transition">
 <style>
   div {
@@ -33,7 +32,7 @@
     test.addEventListener("click", function(evt) {
       if (clicked == 0) {
         ref.setAttribute("style", "background-color: green;");
-        setInterval(function() {console.log(clicked);
+        setInterval(function() {
           if (clicked == 2) {
             test.setAttribute("style", "background-color: green;");
           }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-003.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-003-manual.html
similarity index 93%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-003.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-003-manual.html
index 3062e9a..e3680ca 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-delay-003-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.4. The 'transition-delay' Property" href="http://www.w3.org/TR/css3-transitions/#transition-delay-property">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'transition-delay' property set negative number will not delay the execution of transition">
 <style>
   div {
@@ -33,7 +32,7 @@
     test.addEventListener("click", function(evt) {
       if (clicked == 0) {
         ref.setAttribute("style", "background-color: green;");
-        setInterval(function() {console.log(clicked);
+        setInterval(function() {
           if (clicked == 2) {
             test.setAttribute("style", "background-color: green;");
           }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-002.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-002-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-002.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-002-manual.html
index e9ba5760..03f514d3 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-002-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.2. The 'transition-duration' Property" href="http://www.w3.org/TR/css3-transitions/#transition-duration-property">
-<meta name="flags" content="interact">
 <meta name="assert" content="The 'transition-duration' property set positive number specifies the time that transition from the old value to the new value should take.">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-003.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-003-manual.html
similarity index 97%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-003.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-003-manual.html
index bb68b5f..cb56100 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-003-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.2. The 'transition-duration' Property" href="http://www.w3.org/TR/css3-transitions/#transition-duration-property">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the initial value of 'transition-duration' property is '0s' which means the transition is immediate.">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-004.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-004-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-004.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-004-manual.html
index 01c424733..b93904bb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-duration-004-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.2. The 'transition-duration' Property" href="http://www.w3.org/TR/css3-transitions/#transition-duration-property">
-<meta name="flags" content="interact">
 <meta name="assert" content="A negative value for 'transition-duration renders the declaration invalid which means the transition is immediate.">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-003.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-003-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-003.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-003-manual.html
index 17668b05..291204d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-003-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.1. The 'transition-property' Property" href="http://www.w3.org/TR/css3-transitions/#transition-property-property">
-<meta name="flags" content="interact">
 <meta name="assert" content="The 'transition-duration' property set 'none' means that no property will be transitioned.">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-004.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-004-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-004.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-004-manual.html
index 6393c6e..d2e84c9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-004-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.1. The 'transition-property' Property" href="http://www.w3.org/TR/css3-transitions/#transition-property-property">
-<meta name="flags" content="interact">
 <meta name="assert" content="The 'transition-duration' property set 'all' means that all properties are transitioned.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-005.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-005-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-005.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-005-manual.html
index a10ff4a..e699410 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-005.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-005-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.1. The 'transition-property' Property" href="http://www.w3.org/TR/css3-transitions/#transition-property-property">
-<meta name="flags" content="interact">
 <meta name="assert" content="The 'transition-duration' property set more than one properties like 'height, width'
 means only the specified properties will be transitioned.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-006.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-006-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-006.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-006-manual.html
index e7d8c5e3..6032a401 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-006.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-006-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'background-position' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-007.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-007-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-007.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-007-manual.html
index 0d006fe..898aea5b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-007.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-007-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'border-bottom-color' property animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-008.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-008-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-008.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-008-manual.html
index 47ca1eee..53caa10 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-008.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-008-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'border-bottom-width' property animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-009.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-009-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-009.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-009-manual.html
index b45948b..5f27285 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-009.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-009-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'border-left-color' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-010.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-010-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-010.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-010-manual.html
index f05f6aa..a7e06ef7 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-010.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-010-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'border-left-width' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-011.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-011-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-011.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-011-manual.html
index 7b3716d..3799ad2 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-011.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-011-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'border-right-color' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-012.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-012-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-012.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-012-manual.html
index 0e19e52..8587e26 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-012.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-012-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'border-right-width' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-013.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-013-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-013.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-013-manual.html
index 9fb075b..3b13c038 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-013.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-013-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'border-top-color' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-014.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-014-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-014.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-014-manual.html
index 91075ff..d4aca380 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-014.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-014-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'border-top-width' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-015.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-015-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-015.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-015-manual.html
index b37d4fc..e5805bc 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-015.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-015-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'border-spacing' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-016.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-016-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-016.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-016-manual.html
index 2670cd7..d2cc57c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-016.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-016-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'bottom' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-017.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-017-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-017.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-017-manual.html
index 3b94ecd..f24c51a7 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-017.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-017-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'clip' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-018.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-018-manual.html
similarity index 95%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-018.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-018-manual.html
index 593e3ff..e0b6bd9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-018.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-018-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'color' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-019.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-019-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-019.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-019-manual.html
index 83f9000..24751292 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-019.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-019-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'font-size' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-020.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-020-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-020.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-020-manual.html
index af0aea41..e4f7b57 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-020.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-020-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'font-weight' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-021.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-021-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-021.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-021-manual.html
index f499c9a..c049222 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-021.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-021-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'left' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-022.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-022-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-022.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-022-manual.html
index f369549..ece1a32 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-022.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-022-manual.html
@@ -4,7 +4,7 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="ahem interact">
+<meta name="flags" content="ahem">
 <meta name="assert" content="Test checks that the 'letter-spacing' property is animatable.">
 <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-023.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-023-manual.html
similarity index 95%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-023.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-023-manual.html
index 81c8cadac..267be61 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-023.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-023-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'line-height' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-024.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-024-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-024.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-024-manual.html
index b0e9d41d..fad3d7a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-024.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-024-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'margin-bottom' property is animatable.">
 <style>
   #ref {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-025.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-025-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-025.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-025-manual.html
index bfa6f698..c0f27618 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-025.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-025-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'margin-left' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-026.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-026-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-026.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-026-manual.html
index d6f0481..35846e3f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-026.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-026-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'margin-right' property is animatable.">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-027.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-027-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-027.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-027-manual.html
index 01f50dc..5ad5f5d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-027.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-027-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'max-height' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-028.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-028-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-028.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-028-manual.html
index 83d87511..be319d2c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-028.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-028-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'max-width' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-029.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-029-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-029.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-029-manual.html
index fb435c91..84c5532 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-029.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-029-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'min-height' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-030.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-030-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-030.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-030-manual.html
index 286af42..ba75f7c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-030.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-030-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'min-width' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-031.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-031-manual.html
similarity index 95%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-031.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-031-manual.html
index d5bf232..14cf23c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-031.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-031-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'opacity' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-032.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-032-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-032.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-032-manual.html
index 020a857..c659660 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-032.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-032-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'outline-color' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-033.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-033-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-033.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-033-manual.html
index fcb999fa..fd7646c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-033.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-033-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'outline-width' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-034.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-034-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-034.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-034-manual.html
index b4da3a9b..0780412 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-034.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-034-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'padding-bottom' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-035.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-035-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-035.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-035-manual.html
index 27c9ea9..4a9e9b12 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-035.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-035-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'padding-left' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-036.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-036-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-036.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-036-manual.html
index 326e615..746bc40 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-036.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-036-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'padding-right' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-037.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-037-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-037.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-037-manual.html
index 79ce16f..d031caa 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-037.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-037-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'padding-top' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-038.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-038-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-038.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-038-manual.html
index 5b16e41..2f9302b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-038.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-038-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'right' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-039.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-039-manual.html
similarity index 95%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-039.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-039-manual.html
index 0ef6da5..bf0b209b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-039.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-039-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'text-indent' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-040.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-040-manual.html
similarity index 95%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-040.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-040-manual.html
index b333f371..83a3cf80 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-040.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-040-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'text-shadow' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-041.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-041-manual.html
similarity index 95%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-041.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-041-manual.html
index b3386a0..04f8351 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-041.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-041-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'top' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-042.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-042-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-042.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-042-manual.html
index fbb3e2d..15977497 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-042.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-042-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'vertical-align' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-043.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-043-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-043.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-043-manual.html
index b87dc84..3b939bcf 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-043.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-043-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the 'visibility' property is animatable.">
 <style>
   #test {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-044.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-044-manual.html
similarity index 95%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-044.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-044-manual.html
index f1d584f..d7c2f46 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-044.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-044-manual.html
@@ -4,7 +4,7 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="ahem interact">
+<meta name="flags" content="ahem">
 <meta name="assert" content="Test checks that the 'word-spacing' property is animatable.">
 <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-045.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-045-manual.html
similarity index 95%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-045.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-045-manual.html
index 3fa82c1..912ed4d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-045.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-property-045-manual.html
@@ -4,7 +4,7 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="7.1. Properties from CSS" href="http://www.w3.org/TR/css3-transitions/#animatable-css">
-<meta name="flags" content="ahem interact">
+<meta name="flags" content="ahem">
 <meta name="assert" content="Test checks that the 'z-index' property is animatable.">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-001.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-001.html
deleted file mode 100644
index 4c9598f..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-001.html
+++ /dev/null
@@ -1,94 +0,0 @@
-<!DOCTYPE html>
-<html>
-    <head>
-        <meta charset="utf-8">
-        <title>CSS Transitions Test: Parsing transition-timing-function</title>
-        <meta name="assert" content="Test checks that transition-timing-function values are parsed properly">
-        <link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function-property">
-        <link rel="author" title="Rodney Rehm" href="http://rodneyrehm.de/en/">
-        <meta name="flags" content="dom">
-
-        <script src="/resources/testharness.js" type="text/javascript"></script>
-        <script src="/resources/testharnessreport.js" type="text/javascript"></script>
-
-        <script src="./support/vendorPrefix.js" type="text/javascript"></script>
-        <script src="./support/helper.js" type="text/javascript"></script>
-    </head>
-    <body>
-        <!-- required by testharnessreport.js -->
-        <div id="log"></div>
-        <!-- elements used for testing -->
-        <div id="container">
-            <div id="transition"></div>
-        </div>
-
-        <script>
-            var transition = document.getElementById('transition');
-            var defaultValue = 'ease';
-            var values = {
-                // keywords
-                'ease': 'ease',
-                'linear': 'linear',
-                'ease-in': 'ease-in',
-                'ease-out': 'ease-out',
-                'ease-in-out': 'ease-in-out',
-                'step-start': 'steps(1, start)',
-                'step-end': 'steps(1)',
-                // cubic bezier
-                'cubic-bezier(0.1, 0.2, 0.3, 0.4)': 'cubic-bezier(0.1, 0.2, 0.3, 0.4)',
-                'cubic-bezier(0.1, -0.2, 0.3, -0.4)': 'cubic-bezier(0.1, -0.2, 0.3, -0.4)',
-                'cubic-bezier(0.1, 1.2, 0.3, 1.4)': 'cubic-bezier(0.1, 1.2, 0.3, 1.4)',
-                // steps
-                'steps(3, start)': 'steps(3, start)',
-                'steps(3, end)': 'steps(3)',
-                'steps(3)': 'steps(3)',
-                'steps(3, jump-start)': 'steps(3, jump-start)',
-                'steps(3, jump-end)': 'steps(3)',
-                'steps(3, jump-both)': 'steps(3, jump-both)',
-                'steps(3, jump-none)': 'steps(3, jump-none)',
-                // invalid
-                'cubic-bezier(foobar)': defaultValue,
-                'steps(foobar)': defaultValue,
-                'steps(3.3, end)': defaultValue,
-                'steps(3, top)': defaultValue,
-                'steps(-3, top)': defaultValue,
-                'steps(0, jump-start)': defaultValue,
-                'steps(0, jump-end)': defaultValue,
-                'steps(0, jump-both)': defaultValue,
-                'steps(1, jump-none)': defaultValue,
-                // Both x values must be in the range [0, 1]
-                'cubic-bezier(-0.1, -0.2, -0.3, -0.4)': defaultValue,
-                'cubic-bezier(1.1, 1.2, 1.3, 1.4)': defaultValue
-            };
-
-            // these tests are supposed to fail and
-            // possibly make the engine issue a parser warning
-            var invalidTests = {
-                'cubic-bezier(foobar)': true,
-                'steps(foobar)': true,
-                'steps(3.3, end)': true,
-                'steps(3, top)': true,
-                'steps(-3, top)': true,
-                // Both x values must be in the range [0, 1]
-                'cubic-bezier(-0.1, -0.2, -0.3, -0.4)': true,
-                'cubic-bezier(1.1, 1.2, 1.3, 1.4)': true
-            };
-
-            for (var key in values) {
-                if (Object.prototype.hasOwnProperty.call(values, key)) {
-                    test(function() {
-                        setStyle('#transition', {
-                            'transition-timing-function': key
-                        });
-                        var result = computedStyle(transition, 'transition-timing-function');
-                        assert_equals(result, values[key], "Expected computed value");
-                    }, "parse '" + key + "'",
-                    {
-                        // mark tests that fail as such
-                        flags: invalidTests[key] ? "invalid" : ""
-                    });
-                }
-            }
-        </script>
-    </body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-002.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-002-manual.html
similarity index 97%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-002.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-002-manual.html
index 527eb9e..abd729b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-002-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
 <meta name="assert" content="The 'transition-timing-function' property set 'ease' is equivalent to cubic-bezier(0.25, 0.1, 0.25, 1.0)">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-003.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-003-manual.html
similarity index 97%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-003.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-003-manual.html
index 353b94e..9fee35a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-003-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
 <meta name="assert" content="The 'transition-timing-function' property set 'ease-in' is equivalent to cubic-bezier(0.42, 0, 1.0, 1.0)">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-004.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-004-manual.html
similarity index 97%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-004.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-004-manual.html
index 0601b4b..f57d49ae 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-004-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
 <meta name="assert" content="The 'transition-timing-function' property set 'ease-in-out' is equivalent to cubic-bezier(0.42, 0, 0.58, 1.0)">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-005.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-005-manual.html
similarity index 97%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-005.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-005-manual.html
index eb46ece..27726cd 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-005.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-005-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
 <meta name="assert" content="The 'transition-timing-function' property set 'ease-out' is equivalent to cubic-bezier(0, 0, 0.58, 1.0)">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-006.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-006-manual.html
similarity index 97%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-006.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-006-manual.html
index d8640d93..d97e6bb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-006.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-006-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
 <meta name="assert" content="The 'transition-timing-function' property set 'linear' is equivalent to cubic-bezier(0.0, 0.0, 1.0, 1.0)">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-007.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-007.html
deleted file mode 100644
index 72ffbf6..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-007.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>CSS Transitions Test: transition-timing-function - 'step-end' equivalent to 'steps(1, end)'</title>
-<link rel="author" title="Intel" href="http://www.intel.com">
-<link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
-<link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
-<meta name="assert" content="The 'transition-timing-function' property set 'step-end' is equivalent to 'steps(1, end)'">
-<style>
-  div {
-    height: 100px;
-    transition: width 2s;
-    width: 100px;
-  }
-  #test1 {
-    background-color: blue;
-    transition-timing-function: step-end;
-  }
-  #test2 {
-    background-color: yellow;
-    transition-timing-function: steps(1, end);
-  }
-</style>
-<body>
-  <p>Click the 'Start' button. Test passes if the width growth of blue square is <strong>equivalent</strong> to the yellow square.</p>
-  <div id="test1"></div>
-  <div id="test2"></div>
-  <button>Start</button>
-  <script>
-    (function() {
-      var button = document.querySelector("button");
-      button.addEventListener("click", function(evt) {
-        var test1 = document.querySelector("#test1"),
-            test2 = document.querySelector("#test2");
-        test1.setAttribute("style", "width: 300px");
-        test2.setAttribute("style", "width: 300px");
-      }, false);
-    })();
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-008.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-008.html
deleted file mode 100644
index 2e20f7e..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-008.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>CSS Transitions Test: transition-timing-function - 'step-start' equivalent to 'steps(1, start)'</title>
-<link rel="author" title="Intel" href="http://www.intel.com">
-<link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
-<link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
-<meta name="assert" content="The 'transition-timing-function' property set 'step-start' is equivalent to 'steps(1, start)'">
-<style>
-  div {
-    height: 100px;
-    transition: width 2s;
-    width: 100px;
-  }
-  #test1 {
-    background-color: blue;
-    transition-timing-function: step-start;
-  }
-  #test2 {
-    background-color: yellow;
-    transition-timing-function: steps(1, start);
-  }
-</style>
-<body>
-  <p>Click the 'Start' button. Test passes if the width growth of blue square is <strong>equivalent</strong> to the yellow square.</p>
-  <div id="test1"></div>
-  <div id="test2"></div>
-  <button>Start</button>
-  <script>
-    (function() {
-      var button = document.querySelector("button");
-      button.addEventListener("click", function(evt) {
-        var test1 = document.querySelector("#test1"),
-            test2 = document.querySelector("#test2");
-        test1.setAttribute("style", "width: 300px");
-        test2.setAttribute("style", "width: 300px");
-      }, false);
-    })();
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-009.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-009.html
deleted file mode 100644
index a3e2f1a..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-009.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>CSS Transitions Test: transition-timing-function - ease(initial value)</title>
-<link rel="author" title="Intel" href="http://www.intel.com">
-<link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
-<link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
-<meta name="assert" content="Test checks that the initial value of 'transition-timing-function' property is 'ease'.">
-<style>
-  div {
-    height: 100px;
-    transition-duration: 2s;
-    transition-property: width;
-    width: 100px;
-  }
-  #test1 {
-    background-color: blue;
-    transition-timing-function: ease;
-  }
-  #test2 {
-    background-color: yellow;
-  }
-</style>
-<body>
-  <p>Click the 'Start' button. Test passes if the width growth of blue square is <strong>equivalent</strong> to the yellow square.</p>
-  <div id="test1"></div>
-  <div id="test2"></div>
-  <button>Start</button>
-  <script>
-    (function() {
-      var button = document.querySelector("button");
-      button.addEventListener("click", function(evt) {
-        var test1 = document.querySelector("#test1"),
-            test2 = document.querySelector("#test2");
-        test1.setAttribute("style", "width: 300px");
-        test2.setAttribute("style", "width: 300px");
-      }, false);
-    })();
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-010.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-010-manual.html
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-010.html
rename to third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-010-manual.html
index 16af806..953d721 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-010.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-010-manual.html
@@ -4,7 +4,6 @@
 <link rel="author" title="Intel" href="http://www.intel.com">
 <link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
 <link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
 <meta name="assert" content="Test checks that the first parameter of 'steps()' function specifies the number of intervals.">
 <style>
   div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-011.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-011.html
deleted file mode 100644
index 5d0f5a2..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-011.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>CSS Transitions Test: transition-timing-function - steps(the second parameter default 'end')</title>
-<link rel="author" title="Intel" href="http://www.intel.com">
-<link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
-<link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
-<meta name="assert" content="Test checks that the second parameter of 'steps()' function is default 'end'.">
-<style>
-  div {
-    height: 100px;
-    transition-duration: 2s;
-    transition-property: width;
-    width: 100px;
-  }
-  #test1 {
-    background-color: blue;
-    transition-timing-function: steps(2);
-  }
-  #test2 {
-    background-color: yellow;
-    transition-timing-function: steps(2, end);
-  }
-</style>
-<body>
-  <p>Click the 'Start' button. Test passes if the width growth of blue square is <strong>equivalent</strong> to the yellow square.</p>
-  <div id="test1"></div>
-  <div id="test2"></div>
-  <button>Start</button>
-  <script>
-    (function() {
-      var button = document.querySelector("button");
-      button.addEventListener("click", function(evt) {
-        var test1 = document.querySelector("#test1"),
-            test2 = document.querySelector("#test2");
-        test1.setAttribute("style", "width: 300px");
-        test2.setAttribute("style", "width: 300px");
-      }, false);
-    })();
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-012.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-012.html
deleted file mode 100644
index f3bc812..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-012.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>CSS Transitions Test: transition-timing-function - steps(-2)</title>
-<link rel="author" title="Intel" href="http://www.intel.com">
-<link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
-<link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
-<meta name="assert" content="Test checks that the 'steps()' function with negative number is invalid, the transition-timing-function will use 'ease' as default.">
-<style>
-  div {
-    height: 100px;
-    transition-duration: 2s;
-    transition-property: width;
-    width: 100px;
-  }
-  #test1 {
-    background-color: blue;
-    transition-timing-function: steps(-2);
-  }
-  #test2 {
-    background-color: yellow;
-    transition-timing-function: ease;
-  }
-</style>
-<body>
-  <p>Click the 'Start' button. Test passes if the width growth of blue square is <strong>equivalent</strong> to the yellow square.</p>
-  <div id="test1"></div>
-  <div id="test2"></div>
-  <button>Start</button>
-  <script>
-    (function() {
-      var button = document.querySelector("button");
-      button.addEventListener("click", function(evt) {
-        var test1 = document.querySelector("#test1"),
-            test2 = document.querySelector("#test2");
-        test1.setAttribute("style", "width: 300px");
-        test2.setAttribute("style", "width: 300px");
-      }, false);
-    })();
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-013.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-013.html
deleted file mode 100644
index 5d89c45e..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transition-timing-function-013.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>CSS Transitions Test: transition-timing-function - steps(0)</title>
-<link rel="author" title="Intel" href="http://www.intel.com">
-<link rel="author" title="Shiyou Tan" href="mailto:shiyoux.tan@intel.com">
-<link rel="help" title="2.3. The 'transition-timing-function' Property" href="http://www.w3.org/TR/css3-transitions/#transition-timing-function">
-<meta name="flags" content="interact">
-<meta name="assert" content="Test checks that the 'steps()' function with '0' is invalid, the transition-timing-function will use 'ease' as default.">
-<style>
-  div {
-    height: 100px;
-    transition-duration: 2s;
-    transition-property: width;
-    width: 100px;
-  }
-  #test1 {
-    background-color: blue;
-    transition-timing-function: steps(0);
-  }
-  #test2 {
-    background-color: yellow;
-    transition-timing-function: ease;
-  }
-</style>
-<body>
-  <p>Click the 'Start' button. Test passes if the width growth of blue square is <strong>equivalent</strong> to the yellow square.</p>
-  <div id="test1"></div>
-  <div id="test2"></div>
-  <button>Start</button>
-  <script>
-    (function() {
-      var button = document.querySelector("button");
-      button.addEventListener("click", function(evt) {
-        var test1 = document.querySelector("#test1"),
-            test2 = document.querySelector("#test2");
-        test1.setAttribute("style", "width: 300px");
-        test2.setAttribute("style", "width: 300px");
-      }, false);
-    })();
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/intersection-observer/v2/delay-test.html b/third_party/blink/web_tests/external/wpt/intersection-observer/v2/delay-test.html
index 231df32c..e3906ea 100644
--- a/third_party/blink/web_tests/external/wpt/intersection-observer/v2/delay-test.html
+++ b/third_party/blink/web_tests/external/wpt/intersection-observer/v2/delay-test.html
@@ -46,7 +46,7 @@
   // The first notification should be sent without delay.
   waitForNotification(t, t.step_func(step0));
 
-  function waitForDelay(timerExpiredBeforeLastFrame) {
+  function waitForDelay(timerExpiredBeforeLastFrame, nextStep) {
     requestAnimationFrame(t.step_func(() => {
       if (timerExpiredBeforeLastFrame) {
         // New notifications should have been generated during the previous
@@ -54,11 +54,11 @@
         assert_equals(entries.length, 2);
         assert_greater_than(entries[1].time - entries[0].time, delay);
         assert_false(entries[1].isVisible);
-        t.done();
+        nextStep();
       } else {
         // Observer may not have updated yet. Wait for next frame.
         let timerExpired = performance.now() - entries[0].time >= delay;
-        waitForDelay(timerExpired);
+        waitForDelay(timerExpired, nextStep);
       }
     }));
   }
@@ -69,7 +69,20 @@
     // This should trigger a notification on the next run.
     occluder.style.marginTop = "-10px";
     // Enter a rAF loop until the delay timer expires.
-    waitForDelay(false);
+    waitForDelay(false, step1);
+  }
+
+  function step1() {
+    occluder.style.marginTop = "10px";
+    // This style invalidation should cause a frame to run before the observer
+    // can generate a notification (due to delay parameter). Make sure the
+    // notification will still be generated even if we don't force more frames
+    // with a rAF loop.
+    t.step_timeout(() => {
+      assert_equals(entries.length, 3);
+      assert_true(entries[0].isVisible);
+      t.done();
+    }, 2 * delay);
   }
 
 }, "'delay' parameter throttles frequency of notifications.");
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrSession_input_events_end.https.html b/third_party/blink/web_tests/external/wpt/webxr/xrSession_input_events_end.https.html
index 37e0206..a932aad 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/xrSession_input_events_end.https.html
+++ b/third_party/blink/web_tests/external/wpt/webxr/xrSession_input_events_end.https.html
@@ -10,10 +10,16 @@
 
 let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE;
 
+let gl = null;
+
 function requestImmersiveSession() {
   return new Promise((resolve, reject) => {
     navigator.xr.test.simulateUserActivation(() => {
       navigator.xr.requestSession('immersive-vr').then((session) => {
+        session.updateRenderState({
+          baseLayer: new XRWebGLLayer(session, gl)
+        });
+
         resolve(session);
       }, (err) => {
         reject(err);
@@ -22,8 +28,8 @@
   });
 }
 
-let testFunction = function(session, fakeDeviceController, t) {
-
+let testFunction = function(session, fakeDeviceController, t, sessionObjects) {
+  gl = sessionObjects.gl;
   // helper method to send a click and then request a dummy animation frame to
   // ensure that the click propagates.  We're doing everything in these tests
   // from event watchers, we just need to trigger the add/click to make the
diff --git a/third_party/blink/web_tests/platform/mac/virtual/threaded/external/wpt/css/css-scroll-snap/scroll-margin-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/threaded/external/wpt/css/css-scroll-snap/scroll-margin-expected.txt
deleted file mode 100644
index d3bfa9a..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/threaded/external/wpt/css/css-scroll-snap/scroll-margin-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-PASS Snaps to the positions adjusted by scroll-margin
-FAIL scroll-margin doesn't contribute to the snap position of the element if it's outside of the scroll port assert_equals: expected 0 but got 400
-Harness: the test ran to completion.
-
diff --git a/third_party/closure_compiler/externs/autofill_private.js b/third_party/closure_compiler/externs/autofill_private.js
index d905d06..1263cf2 100644
--- a/third_party/closure_compiler/externs/autofill_private.js
+++ b/third_party/closure_compiler/externs/autofill_private.js
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -197,6 +197,12 @@
 chrome.autofillPrivate.logServerCardLinkClicked = function() {};
 
 /**
+ * Enables or disables FIDO Authentication for credit card unmasking.
+ * @param {boolean} enabled
+ */
+chrome.autofillPrivate.setCreditCardFIDOAuthEnabledState = function(enabled) {};
+
+/**
  * Fired when the address list has changed, meaning that an entry has been
  * added, removed, or changed. |entries| The updated list of entries.
  * @type {!ChromeEvent}
diff --git a/third_party/feed/BUILD.gn b/third_party/feed/BUILD.gn
index 3757358..a827f47 100644
--- a/third_party/feed/BUILD.gn
+++ b/third_party/feed/BUILD.gn
@@ -34,6 +34,9 @@
   resource_dirs = [
     "src/src/main/java/com/google/android/libraries/feed/sharedstream/ui/res",
   ]
+  deps = [
+    "//chrome/android:chrome_app_java_resources",
+  ]
   custom_package = "com.google.android.libraries.feed.sharedstream.ui"
 }
 
@@ -47,6 +50,9 @@
   resource_dirs = [
     "src/src/main/java/com/google/android/libraries/feed/sharedstream/ui/res",
   ]
+  deps = [
+    "//chrome/android:chrome_app_java_resources",
+  ]
   custom_package =
       "com.google.android.libraries.feed.basicstream.internal.drivers"
 }
@@ -65,6 +71,9 @@
     "src/src/main/java/com/google/android/libraries/feed/basicstream/res/",
     "src/src/main/java/com/google/android/libraries/feed/sharedstream/ui/res",
   ]
+  deps = [
+    "//chrome/android:chrome_app_java_resources",
+  ]
   custom_package = "com.google.android.libraries.feed.basicstream"
 }
 
@@ -78,6 +87,9 @@
   resource_dirs = [
     "src/src/main/java/com/google/android/libraries/feed/sharedstream/ui/res",
   ]
+  deps = [
+    "//chrome/android:chrome_app_java_resources",
+  ]
   custom_package =
       "com.google.android.libraries.feed.sharedstream.contextmenumanager"
 }
diff --git a/third_party/feed/README.chromium b/third_party/feed/README.chromium
index 0426612..b14e6b2 100644
--- a/third_party/feed/README.chromium
+++ b/third_party/feed/README.chromium
@@ -2,7 +2,7 @@
 Short name: feed
 URL: https://chromium.googlesource.com/feed
 Version: 0
-Revision: 4e6265827406b37da706e1e404cac5f99ae2854e
+Revision: c25c8f41145ed610d1a73004e8bcd62d8e8111c1
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index 7deb8be..1c5c6d5 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -5,9 +5,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Tuesday September 10 2019
+Date: Friday September 20 2019
 Branch: master
-Commit: c094391e954aa274b9dcce3d6afcb5ba6bae7eff
+Commit: b8d86733e9d9c58e17028720751f96dad2df7a09
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index d7b78c35..6c549fc 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -2,7 +2,7 @@
 #define VERSION_MAJOR  1
 #define VERSION_MINOR  8
 #define VERSION_PATCH  1
-#define VERSION_EXTRA  "131-gc094391e95"
+#define VERSION_EXTRA  "139-gb8d86733e9"
 #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.8.1-131-gc094391e95"
-#define VERSION_STRING      " v1.8.1-131-gc094391e95"
+#define VERSION_STRING_NOSP "v1.8.1-139-gb8d86733e9"
+#define VERSION_STRING      " v1.8.1-139-gb8d86733e9"
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 264f9c7..5c88f3b8 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 267295422
-Date: 2019/09/05 UTC
+Version: 270770521
+Date: 2019/09/23 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/omnibox_event.proto b/third_party/metrics_proto/omnibox_event.proto
index e0522f1..a5d58d1 100644
--- a/third_party/metrics_proto/omnibox_event.proto
+++ b/third_party/metrics_proto/omnibox_event.proto
@@ -206,6 +206,8 @@
     // Non personalized query suggestions generated from a lightweight on device
     // head model.
     ON_DEVICE_HEAD = 17;
+    // Zero-prefix query suggestions based on device local history.
+    ZERO_SUGGEST_LOCAL_HISTORY = 18;
   }
 
   // The result set displayed on the completion popup
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index fc799c2..ca8f930 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -15,6 +15,7 @@
 import os
 import posixpath
 import re
+import string
 import subprocess
 import sys
 import tempfile
@@ -34,6 +35,7 @@
 import nm
 import obj_analyzer
 import path_util
+import string_extract
 
 sys.path.insert(1, os.path.join(path_util.SRC_ROOT, 'tools', 'grit'))
 from grit.format import data_pack
@@ -159,6 +161,10 @@
     elif symbol.IsDex():
       symbol.full_name, symbol.template_name, symbol.name = (
           function_signature.ParseJava(full_name))
+    elif symbol.IsStringLiteral():
+      symbol.full_name = full_name
+      symbol.template_name = full_name
+      symbol.name = full_name
     elif symbol.IsNative():
       # Remove [clone] suffix, and set flag accordingly.
       # Search from left-to-right, as multiple [clone]s can exist.
@@ -844,6 +850,24 @@
         num_deduced, num_arbitrations, num_unassigned)
 
 
+def _NameStringLiterals(raw_symbols, elf_path, tool_prefix):
+  # Assign ASCII-readable string literals names like "string contents".
+  STRING_LENGTH_CUTOFF = 30
+
+  for sym, name in string_extract.ReadStringLiterals(raw_symbols, elf_path,
+                                                     tool_prefix):
+    # Newlines and tabs are used as delimiters in file_format.py
+    # At this point, names still have a terminating null byte.
+    name = name.translate(None, '\t\n').strip('\00')
+    is_printable = all(c in string.printable for c in name)
+    if not is_printable:
+      sym.full_name = models.STRING_LITERAL_NAME
+    elif len(name) > STRING_LENGTH_CUTOFF:
+      sym.full_name = '"{}[...]"'.format(name[:STRING_LENGTH_CUTOFF])
+    else:
+      sym.full_name = '"{}"'.format(name)
+
+
 def _ParseElfInfo(map_path, elf_path, tool_prefix, track_string_literals,
                   outdir_context=None, linker_name=None):
   """Adds ELF section sizes and symbols."""
@@ -956,6 +980,9 @@
 
   linker_map_parser.DeduceObjectPathsFromThinMap(raw_symbols, linker_map_extras)
 
+  if elf_path:
+    _NameStringLiterals(raw_symbols, elf_path, tool_prefix)
+
   # If we have an ELF file, use its sizes as the source of truth, since some
   # sections can differ from the .map.
   return (elf_section_sizes if elf_path else map_section_sizes, raw_symbols,
@@ -1521,7 +1548,7 @@
 
   # Do not call _NormalizeNames() during archive since that method tends to need
   # tweaks over time. Calling it only when loading .size files allows for more
-  # flexability.
+  # flexibility.
   if normalize_names:
     _NormalizeNames(raw_symbols)
 
diff --git a/tools/binary_size/libsupersize/console.py b/tools/binary_size/libsupersize/console.py
index ae1d3ce..b13164e 100644
--- a/tools/binary_size/libsupersize/console.py
+++ b/tools/binary_size/libsupersize/console.py
@@ -126,26 +126,8 @@
     elf_path = self._ElfPathForSymbol(
         size_info, tool_prefix, elf_path)
 
-    address, offset, _ = string_extract.LookupElfRodataInfo(
-        elf_path, tool_prefix)
-    adjust = offset - address
-    ret = []
-    with open(elf_path, 'rb') as f:
-      for symbol in thing:
-        if symbol.section != 'r' or (
-            not all_rodata and not symbol.IsStringLiteral()):
-          continue
-        f.seek(symbol.address + adjust)
-        data = f.read(symbol.size_without_padding)
-        # As of Oct 2017, there are ~90 symbols name .L.str(.##). These appear
-        # in the linker map file explicitly, and there doesn't seem to be a
-        # pattern as to which variables lose their kConstant name (the more
-        # common case), or which string literals don't get moved to
-        # ** merge strings (less common).
-        if symbol.IsStringLiteral() or (
-            all_rodata and data and data[-1] == '\0'):
-          ret.append((symbol, data))
-    return ret
+    return string_extract.ReadStringLiterals(
+        thing, elf_path, tool_prefix, all_rodata=all_rodata)
 
   def _DiffFunc(self, before=None, after=None, sort=True):
     """Diffs two SizeInfo objects. Returns a DeltaSizeInfo.
diff --git a/tools/binary_size/libsupersize/models.py b/tools/binary_size/libsupersize/models.py
index 84c35d2f..6ad2d5a 100644
--- a/tools/binary_size/libsupersize/models.py
+++ b/tools/binary_size/libsupersize/models.py
@@ -168,7 +168,6 @@
 
 STRING_LITERAL_NAME = 'string literal'
 
-
 class BaseSizeInfo(object):
   """Base class for SizeInfo and DeltaSizeInfo.
 
@@ -359,7 +358,10 @@
         self.name.endswith(']') and not self.name.endswith('[]'))
 
   def IsStringLiteral(self):
-    return self.full_name == STRING_LITERAL_NAME
+    # String literals have names like "string" or "very_long_str[...]", while
+    # non-ASCII strings are named STRING_LITERAL_NAME.
+    return self.full_name.startswith(
+        '"') or self.full_name == STRING_LITERAL_NAME
 
   # Used for diffs to know whether or not it is accurate to consider two symbols
   # with the same name as being the same.
diff --git a/tools/binary_size/libsupersize/string_extract.py b/tools/binary_size/libsupersize/string_extract.py
index 52e5cb98..79fd1be 100644
--- a/tools/binary_size/libsupersize/string_extract.py
+++ b/tools/binary_size/libsupersize/string_extract.py
@@ -10,6 +10,9 @@
 ReadFileChunks():
   Reads raw data from a file, given a list of ranges in the file.
 
+ReadStringLiterals():
+  Reads the ELF file to find the string contents of a list of string literals.
+
 ResolveStringPiecesIndirect():
   BulkForkAndCall() target: Given {path: [string addresses]} and
   [raw_string_data for each string_section]:
@@ -290,3 +293,29 @@
 
   ret = _AnnotateStringData(string_data, GeneratePathAndValues())
   return [concurrent.EncodeDictOfLists(x) for x in ret]
+
+
+def ReadStringLiterals(symbols, elf_path, tool_prefix, all_rodata=False):
+  """Returns an iterable of (symbol, string) for all string literal symbols.
+
+  Args:
+    symbols: An iterable of Symbols
+    elf_path: Path to the executable containing the symbols.
+    all_rodata: Assume every symbol within .rodata that ends with a \0 is a
+         string literal.
+  """
+  address, offset, _ = LookupElfRodataInfo(elf_path, tool_prefix)
+  adjust = offset - address
+  with open(elf_path, 'rb') as f:
+    for symbol in symbols:
+      if symbol.section != 'r':
+        continue
+      f.seek(symbol.address + adjust)
+      data = f.read(symbol.size_without_padding)
+      # As of Oct 2017, there are ~90 symbols name .L.str(.##). These appear
+      # in the linker map file explicitly, and there doesn't seem to be a
+      # pattern as to which variables lose their kConstant name (the more
+      # common case), or which string literals don't get moved to
+      # ** merge strings (less common).
+      if symbol.IsStringLiteral() or (all_rodata and data and data[-1] == '\0'):
+        yield ((symbol, data))
diff --git a/tools/binary_size/libsupersize/testdata/Archive_Apk.golden b/tools/binary_size/libsupersize/testdata/Archive_Apk.golden
index e173eb9e..4335852 100644
--- a/tools/binary_size/libsupersize/testdata/Archive_Apk.golden
+++ b/tools/binary_size/libsupersize/testdata/Archive_Apk.golden
@@ -288,9 +288,9 @@
 .other@0(size_without_padding=1024,padding=0,full_name=res/drawable-v13/test.xml,object_path=,source_path=chrome/android/res/drawable/test.xml,flags={},num_aliases=1,component=)
 .other@0(size_without_padding=0,padding=764,full_name=Overhead: APK file,object_path=,source_path=,flags={},num_aliases=1,component=)
 .other@0(size_without_padding=0,padding=33984171,full_name=Overhead: ELF file,object_path=,source_path=,flags={},num_aliases=1,component=)
-.rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2,component=Blink>Internal)
-.rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=2,component=Internal>Android)
-.rodata@266e605(size_without_padding=16,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android)
+.rodata@266e600(size_without_padding=5,padding=0,full_name="Str1",object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2,component=Blink>Internal)
+.rodata@266e600(size_without_padding=5,padding=0,full_name="Str1",object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=2,component=Internal>Android)
+.rodata@266e605(size_without_padding=16,padding=0,full_name="String literal2",object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android)
 .rodata@266e630(size_without_padding=16,padding=27,full_name=** merge strings,object_path=,source_path=,flags={},num_aliases=1,component=)
 .rodata@284d600(size_without_padding=3425,padding=1961920,full_name=** merge constants,object_path=,source_path=,flags={},num_aliases=1,component=)
 .rodata@284e364(size_without_padding=0,padding=3,full_name=** symbol gap 0,object_path=,source_path=,flags={},num_aliases=1,component=)
diff --git a/tools/binary_size/libsupersize/testdata/Archive_Elf.golden b/tools/binary_size/libsupersize/testdata/Archive_Elf.golden
index e87d5712..71c8468 100644
--- a/tools/binary_size/libsupersize/testdata/Archive_Elf.golden
+++ b/tools/binary_size/libsupersize/testdata/Archive_Elf.golden
@@ -85,9 +85,9 @@
 .data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=)
 .data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=)
 .other@0(size_without_padding=0,padding=33984171,full_name=Overhead: ELF file,object_path=,source_path=,flags={},num_aliases=1,component=)
-.rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2,component=Blink>Internal)
-.rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=2,component=Internal>Android)
-.rodata@266e605(size_without_padding=16,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android)
+.rodata@266e600(size_without_padding=5,padding=0,full_name="Str1",object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2,component=Blink>Internal)
+.rodata@266e600(size_without_padding=5,padding=0,full_name="Str1",object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=2,component=Internal>Android)
+.rodata@266e605(size_without_padding=16,padding=0,full_name="String literal2",object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android)
 .rodata@266e630(size_without_padding=16,padding=27,full_name=** merge strings,object_path=,source_path=,flags={},num_aliases=1,component=)
 .rodata@284d600(size_without_padding=3425,padding=1961920,full_name=** merge constants,object_path=,source_path=,flags={},num_aliases=1,component=)
 .rodata@284e364(size_without_padding=0,padding=3,full_name=** symbol gap 0,object_path=,source_path=,flags={},num_aliases=1,component=)
diff --git a/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden b/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden
index 01c78def..ffc0afab 100644
--- a/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden
+++ b/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden
@@ -289,9 +289,9 @@
 .other@0(size_without_padding=1024,padding=0,full_name=res/drawable-v13/test.xml,object_path=,source_path=chrome/android/res/drawable/test.xml,flags={},num_aliases=1,component=)
 .other@0(size_without_padding=0,padding=764,full_name=Overhead: APK file,object_path=,source_path=,flags={},num_aliases=1,component=)
 .other@0(size_without_padding=0,padding=33984171,full_name=Overhead: ELF file,object_path=,source_path=,flags={},num_aliases=1,component=)
-.rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2,component=Blink>Internal)
-.rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=2,component=Internal>Android)
-.rodata@266e605(size_without_padding=16,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android)
+.rodata@266e600(size_without_padding=5,padding=0,full_name="Str1",object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2,component=Blink>Internal)
+.rodata@266e600(size_without_padding=5,padding=0,full_name="Str1",object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=2,component=Internal>Android)
+.rodata@266e605(size_without_padding=16,padding=0,full_name="String literal2",object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android)
 .rodata@266e630(size_without_padding=16,padding=27,full_name=** merge strings,object_path=,source_path=,flags={},num_aliases=1,component=)
 .rodata@284d600(size_without_padding=3425,padding=1961920,full_name=** merge constants,object_path=,source_path=,flags={},num_aliases=1,component=)
 .rodata@284e364(size_without_padding=0,padding=3,full_name=** symbol gap 0,object_path=,source_path=,flags={},num_aliases=1,component=)
diff --git a/tools/binary_size/libsupersize/testdata/Archive_Pak_Files.golden b/tools/binary_size/libsupersize/testdata/Archive_Pak_Files.golden
index 13c0f0ec..ce5b255 100644
--- a/tools/binary_size/libsupersize/testdata/Archive_Pak_Files.golden
+++ b/tools/binary_size/libsupersize/testdata/Archive_Pak_Files.golden
@@ -87,9 +87,9 @@
 .data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=)
 .data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=)
 .other@0(size_without_padding=0,padding=33984171,full_name=Overhead: ELF file,object_path=,source_path=,flags={},num_aliases=1,component=)
-.rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2,component=Blink>Internal)
-.rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=2,component=Internal>Android)
-.rodata@266e605(size_without_padding=16,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android)
+.rodata@266e600(size_without_padding=5,padding=0,full_name="Str1",object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2,component=Blink>Internal)
+.rodata@266e600(size_without_padding=5,padding=0,full_name="Str1",object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=2,component=Internal>Android)
+.rodata@266e605(size_without_padding=16,padding=0,full_name="String literal2",object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android)
 .rodata@266e630(size_without_padding=16,padding=27,full_name=** merge strings,object_path=,source_path=,flags={},num_aliases=1,component=)
 .rodata@284d600(size_without_padding=3425,padding=1961920,full_name=** merge constants,object_path=,source_path=,flags={},num_aliases=1,component=)
 .rodata@284e364(size_without_padding=0,padding=3,full_name=** symbol gap 0,object_path=,source_path=,flags={},num_aliases=1,component=)
diff --git a/tools/binary_size/libsupersize/testdata/Console.golden b/tools/binary_size/libsupersize/testdata/Console.golden
index 24494bf..5d58f265 100644
--- a/tools/binary_size/libsupersize/testdata/Console.golden
+++ b/tools/binary_size/libsupersize/testdata/Console.golden
@@ -60,8 +60,8 @@
 
 # For even more inspiration, look at canned_queries.py
 # (and feel free to add your own!).
-0: (.rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2,component=Blink>Internal), 'Str1\x00')
-1: (.rodata@266e605(size_without_padding=16,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android), 'String literal2\x00')
+0: (.rodata@266e600(size_without_padding=5,padding=0,full_name="Str1",object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2,component=Blink>Internal), 'Str1\x00')
+1: (.rodata@266e605(size_without_padding=16,padding=0,full_name="String literal2",object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android), 'String literal2\x00')
 Metadata:
     elf_arch=arm
     elf_build_id=WhatAnAmazingBuildId
@@ -126,11 +126,11 @@
 13)  34774455 (92.7%) o@0x0        33984171       {no path}
              Overhead: ELF file
 14)  34774457 (92.7%) r@0x266e600  2.5 (size=5)   base/page_allocator.cc
-             string literal (num_aliases=2)
+             "Str1" (num_aliases=2)
 15)  34774460 (92.7%) r@0x266e600  2.5 (size=5)   $root_gen_dir/third_party/icu/ucnv_ext.c
-             string literal (num_aliases=2)
+             "Str1" (num_aliases=2)
 16)  34774476 (92.7%) r@0x266e605  16             $root_gen_dir/third_party/icu/ucnv_ext.c
-             string literal
+             "String literal2"
 17)  34774519 (92.7%) r@0x266e630  43             {no path}
              ** merge strings
 18)  36739864 (98.0%) r@0x284d600  1965345        {no path}
diff --git a/tools/binary_size/libsupersize/testdata/Csv.golden b/tools/binary_size/libsupersize/testdata/Csv.golden
index ecd143c..07f40e9 100644
--- a/tools/binary_size/libsupersize/testdata/Csv.golden
+++ b/tools/binary_size/libsupersize/testdata/Csv.golden
@@ -25,9 +25,9 @@
 ,0x2cd84e0,16,16,1,32.0,R,.Lswitch.table.45
 ,0x2cd84f0,8,0,1,8.0,R,kSystemClassPrefixes
 ,0x0,0,33984171,1,33984171.0,o,Overhead: ELF file
-,0x266e600,5,0,2,2.5,r,string literal
-,0x266e600,5,0,2,2.5,r,string literal
-,0x266e605,16,0,1,16.0,r,string literal
+,0x266e600,5,0,2,2.5,r,"""Str1"""
+,0x266e600,5,0,2,2.5,r,"""Str1"""
+,0x266e605,16,0,1,16.0,r,"""String literal2"""
 ,0x266e630,16,27,1,43.0,r,** merge strings
 ,0x284d600,3425,1961920,1,1965345.0,r,** merge constants
 ,0x284e364,0,3,1,3.0,r,** symbol gap 0
diff --git a/tools/binary_size/libsupersize/testdata/FullDescription.golden b/tools/binary_size/libsupersize/testdata/FullDescription.golden
index 3cc6349..14b93d1b 100644
--- a/tools/binary_size/libsupersize/testdata/FullDescription.golden
+++ b/tools/binary_size/libsupersize/testdata/FullDescription.golden
@@ -163,13 +163,13 @@
              flags={}  name=Overhead: ELF file
 14)  34774457 (92.7%) r@0x266e600  pss=2.5 (size=5)  padding=0  num_aliases=2
              source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
-             flags={}  name=string literal
+             flags={}  name="Str1"
 15)  34774460 (92.7%) r@0x266e600  pss=2.5 (size=5)  padding=0  num_aliases=2
              source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
-             flags={gen}  name=string literal
+             flags={gen}  name="Str1"
 16)  34774476 (92.7%) r@0x266e605  pss=16  padding=0  num_aliases=1
              source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
-             flags={gen}  name=string literal
+             flags={gen}  name="String literal2"
 17)  34774519 (92.7%) r@0x266e630  pss=43  padding=27  num_aliases=1
              source_path= 	object_path=
              flags={}  name=** merge strings
@@ -344,13 +344,13 @@
              flags={}  name=Overhead: ELF file
 14)  34774457 (92.7%) r@0x266e600  pss=2.5 (size=5)  padding=0  num_aliases=2
              source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
-             flags={}  name=string literal
+             flags={}  name="Str1"
 15)  34774460 (92.7%) r@0x266e600  pss=2.5 (size=5)  padding=0  num_aliases=2
              source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
-             flags={gen}  name=string literal
+             flags={gen}  name="Str1"
 16)  34774476 (92.7%) r@0x266e605  pss=16  padding=0  num_aliases=1
              source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
-             flags={gen}  name=string literal
+             flags={gen}  name="String literal2"
 17)  34774519 (92.7%) r@0x266e630  pss=43  padding=27  num_aliases=1
              source_path= 	object_path=
              flags={}  name=** merge strings
diff --git a/tools/binary_size/libsupersize/testdata/SymbolGroupMethods.golden b/tools/binary_size/libsupersize/testdata/SymbolGroupMethods.golden
index d3042b2..ae370478 100644
--- a/tools/binary_size/libsupersize/testdata/SymbolGroupMethods.golden
+++ b/tools/binary_size/libsupersize/testdata/SymbolGroupMethods.golden
@@ -1,9 +1,9 @@
 GroupedByName()
-Showing 46 symbols (46 unique) with total pss: 37499787 bytes
+Showing 47 symbols (47 unique) with total pss: 37499787 bytes
 Histogram of symbols based on PSS:
       {0}: 6    [8,16): 3     [64,128): 2      [2048,4096): 1      [524288,1048576): 2
     [2,4): 3   [16,32): 11   [128,256): 1     [8192,16384): 1     [1048576,2097152): 1
-    [4,8): 5   [32,64): 7    [256,512): 1   [65536,131072): 1   [33554432,67108864): 1
+    [4,8): 6   [32,64): 7    [256,512): 1   [65536,131072): 1   [33554432,67108864): 1
 Sizes: .text=81.8kb     .rodata=2.52mb     .data.rel.ro=92 bytes   .data=168 bytes  .bss=512kb      .other=32.4mb     total=35.8mb
 Counts: .text=21 .rodata=11 .data.rel.ro=3 .data=5 .bss=6 .other=1
 Number of unique paths: 9
@@ -25,44 +25,45 @@
 11)    790276 (2.1%)  *@Group      32              .Lswitch.table.45 (count=1)
 12)    790284 (2.1%)  *@Group      8               kSystemClassPrefixes (count=1)
 13)  34774455 (92.7%) *@Group      33984171        Overhead: ELF file (count=1)
-14)  34774476 (92.7%) *@Group      21              string literal (count=3)
-15)  34774519 (92.7%) *@Group      43              ** merge strings (count=1)
-16)  36739864 (98.0%) *@Group      1965345         ** merge constants (count=1)
-17)  36739867 (98.0%) *@Group      3               ** symbol gap 0 (count=1)
-18)  36739875 (98.0%) *@Group      8                (count=1)
-19)  36739919 (98.0%) *@Group      44              Name (count=1)
-20)  36739951 (98.0%) *@Group      32              chrome::mojom::FilePatcher::Name_ (count=1)
-21)  37415991 (99.8%) *@Group      676040          kAnimationFrameTimeHistogramClassPath (count=1)
-22)  37415995 (99.8%) *@Group      4               blink::CSSValueKeywordsHash::findValueImpl::value_word_list (count=1)
-23)  37416011 (99.8%) *@Group      16              _GLOBAL__sub_I_page_allocator.cc (count=1)
-24)  37416067 (99.8%) *@Group      56              _GLOBAL__sub_I_bbr_sender.cc (count=1)
-25)  37416095 (99.8%) *@Group      28              _GLOBAL__sub_I_pacing_sender.cc (count=1)
-26)  37416165 (99.8%) *@Group      70              extFromUUseMapping (count=2)
-27)  37425923 (99.8%) *@Group      9758            ** symbol gaps (count=1)
-28)  37426371 (99.8%) *@Group      448             ucnv_extMatchFromU (count=1)
-29)  37426399 (99.8%) *@Group      28              _GLOBAL__sub_I_SkDeviceProfile.cpp (count=1)
-30)  37495523 (100.0%) *@Group      69124           foo_bar (count=1)
-31)  37495547 (100.0%) *@Group      24              BazAlias (count=1)
-32)  37495577 (100.0%) *@Group      30              blink::ContiguousContainerBase::shrinkToFit (count=3)
-33)  37495580 (100.0%) *@Group      3               BarAlias (count=1)
-34)  37495583 (100.0%) *@Group      3               FooAlias (count=1)
-35)  37495611 (100.0%) *@Group      28              blink::ContiguousContainerBase::ContiguousContainerBase (count=1)
-36)  37495705 (100.0%) *@Group      94              blink::PaintChunker::releasePaintChunks (count=1)
-37)  37499739 (100.0%) *@Group      4034            ** outlined function (count=1)
-38)  37499763 (100.0%) *@Group      24              ** outlined function * 2 (count=1)
-39)  37499787 (100.0%) *@Group      24              aliasedWithOutlinedFunction (count=1)
-40)  37499787 (100.0%) *@Group      0               ff_cos_131072 (count=1)
-41)  37499787 (100.0%) *@Group      0               ff_cos_131072_fixed (count=1)
-42)  37499787 (100.0%) *@Group      0               ff_cos_65536 (count=1)
-43)  37499787 (100.0%) *@Group      0               g_chrome_content_browser_client (count=1)
-44)  37499787 (100.0%) *@Group      0               SaveHistogram::atomic_histogram_pointer (count=1)
-45)  37499787 (100.0%) *@Group      0               g_AnimationFrameTimeHistogram_clazz (count=1)
+14)  34774460 (92.7%) *@Group      5               "Str1" (count=2)
+15)  34774476 (92.7%) *@Group      16              "String literal2" (count=1)
+16)  34774519 (92.7%) *@Group      43              ** merge strings (count=1)
+17)  36739864 (98.0%) *@Group      1965345         ** merge constants (count=1)
+18)  36739867 (98.0%) *@Group      3               ** symbol gap 0 (count=1)
+19)  36739875 (98.0%) *@Group      8                (count=1)
+20)  36739919 (98.0%) *@Group      44              Name (count=1)
+21)  36739951 (98.0%) *@Group      32              chrome::mojom::FilePatcher::Name_ (count=1)
+22)  37415991 (99.8%) *@Group      676040          kAnimationFrameTimeHistogramClassPath (count=1)
+23)  37415995 (99.8%) *@Group      4               blink::CSSValueKeywordsHash::findValueImpl::value_word_list (count=1)
+24)  37416011 (99.8%) *@Group      16              _GLOBAL__sub_I_page_allocator.cc (count=1)
+25)  37416067 (99.8%) *@Group      56              _GLOBAL__sub_I_bbr_sender.cc (count=1)
+26)  37416095 (99.8%) *@Group      28              _GLOBAL__sub_I_pacing_sender.cc (count=1)
+27)  37416165 (99.8%) *@Group      70              extFromUUseMapping (count=2)
+28)  37425923 (99.8%) *@Group      9758            ** symbol gaps (count=1)
+29)  37426371 (99.8%) *@Group      448             ucnv_extMatchFromU (count=1)
+30)  37426399 (99.8%) *@Group      28              _GLOBAL__sub_I_SkDeviceProfile.cpp (count=1)
+31)  37495523 (100.0%) *@Group      69124           foo_bar (count=1)
+32)  37495547 (100.0%) *@Group      24              BazAlias (count=1)
+33)  37495577 (100.0%) *@Group      30              blink::ContiguousContainerBase::shrinkToFit (count=3)
+34)  37495580 (100.0%) *@Group      3               BarAlias (count=1)
+35)  37495583 (100.0%) *@Group      3               FooAlias (count=1)
+36)  37495611 (100.0%) *@Group      28              blink::ContiguousContainerBase::ContiguousContainerBase (count=1)
+37)  37495705 (100.0%) *@Group      94              blink::PaintChunker::releasePaintChunks (count=1)
+38)  37499739 (100.0%) *@Group      4034            ** outlined function (count=1)
+39)  37499763 (100.0%) *@Group      24              ** outlined function * 2 (count=1)
+40)  37499787 (100.0%) *@Group      24              aliasedWithOutlinedFunction (count=1)
+41)  37499787 (100.0%) *@Group      0               ff_cos_131072 (count=1)
+42)  37499787 (100.0%) *@Group      0               ff_cos_131072_fixed (count=1)
+43)  37499787 (100.0%) *@Group      0               ff_cos_65536 (count=1)
+44)  37499787 (100.0%) *@Group      0               g_chrome_content_browser_client (count=1)
+45)  37499787 (100.0%) *@Group      0               SaveHistogram::atomic_histogram_pointer (count=1)
+46)  37499787 (100.0%) *@Group      0               g_AnimationFrameTimeHistogram_clazz (count=1)
 GroupedByName(depth=1)
-Showing 38 symbols (38 unique) with total pss: 37499787 bytes
+Showing 39 symbols (39 unique) with total pss: 37499787 bytes
 Histogram of symbols based on PSS:
-       {0}: 6    [16,32): 8     [128,256): 2       [8192,16384): 1     [1048576,2097152): 1
-     [2,4): 3    [32,64): 6     [256,512): 1     [65536,131072): 1   [33554432,67108864): 1
-    [8,16): 4   [64,128): 1   [2048,4096): 1   [524288,1048576): 2
+      {0}: 6    [8,16): 4    [64,128): 1      [2048,4096): 1      [524288,1048576): 2
+    [2,4): 3   [16,32): 8   [128,256): 2     [8192,16384): 1     [1048576,2097152): 1
+    [4,8): 1   [32,64): 6   [256,512): 1   [65536,131072): 1   [33554432,67108864): 1
 Sizes: .text=81.8kb     .rodata=2.52mb     .data.rel.ro=92 bytes   .data=168 bytes  .bss=512kb      .other=32.4mb     total=35.8mb
 Counts: .text=21 .rodata=11 .data.rel.ro=3 .data=5 .bss=6 .other=1
 Number of unique paths: 9
@@ -80,40 +81,41 @@
 7)     790308 (2.1%)  *@Group      32              .Lswitch.table.45 (count=1)
 8)     790316 (2.1%)  *@Group      8               kSystemClassPrefixes (count=1)
 9)   34774487 (92.7%) *@Group      33984171        Overhead: ELF file (count=1)
-10)  34774508 (92.7%) *@Group      21              string literal (count=3)
-11)  34774551 (92.7%) *@Group      43              ** merge strings (count=1)
-12)  36739896 (98.0%) *@Group      1965345         ** merge constants (count=1)
-13)  36739899 (98.0%) *@Group      3               ** symbol gap 0 (count=1)
-14)  36739907 (98.0%) *@Group      8                (count=1)
-15)  36739951 (98.0%) *@Group      44              Name (count=1)
-16)  37415991 (99.8%) *@Group      676040          kAnimationFrameTimeHistogramClassPath (count=1)
-17)  37416147 (99.8%) *@Group      156             blink (count=6)
-18)  37416163 (99.8%) *@Group      16              _GLOBAL__sub_I_page_allocator.cc (count=1)
-19)  37416219 (99.8%) *@Group      56              _GLOBAL__sub_I_bbr_sender.cc (count=1)
-20)  37416247 (99.8%) *@Group      28              _GLOBAL__sub_I_pacing_sender.cc (count=1)
-21)  37416317 (99.8%) *@Group      70              extFromUUseMapping (count=2)
-22)  37426075 (99.8%) *@Group      9758            ** symbol gaps (count=1)
-23)  37426523 (99.8%) *@Group      448             ucnv_extMatchFromU (count=1)
-24)  37426551 (99.8%) *@Group      28              _GLOBAL__sub_I_SkDeviceProfile.cpp (count=1)
-25)  37495675 (100.0%) *@Group      69124           foo_bar (count=1)
-26)  37495699 (100.0%) *@Group      24              BazAlias (count=1)
-27)  37495702 (100.0%) *@Group      3               BarAlias (count=1)
-28)  37495705 (100.0%) *@Group      3               FooAlias (count=1)
-29)  37499739 (100.0%) *@Group      4034            ** outlined function (count=1)
-30)  37499763 (100.0%) *@Group      24              ** outlined function * 2 (count=1)
-31)  37499787 (100.0%) *@Group      24              aliasedWithOutlinedFunction (count=1)
-32)  37499787 (100.0%) *@Group      0               ff_cos_131072 (count=1)
-33)  37499787 (100.0%) *@Group      0               ff_cos_131072_fixed (count=1)
-34)  37499787 (100.0%) *@Group      0               ff_cos_65536 (count=1)
-35)  37499787 (100.0%) *@Group      0               g_chrome_content_browser_client (count=1)
-36)  37499787 (100.0%) *@Group      0               SaveHistogram (count=1)
-37)  37499787 (100.0%) *@Group      0               g_AnimationFrameTimeHistogram_clazz (count=1)
+10)  34774492 (92.7%) *@Group      5               "Str1" (count=2)
+11)  34774508 (92.7%) *@Group      16              "String literal2" (count=1)
+12)  34774551 (92.7%) *@Group      43              ** merge strings (count=1)
+13)  36739896 (98.0%) *@Group      1965345         ** merge constants (count=1)
+14)  36739899 (98.0%) *@Group      3               ** symbol gap 0 (count=1)
+15)  36739907 (98.0%) *@Group      8                (count=1)
+16)  36739951 (98.0%) *@Group      44              Name (count=1)
+17)  37415991 (99.8%) *@Group      676040          kAnimationFrameTimeHistogramClassPath (count=1)
+18)  37416147 (99.8%) *@Group      156             blink (count=6)
+19)  37416163 (99.8%) *@Group      16              _GLOBAL__sub_I_page_allocator.cc (count=1)
+20)  37416219 (99.8%) *@Group      56              _GLOBAL__sub_I_bbr_sender.cc (count=1)
+21)  37416247 (99.8%) *@Group      28              _GLOBAL__sub_I_pacing_sender.cc (count=1)
+22)  37416317 (99.8%) *@Group      70              extFromUUseMapping (count=2)
+23)  37426075 (99.8%) *@Group      9758            ** symbol gaps (count=1)
+24)  37426523 (99.8%) *@Group      448             ucnv_extMatchFromU (count=1)
+25)  37426551 (99.8%) *@Group      28              _GLOBAL__sub_I_SkDeviceProfile.cpp (count=1)
+26)  37495675 (100.0%) *@Group      69124           foo_bar (count=1)
+27)  37495699 (100.0%) *@Group      24              BazAlias (count=1)
+28)  37495702 (100.0%) *@Group      3               BarAlias (count=1)
+29)  37495705 (100.0%) *@Group      3               FooAlias (count=1)
+30)  37499739 (100.0%) *@Group      4034            ** outlined function (count=1)
+31)  37499763 (100.0%) *@Group      24              ** outlined function * 2 (count=1)
+32)  37499787 (100.0%) *@Group      24              aliasedWithOutlinedFunction (count=1)
+33)  37499787 (100.0%) *@Group      0               ff_cos_131072 (count=1)
+34)  37499787 (100.0%) *@Group      0               ff_cos_131072_fixed (count=1)
+35)  37499787 (100.0%) *@Group      0               ff_cos_65536 (count=1)
+36)  37499787 (100.0%) *@Group      0               g_chrome_content_browser_client (count=1)
+37)  37499787 (100.0%) *@Group      0               SaveHistogram (count=1)
+38)  37499787 (100.0%) *@Group      0               g_AnimationFrameTimeHistogram_clazz (count=1)
 GroupedByName(depth=-1)
-Showing 41 symbols (41 unique) with total pss: 37499787 bytes
+Showing 42 symbols (42 unique) with total pss: 37499787 bytes
 Histogram of symbols based on PSS:
       {0}: 6    [8,16): 4    [64,128): 2      [2048,4096): 1      [524288,1048576): 2
     [2,4): 3   [16,32): 8   [128,256): 1     [8192,16384): 1     [1048576,2097152): 1
-    [4,8): 1   [32,64): 8   [256,512): 1   [65536,131072): 1   [33554432,67108864): 1
+    [4,8): 2   [32,64): 8   [256,512): 1   [65536,131072): 1   [33554432,67108864): 1
 Sizes: .text=81.8kb     .rodata=2.52mb     .data.rel.ro=92 bytes   .data=168 bytes  .bss=512kb      .other=32.4mb     total=35.8mb
 Counts: .text=21 .rodata=11 .data.rel.ro=3 .data=5 .bss=6 .other=1
 Number of unique paths: 9
@@ -131,42 +133,43 @@
 7)     790276 (2.1%)  *@Group      32              .Lswitch.table.45 (count=1)
 8)     790284 (2.1%)  *@Group      8               kSystemClassPrefixes (count=1)
 9)   34774455 (92.7%) *@Group      33984171        Overhead: ELF file (count=1)
-10)  34774476 (92.7%) *@Group      21              string literal (count=3)
-11)  34774519 (92.7%) *@Group      43              ** merge strings (count=1)
-12)  36739864 (98.0%) *@Group      1965345         ** merge constants (count=1)
-13)  36739867 (98.0%) *@Group      3               ** symbol gap 0 (count=1)
-14)  36739875 (98.0%) *@Group      8                (count=1)
-15)  36739919 (98.0%) *@Group      44              Name (count=1)
-16)  36739951 (98.0%) *@Group      32              chrome::mojom::FilePatcher (count=1)
-17)  37415991 (99.8%) *@Group      676040          kAnimationFrameTimeHistogramClassPath (count=1)
-18)  37415995 (99.8%) *@Group      4               blink::CSSValueKeywordsHash::findValueImpl (count=1)
-19)  37416011 (99.8%) *@Group      16              _GLOBAL__sub_I_page_allocator.cc (count=1)
-20)  37416067 (99.8%) *@Group      56              _GLOBAL__sub_I_bbr_sender.cc (count=1)
-21)  37416095 (99.8%) *@Group      28              _GLOBAL__sub_I_pacing_sender.cc (count=1)
-22)  37416165 (99.8%) *@Group      70              extFromUUseMapping (count=2)
-23)  37425923 (99.8%) *@Group      9758            ** symbol gaps (count=1)
-24)  37426371 (99.8%) *@Group      448             ucnv_extMatchFromU (count=1)
-25)  37426399 (99.8%) *@Group      28              _GLOBAL__sub_I_SkDeviceProfile.cpp (count=1)
-26)  37495523 (100.0%) *@Group      69124           foo_bar (count=1)
-27)  37495547 (100.0%) *@Group      24              BazAlias (count=1)
-28)  37495605 (100.0%) *@Group      58              blink::ContiguousContainerBase (count=4)
-29)  37495608 (100.0%) *@Group      3               BarAlias (count=1)
-30)  37495611 (100.0%) *@Group      3               FooAlias (count=1)
-31)  37495705 (100.0%) *@Group      94              blink::PaintChunker (count=1)
-32)  37499739 (100.0%) *@Group      4034            ** outlined function (count=1)
-33)  37499763 (100.0%) *@Group      24              ** outlined function * 2 (count=1)
-34)  37499787 (100.0%) *@Group      24              aliasedWithOutlinedFunction (count=1)
-35)  37499787 (100.0%) *@Group      0               ff_cos_131072 (count=1)
-36)  37499787 (100.0%) *@Group      0               ff_cos_131072_fixed (count=1)
-37)  37499787 (100.0%) *@Group      0               ff_cos_65536 (count=1)
-38)  37499787 (100.0%) *@Group      0               g_chrome_content_browser_client (count=1)
-39)  37499787 (100.0%) *@Group      0               SaveHistogram (count=1)
-40)  37499787 (100.0%) *@Group      0               g_AnimationFrameTimeHistogram_clazz (count=1)
+10)  34774460 (92.7%) *@Group      5               "Str1" (count=2)
+11)  34774476 (92.7%) *@Group      16              "String literal2" (count=1)
+12)  34774519 (92.7%) *@Group      43              ** merge strings (count=1)
+13)  36739864 (98.0%) *@Group      1965345         ** merge constants (count=1)
+14)  36739867 (98.0%) *@Group      3               ** symbol gap 0 (count=1)
+15)  36739875 (98.0%) *@Group      8                (count=1)
+16)  36739919 (98.0%) *@Group      44              Name (count=1)
+17)  36739951 (98.0%) *@Group      32              chrome::mojom::FilePatcher (count=1)
+18)  37415991 (99.8%) *@Group      676040          kAnimationFrameTimeHistogramClassPath (count=1)
+19)  37415995 (99.8%) *@Group      4               blink::CSSValueKeywordsHash::findValueImpl (count=1)
+20)  37416011 (99.8%) *@Group      16              _GLOBAL__sub_I_page_allocator.cc (count=1)
+21)  37416067 (99.8%) *@Group      56              _GLOBAL__sub_I_bbr_sender.cc (count=1)
+22)  37416095 (99.8%) *@Group      28              _GLOBAL__sub_I_pacing_sender.cc (count=1)
+23)  37416165 (99.8%) *@Group      70              extFromUUseMapping (count=2)
+24)  37425923 (99.8%) *@Group      9758            ** symbol gaps (count=1)
+25)  37426371 (99.8%) *@Group      448             ucnv_extMatchFromU (count=1)
+26)  37426399 (99.8%) *@Group      28              _GLOBAL__sub_I_SkDeviceProfile.cpp (count=1)
+27)  37495523 (100.0%) *@Group      69124           foo_bar (count=1)
+28)  37495547 (100.0%) *@Group      24              BazAlias (count=1)
+29)  37495605 (100.0%) *@Group      58              blink::ContiguousContainerBase (count=4)
+30)  37495608 (100.0%) *@Group      3               BarAlias (count=1)
+31)  37495611 (100.0%) *@Group      3               FooAlias (count=1)
+32)  37495705 (100.0%) *@Group      94              blink::PaintChunker (count=1)
+33)  37499739 (100.0%) *@Group      4034            ** outlined function (count=1)
+34)  37499763 (100.0%) *@Group      24              ** outlined function * 2 (count=1)
+35)  37499787 (100.0%) *@Group      24              aliasedWithOutlinedFunction (count=1)
+36)  37499787 (100.0%) *@Group      0               ff_cos_131072 (count=1)
+37)  37499787 (100.0%) *@Group      0               ff_cos_131072_fixed (count=1)
+38)  37499787 (100.0%) *@Group      0               ff_cos_65536 (count=1)
+39)  37499787 (100.0%) *@Group      0               g_chrome_content_browser_client (count=1)
+40)  37499787 (100.0%) *@Group      0               SaveHistogram (count=1)
+41)  37499787 (100.0%) *@Group      0               g_AnimationFrameTimeHistogram_clazz (count=1)
 GroupedByName(depth=1, min_count=2)
-Showing 38 symbols (36 unique) with total pss: 37499787 bytes
+Showing 39 symbols (37 unique) with total pss: 37499787 bytes
 Histogram of symbols based on PSS:
      [2,4): 3    [16,32): 9     [128,256): 3      [8192,16384): 1     [262144,524288): 1   [33554432,67108864): 1
-     [4,8): 1    [32,64): 6     [256,512): 1    [65536,131072): 1    [524288,1048576): 2
+     [4,8): 2    [32,64): 6     [256,512): 1    [65536,131072): 1    [524288,1048576): 2
     [8,16): 4   [64,128): 1   [2048,4096): 1   [131072,262144): 2   [1048576,2097152): 1
 Sizes: .text=81.8kb     .rodata=2.52mb     .data.rel.ro=92 bytes   .data=168 bytes  .bss=512kb      .other=32.4mb     total=35.8mb
 Counts: .text=21 .rodata=11 .data.rel.ro=3 .data=5 .bss=6 .other=1
@@ -195,58 +198,60 @@
              kSystemClassPrefixes
 9)   34774487 (92.7%) o@0x0        33984171       {no path}
              Overhead: ELF file
-10)  34774508 (92.7%) *@Group      21             {no path}
-             string literal (count=3)
-11)  34774551 (92.7%) r@0x266e630  43             {no path}
+10)  34774492 (92.7%) *@Group      5              {no path}
+             "Str1" (count=2)
+11)  34774508 (92.7%) r@0x266e605  16             $root_gen_dir/third_party/icu/ucnv_ext.c
+             "String literal2"
+12)  34774551 (92.7%) r@0x266e630  43             {no path}
              ** merge strings
-12)  36739896 (98.0%) r@0x284d600  1965345        {no path}
+13)  36739896 (98.0%) r@0x284d600  1965345        {no path}
              ** merge constants
-13)  36739899 (98.0%) r@0x284e364  3              {no path}
+14)  36739899 (98.0%) r@0x284e364  3              {no path}
              ** symbol gap 0
-14)  36739907 (98.0%) r@0x284e364  8              base/page_allocator.cc
-15)  36739951 (98.0%) r@0x284e370  44             base/page_allocator.cc
+15)  36739907 (98.0%) r@0x284e364  8              base/page_allocator.cc
+16)  36739951 (98.0%) r@0x284e370  44             base/page_allocator.cc
              Name
-16)  37415991 (99.8%) r@0x28f3450  676040         third_party/paint.cc
+17)  37415991 (99.8%) r@0x28f3450  676040         third_party/paint.cc
              kAnimationFrameTimeHistogramClassPath
-17)  37416147 (99.8%) *@Group      156            {no path}
+18)  37416147 (99.8%) *@Group      156            {no path}
              blink (count=6)
-18)  37416163 (99.8%) t@0x28d900   16             base/page_allocator.cc
+19)  37416163 (99.8%) t@0x28d900   16             base/page_allocator.cc
              _GLOBAL__sub_I_page_allocator.cc
-19)  37416219 (99.8%) t@0x28d910   56             base/page_allocator.cc
+20)  37416219 (99.8%) t@0x28d910   56             base/page_allocator.cc
              _GLOBAL__sub_I_bbr_sender.cc
-20)  37416247 (99.8%) t@0x28d948   28             base/page_allocator.cc
+21)  37416247 (99.8%) t@0x28d948   28             base/page_allocator.cc
              _GLOBAL__sub_I_pacing_sender.cc
-21)  37416317 (99.8%) *@Group      70             base/page_allocator.cc
+22)  37416317 (99.8%) *@Group      70             base/page_allocator.cc
              extFromUUseMapping (count=2)
-22)  37426075 (99.8%) t@Group      9758           {no path}
+23)  37426075 (99.8%) t@Group      9758           {no path}
              ** symbol gaps (count=2)
-23)  37426523 (99.8%) t@0x28f000   448            $root_gen_dir/third_party/icu/ucnv_ext.c
+24)  37426523 (99.8%) t@0x28f000   448            $root_gen_dir/third_party/icu/ucnv_ext.c
              ucnv_extMatchFromU
-24)  37426551 (99.8%) t@0x28f1c8   28             $root_gen_dir/third_party/icu/ucnv_ext.c
+25)  37426551 (99.8%) t@0x28f1c8   28             $root_gen_dir/third_party/icu/ucnv_ext.c
              _GLOBAL__sub_I_SkDeviceProfile.cpp
-25)  37495675 (100.0%) t@0x28f1e0   69124          $root_gen_dir/third_party/icu/ucnv_ext.c
+26)  37495675 (100.0%) t@0x28f1e0   69124          $root_gen_dir/third_party/icu/ucnv_ext.c
              foo_bar
-26)  37495699 (100.0%) t@0x2a0000   24 (size=48)   $root_gen_dir/third_party/icu/ucnv_ext.c
+27)  37495699 (100.0%) t@0x2a0000   24 (size=48)   $root_gen_dir/third_party/icu/ucnv_ext.c
              BazAlias (num_aliases=2)
-27)  37495702 (100.0%) t@0x2a0010   3 (size=12)    third_party/fft_float.cc
+28)  37495702 (100.0%) t@0x2a0010   3 (size=12)    third_party/fft_float.cc
              BarAlias (num_aliases=4)
-28)  37495705 (100.0%) t@0x2a0010   3 (size=12)    third_party/fft_float.cc
+29)  37495705 (100.0%) t@0x2a0010   3 (size=12)    third_party/fft_float.cc
              FooAlias (num_aliases=4)
-29)  37499739 (100.0%) t@0x2a2000   4034           third_party/container/container.c
+30)  37499739 (100.0%) t@0x2a2000   4034           third_party/container/container.c
              ** outlined function
-30)  37499763 (100.0%) t@0x2a2020   24 (size=48)   {no path}
+31)  37499763 (100.0%) t@0x2a2020   24 (size=48)   {no path}
              ** outlined function * 2 (num_aliases=2)
-31)  37499787 (100.0%) t@0x2a2020   24 (size=48)   {no path}
+32)  37499787 (100.0%) t@0x2a2020   24 (size=48)   {no path}
              aliasedWithOutlinedFunction (num_aliases=2)
-32)  37499787 (100.0%) b@0x0        262144         third_party/fft_float.cc
+33)  37499787 (100.0%) b@0x0        262144         third_party/fft_float.cc
              ff_cos_131072
-33)  37499787 (100.0%) b@0x0        131072         third_party/fft_fixed.cc
+34)  37499787 (100.0%) b@0x0        131072         third_party/fft_fixed.cc
              ff_cos_131072_fixed
-34)  37499787 (100.0%) b@0x0        131072         third_party/fft_float.cc
+35)  37499787 (100.0%) b@0x0        131072         third_party/fft_float.cc
              ff_cos_65536
-35)  37499787 (100.0%) b@0x2dffda0  28             $root_gen_dir/third_party/icu/ucnv_ext.c
+36)  37499787 (100.0%) b@0x2dffda0  28             $root_gen_dir/third_party/icu/ucnv_ext.c
              g_chrome_content_browser_client
-36)  37499787 (100.0%) b@0x2dffe80  200            $root_gen_dir/third_party/icu/ucnv_ext.c
+37)  37499787 (100.0%) b@0x2dffe80  200            $root_gen_dir/third_party/icu/ucnv_ext.c
              SaveHistogram::atomic_histogram_pointer
-37)  37499787 (100.0%) b@0x2dffe84  4              $root_gen_dir/third_party/icu/ucnv_ext.c
+38)  37499787 (100.0%) b@0x2dffe84  4              $root_gen_dir/third_party/icu/ucnv_ext.c
              g_AnimationFrameTimeHistogram_clazz
diff --git a/tools/binary_size/trybot_commit_size_checker.py b/tools/binary_size/trybot_commit_size_checker.py
index a89dea8..0310b5f 100755
--- a/tools/binary_size/trybot_commit_size_checker.py
+++ b/tools/binary_size/trybot_commit_size_checker.py
@@ -275,7 +275,7 @@
           'url': '{{' + _TEXT_FILENAME + '}}',
       },
       {
-          'name': '>>> Supersize HTML Diff <<<',
+          'name': '>>> SuperSize HTML Diff <<<',
           'url': _HTML_REPORT_BASE_URL + '{{' + _NDJSON_FILENAME + '}}',
       },
   ]
@@ -295,7 +295,7 @@
 
   binary_size_extras = [
       {
-          'text': 'Supersize HTML Diff',
+          'text': 'SuperSize HTML Diff',
           'url': _HTML_REPORT_BASE_URL + '{{' + _NDJSON_FILENAME + '}}',
       },
       {
diff --git a/tools/bisect_repackage/bisect_repackage.py b/tools/bisect_repackage/bisect_repackage.py
index 76cd60a..9a8500a 100644
--- a/tools/bisect_repackage/bisect_repackage.py
+++ b/tools/bisect_repackage/bisect_repackage.py
@@ -45,6 +45,7 @@
         'chrome_100_percent.pak',
         'chrome_200_percent.pak',
         'chromedriver',
+        'crashpad_handler',
         'default_apps/',
         'icudtl.dat',
         'ClearKeyCdm/',
@@ -98,6 +99,7 @@
 CHROME_STRIP_LIST = {
     'linux': [
         'chrome',
+        'crashpad_handler',
         'nacl_helper'
     ]
 }
diff --git a/tools/git/OWNERS b/tools/git/OWNERS
index e0511af8..734ea1a 100644
--- a/tools/git/OWNERS
+++ b/tools/git/OWNERS
@@ -1 +1,2 @@
 per-file move_source_file.py=satorux@chromium.org
+per-file suggest_owners.py=mheikal@chromium.org
diff --git a/tools/git/suggest_owners.py b/tools/git/suggest_owners.py
index 9fa3ed88..2ff0886 100755
--- a/tools/git/suggest_owners.py
+++ b/tools/git/suggest_owners.py
@@ -5,6 +5,7 @@
 
 import argparse
 import subprocess
+import pickle
 import os
 from os import path
 from datetime import date, timedelta
@@ -12,166 +13,340 @@
 
 Commit = namedtuple('Commit', ['hash', 'author', 'commit_date', 'dirs'])
 
-# Takes a git command arguments and runs it returning the output (throwing an
-# exception on error).
+# dict mapping each subdirectory and author to the number of their commits and
+# modifications in that directory
+DIRECTORY_AUTHORS = defaultdict(dict)
+
+# cache for directory owners for memoisation purposes
+OWNERS_CACHE = {}
+
+# filename for pickle cache
+CACHE_FILENAME = 'suggest_owners.cache'
+
 def _RunGitCommand(options, cmd_args):
-  repo_path = os.path.join(options.repo_path, '.git')
+  repo_path = path.join(options.repo_path, '.git')
   cmd = ['git', '--git-dir', repo_path] + cmd_args
+  print '>', ' '.join(cmd)
   return subprocess.check_output(cmd)
 
 
-# return true if this author is a chromium dev and is not a bot. Pretty naive,
-# looks for roller in the username.
-def _IsValidAuthor(author):
+def _ValidAuthor(author):
   return author.find('@chromium.org') > -1 and author.find('roller') == -1
 
 
-# Get a list of commits from the repo and return a nested dictionary
-# directory -> author -> num_commits
-def processAllCommits(options):
+# Returns additions/deletions by a commit to a directory (and its descendants).
+def getEditsForDirectory(commit, directory):
+  additions = deletions = 0
+  for commit_directory, (directory_additions, directory_deletions) \
+      in commit.dirs.items():
+    # check if commit_directory is same as or a descendant of directory
+    if isSubDirectory(directory, commit_directory):
+      additions += directory_additions
+      deletions += directory_deletions
+  return additions, deletions
+
+
+# This propagates a commit touching a directory to also be touching all
+# ancesstor directories.
+def _PropagateCommit(options, commit):
+  touched_dirs = set()
+  # first get all the touched dirs and their ancestors
+  for directory in commit.dirs.iterkeys():
+    while directory != '':
+      touched_dirs.add(directory)
+      # get the parent directory
+      directory = path.dirname(directory)
+  # loop over them and calculate the edits per directory
+  for directory in touched_dirs:
+    author_commits, author_additions, author_deletions = \
+        DIRECTORY_AUTHORS[directory].get(commit.author, (0,0,0))
+    directory_additions, directory_deletions = \
+        getEditsForDirectory(commit, directory)
+    DIRECTORY_AUTHORS[directory][commit.author] = \
+        (author_commits + 1, author_additions + directory_additions,
+         author_deletions + directory_deletions)
+
+
+# Checks if child_directory is same as or below parent_directory. For some
+# reason the os.path module does not have this functionality.
+def isSubDirectory(parent_directory, child_directory):
+  parent_directory = parent_directory + '/'
+  child_directory = child_directory + '/'
+  return child_directory.startswith(parent_directory)
+
+
+def _GetGitLogCmd(options):
+  # TODO(mheikal): git-log with --numstat vs --name-only takes 10x the time to
+  # complete. It takes >15 mins for git log --numstat to return the 1 year git
+  # history of the full repo. Should probably add a script flag to switch off
+  # keeping track of number of modifications per commit.
   date_limit = date.today() - timedelta(days=options.days_ago)
   format_string = "%h,%ae,%cI"
   cmd_args = [
     'log',
     '--since', date_limit.isoformat(),
-    '--name-only',
+    '--numstat',
     '--pretty=format:%s'%format_string,
   ]
-
   # has to be last arg
   if options.subdirectory:
     cmd_args += ['--', options.subdirectory]
+  return cmd_args
 
-  output = _RunGitCommand(options, cmd_args)
+
+def _ParseCommitLine(line):
+  commit_hash, author, commit_date = line.split(",")
+  return Commit(hash=commit_hash, author=author, commit_date=commit_date,
+                dirs={})
+
+
+def _ParseFileStatsLine(current_commit, line):
+  try:
+    additions, deletions, filepath = line.split('\t')
+  except ValueError:
+    return False
+  if additions == '-':
+    additions = 0
+  else:
+    additions = int(additions)
+  if deletions == '-':
+    deletions = 0
+  else:
+    deletions = int(deletions)
+  dir_path = path.dirname(filepath)
+  commit_additions, commit_deletions = \
+      current_commit.dirs.get(dir_path, (0,0))
+  current_commit.dirs[dir_path] = (
+      additions + commit_additions, deletions + commit_deletions)
+  return True
+
+
+def processAllCommits(options):
+  if not options.subdirectory and options.days_ago > 100:
+    print ('git log for your query might take > 5 minutes, limit by a '
+           'subdirectory or reduce the number of days of history to low double '
+           'digits to make this faster. There is no progress indicator, it is '
+           'all waiting for single git log to finish.')
+  output = _RunGitCommand(options, _GetGitLogCmd(options))
   current_commit = None
-  author = None
-  directory_authors = defaultdict(Counter)
   for line in output.splitlines():
     if current_commit is None:
-      commit_hash, author, commit_date = line.split(",")
-      current_commit = Commit(hash=commit_hash, author=author,
-                              commit_date=commit_date, dirs=set())
+      current_commit = _ParseCommitLine(line)
     else:
       if line == '': # all commit details read
-        if _IsValidAuthor(current_commit.author):
-          for directory in current_commit.dirs:
-            if directory == '':
-              continue
-            directory_authors[directory][author] += 1
+        if _ValidAuthor(current_commit.author):
+          _PropagateCommit(options, current_commit)
         current_commit = None
       else:
-        current_commit.dirs.add(os.path.dirname(line))
-  return directory_authors
+        # Merge commits weird out git-log. If we fail to parse the line, then
+        # the last commit was a merge and this line is actually another commit
+        # description line.
+        if not _ParseFileStatsLine(current_commit, line):
+          current_commit = _ParseCommitLine(line)
+  # process the final commit
+  if _ValidAuthor(current_commit.author):
+    _PropagateCommit(options, current_commit)
 
 
-# Return a list of owners for a given directory by reading OWNERS files in its
-# ancestors. The parsing of OWNERS files is pretty naive, it does not handle
-# file imports.
-def _GetOwners(options, repo_subdir):
-  directory_path = os.path.join(options.repo_path, repo_subdir)
-  owners_path = os.path.join(directory_path, 'OWNERS')
-  owners = []
-  while directory_path != '':
-    if os.path.isfile(owners_path):
-      with open(owners_path) as f:
-        owners.extend([line.strip() for line in f.readlines() if
-                       line.find('@chromium.org') > -1])
-    directory_path = path.dirname(directory_path)
-    owners_path = os.path.join(directory_path, 'OWNERS')
+def _CountCommits(directory):
+  return sum(
+      [count for (count, _a, _d) in DIRECTORY_AUTHORS[directory].itervalues()])
+
+
+def _GetOwnerLevel(options, author, directory):
+  sorted_owners = sorted(_GetOwners(options, directory), key=lambda (o,l): l)
+  for owner, level in sorted_owners:
+    if author == owner:
+      return level
+  else:
+    return -1
+
+
+# Returns the owners for a repo subdirectory. This does not understand per-file
+# directives.
+# TODO(mheikal): use depot_tools owners.py for parsing owners files.
+def _GetOwners(options, directory_path):
+  if directory_path in OWNERS_CACHE:
+    return OWNERS_CACHE[directory_path]
+  owners_path = path.join(options.repo_path, directory_path, 'OWNERS')
+  owners = set()
+  parent_dir = directory_path
+  owner_level = 0
+  while parent_dir != '':
+    if path.isfile(owners_path):
+      parsed_owners, noparent = _ParseOwnersFile(options, owners_path)
+      owners.update([(owner, owner_level) for owner in parsed_owners])
+      owner_level += 1
+      if noparent:
+        break
+    parent_dir = path.dirname(parent_dir)
+    owners_path = path.join(parent_dir, 'OWNERS')
+  OWNERS_CACHE[directory_path] = set(owners)
   return owners
 
 
-# Return the number of commits for a given directory
-def _CountDirectoryCommits(directory_authors, directory):
-  return sum(directory_authors[directory].values())
+# Parse an OWNERS file, returns set of owners and if the file sets noparent
+def _ParseOwnersFile(options, filepath):
+  owners = set()
+  noparent = False
+  with open(filepath) as f:
+    for line in f.readlines():
+      line = line.strip()
+      # The script deals with directories so per-files are ignored.
+      if line == '' or line[0] == '#' or line.startswith('per-file'):
+        continue
+      if line.startswith('file://'):
+        relpath = line[7:]
+        abspath = path.join(options.repo_path, relpath)
+        parsed_owners, _ = _ParseOwnersFile(options, abspath)
+        owners.update(parsed_owners)
+      if line == 'set noparent':
+        noparent = True
+      index = line.find('@chromium.org')
+      if index > -1:
+        owners.add(line[:index + len('@chromium.org')])
+  return owners, noparent
 
 
-# Given a directory merge all its children's commits into its own, then delete
-# each child subdirectory's entry if it has too few commits.
-def _GroupToParentDirectory(options, directory_authors, parent):
-  global DIRECTORY_AUTHORS
-  parent_path = path.join(options.repo_path, parent)
-
-  for entry in os.listdir(parent_path):
-    if path.isdir(os.path.join(parent_path, entry)):
-      entry_dir = path.join(parent, entry)
-      directory_authors[parent].update(directory_authors[entry_dir])
-      commit_count = _CountDirectoryCommits(directory_authors, entry_dir)
-      if  commit_count < options.dir_commit_limit:
-        directory_authors.pop(entry_dir)
+# Trivial directories are ones that just contain a single child subdir and
+# nothing else.
+def _IsTrivialDirectory(options, repo_subdir):
+  try:
+    return len(os.listdir(path.join(options.repo_path, repo_subdir))) == 1
+  except OSError:
+    # directory no longer exists
+    return False
 
 
-# Merge directories with too few commits into their parent directory. This
-# method changes the directory_authors dict in-place.
-def mergeDirectories(options, directory_authors):
-  changed = False
-  for directory in directory_authors.keys():
-    if not path.exists(path.join(options.repo_path, directory)):
-      del directory_authors[directory]
+def computeSuggestions(options):
+  directory_suggestions = []
+  for directory, authors in sorted(
+      DIRECTORY_AUTHORS.iteritems(), key=lambda (d, a): d):
+    if _IsTrivialDirectory(options, directory):
       continue
-    num_commits = _CountDirectoryCommits(directory_authors, directory)
-    if num_commits == 0:
+    if _CountCommits(directory) < options.dir_commit_limit:
       continue
-    elif num_commits < options.dir_commit_limit:
-      parent = os.path.dirname(directory)
-      _GroupToParentDirectory(options, directory_authors, parent)
-      changed = True
-  return changed
-
-
-# Retrieves a set of authors that should not be suggested for a directory
-def _GetIgnoredAuthors(options, repo_subdir):
-  if options.ignore_authors:
-    ignored_authors = set(map(str.strip, options.ignore_authors.split(',')))
-  else:
-    ignored_authors = set()
-  ignored_authors.update(_GetOwners(options, repo_subdir))
-  return ignored_authors
-
-
-# Prints out a list of suggested new owners for each directory with a high
-# enough commit count.
-def outputSuggestions(options, directory_authors):
-  for directory, authors in sorted(directory_authors.iteritems()):
-    commit_count = _CountDirectoryCommits(directory_authors, directory)
-    if commit_count < options.dir_commit_limit:
+    # skip suggestions for directories outside the passed in directory
+    if (options.subdirectory
+        and not isSubDirectory(options.subdirectory, directory)):
       continue
-    ignored_authors = _GetIgnoredAuthors(options, directory)
-    suggestions = [(a,c) for a,c in authors.most_common()
-                   if a not in ignored_authors and c >= options.author_cl_limit]
-    print "%s: %d commits in the last %d days" % \
-        (directory, commit_count, options.days_ago)
-    for author, commit_count in suggestions[:options.max_suggestions]:
-      print author, commit_count
+    # sort authors by descending number of commits
+    sorted_authors = sorted(authors.items(),
+                            key=lambda (author, details): -details[0])
+    # keep only authors above the limit
+    suggestions = [(a,c) for a,c in sorted_authors if \
+                   a not in options.ignore_authors \
+                   and c[0] >= options.author_cl_limit]
+    directory_suggestions.append((directory, suggestions))
+  return directory_suggestions
+
+
+def _PrintSettings(options):
+  print ('Showing directories with at least ({}) commits in the last ({}) '
+         'days.'.format(options.dir_commit_limit, options.days_ago))
+  print ('Showing top ({}) committers who have commited at least ({}) commits '
+         'to the directory in the last ({}) days.'.format(
+             options.max_suggestions, options.author_cl_limit,
+             options.days_ago))
+  print '(owners+N) represents distance through OWNERS files for said owner\n'
+
+
+def printSuggestions(options, directory_suggestions):
+  print '\nCommit stats:'
+  _PrintSettings(options)
+  for directory, suggestions in directory_suggestions:
+    print '{}: {} commits in the last {} days'.format(
+        directory, _CountCommits(directory), options.days_ago)
+    non_owner_suggestions = 0
+    for author, (commit_count, additions, deletions) in suggestions:
+      owner_level = _GetOwnerLevel(options, author, directory)
+      if owner_level > -1:
+        owner_string = ' (owner+{})'.format(owner_level)
+      else:
+        non_owner_suggestions +=1
+        owner_string = ''
+      print '{}{}, commits: {}, additions:{}, deletions: {}'.format(
+          author, owner_string, commit_count, additions, deletions)
+      if non_owner_suggestions >= options.max_suggestions:
+        break
     print
 
 
-# main 2.0
+def _GetHeadCommitHash(options):
+  return _RunGitCommand(options, ['rev-parse', 'HEAD']).strip()
+
+
+def _GetCacheMetadata(options):
+  return _GetHeadCommitHash(options), options.days_ago, options.subdirectory
+
+
+def _IsCacheValid(options, metadata):
+  head_hash, days_ago, cached_subdirectory = metadata
+  if head_hash != _GetHeadCommitHash(options):
+    return False
+  if days_ago != options.days_ago:
+    return False
+  if (cached_subdirectory is not None
+      and not isSubDirectory(cached_subdirectory, options.subdirectory)):
+    return False
+  return True
+
+
+def cacheProcessedCommits(options):
+  metadata = _GetCacheMetadata(options)
+  with open(CACHE_FILENAME, 'w') as f:
+    pickle.dump((metadata, DIRECTORY_AUTHORS), f)
+
+
+def maybeRestoreProcessedCommits(options):
+  global DIRECTORY_AUTHORS
+  if not path.exists(CACHE_FILENAME):
+    return False
+  with open(CACHE_FILENAME) as f:
+    stored_metadata, cached_directory_authors = pickle.load(f)
+    if _IsCacheValid(options, stored_metadata):
+      print 'Loading from cache'
+      DIRECTORY_AUTHORS = cached_directory_authors
+      return True
+    else:
+      print 'Cache is stale or invalid, must rerun `git log`'
+      return False
+
 def do(options):
-  directory_authors = processAllCommits(options)
-  while mergeDirectories(options, directory_authors):
-    pass
-  outputSuggestions(options, directory_authors)
+  if options.skip_cache or not maybeRestoreProcessedCommits(options):
+    processAllCommits(options)
+    cacheProcessedCommits(options)
+  directory_suggestions = computeSuggestions(options)
+  printSuggestions(options, directory_suggestions)
 
 
 def main():
   parser = argparse.ArgumentParser(
       formatter_class=argparse.ArgumentDefaultsHelpFormatter)
   parser.add_argument('repo_path')
-  parser.add_argument('--days-ago', help='Number of days of history to search'
-                      ' through.', default=365)
-  parser.add_argument('--subdirectory', help='Limit to this subdirectory')
-  parser.add_argument('--ignore-authors', help='Ignore this comma separated'
-                      ' list of authors')
-  parser.add_argument('--max-suggestions', help='Maximum number of suggested'
-                      ' authors per directory.', default=5)
-  parser.add_argument('--author-cl-limit', help='Do not suggest authors who'
-                      ' have commited less than this to the directory.',
-                      default=10)
-  parser.add_argument('--dir-commit-limit', help='Merge directories with less'
-                      ' than this number of commits into their parent'
-                      ' directory.', default=100)
+  parser.add_argument('--days-ago', type=int,
+                      help='Number of days of history to search through.',
+                      default=365, metavar='DAYS_AGO')
+  parser.add_argument('--subdirectory',
+                      help='Limit suggestions to this subdirectory', default='')
+  parser.add_argument('--ignore-authors',
+                      help='Ignore this comma separated list of authors')
+  parser.add_argument('--max-suggestions', type=int, help='Maximum number of '
+                      'suggested authors per directory.', default=5)
+  parser.add_argument('--author-cl-limit', type=int, help='Do not suggest '
+                      'authors who have commited less than this to the '
+                      'directory in the last DAYS_AGO days.', default=10)
+  parser.add_argument('--dir-commit-limit', type=int, help='Skip directories '
+                      'with less than this number of commits in the last '
+                      'DAYS_AGO days.', default=100)
+  parser.add_argument('--skip-cache', action='store_true',
+                      help='Do not read from cache.', default=False)
   options = parser.parse_args()
+  if options.ignore_authors:
+    options.ignore_authors = set(
+        map(str.strip, options.ignore_authors.split(',')))
+  else:
+    options.ignore_authors = set()
   do(options)
 
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 05d1f9c..aa0a8ad 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -5904,6 +5904,11 @@
   <int value="1" label="Completed"/>
 </enum>
 
+<enum name="BooleanCompositorCSSPaint">
+  <int value="0" label="A CSS Paint is fall back on the main thread"/>
+  <int value="1" label="A CSS Paint is painted on the compositor thread"/>
+</enum>
+
 <enum name="BooleanConfirmed">
   <int value="0" label="Not Confirmed"/>
   <int value="1" label="Confirmed"/>
@@ -20653,6 +20658,7 @@
   <int value="1381" label="AUTOTESTPRIVATE_GETARCSTARTTIME"/>
   <int value="1382" label="AUTOTESTPRIVATE_SETOVERVIEWMODESTATE"/>
   <int value="1383" label="AUTOTESTPRIVATE_TAKESCREENSHOTFORDISPLAY"/>
+  <int value="1384" label="AUTOFILLPRIVATE_SETCREDITCARDFIDOAUTHENABLEDSTATE"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -34829,6 +34835,7 @@
   <int value="-2020721975" label="smart-virtual-keyboard"/>
   <int value="-2020024440" label="scroll-end-effect"/>
   <int value="-2017953534" label="enable-hosted-app-shim-creation"/>
+  <int value="-2015293660" label="AccessibilityExposeDisplayNone:disabled"/>
   <int value="-2013551096" label="ViewsSimplifiedFullscreenUI:disabled"/>
   <int value="-2013124655" label="EnableEphemeralFlashPermission:disabled"/>
   <int value="-2012990889" label="SpannableInlineAutocomplete:enabled"/>
@@ -34951,6 +34958,7 @@
       label="enable-zero-suggest-most-visited-without-serp"/>
   <int value="-1886713568"
       label="AutofillGetPaymentsIdentityFromSync:disabled"/>
+  <int value="-1885074774" label="ShelfScrollable:enabled"/>
   <int value="-1883170077" label="EnableHtmlBaseUsernameDetector:disabled"/>
   <int value="-1882330924" label="NTPArticleSuggestions:enabled"/>
   <int value="-1881250251" label="InterestFeedContentSuggestions:disabled"/>
@@ -35100,7 +35108,6 @@
   <int value="-1684773837" label="TabEngagementReportingAndroid:disabled"/>
   <int value="-1684123448" label="disable-best-effort-tasks"/>
   <int value="-1682843294" label="DataReductionProxyDecidesTransform:enabled"/>
-  <int value="-1681832882" label="shelf-scrollable"/>
   <int value="-1679624666" label="NtpRealbox:enabled"/>
   <int value="-1677715989" label="UnifiedConsent:disabled"/>
   <int value="-1676256979"
@@ -35560,6 +35567,7 @@
   <int value="-1118921985" label="ForceEnableSystemAec:enabled"/>
   <int value="-1114080030" label="ResourceLoadingHints:enabled"/>
   <int value="-1112782121" label="AndroidSigninPromos:disabled"/>
+  <int value="-1109826787" label="AccessibilityExposeDisplayNone:enabled"/>
   <int value="-1107762575" label="enable-data-reduction-proxy-config-client"/>
   <int value="-1107103335" label="FsNosymfollow:enabled"/>
   <int value="-1105637876" label="FilteringScrollPrediction:enabled"/>
@@ -35596,6 +35604,7 @@
   <int value="-1074257709" label="ScalableAppList:enabled"/>
   <int value="-1074107607" label="data-reduction-proxy-experiment"/>
   <int value="-1073479583" label="ShowArcFilesApp:disabled"/>
+  <int value="-1069628248" label="OmniboxZeroSuggestionsOnSERP:enabled"/>
   <int value="-1069453905" label="CCTModuleUseIntentExtras:disabled"/>
   <int value="-1067635248" label="SpeculativeResourcePrefetching:disabled"/>
   <int value="-1064733740" label="ui-show-composited-layer-borders"/>
@@ -35814,6 +35823,7 @@
   <int value="-787969387" label="HTTPSServerPreviewsUsingURLLoader:enabled"/>
   <int value="-787876637" label="HomeLauncherGestures:enabled"/>
   <int value="-787426248" label="ChromeHomeSurvey:disabled"/>
+  <int value="-787238455" label="OmniboxZeroSuggestionsOnSERP:disabled"/>
   <int value="-780798969" label="disable-single-click-autofill"/>
   <int value="-778126349" label="DownloadsLocationChange:enabled"/>
   <int value="-777126951" label="FontSrcLocalMatching:enabled"/>
@@ -36554,6 +36564,7 @@
   <int value="223591010" label="CastMediaRouteProvider:enabled"/>
   <int value="223662457" label="BackgroundLoadingForDownloads:enabled"/>
   <int value="229476202" label="CryptAuthV2Enrollment:enabled"/>
+  <int value="229877058" label="ShelfScrollable:disabled"/>
   <int value="233721248" label="DownloadRename:disabled"/>
   <int value="237570976" label="CommandLineOnNonRooted:disabled"/>
   <int value="237964589"
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 0ad0b7e..355f8ac 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -14523,6 +14523,16 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.CSSPaintValue.PaintOffThread"
+    enum="BooleanCompositorCSSPaint" expires_after="2021-09-08">
+  <owner>xidachen@chromium.org</owner>
+  <owner>smcgruer@chromium.org</owner>
+  <summary>
+    Records if a CSS Paint is painted on the compositor thread or has fallen
+    back to the main thread.
+  </summary>
+</histogram>
+
 <histogram name="Blink.DecodedImage.CanvasExpanded"
     enum="BooleanCanvasExpanded" expires_after="2016-11-30">
   <obsolete>
@@ -62251,6 +62261,17 @@
   </summary>
 </histogram>
 
+<histogram name="Media.MCVD.ForwardVideoFrameTiming" units="ms"
+    expires_after="2019-12-31">
+  <owner>liberato@chromium.org</owner>
+  <owner>sandersd@chromium.org</owner>
+  <owner>tguilbert@chromium.org</owner>
+  <summary>
+    The time between MediaCodec::dequeueOutputBuffer() and the dispatch of the
+    corresponding VideoFrame from MediaCodecVideoDecoder.
+  </summary>
+</histogram>
+
 <histogram name="Media.MeanTimeBetweenRebuffers" units="ms"
     expires_after="2020-02-02">
   <owner>dalecurtis@chromium.org</owner>
@@ -71415,6 +71436,17 @@
   </summary>
 </histogram>
 
+<histogram name="Navigation.IsLockedProcess.HTTPOrHTTPS"
+    enum="NavigationIsLockedProcess" expires_after="2020-12-31">
+  <owner>alexmos@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
+  <summary>
+    Whether the navigation commits in a process that is locked to an origin.
+    Logged at ready-to-commit time for every navigation that commits with a HTTP
+    or HTTPS URL scheme, excluding same-document navigations and errors.
+  </summary>
+</histogram>
+
 <histogram name="Navigation.IsMobileOptimized" enum="BooleanIsMobileOptimized"
     expires_after="2018-03-05">
   <obsolete>
@@ -71687,6 +71719,18 @@
   </summary>
 </histogram>
 
+<histogram name="Navigation.RequiresDedicatedProcess.HTTPOrHTTPS"
+    enum="NavigationRequiresDedicatedProcess" expires_after="2020-12-31">
+  <owner>alexmos@chromium.org</owner>
+  <owner>lukasza@chromium.org</owner>
+  <summary>
+    Whether the navigation commits in a SiteInstance that requires a dedicated
+    process. Logged at ready-to-commit time for every navigation that commits
+    with a HTTP or HTTPS URL scheme, excluding same-document navigations and
+    errors.
+  </summary>
+</histogram>
+
 <histogram
     name="Navigation.ResourceHandler.ProceedWithResponseUntilFirstReadCompleted"
     units="ms">
@@ -71719,6 +71763,9 @@
 
 <histogram name="Navigation.SandboxFrameBackForwardStaysWithinSubtree"
     enum="BooleanWithinSubtree" expires_after="M78">
+  <obsolete>
+    Removed as of 09/2019.
+  </obsolete>
   <owner>creis@chromium.org</owner>
   <owner>dtapuska@chromium.org</owner>
   <summary>
@@ -77920,6 +77967,9 @@
 </histogram>
 
 <histogram name="Net.PreconnectSkippedToProxyServers" units="count">
+  <obsolete>
+    Deprecated September 2019
+  </obsolete>
   <owner>tbansal@chromium.org</owner>
   <summary>
     Indicates number of times a preconnect to proxy server was skipped due to an
@@ -89578,6 +89628,16 @@
   </summary>
 </histogram>
 
+<histogram name="Notifications.Triggers.DisplayDelay" units="ms"
+    expires_after="M82">
+  <owner>knollr@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <summary>
+    Delay between the expected and actual display time of a notification.
+    Recorded just before displaying a notification with a show trigger.
+  </summary>
+</histogram>
+
 <histogram name="Notifications.Triggers.HasShowTrigger" enum="Boolean"
     expires_after="M80">
   <owner>knollr@chromium.org</owner>
@@ -162629,6 +162689,7 @@
   <suffix name="ar" label="Augmented Reality Module"/>
   <suffix name="autofill_assistant" label="Assistant-in-Chrome Module"/>
   <suffix name="dev_ui" label="Developer UI Module"/>
+  <suffix name="extra_icu" label="Extra ICU Module"/>
   <suffix name="tab_ui" label="Tab Management Module"/>
   <suffix name="test_dummy" label="Test Dummy Module"/>
   <suffix name="vr" label="Virtual Reality Module"/>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 3743330c..98fdcd8 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -4910,7 +4910,7 @@
 </event>
 
 <event name="NavigationPredictorAnchorElementMetrics">
-  <owner>ryansturm@chromium.com</owner>
+  <owner>ryansturm@chromium.org</owner>
   <summary>
     Metrics that describe an anchor element's data. This event is emitted at
     most 10 times per page load, once for each of the top n URLs on a page, and
@@ -4961,6 +4961,7 @@
 </event>
 
 <event name="NavigationPredictorPageLinkClick">
+  <owner>ryansturm@chromium.org</owner>
   <summary>
     Sent on an in-page link click. An integer from 0-10 (1-indexed),
     corresponding to the index of that anchor element in the list of top ten
@@ -4971,7 +4972,7 @@
 </event>
 
 <event name="NavigationPredictorPageLinkMetrics" singular="True">
-  <owner>ryansturm@chromium.com</owner>
+  <owner>ryansturm@chromium.org</owner>
   <summary>
     Metrics that describe aggregate information about the links within a page.
     Some anchor elements present on a page may not be represented in these
diff --git a/tools/perf/benchmarks/system_health_smoke_test.py b/tools/perf/benchmarks/system_health_smoke_test.py
index bb87abc..21d8839 100644
--- a/tools/perf/benchmarks/system_health_smoke_test.py
+++ b/tools/perf/benchmarks/system_health_smoke_test.py
@@ -44,6 +44,7 @@
   'system_health.memory_mobile/browse:social:tumblr_infinite_scroll',
   'system_health.memory_mobile/browse:tools:maps',
   'system_health.memory_mobile/browse:news:cnn',
+  'system_health.memory_mobile/load:media:facebook_photos',
   'system_health.memory_mobile/load:news:cnn',
   'system_health.memory_mobile/load:tools:stackoverflow',
   'system_health.memory_desktop/load_accessibility:shopping:amazon',
diff --git a/tools/perf/chrome_telemetry_build/BUILD.gn b/tools/perf/chrome_telemetry_build/BUILD.gn
index a95b547..bb36f70 100644
--- a/tools/perf/chrome_telemetry_build/BUILD.gn
+++ b/tools/perf/chrome_telemetry_build/BUILD.gn
@@ -82,6 +82,12 @@
   data_deps = [
     "//third_party/catapult:telemetry_chrome_test_support",
   ]
+  if (is_chromeos) {
+    data_deps += [
+      "//third_party/breakpad:minidump_stackwalk",
+      "//third_party/breakpad:dump_syms",
+    ]
+  }
   if (is_android) {
     data += [
       "//build/android/stacktrace/",
diff --git a/tools/perf/core/minidump_unittest.py b/tools/perf/core/minidump_unittest.py
index 6b236224..04d5f03 100644
--- a/tools/perf/core/minidump_unittest.py
+++ b/tools/perf/core/minidump_unittest.py
@@ -16,7 +16,7 @@
   @decorators.Isolated
   # ChromeOS and Android are currently hard coded to return None for minidump
   # paths, so disable on those platforms.
-  @decorators.Disabled('chromeos', 'android')
+  @decorators.Disabled('android')
   def testSymbolizeMinidump(self):
     # Wait for the browser to restart fully before crashing
     self._LoadPageThenWait('var sam = "car";', 'sam')
@@ -88,7 +88,7 @@
       self.assertTrue(crash_function in sections[4])
 
   @decorators.Isolated
-  @decorators.Disabled('chromeos', 'android')
+  @decorators.Disabled('android')
   def testMultipleCrashMinidumps(self):
     # Wait for the browser to restart fully before crashing
     self._LoadPageThenWait('var cat = "dog";', 'cat')
diff --git a/tools/perf/page_sets/data/system_health_mobile.json b/tools/perf/page_sets/data/system_health_mobile.json
index 4c8fb26..6c471ac6 100644
--- a/tools/perf/page_sets/data/system_health_mobile.json
+++ b/tools/perf/page_sets/data/system_health_mobile.json
@@ -144,6 +144,9 @@
         "load:media:facebook_photos": {
             "DEFAULT": "system_health_mobile_040.wprgo"
         },
+        "load:media:facebook_photos:2019": {
+            "DEFAULT": "system_health_mobile_6fb624f9e6.wprgo"
+        },
         "load:media:flickr": {
             "DEFAULT": "system_health_mobile_003.wprgo"
         },
@@ -255,6 +258,9 @@
         "load:social:instagram": {
             "DEFAULT": "system_health_mobile_001.wprgo"
         },
+        "load:social:instagram:2019": {
+            "DEFAULT": "system_health_mobile_71bac3270a.wprgo"
+        },
         "load:social:pinterest": {
             "DEFAULT": "system_health_mobile_001.wprgo"
         },
diff --git a/tools/perf/page_sets/data/system_health_mobile_6fb624f9e6.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_6fb624f9e6.wprgo.sha1
new file mode 100644
index 0000000..17df97c
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_6fb624f9e6.wprgo.sha1
@@ -0,0 +1 @@
+6fb624f9e6ceef74d330750d57a078c9058d37b6
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_mobile_71bac3270a.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_71bac3270a.wprgo.sha1
new file mode 100644
index 0000000..a31b424c
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_71bac3270a.wprgo.sha1
@@ -0,0 +1 @@
+71bac3270a61abec13028982d17b228db59b9d4b
\ No newline at end of file
diff --git a/tools/perf/page_sets/system_health/loading_stories.py b/tools/perf/page_sets/system_health/loading_stories.py
index ec5e6f1..a4a2e2b 100644
--- a/tools/perf/page_sets/system_health/loading_stories.py
+++ b/tools/perf/page_sets/system_health/loading_stories.py
@@ -134,6 +134,11 @@
   TAGS = [story_tags.YEAR_2018]
   SUPPORTED_PLATFORMS = platforms.DESKTOP_ONLY
 
+class LoadInstagramMobileStory2019(_LoadingStory):
+  NAME = 'load:social:instagram:2019'
+  URL = 'https://www.instagram.com/selenagomez/'
+  TAGS = [story_tags.YEAR_2019]
+  SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
 
 class LoadPinterestStory(_LoadingStory):
   NAME = 'load:social:pinterest'
@@ -313,6 +318,14 @@
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
   TAGS = [story_tags.EMERGING_MARKET, story_tags.YEAR_2016]
 
+class LoadFacebookPhotosMobileStory2019(_LoadingStory):
+  """Load a page of rihanna's facebook with a photo."""
+  NAME = 'load:media:facebook_photos:2019'
+  URL = (
+      'https://m.facebook.com/rihanna/photos/a.207477806675/10156574885461676/?type=3&source=54&ref=page_internal')
+  SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
+  TAGS = [story_tags.EMERGING_MARKET, story_tags.YEAR_2019]
+
 
 class LoadFacebookPhotosDesktopStory2018(_LoadingStory):
   """Load a page of rihanna's facebook with a photo."""
diff --git a/tools/polymer/polymer.py b/tools/polymer/polymer.py
index d6b8ea1..ef9a2b73 100644
--- a/tools/polymer/polymer.py
+++ b/tools/polymer/polymer.py
@@ -304,7 +304,7 @@
   IIFE_CLOSING = '})();'
 
   # Remove this line.
-  CR_DEFINE_START_REGEX = 'cr.define\('
+  CR_DEFINE_START_REGEX = r'cr.define\('
   # Ignore all lines after this comment, including the line it appears on.
   CR_DEFINE_END_REGEX = r'\s*// #cr_define_end'
 
diff --git a/tools/traffic_annotation/bin/README.md b/tools/traffic_annotation/bin/README.md
index a1cdde4..414b178 100644
--- a/tools/traffic_annotation/bin/README.md
+++ b/tools/traffic_annotation/bin/README.md
@@ -10,7 +10,7 @@
 ```bash
 git new-branch roll_traffic_annotation_tools
 python tools/clang/scripts/build.py --bootstrap \
-    --without-android --extra-tools traffic_annotation_extractor
+    --without-android --without-fuchsia --extra-tools traffic_annotation_extractor
 cp third_party/llvm-build/Release+Asserts/bin/traffic_annotation_extractor \
     tools/traffic_annotation/bin/linux64/
 
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn
index 239d3d48f..1fd66cdb 100644
--- a/ui/accessibility/BUILD.gn
+++ b/ui/accessibility/BUILD.gn
@@ -33,6 +33,8 @@
 
 jumbo_component("accessibility") {
   sources = [
+    "accessibility_features.cc",
+    "accessibility_features.h",
     "accessibility_switches.cc",
     "accessibility_switches.h",
     "ax_action_data.cc",
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc
new file mode 100644
index 0000000..97eee4d
--- /dev/null
+++ b/ui/accessibility/accessibility_features.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 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 "ui/accessibility/accessibility_features.h"
+
+#include "base/feature_list.h"
+#include "build/build_config.h"
+
+namespace features {
+
+// Enable exposing "display: none" nodes to the browser process AXTree
+const base::Feature kEnableAccessibilityExposeDisplayNone{
+    "AccessibilityExposeDisplayNone", base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool IsAccessibilityExposeDisplayNoneEnabled() {
+  return base::FeatureList::IsEnabled(
+      ::features::kEnableAccessibilityExposeDisplayNone);
+}
+
+}  // namespace features
diff --git a/ui/accessibility/accessibility_features.h b/ui/accessibility/accessibility_features.h
new file mode 100644
index 0000000..c2f5d55f
--- /dev/null
+++ b/ui/accessibility/accessibility_features.h
@@ -0,0 +1,23 @@
+// Copyright (c) 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.
+
+// Define all the base::Features used by ui/accessibility.
+#ifndef UI_ACCESSIBILITY_ACCESSIBILITY_FEATURES_H_
+#define UI_ACCESSIBILITY_ACCESSIBILITY_FEATURES_H_
+
+#include "base/feature_list.h"
+#include "build/build_config.h"
+#include "ui/accessibility/ax_export.h"
+
+namespace features {
+
+AX_EXPORT extern const base::Feature kEnableAccessibilityExposeDisplayNone;
+
+// Returns true if "display: none" nodes should be exposed to the
+// browser process AXTree.
+AX_EXPORT bool IsAccessibilityExposeDisplayNoneEnabled();
+
+}  // namespace features
+
+#endif  // UI_ACCESSIBILITY_ACCESSIBILITY_FEATURES_H_
diff --git a/ui/accessibility/ax_node_position.cc b/ui/accessibility/ax_node_position.cc
index f448bf46..fd49d91 100644
--- a/ui/accessibility/ax_node_position.cc
+++ b/ui/accessibility/ax_node_position.cc
@@ -5,6 +5,7 @@
 #include "ui/accessibility/ax_node_position.h"
 
 #include "base/strings/string_util.h"
+#include "ui/accessibility/accessibility_features.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_tree_manager_map.h"
 
@@ -92,32 +93,24 @@
   if (!IsLeafTextPosition())
     return AsLeafTextPosition()->AsUnignoredTextPosition(adjustment_behavior);
 
-  if (!GetAnchor()->IsIgnored())
-    return Clone();
+  AXPositionInstance unignored_position =
+      CreateUnignoredPositionFromLeafTextPosition(adjustment_behavior);
 
-  // Find the next/previous node that is not ignored.
-  AXNode* unignored_node = GetAnchor();
-  while (unignored_node) {
-    switch (adjustment_behavior) {
-      case AdjustmentBehavior::kMoveRight:
-        unignored_node = unignored_node->GetNextUnignoredInTreeOrder();
-        break;
-      case AdjustmentBehavior::kMoveLeft:
-        unignored_node = unignored_node->GetPreviousUnignoredInTreeOrder();
-    }
-    if (unignored_node && unignored_node->IsText()) {
-      switch (adjustment_behavior) {
-        case AdjustmentBehavior::kMoveRight:
-          return CreateTextPosition(tree_id(), unignored_node->id(), 0,
-                                    ax::mojom::TextAffinity::kDownstream);
-        case AdjustmentBehavior::kMoveLeft:
-          return CreateTextPosition(tree_id(), unignored_node->id(), 0,
-                                    ax::mojom::TextAffinity::kDownstream)
-              ->CreatePositionAtEndOfAnchor();
-      }
+  // If creating an unignored position using |adjustment_behavior| returns a
+  // null position, the position may be at the start or end of a document.
+  // For this case attempt to adjust using the opposite AdjustmentBehavior.
+  if (features::IsAccessibilityExposeDisplayNoneEnabled()) {
+    if (unignored_position->IsNullPosition()) {
+      const AdjustmentBehavior opposite_adjustment =
+          (adjustment_behavior == AdjustmentBehavior::kMoveRight)
+              ? AdjustmentBehavior::kMoveLeft
+              : AdjustmentBehavior::kMoveRight;
+      unignored_position =
+          CreateUnignoredPositionFromLeafTextPosition(opposite_adjustment);
     }
   }
-  return CreateNullPosition();
+
+  return unignored_position;
 }
 
 int AXNodePosition::MaxTextOffset() const {
@@ -372,4 +365,37 @@
   return parent;
 }
 
+AXNodePosition::AXPositionInstance
+AXNodePosition::CreateUnignoredPositionFromLeafTextPosition(
+    AdjustmentBehavior adjustment_behavior) const {
+  DCHECK(IsLeafTextPosition());
+
+  AXNode* unignored_node = GetAnchor();
+  if (!unignored_node->IsIgnored())
+    return Clone();
+
+  // Find the next/previous node that is not ignored.
+  while (unignored_node) {
+    switch (adjustment_behavior) {
+      case AdjustmentBehavior::kMoveRight:
+        unignored_node = unignored_node->GetNextUnignoredInTreeOrder();
+        break;
+      case AdjustmentBehavior::kMoveLeft:
+        unignored_node = unignored_node->GetPreviousUnignoredInTreeOrder();
+    }
+    if (unignored_node && unignored_node->IsText()) {
+      switch (adjustment_behavior) {
+        case AdjustmentBehavior::kMoveRight:
+          return CreateTextPosition(tree_id(), unignored_node->id(), 0,
+                                    ax::mojom::TextAffinity::kDownstream);
+        case AdjustmentBehavior::kMoveLeft:
+          return CreateTextPosition(tree_id(), unignored_node->id(), 0,
+                                    ax::mojom::TextAffinity::kDownstream)
+              ->CreatePositionAtEndOfAnchor();
+      }
+    }
+  }
+  return CreateNullPosition();
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_node_position.h b/ui/accessibility/ax_node_position.h
index 4da2880..16b9cf4 100644
--- a/ui/accessibility/ax_node_position.h
+++ b/ui/accessibility/ax_node_position.h
@@ -73,6 +73,9 @@
                            AXTreeID child_tree_id,
                            AXTreeID* parent_tree_id,
                            int32_t* parent_id);
+
+  AXPositionInstance CreateUnignoredPositionFromLeafTextPosition(
+      AdjustmentBehavior adjustment_behavior) const;
 };
 
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index 89bfc03..84ce65f 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -177,8 +177,8 @@
     case TextUnit_Page: {
       // If the document doesn't support pagination, default to document units
       // per UIA spec
-      AXPositionInstance common_ancestor = start_->LowestCommonAncestor(*end_);
-      if (common_ancestor->GetAnchor()->tree()->HasPaginationSupport()) {
+      const AXNode* common_anchor = start_->LowestCommonAnchor(*end_);
+      if (common_anchor->tree()->HasPaginationSupport()) {
         start_ = start_->CreatePreviousPageStartPosition(
             ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
         end_ = start_->CreateNextPageEndPosition(
@@ -765,13 +765,10 @@
 
   std::vector<gfx::NativeViewAccessible> descendants;
 
-  AXPositionInstance common_ancestor =
-      start_->LowestCommonAncestor(*end_.get());
-
-  AXPlatformNodeDelegate* delegate =
-      GetDelegate(common_ancestor.get())
-          ->GetFromNodeID(common_ancestor->anchor_id())
-          ->GetDelegate();
+  const AXNode* common_anchor = start_->LowestCommonAnchor(*end_);
+  const AXTreeID tree_id = common_anchor->tree()->GetAXTreeID();
+  const AXNode::AXID node_id = common_anchor->id();
+  AXPlatformNodeDelegate* delegate = GetDelegate(tree_id, node_id);
   DCHECK(delegate);
   while (ui::IsIgnored(delegate->GetData())) {
     auto* node = static_cast<AXPlatformNodeWin*>(
diff --git a/ui/base/models/button_menu_item_model.cc b/ui/base/models/button_menu_item_model.cc
index 3e629cd..cfa584fa 100644
--- a/ui/base/models/button_menu_item_model.cc
+++ b/ui/base/models/button_menu_item_model.cc
@@ -37,7 +37,6 @@
   int command_id;
   ButtonType type;
   base::string16 label;
-  int icon_idr;
   bool part_of_group;
 };
 
@@ -53,25 +52,24 @@
 
 void ButtonMenuItemModel::AddGroupItemWithStringId(
     int command_id, int string_id) {
-  Item item = { command_id, TYPE_BUTTON, l10n_util::GetStringUTF16(string_id),
-                -1, true };
+  Item item = {command_id, TYPE_BUTTON, l10n_util::GetStringUTF16(string_id),
+               true};
   items_.push_back(item);
 }
 
-void ButtonMenuItemModel::AddItemWithImage(int command_id,
-                                           int icon_idr) {
-  Item item = { command_id, TYPE_BUTTON, base::string16(), icon_idr, false };
+void ButtonMenuItemModel::AddImageItem(int command_id) {
+  Item item = {command_id, TYPE_BUTTON, base::string16(), false};
   items_.push_back(item);
 }
 
 void ButtonMenuItemModel::AddButtonLabel(int command_id, int string_id) {
-  Item item = { command_id, TYPE_BUTTON_LABEL,
-                l10n_util::GetStringUTF16(string_id), -1, false };
+  Item item = {command_id, TYPE_BUTTON_LABEL,
+               l10n_util::GetStringUTF16(string_id), false};
   items_.push_back(item);
 }
 
 void ButtonMenuItemModel::AddSpace() {
-  Item item = { 0, TYPE_SPACE, base::string16(), -1, false };
+  Item item = {0, TYPE_SPACE, base::string16(), false};
   items_.push_back(item);
 }
 
@@ -109,14 +107,6 @@
   return items_[index].label;
 }
 
-bool ButtonMenuItemModel::GetIconAt(int index, int* icon_idr) const {
-  if (items_[index].icon_idr == -1)
-    return false;
-
-  *icon_idr = items_[index].icon_idr;
-  return true;
-}
-
 bool ButtonMenuItemModel::PartOfGroup(int index) const {
   return items_[index].part_of_group;
 }
diff --git a/ui/base/models/button_menu_item_model.h b/ui/base/models/button_menu_item_model.h
index 22595e6..eabf456 100644
--- a/ui/base/models/button_menu_item_model.h
+++ b/ui/base/models/button_menu_item_model.h
@@ -54,8 +54,11 @@
   // this method will have the same size, based on the largest button.
   void AddGroupItemWithStringId(int command_id, int string_id);
 
-  // Adds a button that has an icon instead of a label.
-  void AddItemWithImage(int command_id, int icon_idr);
+  // Adds a button that has an icon instead of a label. Note that the image
+  // itself must be separately configured by platform-specific code; this method
+  // simply serves to add a blank item in the menu with a specified
+  // |command_id|.
+  void AddImageItem(int command_id);
 
   // Adds a non-clickable button with a desensitized label that doesn't do
   // anything. Usually combined with IsItemForCommandIdDynamic() to add
@@ -84,10 +87,6 @@
   // Returns the current label value for the button at |index|.
   base::string16 GetLabelAt(int index) const;
 
-  // If the button at |index| should have an icon instead, returns true and
-  // sets the IDR |icon|.
-  bool GetIconAt(int index, int* icon) const;
-
   // If the button at |index| should have its size equalized along with all
   // other items that have their PartOfGroup bit set.
   bool PartOfGroup(int index) const;
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index a2e985a..ae43e08 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -13,7 +13,7 @@
 #if defined(OS_WIN)
 // If enabled, calculate native window occlusion - Windows-only.
 const base::Feature kCalculateNativeWinOcclusion{
-    "CalculateNativeWinOcclusion", base::FEATURE_DISABLED_BY_DEFAULT};
+    "CalculateNativeWinOcclusion", base::FEATURE_ENABLED_BY_DEFAULT};
 #endif  // OW_WIN
 
 #if defined(OS_CHROMEOS)
@@ -127,6 +127,10 @@
 const base::Feature kFormControlsRefresh = {"FormControlsRefresh",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable WebUI accessibility enhancements for review and testing.
+const base::Feature kWebUIA11yEnhancements{"WebUIA11yEnhancements",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
 bool IsFormControlsRefreshEnabled() {
   return base::FeatureList::IsEnabled(features::kFormControlsRefresh);
 }
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index 8fa392e..e571ffc 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -87,6 +87,9 @@
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 extern const base::Feature kHandwritingGesture;
 #endif
+
+COMPONENT_EXPORT(UI_BASE_FEATURES)
+extern const base::Feature kWebUIA11yEnhancements;
 }  // namespace features
 
 #endif  // UI_BASE_UI_BASE_FEATURES_H_
diff --git a/ui/base/webui/web_ui_util.cc b/ui/base/webui/web_ui_util.cc
index 29ad19d..09a164647 100644
--- a/ui/base/webui/web_ui_util.cc
+++ b/ui/base/webui/web_ui_util.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "base/base64.h"
+#include "base/feature_list.h"
 #include "base/i18n/rtl.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted_memory.h"
@@ -19,6 +20,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/template_expressions.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/font.h"
@@ -168,6 +170,8 @@
 
 void SetLoadTimeDataDefaults(const std::string& app_locale,
                              base::DictionaryValue* localized_strings) {
+  // Needs to be all lowercase due to
+  localized_strings->SetString("a11yenhanced", GetA11yEnhanced());
   localized_strings->SetString("fontfamily", GetFontFamily());
   localized_strings->SetString("fontsize", GetFontSize());
   localized_strings->SetString("language", l10n_util::GetLanguage(app_locale));
@@ -176,6 +180,7 @@
 
 void SetLoadTimeDataDefaults(const std::string& app_locale,
                              ui::TemplateReplacements* replacements) {
+  (*replacements)["a11yenhanced"] = GetA11yEnhanced();
   (*replacements)["fontfamily"] = GetFontFamily();
   (*replacements)["fontsize"] = GetFontSize();
   (*replacements)["language"] = l10n_util::GetLanguage(app_locale);
@@ -210,6 +215,12 @@
   html->append("</style>");
 }
 
+std::string GetA11yEnhanced() {
+  return base::FeatureList::IsEnabled(features::kWebUIA11yEnhancements)
+             ? "a11y-enhanced"
+             : "";
+}
+
 std::string GetFontFamily() {
   std::string font_family = l10n_util::GetStringUTF8(IDS_WEB_FONT_FAMILY);
 
diff --git a/ui/base/webui/web_ui_util.h b/ui/base/webui/web_ui_util.h
index e0e49eab..bb1f6604 100644
--- a/ui/base/webui/web_ui_util.h
+++ b/ui/base/webui/web_ui_util.h
@@ -87,6 +87,7 @@
 // inline stylesheet.
 UI_BASE_EXPORT void AppendWebUiCssTextDefaults(std::string* html);
 
+UI_BASE_EXPORT std::string GetA11yEnhanced();
 // Get some common font styles for all of WebUI.
 UI_BASE_EXPORT std::string GetFontFamily();
 UI_BASE_EXPORT std::string GetFontSize();
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc
index 929e339..5fd3bb3 100644
--- a/ui/native_theme/native_theme.cc
+++ b/ui/native_theme/native_theme.cc
@@ -84,6 +84,15 @@
   return system_colors_;
 }
 
+base::Optional<SkColor> NativeTheme::GetSystemColorFromMap(
+    SystemThemeColor theme_color) const {
+  auto color = system_colors_.find(theme_color);
+  if (color != system_colors_.end())
+    return color->second;
+
+  return base::nullopt;
+}
+
 bool NativeTheme::HasDifferentSystemColors(
     const std::map<NativeTheme::SystemThemeColor, SkColor>& colors) const {
   return system_colors_ != colors;
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index 3604105..c1d4ad9a2 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -466,6 +466,9 @@
 
   virtual const std::map<SystemThemeColor, SkColor>& GetSystemColors() const;
 
+  base::Optional<SkColor> GetSystemColorFromMap(
+      SystemThemeColor theme_color) const;
+
   bool HasDifferentSystemColors(
       const std::map<SystemThemeColor, SkColor>& colors) const;
 
diff --git a/ui/views/widget/widget_hwnd_utils.cc b/ui/views/widget/widget_hwnd_utils.cc
index abd3b4e..0cccf60 100644
--- a/ui/views/widget/widget_hwnd_utils.cc
+++ b/ui/views/widget/widget_hwnd_utils.cc
@@ -42,7 +42,7 @@
   if (params.show_state == ui::SHOW_STATE_MINIMIZED)
     *style |= WS_MINIMIZE;
   if (!params.accept_events)
-    *ex_style |= WS_EX_TRANSPARENT | WS_EX_LAYERED;
+    *ex_style |= WS_EX_TRANSPARENT;
   DCHECK_NE(Widget::InitParams::ACTIVATABLE_DEFAULT, params.activatable);
   if (params.activatable == Widget::InitParams::ACTIVATABLE_NO)
     *ex_style |= WS_EX_NOACTIVATE;
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js b/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
index cc5403eb..03d15f0 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
@@ -166,8 +166,10 @@
           OncMojo.getIPConfigForType(properties, 'IPv4'));
       let ipv6 = this.getIPConfigUIProperties_(
           OncMojo.getIPConfigForType(properties, 'IPv6'));
+      // If connected and the IP address is automatic and set, show 'Loading' if
+      // the ipv6 address is not set.
       if (OncMojo.connectionStateIsConnected(properties.connectionState) &&
-          ipv4 && ipv4.ipAddress) {
+          this.automatic_ && ipv4 && ipv4.ipAddress) {
         ipv6 = ipv6 || {};
         ipv6.ipAddress = ipv6.ipAddress || this.i18n('loading');
       }
diff --git a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
index 40f2cb02..cb1dc2e 100644
--- a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
+++ b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
@@ -99,14 +99,14 @@
         overflow: auto;
       }
     </style>
-    <dialog id="dialog" on-cancel="onDialogCancel_" on-tap="onDialogTap_"
+    <dialog id="dialog" on-cancel="onDialogCancel_" on-click="onDialogTap_"
         on-close="onDialogClose_">
-      <div id="container" on-tap="onContainerTap_">
+      <div id="container" on-click="onContainerTap_">
         <div class="drawer-header">
           <!-- Hidden from a11y because this icon is decorative. Clicking closes
               the dialog, but screen reader users can do this by pressing ESC,
               so aria-hidden is OK here. -->
-          <iron-icon id="iconButton" icon="[[iconName]]" on-tap="onIconTap_"
+          <iron-icon id="iconButton" icon="[[iconName]]" on-click="onIconTap_"
               title="[[iconTitle]]" hidden="[[!iconName]]" aria-hidden="true">
           </iron-icon>
           <div id="heading" tabindex="-1">[[heading]]</div>
diff --git a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.html b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.html
index 12dfc7f..f889e4bb 100644
--- a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.html
+++ b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.html
@@ -13,12 +13,10 @@
         --cr-icon-button-icon-size: 20px;
         --cr-icon-button-size: 36px;
         --cr-icon-button-height: var(--cr-icon-button-size);
+        --cr-icon-button-transition: 150ms ease-in-out;
         --cr-icon-button-width: var(--cr-icon-button-size);
         /* Copied from paper-fab.html. Prevents square touch highlight. */
         -webkit-tap-highlight-color: transparent;
-        background-position: center;
-        background-repeat: no-repeat;
-        background-size: var(--cr-icon-button-icon-size);
         border-radius: 4px;
         color: var(--cr-icon-button-color);
         cursor: pointer;
@@ -40,25 +38,29 @@
         pointer-events: none;
       }
 
+      :host(.no-overlap) {
+        --cr-icon-button-margin-end: 0;
+        --cr-icon-button-margin-start: 0;
+      }
+
       :host-context([dir=rtl]) {
         transform: scaleX(-1);  /* Invert X: flip on the Y axis (aka mirror). */
       }
 
-      @media (prefers-color-scheme: dark) {
-        :host {
-          --cr-icon-button-color: var(--google-grey-refresh-500);
-        }
+      :host(:not([iron-icon])) #maskedImage {
+        -webkit-mask-image: var(--cr-icon-image);
+        -webkit-mask-position: center;
+        -webkit-mask-repeat: no-repeat;
+        -webkit-mask-size: var(--cr-icon-button-icon-size);
+        background-color: var(--cr-icon-button-color);
+        height: 100%;
+        transition: background-color var(--cr-icon-button-transition);
+        width: 100%;
+        z-index: 1;
       }
 
-      :host paper-ripple {
-        /* GG900 .15 is about GG700 .21 */
-        --paper-ripple-opacity: var(--cr-icon-button-ripple-opacity, .21);
-      }
-
-      @media (prefers-color-scheme: dark) {
-        :host paper-ripple {
-          --paper-ripple-opacity: var(--cr-icon-button-ripple-opacity, .4);
-        }
+      :host-context([a11y-enhanced]):host([ripple-showing_]) #maskedImage {
+        background-color: var(--cr-icon-button-focus-color, white);
       }
 
       #icon {
@@ -74,11 +76,61 @@
       }
 
       iron-icon {
+        --iron-icon-fill-color: var(--cr-icon-button-color);
         --iron-icon-height: var(--cr-icon-button-icon-size);
         --iron-icon-width: var(--cr-icon-button-icon-size);
+        transition: fill var(--cr-icon-button-transition);
+        z-index: 1;
+      }
+
+      :host-context([a11y-enhanced]):host([ripple-showing_]) iron-icon {
+        --iron-icon-fill-color: var(--cr-icon-button-focus-color, white);
+      }
+
+      paper-ripple {
+        /* GG900 .15 is about GG700 .21 */
+        --paper-ripple-opacity: var(--cr-icon-button-ripple-opacity, .21);
+      }
+
+
+      :host-context([a11y-enhanced]) paper-ripple {
+        --paper-ripple-opacity: 1;
+        opacity: 0;
+        transition: opacity var(--cr-icon-button-transition);
+      }
+
+      @media (prefers-color-scheme: dark) {
+        :host {
+          --cr-icon-button-color: var(--google-grey-refresh-500);
+        }
+
+        :host-context([a11y-enhanced]):host([ripple-showing_]) iron-icon {
+          --iron-icon-fill-color: var(--cr-icon-button-focus-color,
+              var(--google-grey-900));
+        }
+
+        :host(:not([iron-icon])) #maskedImage {
+          background-color: var(--cr-icon-button-color,
+              var(--google-grey-refresh-500));
+        }
+
+        :host-context([a11y-enhanced]):host([ripple-showing_]) #maskedImage {
+          background-color: var(--cr-icon-button-focus-color,
+              var(--google-grey-900));
+        }
+
+        paper-ripple {
+          --paper-ripple-opacity: var(--cr-icon-button-ripple-opacity, .4);
+        }
+      }
+
+      :host-context([a11y-enhanced]):host([ripple-showing_]) paper-ripple {
+        opacity: 1;
       }
     </style>
-    <div id="icon"></div>
+    <div id="icon">
+      <div id="maskedImage"></div>
+    </div>
   </template>
   <script src="cr_icon_button.js"></script>
 </dom-module>
diff --git a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js
index 5f103f62..d86cd00 100644
--- a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js
+++ b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js
@@ -44,6 +44,14 @@
     ironIcon: {
       type: String,
       observer: 'onIronIconChanged_',
+      reflectToAttribute: true,
+    },
+
+    /** @private */
+    rippleShowing_: {
+      type: Boolean,
+      value: false,
+      reflectToAttribute: true,
     },
   },
 
@@ -56,6 +64,7 @@
   listeners: {
     blur: 'hideRipple_',
     click: 'onClick_',
+    down: 'showRipple_',
     focus: 'showRipple_',
     keydown: 'onKeyDown_',
     keyup: 'onKeyUp_',
@@ -67,6 +76,7 @@
   hideRipple_: function() {
     if (this.hasRipple()) {
       this.getRipple().clear();
+      this.rippleShowing_ = false;
     }
   },
 
@@ -74,6 +84,7 @@
   showRipple_: function() {
     if (!this.noink && !this.disabled) {
       this.getRipple().showAndHoldDown();
+      this.rippleShowing_ = true;
     }
   },
 
diff --git a/ui/webui/resources/cr_elements/cr_icons_css.html b/ui/webui/resources/cr_elements/cr_icons_css.html
index f3fdcfb..aeddcc3c 100644
--- a/ui/webui/resources/cr_elements/cr_icons_css.html
+++ b/ui/webui/resources/cr_elements/cr_icons_css.html
@@ -4,166 +4,95 @@
 <dom-module id="cr-icons">
   <template>
     <style>
-      :host-context([dir=rtl]) .cr-icon {
-        transform: scaleX(-1);  /* Invert X: flip on the Y axis (aka mirror). */
+      .icon-arrow-back {
+        --cr-icon-image: url(../images/icon_arrow_back.svg);
+      }
+
+      .icon-arrow-dropdown {
+        --cr-icon-image: url(../images/icon_arrow_dropdown.svg);
+      }
+
+      .icon-cancel {
+        --cr-icon-image: url(../images/icon_cancel.svg);
+      }
+
+      .icon-clear {
+        --cr-icon-image: url(../images/icon_clear.svg);
+      }
+
+      .icon-delete-gray {
+        --cr-icon-image: url(../images/icon_delete_gray.svg);
+      }
+
+      .icon-picture-delete {
+        --cr-icon-image: url(../images/icon_picture_delete.svg);
+      }
+
+      .icon-expand-less {
+        --cr-icon-image: url(../images/icon_expand_less.svg);
+      }
+
+      .icon-expand-more {
+        --cr-icon-image: url(../images/icon_expand_more.svg);
+      }
+
+      .icon-external {
+        --cr-icon-image: url(../images/open_in_new.svg);
+      }
+
+      .icon-more-vert {
+        --cr-icon-image: url(../images/icon_more_vert.svg);
+      }
+
+      .icon-refresh {
+        --cr-icon-image: url(../images/icon_refresh.svg);
+      }
+
+      .icon-search {
+        --cr-icon-image: url(../images/icon_search.svg);
+      }
+
+      .icon-settings {
+        --cr-icon-image: url(../images/icon_settings.svg);
+      }
+
+      .icon-visibility {
+        --cr-icon-image: url(../images/icon_visibility.svg);
+      }
+
+      .icon-visibility-off {
+        --cr-icon-image: url(../images/icon_visibility_off.svg);
+      }
+
+      .subpage-arrow {
+        --cr-icon-image: url(../images/arrow_right.svg);
       }
 
       .cr-icon {
         @apply --cr-paper-icon-button-margin;
-        background-position: center;
-        background-repeat: no-repeat;
-        background-size: var(--cr-icon-size);
+        -webkit-mask-image: var(--cr-icon-image);
+        -webkit-mask-position: center;
+        -webkit-mask-repeat: no-repeat;
+        -webkit-mask-size: var(--cr-icon-size);
+        background-color: var(--google-grey-refresh-700);
         flex-shrink: 0;
         height: var(--cr-icon-ripple-size);
         user-select: none;
         width: var(--cr-icon-ripple-size);
       }
 
-      :-webkit-any(cr-icon-button, .cr-icon).no-overlap {
+      :host-context([dir=rtl]) .cr-icon {
+        transform: scaleX(-1);  /* Invert X: flip on the Y axis (aka mirror). */
+      }
+
+      .cr-icon.no-overlap {
         margin-inline-end: 0;
         margin-inline-start: 0;
       }
 
-      :-webkit-any(cr-icon-button, .cr-icon).icon-arrow-back {
-        background-image: url(../images/icon_arrow_back.svg);
-      }
       @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-arrow-back {
-          background-image: url(../images/dark/icon_arrow_back.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-cancel {
-        background-image: url(../images/icon_cancel.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-cancel {
-          background-image: url(../images/dark/icon_cancel.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-clear {
-        background-image: url(../images/icon_clear.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-clear {
-          background-image: url(../images/dark/icon_clear.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-delete-gray {
-        background-image: url(../images/icon_delete_gray.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-delete-gray {
-          background-image: url(../images/dark/icon_delete_gray.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-picture-delete {
-        background-image: url(../images/icon_picture_delete.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-picture-delete {
-          background-image: url(../images/dark/icon_picture_delete.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-expand-less {
-        background-image: url(../images/icon_expand_less.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-expand-less {
-          background-image: url(../images/dark/icon_expand_less.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-expand-more {
-        background-image: url(../images/icon_expand_more.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-expand-more {
-          background-image: url(../images/dark/icon_expand_more.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-external {
-        background-image: url(../images/open_in_new.svg);
-      }
-      /* Open in new is the same for light and dark mode. */
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-more-vert {
-        background-image: url(../images/icon_more_vert.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-more-vert {
-          background-image: url(../images/dark/icon_more_vert.svg);
-        }
-      }
-      :-webkit-any(cr-icon-button, .cr-icon).icon-more-vert-light-mode {
-        background-image: url(../images/icon_more_vert.svg);
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-refresh {
-        background-image: url(../images/icon_refresh.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-refresh {
-          background-image: url(../images/dark/icon_refresh.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-settings {
-        background-image: url(../images/icon_settings.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-settings {
-          background-image: url(../images/dark/icon_settings.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-search {
-        background-image: url(../images/icon_search.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-search {
-          background-image: url(../images/dark/icon_search.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-arrow-dropdown {
-        background-image: url(../images/icon_arrow_dropdown.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-arrow-dropdown {
-          background-image: url(../images/dark/icon_arrow_dropdown.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).subpage-arrow {
-        background-image: url(../images/arrow_right.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).subpage-arrow {
-          background-image: url(../images/dark/arrow_right.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-visibility {
-        background-image: url(../images/icon_visibility.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-visibility {
-          background-image: url(../images/dark/icon_visibility.svg);
-        }
-      }
-
-      :-webkit-any(cr-icon-button, .cr-icon).icon-visibility-off {
-        background-image: url(../images/icon_visibility_off.svg);
-      }
-      @media (prefers-color-scheme: dark) {
-        :-webkit-any(cr-icon-button, .cr-icon).icon-visibility-off {
-          background-image: url(../images/dark/icon_visibility_off.svg);
+        .cr-icon {
+          background-color: var(--google-grey-refresh-500);
         }
       }
     </style>
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
index ace3c147..a899d48 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
@@ -12,6 +12,7 @@
   <template>
     <style include="cr-icons cr-hidden-style">
       :host {
+        --cr-icon-button-focus-color: var(--google-blue-700);
         align-items: center;
         background-color: var(--google-blue-700);
         color: #fff;
@@ -21,6 +22,9 @@
 
       @media (prefers-color-scheme: dark) {
         :host {
+          /* Toolbar background color rgba(255, 255, 255, .04) added to body
+             background color --google-grey-900. */
+          --cr-icon-button-focus-color: #28292c;
           background-color: rgba(255, 255, 255, .04);
           border-bottom: var(--cr-separator-line);
           box-sizing: border-box;
@@ -167,7 +171,7 @@
           <template is="dom-if" if="[[showMenuPromo]]">
             <div id="menuPromo" role="tooltip">
               [[menuPromo]]
-              <button id="closePromo" on-tap="onClosePromoTap_"
+              <button id="closePromo" on-click="onClosePromoTap_"
                   aria-label$="[[closeMenuPromo]]">&#x2715;</button>
             </paper-tooltip>
           </template>
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
index 7d4d186..3eb5dbd 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
@@ -31,15 +31,14 @@
 
       @media (prefers-color-scheme: light) {
         cr-icon-button {
-          --cr-icon-button-color: currentColor;
-          --iron-icon-fill-color:
-              var(--cr-toolbar-search-field-input-color, white);
+          --cr-icon-button-color: var(--cr-toolbar-search-field-input-color,
+              white);
         }
       }
 
       @media (prefers-color-scheme: dark) {
         cr-icon-button {
-          --iron-icon-fill-color: var(--cr-toolbar-search-field-input-color,
+          --cr-icon-button-color: var(--cr-toolbar-search-field-input-color,
               var(--google-grey-refresh-500));
         }
       }
diff --git a/ui/webui/resources/cr_elements/shared_style_css.html b/ui/webui/resources/cr_elements/shared_style_css.html
index 292378f..8b18b8e 100644
--- a/ui/webui/resources/cr_elements/shared_style_css.html
+++ b/ui/webui/resources/cr_elements/shared_style_css.html
@@ -25,16 +25,6 @@
         cursor: pointer;
       }
 
-      .subpage-arrow,
-      .icon-external {
-        display: none;
-      }
-
-      [actionable] :-webkit-any(.subpage-arrow, .icon-external),
-      [actionable]:-webkit-any(.subpage-arrow, .icon-external) {
-        display: block;
-      }
-
       /* Horizontal rule line. */
       .hr {
         border-top: var(--cr-separator-line);
diff --git a/ui/webui/resources/cr_elements_images.grdp b/ui/webui/resources/cr_elements_images.grdp
index 7c12c157..1f0b8227 100644
--- a/ui/webui/resources/cr_elements_images.grdp
+++ b/ui/webui/resources/cr_elements_images.grdp
@@ -9,46 +9,8 @@
            file="images/arrow_right.svg" type="BINDATA" compress="gzip" />
   <include name="IDR_WEBUI_IMAGES_DARK_ARROW_DOWN"
            file="images/dark/arrow_down.svg" type="BINDATA" compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ARROW_RIGHT"
-           file="images/dark/arrow_right.svg" type="BINDATA" compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_ARROW_BACK"
-           file="images/dark/icon_arrow_back.svg" type="BINDATA"
-           compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_ARROW_DROPDOWN"
-           file="images/dark/icon_arrow_dropdown.svg" type="BINDATA"
-           compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_CANCEL"
-           file="images/dark/icon_cancel.svg" type="BINDATA" compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_CLEAR"
-           file="images/dark/icon_clear.svg" type="BINDATA" compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_DELETE_GRAY"
-           file="images/dark/icon_delete_gray.svg" type="BINDATA"
-           compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_EXPAND_LESS"
-           file="images/dark/icon_expand_less.svg" type="BINDATA"
-           compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_EXPAND_MORE"
-           file="images/dark/icon_expand_more.svg" type="BINDATA"
-           compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_MORE_VERT"
-           file="images/dark/icon_more_vert.svg" type="BINDATA"
-           compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_PICTURE_DELETE"
-           file="images/dark/icon_picture_delete.svg" type="BINDATA"
-           compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_REFRESH"
-           file="images/dark/icon_refresh.svg" type="BINDATA" compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_SETTINGS"
-           file="images/dark/icon_settings.svg" type="BINDATA"
-           compress="gzip" />
   <include name="IDR_WEBUI_IMAGES_DARK_ICON_SEARCH"
            file="images/dark/icon_search.svg" type="BINDATA" compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_VISIBILITY"
-           file="images/dark/icon_visibility.svg" type="BINDATA"
-           compress="gzip" />
-  <include name="IDR_WEBUI_IMAGES_DARK_ICON_VISIBILITY_OFF"
-           file="images/dark/icon_visibility_off.svg" type="BINDATA"
-           compress="gzip" />
   <include name="IDR_WEBUI_IMAGES_ICON_ARROW_BACK"
            file="images/icon_arrow_back.svg" type="BINDATA" compress="gzip" />
   <include name="IDR_WEBUI_IMAGES_ICON_ARROW_DROPDOWN"
diff --git a/ui/webui/resources/images/dark/arrow_right.svg b/ui/webui/resources/images/dark/arrow_right.svg
deleted file mode 100644
index a1e71de..0000000
--- a/ui/webui/resources/images/dark/arrow_right.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#9aa0a6"><path d="M10 7l5 5-5 5z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_arrow_back.svg b/ui/webui/resources/images/dark/icon_arrow_back.svg
deleted file mode 100644
index 5747b495..0000000
--- a/ui/webui/resources/images/dark/icon_arrow_back.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#9aa0a6"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_arrow_dropdown.svg b/ui/webui/resources/images/dark/icon_arrow_dropdown.svg
deleted file mode 100644
index e68ecf14..0000000
--- a/ui/webui/resources/images/dark/icon_arrow_dropdown.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#9aa0a6"><path d="M7 10l5 5 5-5z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_cancel.svg b/ui/webui/resources/images/dark/icon_cancel.svg
deleted file mode 100644
index 8cc110e8..0000000
--- a/ui/webui/resources/images/dark/icon_cancel.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 2 4" fill="#9aa0a6"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_clear.svg b/ui/webui/resources/images/dark/icon_clear.svg
deleted file mode 100644
index e54b804..0000000
--- a/ui/webui/resources/images/dark/icon_clear.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#9aa0a6"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_delete_gray.svg b/ui/webui/resources/images/dark/icon_delete_gray.svg
deleted file mode 100644
index fe178de..0000000
--- a/ui/webui/resources/images/dark/icon_delete_gray.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#9aa0a6"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_expand_less.svg b/ui/webui/resources/images/dark/icon_expand_less.svg
deleted file mode 100644
index 2ff69ede..0000000
--- a/ui/webui/resources/images/dark/icon_expand_less.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" fill="#9aa0a6" width="24" height="24"><path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_expand_more.svg b/ui/webui/resources/images/dark/icon_expand_more.svg
deleted file mode 100644
index 9ae4310..0000000
--- a/ui/webui/resources/images/dark/icon_expand_more.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" fill="#9aa0a6" width="24" height="24"><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_more_vert.svg b/ui/webui/resources/images/dark/icon_more_vert.svg
deleted file mode 100644
index 1e86d68ec..0000000
--- a/ui/webui/resources/images/dark/icon_more_vert.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#9aa0a6"><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_picture_delete.svg b/ui/webui/resources/images/dark/icon_picture_delete.svg
deleted file mode 100644
index f582d05..0000000
--- a/ui/webui/resources/images/dark/icon_picture_delete.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#e8eaed"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_refresh.svg b/ui/webui/resources/images/dark/icon_refresh.svg
deleted file mode 100644
index 6542cec..0000000
--- a/ui/webui/resources/images/dark/icon_refresh.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#9aa0a6"><path d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_settings.svg b/ui/webui/resources/images/dark/icon_settings.svg
deleted file mode 100644
index 33a55b9e6..0000000
--- a/ui/webui/resources/images/dark/icon_settings.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#9aa0a6"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.488.488 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_visibility.svg b/ui/webui/resources/images/dark/icon_visibility.svg
deleted file mode 100644
index 2352cc47..0000000
--- a/ui/webui/resources/images/dark/icon_visibility.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#9aa0a6"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/images/dark/icon_visibility_off.svg b/ui/webui/resources/images/dark/icon_visibility_off.svg
deleted file mode 100644
index 9cd712c..0000000
--- a/ui/webui/resources/images/dark/icon_visibility_off.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#9aa0a6"><path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46A11.804 11.804 0 0 0 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/js/chromeos/onc_mojo.js b/ui/webui/resources/js/chromeos/onc_mojo.js
index e979779..1bf6ecc 100644
--- a/ui/webui/resources/js/chromeos/onc_mojo.js
+++ b/ui/webui/resources/js/chromeos/onc_mojo.js
@@ -758,6 +758,11 @@
       }
     }
 
+    // Only populate static ip config properties for IPv4.
+    if (desiredType != 'IPv4') {
+      return undefined;
+    }
+
     if (!ipConfig) {
       ipConfig = /** @type {!mojom.IPConfigProperties} */ ({routingPrefix: 0});
     }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
index 356f54e..908a9c7 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
@@ -5,8 +5,6 @@
 package org.chromium.weblayer_private;
 
 import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.Resources;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -63,16 +61,9 @@
         public void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix) {}
     }
 
-    public BrowserControllerImpl(Context clientContext, Context implContext, ProfileImpl profile) {
+    public BrowserControllerImpl(Context context, ProfileImpl profile) {
         mProfile = profile;
 
-        Context context = new ContextWrapper(clientContext) {
-            @Override
-            public Resources getResources() {
-                // Always use resources from the implementation APK.
-                return implContext.getResources();
-            }
-        };
         // Use false to disable listening to activity state.
         // TODO: this should *not* use ActivityWindowAndroid as that relies on Activity, and this
         // code should not assume it is supplied an Activity.
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
index c6b6624..3e76f82 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
@@ -33,10 +33,8 @@
     }
 
     @Override
-    public IBrowserController createBrowserController(
-            IObjectWrapper clientContext, IObjectWrapper implContext) {
-        return new BrowserControllerImpl(ObjectWrapper.unwrap(clientContext, Context.class),
-                ObjectWrapper.unwrap(implContext, Context.class), this);
+    public IBrowserController createBrowserController(IObjectWrapper context) {
+        return new BrowserControllerImpl(ObjectWrapper.unwrap(context, Context.class), this);
     }
 
     long getNativeProfile() {
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
index 645b24b5..e38fe12 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -4,10 +4,7 @@
 
 package org.chromium.weblayer_private;
 
-import android.app.Application;
 import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.Resources;
 import android.os.IBinder;
 import android.util.AndroidRuntimeException;
 
@@ -35,8 +32,8 @@
     private static final String COMMAND_LINE_FILE = "/data/local/tmp/weblayer-command-line";
 
     @UsedByReflection("WebLayer")
-    public static IBinder create(Application application, Context implContext) {
-        return new WebLayerImpl(application, implContext);
+    public static IBinder create(Context context) {
+        return new WebLayerImpl(context);
     }
 
     @Override
@@ -44,18 +41,12 @@
         return new ProfileImpl(path);
     }
 
-    private WebLayerImpl(Application application, Context implContext) {
-        ContextUtils.initApplicationContext(new ContextWrapper(application) {
-            @Override
-            public Resources getResources() {
-                // Always use resources from the implementation APK.
-                return implContext.getResources();
-            }
-        });
+    private WebLayerImpl(Context context) {
+        ContextUtils.initApplicationContext(context);
         ResourceBundle.setNoAvailableLocalePaks();
         PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
 
-        ChildProcessCreationParams.set(application.getPackageName(), false /* isExternalService */,
+        ChildProcessCreationParams.set(context.getPackageName(), false /* isExternalService */,
                 LibraryProcessType.PROCESS_WEBLAYER_CHILD, true /* bindToCaller */,
                 false /* ignoreVisibilityForImportance */,
                 "org.chromium.weblayer.ChildProcessService$Privileged",
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl
index 3099c12..3e8e2af 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl
@@ -14,9 +14,7 @@
 
   /**
    * Creates a new IBrowserController.
-   * @param clientContext Context from the client
-   * @param implContext Context that refers to the weblayer implementation
+   * @param context Context that refers the the weblayer implementation
    */
-  IBrowserController createBrowserController(in IObjectWrapper clientContext,
-                                             in IObjectWrapper implContext) = 2;
+  IBrowserController createBrowserController(in IObjectWrapper context) = 2;
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/ChildProcessService.java b/weblayer/public/java/org/chromium/weblayer/ChildProcessService.java
index 9514db7..932d7434 100644
--- a/weblayer/public/java/org/chromium/weblayer/ChildProcessService.java
+++ b/weblayer/public/java/org/chromium/weblayer/ChildProcessService.java
@@ -5,9 +5,7 @@
 package org.chromium.weblayer;
 
 import android.app.Service;
-import android.content.ComponentCallbacks;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -33,24 +31,7 @@
                     (IBinder) remoteContext.getClassLoader()
                             .loadClass("org.chromium.weblayer_private.ChildProcessServiceImpl")
                             .getMethod("create", Service.class, Context.class)
-                            .invoke(null, this, new ContextWrapper(remoteContext) {
-                                @Override
-                                public Context getApplicationContext() {
-                                    return remoteContext;
-                                }
-
-                                @Override
-                                public void registerComponentCallbacks(
-                                        ComponentCallbacks callback) {
-                                    getApplication().registerComponentCallbacks(callback);
-                                }
-
-                                @Override
-                                public void unregisterComponentCallbacks(
-                                        ComponentCallbacks callback) {
-                                    getApplication().unregisterComponentCallbacks(callback);
-                                }
-                            }));
+                            .invoke(null, this, remoteContext));
             mImpl.onCreate();
         } catch (Exception e) {
             throw new APICallException(e);
diff --git a/weblayer/public/java/org/chromium/weblayer/Profile.java b/weblayer/public/java/org/chromium/weblayer/Profile.java
index a64d52e..10d5a2d 100644
--- a/weblayer/public/java/org/chromium/weblayer/Profile.java
+++ b/weblayer/public/java/org/chromium/weblayer/Profile.java
@@ -45,9 +45,8 @@
 
     public BrowserFragmentImpl createBrowserFragment(Context context) {
         try {
-            return new BrowserFragmentImpl(
-                    mImpl.createBrowserController(ObjectWrapper.wrap(context),
-                            ObjectWrapper.wrap(WebLayer.createRemoteContext(context))));
+            return new BrowserFragmentImpl(mImpl.createBrowserController(
+                    ObjectWrapper.wrap(WebLayer.createRemoteContext(context))));
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
diff --git a/weblayer/public/java/org/chromium/weblayer/WebLayer.java b/weblayer/public/java/org/chromium/weblayer/WebLayer.java
index 9b96d146..78152de4 100644
--- a/weblayer/public/java/org/chromium/weblayer/WebLayer.java
+++ b/weblayer/public/java/org/chromium/weblayer/WebLayer.java
@@ -5,10 +5,13 @@
 package org.chromium.weblayer;
 
 import android.app.Application;
+import android.content.ComponentCallbacks;
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -65,8 +68,8 @@
             mImpl = IWebLayer.Stub.asInterface(
                     (IBinder) remoteContext.getClassLoader()
                             .loadClass("org.chromium.weblayer_private.WebLayerImpl")
-                            .getMethod("create", Application.class, Context.class)
-                            .invoke(null, application, remoteContext));
+                            .getMethod("create", Context.class)
+                            .invoke(null, remoteContext));
         } catch (Exception e) {
             throw new APICallException(e);
         }
@@ -97,14 +100,49 @@
      * Creates a Context for the remote (weblayer implementation) side.
      */
     static Context createRemoteContext(Context localContext) {
+        Context remoteContext;
         try {
             // TODO(cduvall): Might want to cache the remote context so we don't need to call into
             // package manager more than we need to.
-            return localContext.createPackageContext(getImplPackageName(localContext),
+            remoteContext = localContext.createPackageContext(getImplPackageName(localContext),
                     Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
         } catch (NameNotFoundException e) {
             throw new AndroidRuntimeException(e);
         }
+        return wrapContext(localContext, remoteContext);
+    }
+
+    private static Context wrapContext(Context localContext, Context remoteContext) {
+        return new ContextWrapper(localContext) {
+            @Override
+            public Context getApplicationContext() {
+                if (getBaseContext().getApplicationContext() == getBaseContext()) return this;
+                return wrapContext(getBaseContext().getApplicationContext(), remoteContext);
+            }
+
+            @Override
+            public Resources getResources() {
+                return remoteContext.getResources();
+            }
+
+            @Override
+            public ClassLoader getClassLoader() {
+                return remoteContext.getClassLoader();
+            }
+
+            @Override
+            public void registerComponentCallbacks(ComponentCallbacks callback) {
+                // We have to override registerComponentCallbacks and unregisterComponentCallbacks
+                // since they call getApplicationContext().[un]registerComponentCallbacks()
+                // which causes us to go into a loop.
+                getBaseContext().registerComponentCallbacks(callback);
+            }
+
+            @Override
+            public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+                getBaseContext().unregisterComponentCallbacks(callback);
+            }
+        };
     }
 
     private static String getImplPackageName(Context localContext)